Date: Wed, 20 Apr 2016 21:59:43 +0200
Subject: [PATCH 0046/2085] Use full version numbers in doc/compress.html
---
bin/release | 4 +---
doc/compress.html | 32 ++++++++++++++++----------------
2 files changed, 17 insertions(+), 19 deletions(-)
diff --git a/bin/release b/bin/release
index 2d78557fd0..18c5cdafc7 100755
--- a/bin/release
+++ b/bin/release
@@ -32,11 +32,9 @@ if (bumpOnly) process.exit(0);
child.exec("bash bin/authors.sh", function(){});
-var simple = number.slice(0, number.lastIndexOf("."));
-
rewrite("doc/compress.html", function(cmp) {
return cmp.replace(/HEAD<\/option>/,
- " HEAD \n " + simple + " ");
+ "HEAD \n " + number + " ");
});
rewrite("index.html", function(index) {
diff --git a/doc/compress.html b/doc/compress.html
index 1e507c6c7a..78014a62fa 100644
--- a/doc/compress.html
+++ b/doc/compress.html
@@ -36,22 +36,22 @@ Script compression helper
Version:
HEAD
- 5.14
- 5.13
- 5.13
- 5.12
- 5.11
- 5.10
- 5.9
- 5.8
- 5.7
- 5.6
- 5.5
- 5.4
- 5.3
- 5.2
- 5.1
- 5.0
+ 5.14.0
+ 5.13.2
+ 5.13.0
+ 5.12.0
+ 5.11.0
+ 5.10.0
+ 5.9.0
+ 5.8.0
+ 5.7.0
+ 5.6.0
+ 5.5.0
+ 5.4.0
+ 5.3.0
+ 5.2.0
+ 5.1.0
+ 5.0.0
4.13
4.12
4.11
From c50b08ba4411a68dce8050f3d283fb9cc2b5f8cf Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Wed, 20 Apr 2016 22:07:27 +0200
Subject: [PATCH 0047/2085] Mark release 5.14.2
---
CHANGELOG.md | 10 ++++++++++
doc/compress.html | 1 +
doc/manual.html | 2 +-
index.html | 2 +-
lib/codemirror.js | 2 +-
package.json | 2 +-
6 files changed, 15 insertions(+), 4 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8451136a05..f6f3123732 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,13 @@
+## 5.14.2 (2016-04-20)
+
+### Bugfixes
+
+Push a new package to NPM due to an [NPM bug](https://github.com/npm/npm/issues/5082) omitting the LICENSE file in 5.14.0.
+
+Set `dataTransfer.effectAllowed` in `dragstart` handler to help browsers use the right drag icon.
+
+Add the [mbox mode](http://codemirror.net/mode/mbox/index.html) to `mode/meta.js`.
+
## 5.14.0 (2016-04-20)
### Bugfixes
diff --git a/doc/compress.html b/doc/compress.html
index 78014a62fa..479126921c 100644
--- a/doc/compress.html
+++ b/doc/compress.html
@@ -36,6 +36,7 @@ Script compression helper
Version:
HEAD
+ 5.14.2
5.14.0
5.13.2
5.13.0
diff --git a/doc/manual.html b/doc/manual.html
index 25bee51ffa..8d698b5732 100644
--- a/doc/manual.html
+++ b/doc/manual.html
@@ -69,7 +69,7 @@
User manual and reference guide
- version 5.14.1
+ version 5.14.2
CodeMirror is a code-editor component that can be embedded in
diff --git a/index.html b/index.html
index 0893e0f8fb..99c649141f 100644
--- a/index.html
+++ b/index.html
@@ -96,7 +96,7 @@
This is CodeMirror
- Get the current version:
5.14.0 .
+ Get the current version:
5.14.2 .
You can see the
code or
read the
release notes .
There is a
minification helper .
diff --git a/lib/codemirror.js b/lib/codemirror.js
index f93c95e93a..aa0d3b4ffb 100644
--- a/lib/codemirror.js
+++ b/lib/codemirror.js
@@ -8892,7 +8892,7 @@
// THE END
- CodeMirror.version = "5.14.1";
+ CodeMirror.version = "5.14.2";
return CodeMirror;
});
diff --git a/package.json b/package.json
index 1c3ac8843b..216cf3b4c4 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "codemirror",
- "version":"5.14.1",
+ "version":"5.14.2",
"main": "lib/codemirror.js",
"description": "Full-featured in-browser code editor",
"license": "MIT",
From 6164344b260b9af76826fd71b6923684a156984d Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Wed, 20 Apr 2016 22:08:13 +0200
Subject: [PATCH 0048/2085] Bump version number post-5.14.2
---
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 8d698b5732..4e5f5cede2 100644
--- a/doc/manual.html
+++ b/doc/manual.html
@@ -69,7 +69,7 @@
User manual and reference guide
- version 5.14.2
+ version 5.14.3
CodeMirror is a code-editor component that can be embedded in
diff --git a/lib/codemirror.js b/lib/codemirror.js
index aa0d3b4ffb..238aa4380a 100644
--- a/lib/codemirror.js
+++ b/lib/codemirror.js
@@ -8892,7 +8892,7 @@
// THE END
- CodeMirror.version = "5.14.2";
+ CodeMirror.version = "5.14.3";
return CodeMirror;
});
diff --git a/package.json b/package.json
index 216cf3b4c4..e7d64b40c5 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "codemirror",
- "version":"5.14.2",
+ "version":"5.14.3",
"main": "lib/codemirror.js",
"description": "Full-featured in-browser code editor",
"license": "MIT",
From 255d019adb8a62eaad6740a6d81b6e4f80f891f0 Mon Sep 17 00:00:00 2001
From: Basarat Ali Syed
Date: Fri, 22 Apr 2016 05:02:01 +1000
Subject: [PATCH 0049/2085] [real-world uses] Add alm.tools
---
doc/realworld.html | 1 +
1 file changed, 1 insertion(+)
diff --git a/doc/realworld.html b/doc/realworld.html
index 31139f2e99..4d8871d4b6 100644
--- a/doc/realworld.html
+++ b/doc/realworld.html
@@ -26,6 +26,7 @@ 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)
From 11cd0c68c9bead2762e767181e9e8ec2e0a47579 Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Fri, 22 Apr 2016 11:01:08 +0200
Subject: [PATCH 0050/2085] [markdown mode] Fix some issues with link matching
Closes #3981
---
mode/markdown/markdown.js | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/mode/markdown/markdown.js b/mode/markdown/markdown.js
index 8bd3cc7a25..8995fd5f5a 100644
--- a/mode/markdown/markdown.js
+++ b/mode/markdown/markdown.js
@@ -437,13 +437,13 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
return tokenTypes.image;
}
- if (ch === '[' && stream.match(/.*\](\(.*\)| ?\[.*\])/, false)) {
+ if (ch === '[' && stream.match(/[^\]]*\](\(.*\)| ?\[.*?\])/, false)) {
state.linkText = true;
if (modeCfg.highlightFormatting) state.formatting = "link";
return getType(state);
}
- if (ch === ']' && state.linkText && stream.match(/\(.*\)| ?\[.*\]/, false)) {
+ if (ch === ']' && state.linkText && stream.match(/\(.*?\)| ?\[.*?\]/, false)) {
if (modeCfg.highlightFormatting) state.formatting = "link";
var type = getType(state);
state.linkText = false;
From 045882730f65ff0e853e54a32d729fd66e5a7489 Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Tue, 26 Apr 2016 10:08:57 +0200
Subject: [PATCH 0051/2085] [vim bindings] Prevent line-wrapping between prompt
prefix and prompt input
Issue #3991
---
keymap/vim.js | 15 ++++-----------
1 file changed, 4 insertions(+), 11 deletions(-)
diff --git a/keymap/vim.js b/keymap/vim.js
index b2bee5a1c7..4278ee9798 100644
--- a/keymap/vim.js
+++ b/keymap/vim.js
@@ -3782,17 +3782,10 @@
}
}
function makePrompt(prefix, desc) {
- var raw = '';
- if (prefix) {
- raw += '' + prefix + ' ';
- }
- raw += ' ' +
- '';
- 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 0052/2085] [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 cd8129f757..b4844baf02 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 0053/2085] [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 b4844baf02..ed6af2c83c 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 0054/2085] 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 4d8871d4b6..7b7eeeaf1a 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 0055/2085] [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 695d5ceff3..cac358a5df 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 0056/2085] [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 cac358a5df..17c7f2ac19 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 0057/2085] 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 238aa4380a..3d78108fd0 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 0058/2085] [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 3cf67413ab..d9b64ba9af 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 0059/2085] 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 3d78108fd0..3c154b9516 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 0060/2085] 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 4e5f5cede2..8c99a99efa 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 0061/2085] 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 6b51aba07e..9618f8e42b 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 86def73ebb..20ae1e19ca 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 6574fbd569..d74083ee1a 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 1db069a90e..51ed105aca 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 8995fd5f5a..fd8f647fd2 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 8e87b59eca..6c72074666 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 164464d066..991a97efcd 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 0062/2085] 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 3c154b9516..f00f2da966 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 0063/2085] [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 fd8f647fd2..2e2b5fe46c 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 0064/2085] [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 2e2b5fe46c..37329c2385 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 e2b3a81527..e76eae9027 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 0065/2085] [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 22494467b2..57ba812d72 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 0066/2085] [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 17c7f2ac19..8f6757c58e 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 c26003266c..bea85b8693 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 0067/2085] [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 102e8f8269..9aed0efccc 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 0068/2085] 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 f00f2da966..33928bc8ea 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 0069/2085] [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 8f6757c58e..aadab9e114 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 0070/2085] 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 33928bc8ea..2fe344e071 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 0071/2085] 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 2fe344e071..37a0fd92dd 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 0072/2085] 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 37a0fd92dd..fe51bd3bdc 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 0073/2085] [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 414a9a0e06..f097cfe345 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 0074/2085] [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 f27582ef86..9ed33cffef 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 0075/2085] [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 e9656e3dcc..ea7bd01d84 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 0076/2085] [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 1cf66a9fa9..4bd815eb54 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 0077/2085] 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 d9b64ba9af..c1749e7196 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 0078/2085] 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 3eb9d8eae7..af7fce2a82 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 1605f6c2a9..13c0f0cd88 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 504727f38c..f8c67b8976 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 0079/2085] 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 fe51bd3bdc..0a343efaa8 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 0080/2085] 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 0a343efaa8..a51a525e2d 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 0081/2085] 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 a51a525e2d..c289defd14 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 0082/2085] [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 fa5721d5d0..a929321d2f 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 0083/2085] 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 c289defd14..6cca44657c 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 0084/2085] 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 6cca44657c..99df533b9b 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 0085/2085] [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 aadab9e114..a37921fdae 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 0086/2085] [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 32ba2f35ea..23f3e03f81 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 0087/2085] [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 a929321d2f..5f3b3d64df 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 0088/2085] [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 62c4f68d64..ed8f6d856c 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 0089/2085] 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 5f3b3d64df..ca875411a2 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 0090/2085] 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 f64cb6d103..5e373a0202 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 f6f3123732..562034a186 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 479126921c..061c3c6971 100644
--- a/doc/compress.html
+++ b/doc/compress.html
@@ -36,6 +36,7 @@ Script compression helper
Version:
HEAD
+ 5.15.0
5.14.2
5.14.0
5.13.2
diff --git a/doc/manual.html b/doc/manual.html
index 8c99a99efa..99d01e7394 100644
--- a/doc/manual.html
+++ b/doc/manual.html
@@ -69,7 +69,7 @@
User manual and reference guide
- version 5.14.3
+ version 5.15.0
CodeMirror is a code-editor component that can be embedded in
diff --git a/doc/releases.html b/doc/releases.html
index a8a7408b09..587406b292 100644
--- a/doc/releases.html
+++ b/doc/releases.html
@@ -30,6 +30,21 @@
Release notes and version history
Version 5.x
+ 20-05-2016: Version 5.15.0 :
+
+
+ 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.
+ Explicitly bind Ctrl-O on OS X to make that binding (“open line”) act as expected.
+ Pasting linewise-copied content when there is no selection now inserts the lines above the current line.
+ markdown mode : Fix several issues in matching link targets.
+ clike mode : Improve indentation of C++ template declarations.
+ javascript mode : Support async/await and improve support for TypeScript type syntax.
+
+
20-04-2016: Version 5.14.0 :
diff --git a/index.html b/index.html
index 99c649141f..eb8847f341 100644
--- a/index.html
+++ b/index.html
@@ -96,7 +96,7 @@ This is CodeMirror
- Get the current version:
5.14.2 .
+ Get the current version:
5.15.0 .
You can see the
code or
read the
release notes .
There is a
minification helper .
diff --git a/lib/codemirror.js b/lib/codemirror.js
index 99df533b9b..8944bea502 100644
--- a/lib/codemirror.js
+++ b/lib/codemirror.js
@@ -8908,7 +8908,7 @@
// THE END
- CodeMirror.version = "5.14.3";
+ CodeMirror.version = "5.15.0";
return CodeMirror;
});
diff --git a/package.json b/package.json
index e7d64b40c5..f9539664ec 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "codemirror",
- "version":"5.14.3",
+ "version":"5.15.0",
"main": "lib/codemirror.js",
"description": "Full-featured in-browser code editor",
"license": "MIT",
From ac3530e32682618c59002fab33b84be2f6b8728e Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Fri, 20 May 2016 12:03:08 +0200
Subject: [PATCH 0091/2085] Bump version number post-5.15.0
---
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 99d01e7394..ab4810a484 100644
--- a/doc/manual.html
+++ b/doc/manual.html
@@ -69,7 +69,7 @@
User manual and reference guide
- version 5.15.0
+ version 5.15.1
CodeMirror is a code-editor component that can be embedded in
diff --git a/lib/codemirror.js b/lib/codemirror.js
index 8944bea502..2b57b0f780 100644
--- a/lib/codemirror.js
+++ b/lib/codemirror.js
@@ -8908,7 +8908,7 @@
// THE END
- CodeMirror.version = "5.15.0";
+ CodeMirror.version = "5.15.1";
return CodeMirror;
});
diff --git a/package.json b/package.json
index f9539664ec..58d703f151 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "codemirror",
- "version":"5.15.0",
+ "version":"5.15.1",
"main": "lib/codemirror.js",
"description": "Full-featured in-browser code editor",
"license": "MIT",
From 0d4374d10ee32be30796de780311aaca8e4fc6d0 Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Fri, 20 May 2016 20:58:40 +0200
Subject: [PATCH 0092/2085] Revert part of 9ad383797c28
It breaks document tree rebalancing.
Closes #4032
---
lib/codemirror.js | 42 ++++++++++++++++++------------------------
1 file changed, 18 insertions(+), 24 deletions(-)
diff --git a/lib/codemirror.js b/lib/codemirror.js
index 2b57b0f780..937ce05a28 100644
--- a/lib/codemirror.js
+++ b/lib/codemirror.js
@@ -7342,31 +7342,25 @@
},
// When a node has grown, check whether it should be split.
maybeSpill: function() {
+ if (this.children.length <= 10) return;
var me = this;
- 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();
+ 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();
},
iterN: function(at, n, op) {
for (var i = 0; i < this.children.length; ++i) {
From 8097c7e75ce7ef0512debe9967d7568626106e53 Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Fri, 20 May 2016 21:00:19 +0200
Subject: [PATCH 0093/2085] Mark release 5.15.2
---
CHANGELOG.md | 6 ++++++
doc/compress.html | 1 +
doc/manual.html | 2 +-
doc/releases.html | 6 ++++++
index.html | 2 +-
lib/codemirror.js | 2 +-
package.json | 2 +-
7 files changed, 17 insertions(+), 4 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 562034a186..146af30edd 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,9 @@
+## 5.15.2 (2016-05-20)
+
+### Bugfixes
+
+Fix a critical document corruption bug that occurs when a document is gradually grown.
+
## 5.15.0 (2016-05-20)
### Bugfixes
diff --git a/doc/compress.html b/doc/compress.html
index 061c3c6971..fe27e025cc 100644
--- a/doc/compress.html
+++ b/doc/compress.html
@@ -36,6 +36,7 @@ Script compression helper
Version:
HEAD
+ 5.15.2
5.15.0
5.14.2
5.14.0
diff --git a/doc/manual.html b/doc/manual.html
index ab4810a484..3f898bd9ff 100644
--- a/doc/manual.html
+++ b/doc/manual.html
@@ -69,7 +69,7 @@
User manual and reference guide
- version 5.15.1
+ version 5.15.2
CodeMirror is a code-editor component that can be embedded in
diff --git a/doc/releases.html b/doc/releases.html
index 587406b292..594298ef6a 100644
--- a/doc/releases.html
+++ b/doc/releases.html
@@ -30,6 +30,12 @@
Release notes and version history
Version 5.x
+ 20-05-2016: Version 5.15.2 :
+
+
+ Fix a critical document corruption bug that occurs when a document is gradually grown.
+
+
20-05-2016: Version 5.15.0 :
diff --git a/index.html b/index.html
index eb8847f341..e987926921 100644
--- a/index.html
+++ b/index.html
@@ -96,7 +96,7 @@ This is CodeMirror
- Get the current version:
5.15.0 .
+ Get the current version:
5.15.2 .
You can see the
code or
read the
release notes .
There is a
minification helper .
diff --git a/lib/codemirror.js b/lib/codemirror.js
index 937ce05a28..5439299b39 100644
--- a/lib/codemirror.js
+++ b/lib/codemirror.js
@@ -8902,7 +8902,7 @@
// THE END
- CodeMirror.version = "5.15.1";
+ CodeMirror.version = "5.15.2";
return CodeMirror;
});
diff --git a/package.json b/package.json
index 58d703f151..3200d2326a 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "codemirror",
- "version":"5.15.1",
+ "version":"5.15.2",
"main": "lib/codemirror.js",
"description": "Full-featured in-browser code editor",
"license": "MIT",
From 208af47a5963c04a45406b78ea18d716a9e0cd44 Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Fri, 20 May 2016 21:01:55 +0200
Subject: [PATCH 0094/2085] Bump version number post-5.15.2
---
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 3f898bd9ff..8c39e7f941 100644
--- a/doc/manual.html
+++ b/doc/manual.html
@@ -69,7 +69,7 @@
User manual and reference guide
- version 5.15.2
+ version 5.15.3
CodeMirror is a code-editor component that can be embedded in
diff --git a/lib/codemirror.js b/lib/codemirror.js
index 5439299b39..c56e147eeb 100644
--- a/lib/codemirror.js
+++ b/lib/codemirror.js
@@ -8902,7 +8902,7 @@
// THE END
- CodeMirror.version = "5.15.2";
+ CodeMirror.version = "5.15.3";
return CodeMirror;
});
diff --git a/package.json b/package.json
index 3200d2326a..d4dd2b00ef 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "codemirror",
- "version":"5.15.2",
+ "version":"5.15.3",
"main": "lib/codemirror.js",
"description": "Full-featured in-browser code editor",
"license": "MIT",
From 14f9ee8730d2acfdc74a4963b1e1a8ea3f5ef47e Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Sat, 21 May 2016 10:58:22 +0200
Subject: [PATCH 0095/2085] Add a github release upload script
---
bin/upload-release.js | 35 +++++++++++++++++++++++++++++++++++
1 file changed, 35 insertions(+)
create mode 100644 bin/upload-release.js
diff --git a/bin/upload-release.js b/bin/upload-release.js
new file mode 100644
index 0000000000..7c8551468c
--- /dev/null
+++ b/bin/upload-release.js
@@ -0,0 +1,35 @@
+"use strict"
+
+let version = process.argv[2]
+let auth = process.argv[3]
+
+if (!auth) {
+ console.log("Usage: upload-release.js [TAG] [github-user:password]")
+ process.exit(1)
+}
+
+require('child_process').exec("git --no-pager show -s --format='%s' " + version, (error, stdout) => {
+ if (error) throw error
+ let message = stdout.split("\n").slice(2)
+ message = message.slice(0, message.indexOf("-----BEGIN PGP SIGNATURE-----")).join("\n")
+
+ let req = require("https").request({
+ host: "api.github.com",
+ auth: auth,
+ headers: {"user-agent": "Release uploader"},
+ path: "/repos/codemirror/codemirror/releases",
+ method: "POST"
+ }, res => {
+ if (res.statusCode >= 300) {
+ console.error(res.statusMessage)
+ res.on("data", d => console.log(d.toString()))
+ res.on("end", process.exit(1))
+ }
+ })
+ req.write(JSON.stringify({
+ tag_name: version,
+ name: version,
+ body: message
+ }))
+ req.end()
+})
From 320b301b67ceadb1bd77414aaf0aa0852730de01 Mon Sep 17 00:00:00 2001
From: Timothy Gu
Date: Mon, 23 May 2016 22:55:12 -0700
Subject: [PATCH 0096/2085] [solarized theme] Overhaul
---
theme/solarized.css | 36 +++++++++++++++++++++---------------
1 file changed, 21 insertions(+), 15 deletions(-)
diff --git a/theme/solarized.css b/theme/solarized.css
index 7882c93763..4b1e16307a 100644
--- a/theme/solarized.css
+++ b/theme/solarized.css
@@ -4,7 +4,7 @@ http://ethanschoonover.com/solarized
*/
/*
-Solarized color pallet
+Solarized color palette
http://ethanschoonover.com/solarized/img/solarized-palette.png
*/
@@ -34,7 +34,7 @@ http://ethanschoonover.com/solarized/img/solarized-palette.png
}
.cm-s-solarized.cm-s-dark {
color: #839496;
- background-color: #002b36;
+ background-color: #002b36;
text-shadow: #002b36 0 1px;
}
.cm-s-solarized.cm-s-light {
@@ -113,32 +113,34 @@ http://ethanschoonover.com/solarized/img/solarized-palette.png
box-shadow: inset 7px 0 12px -6px #000;
}
-/* Gutter border and some shadow from it */
+/* Remove gutter border */
.cm-s-solarized .CodeMirror-gutters {
- border-right: 1px solid;
+ border-right: 0;
}
/* Gutter colors and line number styling based of color scheme (dark / light) */
/* Dark */
.cm-s-solarized.cm-s-dark .CodeMirror-gutters {
- background-color: #002b36;
- border-color: #00232c;
+ background-color: #073642;
}
.cm-s-solarized.cm-s-dark .CodeMirror-linenumber {
+ color: #586e75;
text-shadow: #021014 0 -1px;
}
/* Light */
.cm-s-solarized.cm-s-light .CodeMirror-gutters {
- background-color: #fdf6e3;
- border-color: #eee8d5;
+ background-color: #eee8d5;
+}
+
+.cm-s-solarized.cm-s-light .CodeMirror-linenumber {
+ color: #839496;
}
/* Common */
.cm-s-solarized .CodeMirror-linenumber {
- color: #586e75;
padding: 0 5px;
}
.cm-s-solarized .CodeMirror-guttermarker-subtle { color: #586e75; }
@@ -149,15 +151,19 @@ http://ethanschoonover.com/solarized/img/solarized-palette.png
color: #586e75;
}
+/* Cursor */
.cm-s-solarized .CodeMirror-cursor { border-left: 1px solid #819090; }
-/*
-Active line. Negative margin compensates left padding of the text in the
-view-port
-*/
+/* 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-dark.cm-fat-cursor .CodeMirror-cursor { background: #586e75; }
+.cm-s-solarized.cm-s-dark .cm-animate-fat-cursor { background-color: #586e75; }
+
+/* Active line */
.cm-s-solarized.cm-s-dark .CodeMirror-activeline-background {
- background: rgba(255, 255, 255, 0.10);
+ background: rgba(255, 255, 255, 0.06);
}
.cm-s-solarized.cm-s-light .CodeMirror-activeline-background {
- background: rgba(0, 0, 0, 0.10);
+ background: rgba(0, 0, 0, 0.06);
}
From 352e34ca49b7da33add8b4b85599271323cfba8e Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Wed, 25 May 2016 22:37:08 +0200
Subject: [PATCH 0097/2085] [show-hint addon] Fix scrollbar on hint widget
overlapping text
See https://discuss.codemirror.net/t/width-of-sql-hint-dropdown-in-firefox-is-not-always-correct/757
---
addon/hint/show-hint.css | 1 -
addon/hint/show-hint.js | 2 ++
2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/addon/hint/show-hint.css b/addon/hint/show-hint.css
index 924e638f7f..f3c4f245dd 100644
--- a/addon/hint/show-hint.css
+++ b/addon/hint/show-hint.css
@@ -26,7 +26,6 @@
padding: 0 4px;
border-radius: 2px;
max-width: 19em;
- overflow: hidden;
white-space: pre;
color: black;
cursor: pointer;
diff --git a/addon/hint/show-hint.js b/addon/hint/show-hint.js
index f426b5c8ca..cc47af206c 100644
--- a/addon/hint/show-hint.js
+++ b/addon/hint/show-hint.js
@@ -229,6 +229,7 @@
var winH = window.innerHeight || Math.max(document.body.offsetHeight, document.documentElement.offsetHeight);
(completion.options.container || document.body).appendChild(hints);
var box = hints.getBoundingClientRect(), overlapY = box.bottom - winH;
+ var scrolls = hints.scrollHeight > hints.clientHeight + 1
if (overlapY > 0) {
var height = box.bottom - box.top, curTop = pos.top - (pos.bottom - box.top);
if (curTop - height > 0) { // Fits above cursor
@@ -253,6 +254,7 @@
}
hints.style.left = (left = pos.left - overlapX) + "px";
}
+ if (scrolls) hints.style.paddingRight = cm.display.nativeBarWidth + "px"
cm.addKeyMap(this.keyMap = buildKeyMap(completion, {
moveFocus: function(n, avoidWrap) { widget.changeActive(widget.selectedHint + n, avoidWrap); },
From df3a0ae0fb7876a4ef58c324878e6eea03b0d66f Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Thu, 26 May 2016 09:54:05 +0200
Subject: [PATCH 0098/2085] [show-hint addon] Fix selected-option background
size for scrolling widget
---
addon/hint/show-hint.css | 2 +-
addon/hint/show-hint.js | 3 ++-
2 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/addon/hint/show-hint.css b/addon/hint/show-hint.css
index f3c4f245dd..453dee4bb7 100644
--- a/addon/hint/show-hint.css
+++ b/addon/hint/show-hint.css
@@ -16,6 +16,7 @@
background: white;
font-size: 90%;
font-family: monospace;
+ max-width: 19em;
max-height: 20em;
overflow-y: auto;
@@ -25,7 +26,6 @@
margin: 0;
padding: 0 4px;
border-radius: 2px;
- max-width: 19em;
white-space: pre;
color: black;
cursor: pointer;
diff --git a/addon/hint/show-hint.js b/addon/hint/show-hint.js
index cc47af206c..64ec9289cc 100644
--- a/addon/hint/show-hint.js
+++ b/addon/hint/show-hint.js
@@ -254,7 +254,8 @@
}
hints.style.left = (left = pos.left - overlapX) + "px";
}
- if (scrolls) hints.style.paddingRight = cm.display.nativeBarWidth + "px"
+ if (scrolls) for (var node = hints.firstChild; node; node = node.nextSibling)
+ node.style.paddingRight = cm.display.nativeBarWidth + "px"
cm.addKeyMap(this.keyMap = buildKeyMap(completion, {
moveFocus: function(n, avoidWrap) { widget.changeActive(widget.selectedHint + n, avoidWrap); },
From dabefce1b7405871564b2aab3bedbfb6cc261753 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jakub=20Vr=C3=A1na?=
Date: Wed, 1 Jun 2016 16:06:39 -0700
Subject: [PATCH 0099/2085] [soy mode] Support backslash in strings
---
mode/soy/soy.js | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/mode/soy/soy.js b/mode/soy/soy.js
index 79bfc24dfd..580c306f15 100644
--- a/mode/soy/soy.js
+++ b/mode/soy/soy.js
@@ -121,10 +121,11 @@
return tokenUntil(stream, state, /\{\/literal}/);
case "string":
- if (stream.match(/^.*?"/)) {
- state.soyState.pop();
- } else {
+ var match = stream.match(/^.*?("|\\[\s\S])/);
+ if (!match) {
stream.skipToEnd();
+ } else if (match[1] == "\"") {
+ state.soyState.pop();
}
return "string";
}
From bb91581b0a3ab11ca325445d621d814de5a84c97 Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Thu, 2 Jun 2016 10:16:18 +0200
Subject: [PATCH 0100/2085] [rulers addon] Make sure rulers don't cause
scrollbars
Closes #4042
---
addon/display/rulers.js | 38 +++++++++++++-------------------------
lib/codemirror.css | 6 ++++++
2 files changed, 19 insertions(+), 25 deletions(-)
diff --git a/addon/display/rulers.js b/addon/display/rulers.js
index 01f65667c4..730054473a 100644
--- a/addon/display/rulers.js
+++ b/addon/display/rulers.js
@@ -11,30 +11,26 @@
})(function(CodeMirror) {
"use strict";
- CodeMirror.defineOption("rulers", false, function(cm, val, old) {
- if (old && old != CodeMirror.Init) {
- clearRulers(cm);
- cm.off("refresh", refreshRulers);
+ CodeMirror.defineOption("rulers", false, function(cm, val) {
+ if (cm.state.rulerDiv) {
+ cm.display.lineSpace.removeChild(cm.state.rulerDiv)
+ cm.state.rulerDiv = null
+ cm.off("refresh", drawRulers)
}
if (val && val.length) {
- setRulers(cm);
- cm.on("refresh", refreshRulers);
+ cm.state.rulerDiv = cm.display.lineSpace.insertBefore(document.createElement("div"), cm.display.cursorDiv)
+ cm.state.rulerDiv.className = "CodeMirror-rulers"
+ drawRulers(cm)
+ cm.on("refresh", drawRulers)
}
});
- function clearRulers(cm) {
- for (var i = cm.display.lineSpace.childNodes.length - 1; i >= 0; i--) {
- var node = cm.display.lineSpace.childNodes[i];
- if (/(^|\s)CodeMirror-ruler($|\s)/.test(node.className))
- node.parentNode.removeChild(node);
- }
- }
-
- function setRulers(cm) {
+ function drawRulers(cm) {
+ cm.state.rulerDiv.textContent = ""
var val = cm.getOption("rulers");
var cw = cm.defaultCharWidth();
var left = cm.charCoords(CodeMirror.Pos(cm.firstLine(), 0), "div").left;
- var minH = cm.display.scroller.offsetHeight + 30;
+ cm.state.rulerDiv.style.minHeight = (cm.display.scroller.offsetHeight + 30) + "px";
for (var i = 0; i < val.length; i++) {
var elt = document.createElement("div");
elt.className = "CodeMirror-ruler";
@@ -49,15 +45,7 @@
if (conf.width) elt.style.borderLeftWidth = conf.width;
}
elt.style.left = (left + col * cw) + "px";
- elt.style.top = "-50px";
- elt.style.bottom = "-20px";
- elt.style.minHeight = minH + "px";
- cm.display.lineSpace.insertBefore(elt, cm.display.cursorDiv);
+ cm.state.rulerDiv.appendChild(elt)
}
}
-
- function refreshRulers(cm) {
- clearRulers(cm);
- setRulers(cm);
- }
});
diff --git a/lib/codemirror.css b/lib/codemirror.css
index 4bd815eb54..1e24ed8f7e 100644
--- a/lib/codemirror.css
+++ b/lib/codemirror.css
@@ -88,8 +88,14 @@
.cm-tab { display: inline-block; text-decoration: inherit; }
+.CodeMirror-rulers {
+ position: absolute;
+ left: 0; right: 0; top: -50px; bottom: -20px;
+ overflow: hidden;
+}
.CodeMirror-ruler {
border-left: 1px solid #ccc;
+ top: 0; bottom: 0;
position: absolute;
}
From bda854e0711d3d1f21a0804aa4d3b6d972e3a930 Mon Sep 17 00:00:00 2001
From: Sam Wilson
Date: Thu, 2 Jun 2016 13:36:20 -0400
Subject: [PATCH 0101/2085] [foldcode addon] Add clearOnEnter option
Add option 'clearOnEnter' to foldcode.js and default it to true.
This option determines whether the marker added when folding a
line clears when the cursor enters it.
---
addon/fold/foldcode.js | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/addon/fold/foldcode.js b/addon/fold/foldcode.js
index 62911f9352..78b36c4641 100644
--- a/addon/fold/foldcode.js
+++ b/addon/fold/foldcode.js
@@ -49,7 +49,7 @@
});
var myRange = cm.markText(range.from, range.to, {
replacedWith: myWidget,
- clearOnEnter: true,
+ clearOnEnter: getOption(cm, options, "clearOnEnter"),
__isFold: true
});
myRange.on("clear", function(from, to) {
@@ -129,7 +129,8 @@
rangeFinder: CodeMirror.fold.auto,
widget: "\u2194",
minFoldSize: 0,
- scanUp: false
+ scanUp: false,
+ clearOnEnter: true
};
CodeMirror.defineOption("foldOptions", null);
From 431dd1b16818c2516c9cbbf5d957189881283ac6 Mon Sep 17 00:00:00 2001
From: Sam Wilson
Date: Thu, 2 Jun 2016 15:37:46 -0400
Subject: [PATCH 0102/2085] [foldcode addon] Change arrow appearance when a
single line is collapsed
Use findMarks instead of findMarksAt in foldGutter.js to correctly
find marks when a single line is collapsed so that the arrow displays
correctly as collapsed.
---
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 f101e26709..9d32326566 100644
--- a/addon/fold/foldgutter.js
+++ b/addon/fold/foldgutter.js
@@ -50,7 +50,7 @@
}
function isFolded(cm, line) {
- var marks = cm.findMarksAt(Pos(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];
}
From ecd6976f2be26614366831c37819738efc8e9dfa Mon Sep 17 00:00:00 2001
From: Harshvardhan Gupta
Date: Tue, 7 Jun 2016 01:57:53 -0400
Subject: [PATCH 0103/2085] [sql-hint addon] Use all applicable decimal
positions in convertNumberToCur
Closes #4057
---
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 ed8f6d856c..4e84ab9b91 100644
--- a/addon/hint/sql-hint.js
+++ b/addon/hint/sql-hint.js
@@ -184,7 +184,7 @@
}
function convertNumberToCur(num) {
- return Pos(Math.floor(num), +num.toString().split('.').pop());
+ return Pos(Math.floor(num), +num.toFixed(6).toString().split('.').pop());
}
function findTableByAlias(alias, editor) {
From 92e9634f25f162220eff5cf5569c4f42b970f33b Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Tue, 7 Jun 2016 09:45:00 +0200
Subject: [PATCH 0104/2085] [sql-hint addon] Remove strange hack
Issue #4057
---
addon/hint/sql-hint.js | 22 ++++++----------------
1 file changed, 6 insertions(+), 16 deletions(-)
diff --git a/addon/hint/sql-hint.js b/addon/hint/sql-hint.js
index 4e84ab9b91..1ee4f0bbed 100644
--- a/addon/hint/sql-hint.js
+++ b/addon/hint/sql-hint.js
@@ -18,7 +18,7 @@
QUERY_DIV: ";",
ALIAS_KEYWORD: "AS"
};
- var Pos = CodeMirror.Pos;
+ var Pos = CodeMirror.Pos, cmpPos = CodeMirror.cmpPos;
function isArray(val) { return Object.prototype.toString.call(val) == "[object Array]" }
@@ -178,15 +178,6 @@
}
}
- function convertCurToNumber(cur) {
- // max characters of a line is 999,999.
- return cur.line + cur.ch / Math.pow(10, 6);
- }
-
- function convertNumberToCur(num) {
- return Pos(Math.floor(num), +num.toFixed(6).toString().split('.').pop());
- }
-
function findTableByAlias(alias, editor) {
var doc = editor.doc;
var fullQuery = doc.getValue();
@@ -209,15 +200,14 @@
separator.push(Pos(editor.lastLine(), editor.getLineHandle(editor.lastLine()).text.length));
//find valid range
- var prevItem = 0;
- var current = convertCurToNumber(editor.getCursor());
+ var prevItem = null;
+ var current = editor.getCursor()
for (var i = 0; i < separator.length; i++) {
- var _v = convertCurToNumber(separator[i]);
- if (current > prevItem && current <= _v) {
- validRange = { start: convertNumberToCur(prevItem), end: convertNumberToCur(_v) };
+ if ((prevItem == null || cmpPos(current, prevItem) > 0) && cmpPos(current, separator[i]) <= 0) {
+ validRange = {start: prevItem, end: separator[i]};
break;
}
- prevItem = _v;
+ prevItem = separator[i];
}
var query = doc.getRange(validRange.start, validRange.end, false);
From 2543f795ae42dd4e86fcd31c1238f0eb404413a0 Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Tue, 7 Jun 2016 12:03:55 +0200
Subject: [PATCH 0105/2085] Disable pointer-events on cursor divs
So that they don't interfere with clicking and dragging
Issue #2556
---
lib/codemirror.css | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/lib/codemirror.css b/lib/codemirror.css
index 1e24ed8f7e..18b0bf70db 100644
--- a/lib/codemirror.css
+++ b/lib/codemirror.css
@@ -297,7 +297,10 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
visibility: hidden;
}
-.CodeMirror-cursor { position: absolute; }
+.CodeMirror-cursor {
+ position: absolute;
+ pointer-events: none;
+}
.CodeMirror-measure pre { position: static; }
div.CodeMirror-cursors {
From eabf5b5d9aefcaa858124e255fdc258b3631d555 Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Tue, 7 Jun 2016 12:05:21 +0200
Subject: [PATCH 0106/2085] Work around fact that FF doesn't set modifiers on
drop events
Store the modifier in the original mouse event to choose between
move and copy drags.
Issue #2556
---
lib/codemirror.js | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/lib/codemirror.js b/lib/codemirror.js
index c56e147eeb..ac0bd54078 100644
--- a/lib/codemirror.js
+++ b/lib/codemirror.js
@@ -3660,6 +3660,7 @@
// Let the drag handler handle this.
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);
@@ -3890,7 +3891,7 @@
try {
var text = e.dataTransfer.getData("Text");
if (text) {
- if (cm.state.draggingText && !(mac ? e.altKey : e.ctrlKey))
+ if (cm.state.draggingText && !cm.state.draggingText.copy)
var selected = cm.listSelections();
setSelectionNoUndo(cm.doc, simpleSelection(pos, pos));
if (selected) for (var i = 0; i < selected.length; ++i)
From 8eb15883501464b4518e40d22421bd2feafa8544 Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Tue, 7 Jun 2016 12:30:53 +0200
Subject: [PATCH 0107/2085] [comment-fold addon] Fix bug that broke folding of
comments at start of line
Closes #4056
---
addon/fold/comment-fold.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/addon/fold/comment-fold.js b/addon/fold/comment-fold.js
index 60fa3e43df..e8d800eb59 100644
--- a/addon/fold/comment-fold.js
+++ b/addon/fold/comment-fold.js
@@ -29,7 +29,7 @@ CodeMirror.registerGlobalHelper("fold", "comment", function(mode) {
}
if (pass == 1 && found < start.ch) return;
if (/comment/.test(cm.getTokenTypeAt(CodeMirror.Pos(line, found + 1))) &&
- (lineText.slice(found - endToken.length, found) == endToken ||
+ (found == 0 || lineText.slice(found - endToken.length, found) == endToken ||
!/comment/.test(cm.getTokenTypeAt(CodeMirror.Pos(line, found))))) {
startCh = found + startToken.length;
break;
From 5c6025a337ff2964f7af29dffe25b4714e8516a4 Mon Sep 17 00:00:00 2001
From: Alasdair Smith
Date: Fri, 3 Jun 2016 12:29:20 +0100
Subject: [PATCH 0108/2085] [search addon] Capture keys for search-related
commands in persistent dialog
---
addon/search/search.js | 16 +++++++++++++++-
1 file changed, 15 insertions(+), 1 deletion(-)
diff --git a/addon/search/search.js b/addon/search/search.js
index e6b4f85a05..0f5d941889 100644
--- a/addon/search/search.js
+++ b/addon/search/search.js
@@ -62,7 +62,21 @@
value: deflt,
selectValueOnOpen: true,
closeOnEnter: false,
- onClose: function() { clearSearch(cm); }
+ onClose: function() { clearSearch(cm); },
+ onKeyDown: function(ev, query) {
+ var cmd = CodeMirror.keyMap['default'][CodeMirror.keyName(ev)];
+ if (cmd) {
+ var nextSearchCmds = ['findNext', 'findPrev'];
+ var searchCmds = ['find', 'findPersistent'];
+ if (nextSearchCmds.indexOf(cmd) !== -1) {
+ startSearch(cm, getSearchState(cm), query);
+ CodeMirror.commands[cmd](cm);
+ CodeMirror.e_stop(ev);
+ } else if (searchCmds.indexOf(cmd) !== -1) {
+ f(query, ev);
+ }
+ }
+ }
});
}
From b7b4269499a6d3a49609c30f661b3bb579283639 Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Tue, 7 Jun 2016 17:19:03 +0200
Subject: [PATCH 0109/2085] [search addon] Adjust patch 5c6025a
Issue #4050
---
addon/search/search.js | 32 +++++++++++++++-----------------
1 file changed, 15 insertions(+), 17 deletions(-)
diff --git a/addon/search/search.js b/addon/search/search.js
index 0f5d941889..00f53a0e01 100644
--- a/addon/search/search.js
+++ b/addon/search/search.js
@@ -57,26 +57,13 @@
return cm.getSearchCursor(query, pos, queryCaseInsensitive(query));
}
- function persistentDialog(cm, text, deflt, f) {
- cm.openDialog(text, f, {
+ function persistentDialog(cm, text, deflt, onEnter, onKeyDown) {
+ cm.openDialog(text, onEnter, {
value: deflt,
selectValueOnOpen: true,
closeOnEnter: false,
onClose: function() { clearSearch(cm); },
- onKeyDown: function(ev, query) {
- var cmd = CodeMirror.keyMap['default'][CodeMirror.keyName(ev)];
- if (cmd) {
- var nextSearchCmds = ['findNext', 'findPrev'];
- var searchCmds = ['find', 'findPersistent'];
- if (nextSearchCmds.indexOf(cmd) !== -1) {
- startSearch(cm, getSearchState(cm), query);
- CodeMirror.commands[cmd](cm);
- CodeMirror.e_stop(ev);
- } else if (searchCmds.indexOf(cmd) !== -1) {
- f(query, ev);
- }
- }
- }
+ onKeyDown: onKeyDown
});
}
@@ -132,7 +119,7 @@
var q = cm.getSelection() || state.lastQuery;
if (persistent && cm.openDialog) {
var hiding = null
- persistentDialog(cm, queryDialog, q, function(query, event) {
+ var searchNext = function(query, event) {
CodeMirror.e_stop(event);
if (!query) return;
if (query != state.queryText) {
@@ -147,6 +134,17 @@
dialog.getBoundingClientRect().bottom - 4 > cm.cursorCoords(to, "window").top)
(hiding = dialog).style.opacity = .4
})
+ };
+ persistentDialog(cm, queryDialog, q, searchNext, function(event, query) {
+ var cmd = CodeMirror.keyMap[cm.getOption("keyMap")][CodeMirror.keyName(event)];
+ if (cmd == "findNext" || cmd == "findPrev") {
+ CodeMirror.e_stop(event);
+ startSearch(cm, getSearchState(cm), query);
+ cm.execCommand(cmd);
+ } else if (cmd == "find" || cmd == "findPersistent") {
+ CodeMirror.e_stop(event);
+ searchNext(query, event);
+ }
});
} else {
dialog(cm, queryDialog, "Search for:", q, function(query) {
From 440395dd715bc063488f402790ee8990b95cedd2 Mon Sep 17 00:00:00 2001
From: Alasdair Smith
Date: Fri, 3 Jun 2016 13:11:21 +0100
Subject: [PATCH 0110/2085] [search addon] Add commands to find next/prev with
persistent dialog
---
addon/search/search.js | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/addon/search/search.js b/addon/search/search.js
index 00f53a0e01..c005866f16 100644
--- a/addon/search/search.js
+++ b/addon/search/search.js
@@ -113,7 +113,7 @@
}
}
- function doSearch(cm, rev, persistent) {
+ function doSearch(cm, rev, persistent, immediate) {
var state = getSearchState(cm);
if (state.query) return findNext(cm, rev);
var q = cm.getSelection() || state.lastQuery;
@@ -146,6 +146,10 @@
searchNext(query, event);
}
});
+ if (immediate) {
+ startSearch(cm, state, q);
+ findNext(cm, rev);
+ }
} else {
dialog(cm, queryDialog, "Search for:", q, function(query) {
if (query && !state.query) cm.operation(function() {
@@ -235,6 +239,8 @@
CodeMirror.commands.find = function(cm) {clearSearch(cm); doSearch(cm);};
CodeMirror.commands.findPersistent = function(cm) {clearSearch(cm); doSearch(cm, false, true);};
+ CodeMirror.commands.findPersistentNext = function(cm) {doSearch(cm, false, true, true);};
+ CodeMirror.commands.findPersistentPrev = function(cm) {doSearch(cm, true, true, true);};
CodeMirror.commands.findNext = doSearch;
CodeMirror.commands.findPrev = function(cm) {doSearch(cm, true);};
CodeMirror.commands.clearSearch = clearSearch;
From 79bec105b4b40a4d159f8613d34d93128c9c3a7d Mon Sep 17 00:00:00 2001
From: Vincent Woo
Date: Thu, 26 May 2016 20:13:32 -0700
Subject: [PATCH 0111/2085] [sublime] Implement smart backspace with
multi-cursor support
---
keymap/sublime.js | 28 ++++++++++++++++++++++++++++
1 file changed, 28 insertions(+)
diff --git a/keymap/sublime.js b/keymap/sublime.js
index c1749e7196..0da347a99e 100644
--- a/keymap/sublime.js
+++ b/keymap/sublime.js
@@ -420,6 +420,34 @@
map[cK + ctrl + "Backspace"] = "delLineLeft";
+ cmds[map["Backspace"] = "smartBackspace"] = function(cm) {
+ if (cm.somethingSelected()) return CodeMirror.Pass;
+
+ cm.operation(function() {
+ var cursors = cm.listSelections();
+ var indentUnit = cm.getOption("indentUnit");
+
+ for (var i = cursors.length - 1; i >= 0; i--) {
+ var cursor = cursors[i].head;
+ var toStartOfLine = cm.getRange({line: cursor.line, ch: 0}, cursor);
+ var column = CodeMirror.countColumn(toStartOfLine, null, cm.getOption("tabSize"));
+
+ // Delete by one character by default
+ var deletePos = cm.findPosH(cursor, -1, "char", false);
+
+ if (toStartOfLine && !/\S/.test(toStartOfLine) && column % indentUnit == 0) {
+ var prevIndent = new Pos(cursor.line,
+ CodeMirror.findColumn(toStartOfLine, column - indentUnit, indentUnit));
+
+ // Smart delete only if we found a valid prevIndent location
+ if (prevIndent.ch != cursor.ch) deletePos = prevIndent;
+ }
+
+ cm.replaceRange("", deletePos, cursor, "+delete");
+ }
+ });
+ };
+
cmds[map[cK + ctrl + "K"] = "delLineRight"] = function(cm) {
cm.operation(function() {
var ranges = cm.listSelections();
From 1843b8c277256b756f9ffb241318ce39e24dd5fb Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Thu, 9 Jun 2016 09:31:13 +0200
Subject: [PATCH 0112/2085] [sublime bindings] Add test for smartBackspace
Issue #4038
---
test/sublime_test.js | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/test/sublime_test.js b/test/sublime_test.js
index c93e041b87..c5c19c0a23 100644
--- a/test/sublime_test.js
+++ b/test/sublime_test.js
@@ -274,6 +274,10 @@
"clearBookmarks",
Pos(0, 0), "selectBookmarks", at(0, 0));
+ stTest("smartBackspace", " foo\n bar",
+ setSel(0, 2, 0, 2, 1, 4, 1, 4, 1, 6, 1, 6), "smartBackspace",
+ val("foo\n br"))
+
stTest("upAndDowncaseAtCursor", "abc\ndef x\nghI",
setSel(0, 1, 0, 3,
1, 1, 1, 1,
From 14307f60e6b90cddcaf009cdb40064a50ea0c428 Mon Sep 17 00:00:00 2001
From: "David H. Bronke"
Date: Fri, 10 Jun 2016 11:24:10 -0500
Subject: [PATCH 0113/2085] [sql mode] Removed `commentHash` and
`commentSpaceRequired` from `text/x-pgsql`.
Closes #3888
Closes #4063.
---
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 daec60ce6f..17a096c5fc 100644
--- a/mode/sql/sql.js
+++ b/mode/sql/sql.js
@@ -368,7 +368,7 @@ CodeMirror.defineMode("sql", function(config, parserConfig) {
atoms: set("false true null unknown"),
operatorChars: /^[*+\-%<>!=&|^]/,
dateSQL: set("date time timestamp"),
- support: set("ODBCdotTable decimallessFloat zerolessFloat binaryNumber hexNumber nCharCast charsetCast commentHash commentSpaceRequired")
+ support: set("ODBCdotTable decimallessFloat zerolessFloat binaryNumber hexNumber nCharCast charsetCast")
});
// Google's SQL-like query language, GQL
From 58b549d2147ba2d4a9f9b4a5394982818455b58c Mon Sep 17 00:00:00 2001
From: "David H. Bronke"
Date: Fri, 10 Jun 2016 12:20:05 -0500
Subject: [PATCH 0114/2085] [sql mode] Added missing operator characters for
`text/x-pgsql`.
Closes #4064.
---
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 17a096c5fc..01ebd80ae1 100644
--- a/mode/sql/sql.js
+++ b/mode/sql/sql.js
@@ -366,7 +366,7 @@ CodeMirror.defineMode("sql", function(config, parserConfig) {
// http://www.postgresql.org/docs/9.5/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 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")
});
From 047afd24c563e97f6ceab8e700deabff272ec51b Mon Sep 17 00:00:00 2001
From: mtaran-google
Date: Tue, 14 Jun 2016 14:56:31 -0700
Subject: [PATCH 0115/2085] [go mode] Add `error` as highlighted type
It is, per https://golang.org/ref/spec#Predeclared_identifiers
---
mode/go/go.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/mode/go/go.js b/mode/go/go.js
index 21fe9a2d6e..3c9ef6b989 100644
--- a/mode/go/go.js
+++ b/mode/go/go.js
@@ -23,7 +23,7 @@ CodeMirror.defineMode("go", function(config) {
"bool":true, "byte":true, "complex64":true, "complex128":true,
"float32":true, "float64":true, "int8":true, "int16":true, "int32":true,
"int64":true, "string":true, "uint8":true, "uint16":true, "uint32":true,
- "uint64":true, "int":true, "uint":true, "uintptr":true
+ "uint64":true, "int":true, "uint":true, "uintptr":true, "error": true
};
var atoms = {
From 3aba5a8ce1ea24dc243df1198920a81957d17c5b Mon Sep 17 00:00:00 2001
From: Jeff Jenkins
Date: Wed, 1 Jun 2016 07:06:43 -0400
Subject: [PATCH 0116/2085] [sublime keymap] Mimic sublime on OS X with
ctrl-shift for selectLines
---
keymap/sublime.js | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/keymap/sublime.js b/keymap/sublime.js
index 0da347a99e..ed6b847428 100644
--- a/keymap/sublime.js
+++ b/keymap/sublime.js
@@ -500,7 +500,8 @@
cm.scrollTo(null, (pos.top + pos.bottom) / 2 - cm.getScrollInfo().clientHeight / 2);
};
- cmds[map["Shift-Alt-Up"] = "selectLinesUpward"] = function(cm) {
+ var selectLinesCombo = mac ? "Ctrl-Shift-" : "Ctrl-Alt-";
+ cmds[map[selectLinesCombo + "Up"] = "selectLinesUpward"] = function(cm) {
cm.operation(function() {
var ranges = cm.listSelections();
for (var i = 0; i < ranges.length; i++) {
@@ -510,7 +511,7 @@
}
});
};
- cmds[map["Shift-Alt-Down"] = "selectLinesDownward"] = function(cm) {
+ cmds[map[selectLinesCombo + "Down"] = "selectLinesDownward"] = function(cm) {
cm.operation(function() {
var ranges = cm.listSelections();
for (var i = 0; i < ranges.length; i++) {
From 76e252d6c87626a4de5cf6a47ada554bb8a14f57 Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Mon, 20 Jun 2016 10:38:36 +0200
Subject: [PATCH 0117/2085] Add kludge to improve handling of clicks after line
wrapping breaks
Issue #4078
---
lib/codemirror.js | 17 +++++++++++++++--
1 file changed, 15 insertions(+), 2 deletions(-)
diff --git a/lib/codemirror.js b/lib/codemirror.js
index ac0bd54078..7fd38c976e 100644
--- a/lib/codemirror.js
+++ b/lib/codemirror.js
@@ -2933,10 +2933,23 @@
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);
+ // 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");
+ 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, ch == from ? fromOutside : toOutside,
- xDiff < -1 ? -1 : xDiff > 1 ? 1 : 0);
+ var pos = PosWithInfo(lineNo, ch, outside, xDiff < -1 ? -1 : xDiff > 1 ? 1 : 0);
return pos;
}
var step = Math.ceil(dist / 2), middle = from + step;
From 592b4d1743a7fb3708c940b079a5913ba6df788d Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Mon, 20 Jun 2016 11:24:14 +0200
Subject: [PATCH 0118/2085] [hardwrap addon] Fix corner case for words whose
length equals the wrap column
Issue #4069
---
addon/wrap/hardwrap.js | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/addon/wrap/hardwrap.js b/addon/wrap/hardwrap.js
index 8806fbe2f2..04851f99ff 100644
--- a/addon/wrap/hardwrap.js
+++ b/addon/wrap/hardwrap.js
@@ -30,7 +30,9 @@
}
function findBreakPoint(text, column, wrapOn, killTrailingSpace) {
- for (var at = column; at > 0; --at)
+ var at = column
+ while (at < text.length && text.charAt(at) == " ") at++
+ for (; at > 0; --at)
if (wrapOn.test(text.slice(at - 1, at + 1))) break;
for (var first = true;; first = false) {
var endOfText = at;
From e1201757de3ec5621a7271f32bd9ac2280a0cf3f Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Mon, 20 Jun 2016 11:39:43 +0200
Subject: [PATCH 0119/2085] [scrollpastend addon] Work around potential
infinite event-handler recursion
See https://discuss.codemirror.net/t/range-error-with-scrollpastend-addon/773
---
addon/scroll/scrollpastend.js | 2 ++
1 file changed, 2 insertions(+)
diff --git a/addon/scroll/scrollpastend.js b/addon/scroll/scrollpastend.js
index 008ae4c7ba..a2ed089b48 100644
--- a/addon/scroll/scrollpastend.js
+++ b/addon/scroll/scrollpastend.js
@@ -40,7 +40,9 @@
if (cm.state.scrollPastEndPadding != padding) {
cm.state.scrollPastEndPadding = padding;
cm.display.lineSpace.parentNode.style.paddingBottom = padding;
+ cm.off("refresh", updateBottomMargin);
cm.setSize();
+ cm.on("refresh", updateBottomMargin);
}
}
});
From 6bd09fcc278ed625d8478177e3a284e91d0b8695 Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Mon, 20 Jun 2016 11:57:58 +0200
Subject: [PATCH 0120/2085] Mark version 5.16.0
---
AUTHORS | 5 +++++
CHANGELOG.md | 20 ++++++++++++++++++++
doc/compress.html | 1 +
doc/manual.html | 2 +-
index.html | 2 +-
lib/codemirror.js | 2 +-
package.json | 2 +-
7 files changed, 30 insertions(+), 4 deletions(-)
diff --git a/AUTHORS b/AUTHORS
index 5e373a0202..ce2be8d6d2 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -14,6 +14,7 @@ Ahmad Amireh
Ahmad M. Zawawi
ahoward
Akeksandr Motsjonov
+Alasdair Smith
Alberto González Palomo
Alberto Pose
Albert Xing
@@ -140,6 +141,7 @@ Darius Roberts
Dave Brondsema
Dave Myers
David Barnett
+David H. Bronke
David Mignot
David Pathakjee
David Vázquez
@@ -213,6 +215,7 @@ Gustavo Rodrigues
Hakan Tunc
Hans Engel
Hardest
+Harshvardhan Gupta
Hasan Karahan
Hector Oswaldo Caballero
Herculano Campos
@@ -258,6 +261,7 @@ Jason Siefken
Jaydeep Solanki
Jean Boussier
Jeff Blaisdell
+Jeff Jenkins
jeffkenton
Jeff Pickhardt
jem (graphite)
@@ -479,6 +483,7 @@ Ruslan Osmanov
Ryan Prior
sabaca
Samuel Ainsworth
+Sam Wilson
sandeepshetty
Sander AKA Redsandro
santec
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 146af30edd..a34cf82ffa 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,23 @@
+## 5.16.0 (2016-06-20)
+
+### Bugfixes
+
+Fix glitches when dragging content caused by the drop indicator receiving mouse events.
+
+Make Control-drag work on Firefox.
+
+Make clicking or selection-dragging at the end of a wrapped line select the right position.
+
+[show-hint addon](http://codemirror.net/doc/manual.html#addon_show-hint): Prevent widget scrollbar from hiding part of the hint text.
+
+[rulers addon](http://codemirror.net/doc/manual.html#addon_rulers): Prevent rulers from forcing a horizontal editor scrollbar.
+
+### New features
+
+[search addon](http://codemirror.net/doc/manual.html#addon_search): Automatically bind search-related keys in persistent dialog.
+
+[sublime keymap](http://codemirror.net/demo/sublime.html): Add a multi-cursor aware smart backspace binding.
+
## 5.15.2 (2016-05-20)
### Bugfixes
diff --git a/doc/compress.html b/doc/compress.html
index fe27e025cc..b890bee021 100644
--- a/doc/compress.html
+++ b/doc/compress.html
@@ -36,6 +36,7 @@ Script compression helper
Version:
HEAD
+ 5.16.0
5.15.2
5.15.0
5.14.2
diff --git a/doc/manual.html b/doc/manual.html
index 8c39e7f941..55b22efd65 100644
--- a/doc/manual.html
+++ b/doc/manual.html
@@ -69,7 +69,7 @@
User manual and reference guide
- version 5.15.3
+ version 5.16.0
CodeMirror is a code-editor component that can be embedded in
diff --git a/index.html b/index.html
index e987926921..528b3a430a 100644
--- a/index.html
+++ b/index.html
@@ -96,7 +96,7 @@
This is CodeMirror
- Get the current version:
5.15.2 .
+ Get the current version:
5.16.0 .
You can see the
code or
read the
release notes .
There is a
minification helper .
diff --git a/lib/codemirror.js b/lib/codemirror.js
index 7fd38c976e..7dc842d3ba 100644
--- a/lib/codemirror.js
+++ b/lib/codemirror.js
@@ -8916,7 +8916,7 @@
// THE END
- CodeMirror.version = "5.15.3";
+ CodeMirror.version = "5.16.0";
return CodeMirror;
});
diff --git a/package.json b/package.json
index d4dd2b00ef..5638b12a1f 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "codemirror",
- "version":"5.15.3",
+ "version":"5.16.0",
"main": "lib/codemirror.js",
"description": "Full-featured in-browser code editor",
"license": "MIT",
From 3adfe9388f1ab6e97ff7c08aa0ff732fd412ccad Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Mon, 20 Jun 2016 11:59:45 +0200
Subject: [PATCH 0121/2085] Bump version post-5.16.0
---
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 55b22efd65..030287c3ef 100644
--- a/doc/manual.html
+++ b/doc/manual.html
@@ -69,7 +69,7 @@
User manual and reference guide
- version 5.16.0
+ version 5.16.2
CodeMirror is a code-editor component that can be embedded in
diff --git a/lib/codemirror.js b/lib/codemirror.js
index 7dc842d3ba..570eba1b35 100644
--- a/lib/codemirror.js
+++ b/lib/codemirror.js
@@ -8916,7 +8916,7 @@
// THE END
- CodeMirror.version = "5.16.0";
+ CodeMirror.version = "5.16.2";
return CodeMirror;
});
diff --git a/package.json b/package.json
index 5638b12a1f..276c2d29e9 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "codemirror",
- "version":"5.16.0",
+ "version":"5.16.2",
"main": "lib/codemirror.js",
"description": "Full-featured in-browser code editor",
"license": "MIT",
From 3a9730faf6bfe9bbb5245d31d471cf2d735e7573 Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Mon, 20 Jun 2016 12:02:15 +0200
Subject: [PATCH 0122/2085] Add forgotten release notes to web site
---
doc/releases.html | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/doc/releases.html b/doc/releases.html
index 594298ef6a..052f7809c0 100644
--- a/doc/releases.html
+++ b/doc/releases.html
@@ -30,6 +30,18 @@ Release notes and version history
Version 5.x
+ 20-06-2016: Version 5.16.0 :
+
+
+ Fix glitches when dragging content caused by the drop indicator receiving mouse events.
+ Make Control-drag work on Firefox.
+ Make clicking or selection-dragging at the end of a wrapped line select the right position.
+ show-hint addon : Prevent widget scrollbar from hiding part of the hint text.
+ rulers addon : Prevent rulers from forcing a horizontal editor scrollbar.
+ search addon : Automatically bind search-related keys in persistent dialog.
+ sublime keymap : Add a multi-cursor aware smart backspace binding.
+
+
20-05-2016: Version 5.15.2 :
From 23196659003e0cd18d5e5af7f500146a552e5c7d Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Thu, 23 Jun 2016 15:13:55 +0200
Subject: [PATCH 0123/2085] [javascript mode] Improve support for async keyword
Closes #4085
---
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 ca875411a2..ddb46bab27 100644
--- a/mode/javascript/javascript.js
+++ b/mode/javascript/javascript.js
@@ -386,7 +386,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 == "keyword c") return cont(noComma ? maybeexpressionNoComma : maybeexpression);
+ if (type == "keyword c" || type == "async") return cont(noComma ? maybeexpressionNoComma : maybeexpression);
if (type == "(") return cont(pushlex(")"), maybeexpression, comprehension, expect(")"), poplex, maybeop);
if (type == "operator" || type == "spread") return cont(noComma ? expressionNoComma : expression);
if (type == "[") return cont(pushlex("]"), arrayLiteral, poplex, maybeop);
@@ -463,6 +463,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);
if (type == "variable" || cx.style == "keyword") {
cx.marked = "property";
if (value == "get" || value == "set") return cont(getterSetter);
From 54d0d82e2f3645444c52d4bb8055d5bb8a3c0b5c Mon Sep 17 00:00:00 2001
From: Yunchi Luo
Date: Sat, 25 Jun 2016 23:12:42 -0400
Subject: [PATCH 0124/2085] [vim mode] Fix Backspace in replace mode...
and remove autocomplete bindings
---
keymap/vim.js | 9 +--------
1 file changed, 1 insertion(+), 8 deletions(-)
diff --git a/keymap/vim.js b/keymap/vim.js
index 4278ee9798..9891eb5fcb 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: 'motion', motion: 'moveByCharacters', motionArgs: { forward: false }, context: 'insert'},
{ keys: '', type: 'keyToKey', toKeys: 'W' },
{ keys: '', type: 'keyToKey', toKeys: 'B', context: 'normal' },
{ keys: '', type: 'keyToKey', toKeys: 'w' },
@@ -4771,13 +4772,6 @@
CodeMirror.keyMap['vim-insert'] = {
// TODO: override navigation keys so that Esc will cancel automatic
// indentation from o, O, i_
- 'Ctrl-N': 'autocomplete',
- 'Ctrl-P': 'autocomplete',
- 'Enter': function(cm) {
- var fn = CodeMirror.commands.newlineAndIndentContinueComment ||
- CodeMirror.commands.newlineAndIndent;
- fn(cm);
- },
fallthrough: ['default'],
attach: attachVimMap,
detach: detachVimMap,
@@ -4785,7 +4779,6 @@
};
CodeMirror.keyMap['vim-replace'] = {
- 'Backspace': 'goCharLeft',
fallthrough: ['vim-insert'],
attach: attachVimMap,
detach: detachVimMap,
From fb3e1dc58d2c80c4535d55911f9326729ef5bc73 Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Tue, 28 Jun 2016 11:16:50 +0200
Subject: [PATCH 0125/2085] [show-hint addon] Drop default max-width for hint
widget
Closes #4080
---
addon/hint/show-hint.css | 1 -
1 file changed, 1 deletion(-)
diff --git a/addon/hint/show-hint.css b/addon/hint/show-hint.css
index 453dee4bb7..5617ccca2b 100644
--- a/addon/hint/show-hint.css
+++ b/addon/hint/show-hint.css
@@ -16,7 +16,6 @@
background: white;
font-size: 90%;
font-family: monospace;
- max-width: 19em;
max-height: 20em;
overflow-y: auto;
From 2c913e5d2914fdc64df80031544a384940006feb Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Tue, 28 Jun 2016 11:37:05 +0200
Subject: [PATCH 0126/2085] Make hack to break up adjacent spaces work for
single-space tokens
Closes #4087
---
lib/codemirror.js | 21 +++++++++++++++------
1 file changed, 15 insertions(+), 6 deletions(-)
diff --git a/lib/codemirror.js b/lib/codemirror.js
index 570eba1b35..476ab2e6e5 100644
--- a/lib/codemirror.js
+++ b/lib/codemirror.js
@@ -6944,6 +6944,7 @@
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 = {};
@@ -7005,7 +7006,7 @@
// 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 ? text.replace(/ {3,}/g, splitSpaces) : text;
+ var displayText = builder.splitSpaces ? splitSpaces(text, builder.trailingSpace) : text
var special = builder.cm.state.specialChars, mustWrap = false;
if (!special.test(text)) {
builder.col += text.length;
@@ -7050,6 +7051,7 @@
builder.pos++;
}
}
+ builder.trailingSpace = displayText.charCodeAt(text.length - 1) == 32
if (style || startStyle || endStyle || mustWrap || css) {
var fullStyle = style || "";
if (startStyle) fullStyle += startStyle;
@@ -7061,11 +7063,17 @@
builder.content.appendChild(content);
}
- function splitSpaces(old) {
- var out = " ";
- for (var i = 0; i < old.length - 2; ++i) out += i % 2 ? " " : "\u00a0";
- out += " ";
- return out;
+ 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)
+ if (ch == " " && spaceBefore && (i == text.length - 1 || text.charCodeAt(i + 1) == 32))
+ ch = "\u00a0"
+ result += ch
+ spaceBefore = ch == " "
+ }
+ return result
}
// Work around nonsense dimensions being reported for stretches of
@@ -7102,6 +7110,7 @@
builder.content.appendChild(widget);
}
builder.pos += size;
+ builder.trailingSpace = false
}
// Outputs a number of spans to make up a line, taking highlighting
From 24d86a87890bd21f8ba23e4c2bb8cfa25db00b3c Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Tue, 28 Jun 2016 11:40:20 +0200
Subject: [PATCH 0127/2085] Stop blocking selection-undo in read-only mode
Closes #4090
---
lib/codemirror.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lib/codemirror.js b/lib/codemirror.js
index 476ab2e6e5..7be2fac1db 100644
--- a/lib/codemirror.js
+++ b/lib/codemirror.js
@@ -4419,7 +4419,7 @@
// Revert a change stored in a document's history.
function makeChangeFromHistory(doc, type, allowSelectionOnly) {
- if (doc.cm && doc.cm.state.suppressEdits) 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;
From 34b3a7b9e77aa8fe67b792fdf5575bd1cdb55132 Mon Sep 17 00:00:00 2001
From: Hendrik Wallbaum
Date: Fri, 24 Jun 2016 17:04:28 +0800
Subject: [PATCH 0128/2085] [markdown mode] Add more token styles for image
references
- Images now come with tons of information to style them. This mainly
enables to style alt-texts and potentially make them look like links.
While still differentialting between links and images.
Closes #4082
---
mode/markdown/markdown.js | 30 ++++++++++++++++++++++++++----
mode/markdown/test.js | 29 ++++++++++++++++++++++++-----
2 files changed, 50 insertions(+), 9 deletions(-)
diff --git a/mode/markdown/markdown.js b/mode/markdown/markdown.js
index 37329c2385..9dd44574fc 100644
--- a/mode/markdown/markdown.js
+++ b/mode/markdown/markdown.js
@@ -63,7 +63,9 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
list2: "variable-3",
list3: "keyword",
hr: "hr",
- image: "tag",
+ image: "image",
+ imageAltText: "image-alt-text",
+ imageMarker: "image-marker",
formatting: "formatting",
linkInline: "link",
linkEmail: "link",
@@ -313,6 +315,9 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
if (state.strikethrough) { styles.push(tokenTypes.strikethrough); }
if (state.linkText) { styles.push(tokenTypes.linkText); }
if (state.code) { styles.push(tokenTypes.code); }
+ if (state.image) { styles.push(tokenTypes.image); }
+ if (state.imageAltText) { styles.push(tokenTypes.imageAltText, "link"); }
+ if (state.imageMarker) { styles.push(tokenTypes.imageMarker); }
}
if (state.header) { styles.push(tokenTypes.header, tokenTypes.header + "-" + state.header); }
@@ -432,12 +437,29 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
}
if (ch === '!' && stream.match(/\[[^\]]*\] ?(?:\(|\[)/, false)) {
- stream.match(/\[[^\]]*\]/);
+ state.imageMarker = true;
+ state.image = true;
+ if (modeCfg.highlightFormatting) state.formatting = "image";
+ return getType(state);
+ }
+
+ if (ch === '[' && state.imageMarker) {
+ state.imageMarker = false;
+ state.imageAltText = true
+ if (modeCfg.highlightFormatting) state.formatting = "image";
+ return getType(state);
+ }
+
+ if (ch === ']' && state.imageAltText) {
+ if (modeCfg.highlightFormatting) state.formatting = "image";
+ var type = getType(state);
+ state.imageAltText = false;
+ state.image = false;
state.inline = state.f = linkHref;
- return tokenTypes.image;
+ return type;
}
- if (ch === '[' && stream.match(/[^\]]*\](\(.*\)| ?\[.*?\])/, false)) {
+ if (ch === '[' && stream.match(/[^\]]*\](\(.*\)| ?\[.*?\])/, false) && !state.image) {
state.linkText = true;
if (modeCfg.highlightFormatting) state.formatting = "link";
return getType(state);
diff --git a/mode/markdown/test.js b/mode/markdown/test.js
index e76eae9027..2f43a170ca 100644
--- a/mode/markdown/test.js
+++ b/mode/markdown/test.js
@@ -22,6 +22,8 @@
"list3" : "override-list3",
"hr" : "override-hr",
"image" : "override-image",
+ "imageAltText": "override-image-alt-text",
+ "imageMarker": "override-image-marker",
"linkInline" : "override-link-inline",
"linkEmail" : "override-link-email",
"linkText" : "override-link-text",
@@ -89,6 +91,9 @@
FT("formatting_escape",
"[formatting-escape \\*]");
+ 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 )]");
+
MT("plainText",
"foo");
@@ -589,6 +594,20 @@
MT("hrDashLong",
"[hr ---------------------------------------]");
+ //Images
+ MT("Images",
+ "[image&image-marker !][image&image-alt-text&link [[alt text]]][string&url (http://link.to/image.jpg)]")
+
+ //Images with highlight alt text
+ MT("imageEm",
+ "[image&image-marker !][image&image-alt-text&link [[][image-alt-text&em&image&link *alt text*][image&image-alt-text&link ]]][string&url (http://link.to/image.jpg)]");
+
+ MT("imageStrong",
+ "[image&image-marker !][image&image-alt-text&link [[][image-alt-text&strong&image&link **alt text**][image&image-alt-text&link ]]][string&url (http://link.to/image.jpg)]");
+
+ MT("imageEmStrong",
+ "[image&image-marker !][image&image-alt-text&link [[][image-alt-text&image&strong&link **][image&image-alt-text&em&strong&link *alt text**][image&image-alt-text&em&link *][image&image-alt-text&link ]]][string&url (http://link.to/image.jpg)]");
+
// Inline link with title
MT("linkTitle",
"[link [[foo]]][string&url (http://example.com/ \"bar\")] hello");
@@ -599,7 +618,7 @@
// Inline link with image
MT("linkImage",
- "[link [[][tag ![[foo]]][string&url (http://example.com/)][link ]]][string&url (http://example.com/)] bar");
+ "[link [[][link&image&image-marker !][link&image&image-alt-text&link [[alt text]]][string&url (http://link.to/image.jpg)][link ]]][string&url (http://example.com/)] bar");
// Inline link with Em
MT("linkEm",
@@ -615,15 +634,15 @@
// Image with title
MT("imageTitle",
- "[tag ![[foo]]][string&url (http://example.com/ \"bar\")] hello");
+ "[image&image-marker !][image&image-alt-text&link [[alt text]]][string&url (http://example.com/ \"bar\")] hello");
// Image without title
MT("imageNoTitle",
- "[tag ![[foo]]][string&url (http://example.com/)] bar");
+ "[image&image-marker !][image&image-alt-text&link [[alt text]]][string&url (http://example.com/)] bar");
// Image with asterisks
MT("imageAsterisks",
- "[tag ![[*foo*]]][string&url (http://example.com/)] bar");
+ "[image&image-marker !][image&image-alt-text&link [[ ][image&image-alt-text&em&link *alt text*][image&image-alt-text&link ]]][string&url (http://link.to/image.jpg)] bar");
// Not a link. Should be normal text due to square brackets being used
// regularly in text, especially in quoted material, and no space is allowed
@@ -878,7 +897,7 @@
"[override-hr * * *]");
TokenTypeOverrideTest("overrideImage",
- "[override-image ![[foo]]][override-link-href&url (http://example.com/)]")
+ "[override-image&override-image-marker !][override-image&override-image-alt-text&link [[alt text]]][override-link-href&url (http://link.to/image.jpg)]");
TokenTypeOverrideTest("overrideLinkText",
"[override-link-text [[foo]]][override-link-href&url (http://example.com)]");
From 6a845599c638513c75b8f04880965314099988b4 Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Fri, 1 Jul 2016 09:51:12 +0200
Subject: [PATCH 0129/2085] [show-hint addon] Move call to getScrollInto to
avoid a reflow
---
addon/hint/show-hint.js | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/addon/hint/show-hint.js b/addon/hint/show-hint.js
index 64ec9289cc..604bd3b715 100644
--- a/addon/hint/show-hint.js
+++ b/addon/hint/show-hint.js
@@ -230,6 +230,8 @@
(completion.options.container || document.body).appendChild(hints);
var box = hints.getBoundingClientRect(), overlapY = box.bottom - winH;
var scrolls = hints.scrollHeight > hints.clientHeight + 1
+ var startScroll = cm.getScrollInfo();
+
if (overlapY > 0) {
var height = box.bottom - box.top, curTop = pos.top - (pos.bottom - box.top);
if (curTop - height > 0) { // Fits above cursor
@@ -273,7 +275,6 @@
cm.on("focus", this.onFocus = function() { clearTimeout(closingOnBlur); });
}
- var startScroll = cm.getScrollInfo();
cm.on("scroll", this.onScroll = function() {
var curScroll = cm.getScrollInfo(), editor = cm.getWrapperElement().getBoundingClientRect();
var newTop = top + startScroll.top - curScroll.top;
From a0547b7b9d887db173b1463b79e64a77f4fd8c32 Mon Sep 17 00:00:00 2001
From: mtaran-google
Date: Fri, 1 Jul 2016 16:01:35 -0700
Subject: [PATCH 0130/2085] [sublime] Make goSubwordLeft/Right shortcuts match
Sublime on OSX
---
keymap/sublime.js | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/keymap/sublime.js b/keymap/sublime.js
index ed6b847428..db9f54de16 100644
--- a/keymap/sublime.js
+++ b/keymap/sublime.js
@@ -52,8 +52,10 @@
});
}
- cmds[map["Alt-Left"] = "goSubwordLeft"] = function(cm) { moveSubword(cm, -1); };
- cmds[map["Alt-Right"] = "goSubwordRight"] = function(cm) { moveSubword(cm, 1); };
+ var goSubwordCombo = mac ? "Ctrl-" : "Alt-";
+
+ 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";
From 84dc2d07fa19c237479c1681385a63d64fddcc74 Mon Sep 17 00:00:00 2001
From: Luke Granger-Brown
Date: Wed, 6 Jul 2016 13:28:29 +0100
Subject: [PATCH 0131/2085] [vim bindings] Make Backspace delete characters
In Replace mode (or Overwrite mode) it should still go backwards
without deleting characters.
---
keymap/vim.js | 19 ++++++++++++++++---
test/vim_test.js | 28 +++++++++++++++++++++++++++-
2 files changed, 43 insertions(+), 4 deletions(-)
diff --git a/keymap/vim.js b/keymap/vim.js
index 9891eb5fcb..afed132e36 100644
--- a/keymap/vim.js
+++ b/keymap/vim.js
@@ -53,7 +53,6 @@
{ keys: '', type: 'keyToKey', toKeys: 'j' },
{ keys: '', type: 'keyToKey', toKeys: 'l' },
{ keys: '', type: 'keyToKey', toKeys: 'h', context: 'normal'},
- { keys: '', type: 'motion', motion: 'moveByCharacters', motionArgs: { forward: false }, context: 'insert'},
{ keys: '', type: 'keyToKey', toKeys: 'W' },
{ keys: '', type: 'keyToKey', toKeys: 'B', context: 'normal' },
{ keys: '', type: 'keyToKey', toKeys: 'w' },
@@ -73,6 +72,7 @@
{ keys: '', type: 'keyToKey', toKeys: '' },
{ keys: '', type: 'keyToKey', toKeys: '' },
{ keys: '', type: 'keyToKey', toKeys: 'j^', context: 'normal' },
+ { keys: '', type: 'action', action: 'toggleOverwrite', context: 'insert' },
// Motions
{ keys: 'H', type: 'motion', motion: 'moveToTopLine', motionArgs: { linewise: true, toJumplist: true }},
{ keys: 'M', type: 'motion', motion: 'moveToMiddleLine', motionArgs: { linewise: true, toJumplist: true }},
@@ -277,6 +277,7 @@
function cmKey(key, cm) {
if (!cm) { return undefined; }
+ if (this[key]) { return this[key]; }
var vimKey = cmKeyToVimKey(key);
if (!vimKey) {
return false;
@@ -289,7 +290,7 @@
}
var modifiers = {'Shift': 'S', 'Ctrl': 'C', 'Alt': 'A', 'Cmd': 'D', 'Mod': 'A'};
- var specialKeys = {Enter:'CR',Backspace:'BS',Delete:'Del'};
+ var specialKeys = {Enter:'CR',Backspace:'BS',Delete:'Del',Insert:'Ins'};
function cmKeyToVimKey(key) {
if (key.charAt(0) == '\'') {
// Keypress character binding of format "'a'"
@@ -2175,6 +2176,17 @@
var registerName = actionArgs.selectedCharacter;
macroModeState.enterMacroRecordMode(cm, registerName);
},
+ toggleOverwrite: function(cm) {
+ if (!cm.state.overwrite) {
+ cm.toggleOverwrite(true);
+ cm.setOption('keyMap', 'vim-replace');
+ CodeMirror.signal(cm, "vim-mode-change", {mode: "replace"});
+ } else {
+ cm.toggleOverwrite(false);
+ cm.setOption('keyMap', 'vim-insert');
+ CodeMirror.signal(cm, "vim-mode-change", {mode: "insert"});
+ }
+ },
enterInsertMode: function(cm, actionArgs, vim) {
if (cm.getOption('readOnly')) { return; }
vim.insertMode = true;
@@ -2220,7 +2232,6 @@
return;
}
}
- cm.setOption('keyMap', 'vim-insert');
cm.setOption('disableInput', false);
if (actionArgs && actionArgs.replace) {
// Handle Replace-mode as a special case of insert mode.
@@ -2228,6 +2239,7 @@
cm.setOption('keyMap', 'vim-replace');
CodeMirror.signal(cm, "vim-mode-change", {mode: "replace"});
} else {
+ cm.toggleOverwrite(false);
cm.setOption('keyMap', 'vim-insert');
CodeMirror.signal(cm, "vim-mode-change", {mode: "insert"});
}
@@ -4779,6 +4791,7 @@
};
CodeMirror.keyMap['vim-replace'] = {
+ 'Backspace': 'goCharLeft',
fallthrough: ['vim-insert'],
attach: attachVimMap,
detach: detachVimMap,
diff --git a/test/vim_test.js b/test/vim_test.js
index fb612b1407..159114cecd 100644
--- a/test/vim_test.js
+++ b/test/vim_test.js
@@ -145,7 +145,7 @@ function testVim(name, run, opts, expectedFail) {
for (var i = 0; i < arguments.length; i++) {
var key = arguments[i];
// Find key in keymap and handle.
- var handled = CodeMirror.lookupKey(key, 'vim-insert', executeHandler);
+ var handled = CodeMirror.lookupKey(key, cm.getOption('keyMap'), executeHandler, cm);
// Record for insert mode.
if (handled == "handled" && cm.state.vim.insertMode && arguments[i] != 'Esc') {
var lastChange = CodeMirror.Vim.getVimGlobalState_().macroModeState.lastInsertModeChanges;
@@ -1419,6 +1419,32 @@ testVim('i_repeat_delete', function(cm, vim, helpers) {
eq('abe', cm.getValue());
helpers.assertCursorAt(0, 1);
}, { value: 'abcde' });
+testVim('insert', function(cm, vim, helpers) {
+ helpers.doKeys('i');
+ eq('vim-insert', cm.getOption('keyMap'));
+ eq(false, cm.state.overwrite);
+ helpers.doKeys('');
+ eq('vim-replace', cm.getOption('keyMap'));
+ eq(true, cm.state.overwrite);
+ helpers.doKeys('');
+ eq('vim-insert', cm.getOption('keyMap'));
+ eq(false, cm.state.overwrite);
+});
+testVim('i_backspace', function(cm, vim, helpers) {
+ cm.setCursor(0, 10);
+ helpers.doKeys('i');
+ helpers.doInsertModeKeys('Backspace');
+ helpers.assertCursorAt(0, 9);
+ eq('012345678', cm.getValue());
+}, { value: '0123456789'});
+testVim('i_overwrite_backspace', function(cm, vim, helpers) {
+ cm.setCursor(0, 10);
+ helpers.doKeys('i');
+ helpers.doKeys('');
+ helpers.doInsertModeKeys('Backspace');
+ helpers.assertCursorAt(0, 9);
+ eq('0123456789', cm.getValue());
+}, { value: '0123456789'});
testVim('A', function(cm, vim, helpers) {
helpers.doKeys('A');
helpers.assertCursorAt(0, lines[0].length);
From 51a1e7da60a99e019f026a118dc7c98c2b1f9d62 Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Tue, 12 Jul 2016 11:26:11 +0200
Subject: [PATCH 0132/2085] Reposition hidden textarea to work around Chrome
IME issue
Chrome would show the IME widget too high because it noticed the textarea
was cut off by the wrapping DIV.
Issue #4095
---
lib/codemirror.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lib/codemirror.js b/lib/codemirror.js
index 7be2fac1db..71010ddab2 100644
--- a/lib/codemirror.js
+++ b/lib/codemirror.js
@@ -1221,7 +1221,7 @@
};
function hiddenTextarea() {
- var te = elt("textarea", null, null, "position: absolute; padding: 0; width: 1px; height: 1em; outline: none");
+ 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
From 43749f8ca08bab0958631e20b8030a3a8de5fddc Mon Sep 17 00:00:00 2001
From: Paul Masson
Date: Thu, 14 Jul 2016 19:40:48 -0700
Subject: [PATCH 0133/2085] Minor rebase to remove Arabic textnode
---
lib/codemirror.js | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/lib/codemirror.js b/lib/codemirror.js
index 71010ddab2..d89b3b48f4 100644
--- a/lib/codemirror.js
+++ b/lib/codemirror.js
@@ -8558,8 +8558,9 @@
if (badBidiRects != null) return badBidiRects;
var txt = removeChildrenAndAdd(measure, document.createTextNode("A\u062eA"));
var r0 = range(txt, 0, 1).getBoundingClientRect();
- if (!r0 || r0.left == r0.right) return false; // Safari returns null in some cases (#2780)
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);
}
From a3d9a31655e11b0088108ac4066111451942e72f Mon Sep 17 00:00:00 2001
From: Siamak Mokhtari
Date: Thu, 23 Jun 2016 16:19:57 +0430
Subject: [PATCH 0134/2085] [panda-syntax theme] Add
---
demo/theme.html | 2 +
theme/panda-syntax.css | 94 ++++++++++++++++++++++++++++++++++++++++++
2 files changed, 96 insertions(+)
create mode 100644 theme/panda-syntax.css
diff --git a/demo/theme.html b/demo/theme.html
index 300e2625e3..60f36d6980 100644
--- a/demo/theme.html
+++ b/demo/theme.html
@@ -32,6 +32,7 @@
+
@@ -114,6 +115,7 @@ Theme Demo
neat
neo
night
+ panda-syntax
paraiso-dark
paraiso-light
pastel-on-dark
diff --git a/theme/panda-syntax.css b/theme/panda-syntax.css
new file mode 100644
index 0000000000..0e03500328
--- /dev/null
+++ b/theme/panda-syntax.css
@@ -0,0 +1,94 @@
+/*
+
+ Name: Panda Syntax
+ Author: Siamak Mokhtari (http://github.com/siamak/)
+
+ CodeMirror template by Siamak Mokhtari (https://github.com/siamak/atom-panda-syntax)
+
+*/
+.cm-s-panda-syntax {
+ /*font-family: 'Operator Mono', 'Source Sans Pro', Helvetica, Arial, sans-serif;*/
+ font-family: 'Operator Mono', 'Source Sans Pro', Menlo, Monaco, Consolas, Courier New, monospace;
+ background: #292A2B;
+ color: #E6E6E6;
+}
+.cm-s-panda-syntax .CodeMirror-activeline-background {
+ background: #404954;
+}
+
+.cm-s-panda-syntax .cm-comment {
+ font-style: italic;
+ color: #676B79;
+}
+.cm-s-panda-syntax .cm-string,
+.cm-s-panda-syntax .cm-string-2 {
+ color: #19F9D8;
+}
+.cm-s-panda-syntax .cm-number {
+ color: #FFB86C;
+}
+.cm-s-panda-syntax .cm-atom {
+ color: #FFB86C;
+}
+
+.cm-s-panda-syntax .cm-keyword {
+ color: #FF75B5;
+}
+.cm-s-panda-syntax .cm-keyword-2 {
+ color: #FF75B5;
+}
+.cm-s-panda-syntax .cm-keyword-3 {
+ color: #B084EB;
+}
+
+.cm-s-panda-syntax .cm-variable {
+ color: #FF9AC1;
+}
+.cm-s-panda-syntax .cm-variable-2 {
+ color: #e6e6e6;
+}
+.cm-s-panda-syntax .cm-variable-3 {
+ color: #82B1FF;
+}
+
+.cm-s-panda-syntax .cm-def {
+ /*font-style: italic;*/
+ color: #e6e6e6;
+}
+.cm-s-panda-syntax .cm-def-2 {
+ font-style: italic;
+ color: #ffcc95;
+}
+
+
+.cm-s-panda-syntax .cm-property {
+ color: #6FC1FF;
+}
+
+.cm-s-panda-syntax .cm-matchingbracket,
+.CodeMirror .CodeMirror-matchingbracket {
+ color: #E6E6E6 !important;
+ border-bottom: 1px dotted #19f9d8;
+ padding-bottom: 2px;
+}
+
+.cm-s-panda-syntax .CodeMirror-gutters {
+ background: #292A2B;
+ color: #757575;
+ border: none;
+}
+.cm-s-panda-syntax .CodeMirror-guttermarker, .cm-s-panda-syntax .CodeMirror-guttermarker-subtle, .cm-s-panda-syntax .CodeMirror-linenumber {
+ color: #757575;
+}
+.cm-s-panda-syntax .CodeMirror-linenumber {
+ padding-right: 10px;
+}
+.cm-s-panda-syntax .CodeMirror-cursor {
+ border-left: 1px solid #757575;
+}
+/*.cm-s-panda-syntax div.CodeMirror-selected { background: rgba(255, 255, 255, 0.5); }*/
+.cm-s-panda-syntax.CodeMirror-focused div.CodeMirror-selected { background: rgba(255, 255, 255, 0.25); }
+.cm-s-panda-syntax .CodeMirror-line::selection, .cm-s-panda-syntax .CodeMirror-line > span::selection, .cm-s-panda-syntax .CodeMirror-line > span > span::selection { background: rgba(255, 255, 255, 0.10); }
+.cm-s-panda-syntax .CodeMirror-line::-moz-selection, .cm-s-panda-syntax .CodeMirror-line > span::-moz-selection, .cm-s-panda-syntax .CodeMirror-line > span > span::-moz-selection { background: rgba(255, 255, 255, 0.10); }
+
+.cm-s-panda-syntax .CodeMirror-activeline-background { background: rgba(99, 123, 156, 0.125); }
From e60f4b7dd88b71e227f79200932c028ca047d78e Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Tue, 19 Jul 2016 13:26:35 +0200
Subject: [PATCH 0135/2085] Don't trust bounding client rects for characters
In some cases, on wrapping points, they span the whole line
Issue #4097
---
lib/codemirror.js | 23 +++++++++++++----------
1 file changed, 13 insertions(+), 10 deletions(-)
diff --git a/lib/codemirror.js b/lib/codemirror.js
index d89b3b48f4..c78a6f22da 100644
--- a/lib/codemirror.js
+++ b/lib/codemirror.js
@@ -2688,6 +2688,16 @@
return {node: node, start: start, end: end, collapse: collapse, coverStart: mStart, coverEnd: mEnd};
}
+ function getUsefulRect(rects, bias) {
+ var rect = nullRect
+ if (bias == "left") for (var i = 0; i < rects.length; i++) {
+ if ((rect = rects[i]).left != rect.right) break
+ } else for (var 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;
@@ -2697,17 +2707,10 @@
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;
- if (ie && ie_version < 9 && start == 0 && end == place.coverEnd - place.coverStart) {
+ if (ie && ie_version < 9 && start == 0 && end == place.coverEnd - place.coverStart)
rect = node.parentNode.getBoundingClientRect();
- } else if (ie && cm.options.lineWrapping) {
- var rects = range(node, start, end).getClientRects();
- if (rects.length)
- rect = rects[bias == "right" ? rects.length - 1 : 0];
- else
- rect = nullRect;
- } else {
- rect = range(node, start, end).getBoundingClientRect() || nullRect;
- }
+ else
+ rect = getUsefulRect(range(node, start, end).getClientRects(), bias)
if (rect.left || rect.right || start == 0) break;
end = start;
start = start - 1;
From 31eb1b2f0b3ef57881d16287bfaf1a14d4df1f4e Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Tue, 19 Jul 2016 13:39:38 +0200
Subject: [PATCH 0136/2085] [javascript mode] Handle trailing commas better
---
mode/javascript/javascript.js | 5 ++++-
mode/javascript/test.js | 3 +++
2 files changed, 7 insertions(+), 1 deletion(-)
diff --git a/mode/javascript/javascript.js b/mode/javascript/javascript.js
index ddb46bab27..d0dc9f0647 100644
--- a/mode/javascript/javascript.js
+++ b/mode/javascript/javascript.js
@@ -495,7 +495,10 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
if (type == ",") {
var lex = cx.state.lexical;
if (lex.info == "call") lex.pos = (lex.pos || 0) + 1;
- return cont(what, proceed);
+ return cont(function(type, value) {
+ if (type == end || value == end) return pass()
+ return pass(what)
+ }, proceed);
}
if (type == end || value == end) return cont();
return cont(expect(end));
diff --git a/mode/javascript/test.js b/mode/javascript/test.js
index cb43d0894d..18f1bd54c9 100644
--- a/mode/javascript/test.js
+++ b/mode/javascript/test.js
@@ -44,6 +44,9 @@
" [keyword import] { [def encrypt], [def decrypt] } [keyword from] [string 'crypto'];",
"}");
+ MT("import_trailing_comma",
+ "[keyword import] {[def foo], [def bar],} [keyword from] [string 'baz']")
+
MT("const",
"[keyword function] [def f]() {",
" [keyword const] [[ [def a], [def b] ]] [operator =] [[ [number 1], [number 2] ]];",
From b8c8a3ee7bfc98e2d3fc2448058a881ec74130ce Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Tue, 19 Jul 2016 13:40:51 +0200
Subject: [PATCH 0137/2085] [javascript mode] Remove support for array/iterator
comprehension
---
mode/javascript/javascript.js | 13 ++-----------
mode/javascript/test.js | 6 ------
2 files changed, 2 insertions(+), 17 deletions(-)
diff --git a/mode/javascript/javascript.js b/mode/javascript/javascript.js
index d0dc9f0647..3909c85e6a 100644
--- a/mode/javascript/javascript.js
+++ b/mode/javascript/javascript.js
@@ -387,7 +387,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
if (atomicTypes.hasOwnProperty(type)) return cont(maybeop);
if (type == "function") return cont(functiondef, maybeop);
if (type == "keyword c" || type == "async") return cont(noComma ? maybeexpressionNoComma : maybeexpression);
- if (type == "(") return cont(pushlex(")"), maybeexpression, comprehension, expect(")"), poplex, maybeop);
+ if (type == "(") return cont(pushlex(")"), maybeexpression, expect(")"), poplex, maybeop);
if (type == "operator" || type == "spread") return cont(noComma ? expressionNoComma : expression);
if (type == "[") return cont(pushlex("]"), arrayLiteral, poplex, maybeop);
if (type == "{") return contCommasep(objprop, "}", null, maybeop);
@@ -641,16 +641,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
}
function arrayLiteral(type) {
if (type == "]") return cont();
- return pass(expressionNoComma, maybeArrayComprehension);
- }
- function maybeArrayComprehension(type) {
- if (type == "for") return pass(comprehension, expect("]"));
- if (type == ",") return cont(commasep(maybeexpressionNoComma, "]"));
- return pass(commasep(expressionNoComma, "]"));
- }
- function comprehension(type) {
- if (type == "for") return cont(forspec, comprehension);
- if (type == "if") return cont(expression, comprehension);
+ return pass(expressionNoComma, commasep(expressionNoComma, "]"));
}
function isContinuedStatement(state, textAfter) {
diff --git a/mode/javascript/test.js b/mode/javascript/test.js
index 18f1bd54c9..8916b7558f 100644
--- a/mode/javascript/test.js
+++ b/mode/javascript/test.js
@@ -78,12 +78,6 @@
" [variable something]([variable-2 a], [meta ...][variable-2 b]);",
"}");
- MT("comprehension",
- "[keyword function] [def f]() {",
- " [[([variable x] [operator +] [number 1]) [keyword for] ([keyword var] [def x] [keyword in] [variable y]) [keyword if] [variable pred]([variable-2 x]) ]];",
- " ([variable u] [keyword for] ([keyword var] [def u] [keyword of] [variable generateValues]()) [keyword if] ([variable-2 u].[property color] [operator ===] [string 'blue']));",
- "}");
-
MT("quasi",
"[variable re][string-2 `fofdlakj${][variable x] [operator +] ([variable re][string-2 `foo`]) [operator +] [number 1][string-2 }fdsa`] [operator +] [number 2]");
From e873e9d67e181c4c303b2594af116533c6332526 Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Tue, 19 Jul 2016 13:48:36 +0200
Subject: [PATCH 0138/2085] [python mode] Make sure the base scope is never
dropped
Closes #4105
---
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 ec662b1a95..be65ad7687 100644
--- a/mode/python/python.js
+++ b/mode/python/python.js
@@ -233,7 +233,7 @@
function dedent(stream, state) {
var indented = stream.indentation();
- while (top(state).offset > indented) {
+ while (state.scopes.length > 1 && top(state).offset > indented) {
if (top(state).type != "py") return true;
state.scopes.pop();
}
From dc4198f467f032ca8240afb773f922547847856e Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Tue, 19 Jul 2016 13:51:08 +0200
Subject: [PATCH 0139/2085] [real-world uses] Add Kodit
---
doc/realworld.html | 1 +
1 file changed, 1 insertion(+)
diff --git a/doc/realworld.html b/doc/realworld.html
index 7b7eeeaf1a..1b272862b8 100644
--- a/doc/realworld.html
+++ b/doc/realworld.html
@@ -107,6 +107,7 @@ CodeMirror real-world uses
JSHint (JS linter)
Jumpseller (online store builder)
kl1p (paste service)
+ Kodit
Kodtest (HTML/JS/CSS playground)
Kotlin (web-based mini-IDE for Kotlin)
Laborate (collaborative coding)
From 8019d7cd83fc1712ea94a59b8a2ea27b946c23fe Mon Sep 17 00:00:00 2001
From: Erik Welander
Date: Tue, 12 Jul 2016 14:57:18 -0700
Subject: [PATCH 0140/2085] [match-highlighter addon] Make scrollbar
annotations case-sensitive
To match the actual match-highlights in the editor.
---
addon/search/match-highlighter.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/addon/search/match-highlighter.js b/addon/search/match-highlighter.js
index 2c2914a9d0..2121de417b 100644
--- a/addon/search/match-highlighter.js
+++ b/addon/search/match-highlighter.js
@@ -72,7 +72,7 @@
cm.addOverlay(state.overlay = makeOverlay(query, hasBoundary, style));
if (state.options.annotateScrollbar && cm.showMatchesOnScrollbar) {
var searchFor = hasBoundary ? new RegExp("\\b" + query + "\\b") : query;
- state.matchesonscroll = cm.showMatchesOnScrollbar(searchFor, true,
+ state.matchesonscroll = cm.showMatchesOnScrollbar(searchFor, false,
{className: "CodeMirror-selection-highlight-scrollbar"});
}
}
From f532bda3aacaa621fd5d72ee1d114f312359ccff Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Tue, 19 Jul 2016 14:02:51 +0200
Subject: [PATCH 0141/2085] [merge addon] Adjust for space above editor when
drawing connections
Issue #4112
---
addon/merge/merge.js | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/addon/merge/merge.js b/addon/merge/merge.js
index d67b760cae..e8a8d689f5 100644
--- a/addon/merge/merge.js
+++ b/addon/merge/merge.js
@@ -284,7 +284,9 @@
if (dv.copyButtons) clear(dv.copyButtons);
var vpEdit = dv.edit.getViewport(), vpOrig = dv.orig.getViewport();
- var sTopEdit = dv.edit.getScrollInfo().top, sTopOrig = dv.orig.getScrollInfo().top;
+ var outerTop = dv.mv.wrap.getBoundingClientRect().top
+ var sTopEdit = outerTop - dv.edit.getScrollerElement().getBoundingClientRect().top + dv.edit.getScrollInfo().top
+ let sTopOrig = outerTop - dv.orig.getScrollerElement().getBoundingClientRect().top + dv.orig.getScrollInfo().top;
for (var i = 0; i < dv.chunks.length; i++) {
var ch = dv.chunks[i];
if (ch.editFrom <= vpEdit.to && ch.editTo >= vpEdit.from &&
From 5d9808a0f4f659b56f3076727acbd2bd9ddb7f8e Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Tue, 19 Jul 2016 14:06:37 +0200
Subject: [PATCH 0142/2085] Fix accidental 'let'
---
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 e8a8d689f5..cc94cafb8c 100644
--- a/addon/merge/merge.js
+++ b/addon/merge/merge.js
@@ -286,7 +286,7 @@
var vpEdit = dv.edit.getViewport(), vpOrig = dv.orig.getViewport();
var outerTop = dv.mv.wrap.getBoundingClientRect().top
var sTopEdit = outerTop - dv.edit.getScrollerElement().getBoundingClientRect().top + dv.edit.getScrollInfo().top
- let sTopOrig = outerTop - dv.orig.getScrollerElement().getBoundingClientRect().top + dv.orig.getScrollInfo().top;
+ var sTopOrig = outerTop - dv.orig.getScrollerElement().getBoundingClientRect().top + dv.orig.getScrollInfo().top;
for (var i = 0; i < dv.chunks.length; i++) {
var ch = dv.chunks[i];
if (ch.editFrom <= vpEdit.to && ch.editTo >= vpEdit.from &&
From b6c7f3ff2e04a3d41937c30cdedbd004e5eb22d2 Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Tue, 19 Jul 2016 14:16:33 +0200
Subject: [PATCH 0143/2085] Mark release 5.17.0
---
AUTHORS | 4 ++++
CHANGELOG.md | 20 ++++++++++++++++++++
doc/compress.html | 1 +
doc/manual.html | 2 +-
doc/releases.html | 12 ++++++++++++
index.html | 2 +-
lib/codemirror.js | 2 +-
package.json | 2 +-
8 files changed, 41 insertions(+), 4 deletions(-)
diff --git a/AUTHORS b/AUTHORS
index ce2be8d6d2..6a96b87c0a 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -218,6 +218,7 @@ Hardest
Harshvardhan Gupta
Hasan Karahan
Hector Oswaldo Caballero
+Hendrik Wallbaum
Herculano Campos
Hiroyuki Makino
hitsthings
@@ -332,6 +333,7 @@ lochel
Lorenzo Stoakes
Luciano Longo
Lu Fangjian
+Luke Granger-Brown
Luke Stagner
lynschinzer
M1cha
@@ -444,6 +446,7 @@ Patrick Stoica
Patrick Strawderman
Paul Garvin
Paul Ivanov
+Paul Masson
Pavel
Pavel Feldman
Pavel Petržela
@@ -507,6 +510,7 @@ sheopory
Shiv Deepak
Shmuel Englard
Shubham Jain
+Siamak Mokhtari
silverwind
sinkuu
snasa
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a34cf82ffa..55ace8ffa3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,23 @@
+## 5.17.0 (2016-07-19)
+
+### Bugfixes
+
+Fix problem with wrapped trailing whitespace displaying incorrectly.
+
+Prevent IME dialog from overlapping typed content in Chrome.
+
+Improve measuring of characters near a line wrap.
+
+[javascript mode](http://codemirror.net/mode/javascript): Improve support for `async`, allow trailing commas in `import` lists.
+
+[vim bindings](http://codemirror.net/demo/vim.html): Fix backspace in replace mode.
+
+[sublime bindings](http://codemirror.net/demo/sublime.html): Fix some key bindings on OS X to match Sublime Text.
+
+### New features
+
+[markdown mode](http://codemirror.net/mode/markdown): Add more classes to image links in highlight-formatting mode.
+
## 5.16.0 (2016-06-20)
### Bugfixes
diff --git a/doc/compress.html b/doc/compress.html
index b890bee021..7f08f71c24 100644
--- a/doc/compress.html
+++ b/doc/compress.html
@@ -36,6 +36,7 @@ Script compression helper
Version:
HEAD
+ 5.17.0
5.16.0
5.15.2
5.15.0
diff --git a/doc/manual.html b/doc/manual.html
index 030287c3ef..f61c1e1d7a 100644
--- a/doc/manual.html
+++ b/doc/manual.html
@@ -69,7 +69,7 @@
User manual and reference guide
- version 5.16.2
+ version 5.17.0
CodeMirror is a code-editor component that can be embedded in
diff --git a/doc/releases.html b/doc/releases.html
index 052f7809c0..17ec88a96d 100644
--- a/doc/releases.html
+++ b/doc/releases.html
@@ -30,6 +30,18 @@
Release notes and version history
Version 5.x
+ 19-07-2016: Version 5.17.0 :
+
+
+ Fix problem with wrapped trailing whitespace displaying incorrectly.
+ Prevent IME dialog from overlapping typed content in Chrome.
+ Improve measuring of characters near a line wrap.
+ javascript mode : Improve support for async, allow trailing commas in import lists.
+ vim bindings : Fix backspace in replace mode.
+ sublime bindings : Fix some key bindings on OS X to match Sublime Text.
+ markdown mode : Add more classes to image links in highlight-formatting mode.
+
+
20-06-2016: Version 5.16.0 :
diff --git a/index.html b/index.html
index 528b3a430a..8860f8b4e3 100644
--- a/index.html
+++ b/index.html
@@ -96,7 +96,7 @@ This is CodeMirror
- Get the current version:
5.16.0 .
+ Get the current version:
5.17.0 .
You can see the
code or
read the
release notes .
There is a
minification helper .
diff --git a/lib/codemirror.js b/lib/codemirror.js
index c78a6f22da..5009f836cb 100644
--- a/lib/codemirror.js
+++ b/lib/codemirror.js
@@ -8929,7 +8929,7 @@
// THE END
- CodeMirror.version = "5.16.2";
+ CodeMirror.version = "5.17.0";
return CodeMirror;
});
diff --git a/package.json b/package.json
index 276c2d29e9..fa09ced888 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "codemirror",
- "version":"5.16.2",
+ "version":"5.17.0",
"main": "lib/codemirror.js",
"description": "Full-featured in-browser code editor",
"license": "MIT",
From 791fdd1f9874c284319b176f080e1dfdfc06e5d8 Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Tue, 19 Jul 2016 14:19:58 +0200
Subject: [PATCH 0144/2085] Bump version number post-5.17.0
---
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 f61c1e1d7a..408ac8bceb 100644
--- a/doc/manual.html
+++ b/doc/manual.html
@@ -69,7 +69,7 @@
User manual and reference guide
- version 5.17.0
+ version 5.17.1
CodeMirror is a code-editor component that can be embedded in
diff --git a/lib/codemirror.js b/lib/codemirror.js
index 5009f836cb..ff7126fb7f 100644
--- a/lib/codemirror.js
+++ b/lib/codemirror.js
@@ -8929,7 +8929,7 @@
// THE END
- CodeMirror.version = "5.17.0";
+ CodeMirror.version = "5.17.1";
return CodeMirror;
});
diff --git a/package.json b/package.json
index fa09ced888..135a9608c1 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "codemirror",
- "version":"5.17.0",
+ "version":"5.17.1",
"main": "lib/codemirror.js",
"description": "Full-featured in-browser code editor",
"license": "MIT",
From 8a1dd1604974840fe69163f958ad204010442aea Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Tue, 19 Jul 2016 23:14:59 +0200
Subject: [PATCH 0145/2085] [lint demo] Remove css lint example
Since the only pre-build version of csslint was changed to some
global-scope-polluting mess that breaks our module loader headers.
Issue #4123
---
demo/lint.html | 68 --------------------------------------------------
1 file changed, 68 deletions(-)
diff --git a/demo/lint.html b/demo/lint.html
index 96009b4e1f..ea0297b27d 100644
--- a/demo/lint.html
+++ b/demo/lint.html
@@ -11,11 +11,9 @@
-
-
@@ -85,66 +83,6 @@ Linter Demo
]
-
From 23172fded8ddc9c9b1bee9c2617f25c884ed9d0d Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Tue, 19 Jul 2016 23:17:38 +0200
Subject: [PATCH 0146/2085] 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 7f08f71c24..80de52da93 100644
--- a/doc/compress.html
+++ b/doc/compress.html
@@ -252,12 +252,14 @@ Script compression helper
continuecomment.js
continuelist.js
css-hint.js
+ css-lint.js
dialog.js
foldcode.js
foldgutter.js
fullscreen.js
hardwrap.js
html-hint.js
+ html-lint.js
indent-fold.js
javascript-hint.js
javascript-lint.js
From f8b765b2eb5ab7d99b038f41de3d35621d71baee Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Fri, 22 Jul 2016 13:54:18 +0200
Subject: [PATCH 0147/2085] [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 70e1ae18c7..76754ed557 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 0148/2085] [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 3909c85e6a..923f42e248 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 0149/2085] [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 923f42e248..da6b760f78 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 0150/2085] 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 ff7126fb7f..4568673e3c 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 0151/2085] [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 d74083ee1a..eb21fcc149 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 0152/2085] [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 da6b760f78..79239ef46f 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 0153/2085] 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 79239ef46f..092bc6f356 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 0154/2085] [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 4b1e16307a..1f39c7edb2 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 0155/2085] 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 4568673e3c..d08aa67f67 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 0156/2085] [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 c005866f16..cd61256519 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 0157/2085] [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 cd61256519..be14c93eed 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 0158/2085] [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 be14c93eed..753b1afe1e 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 0159/2085] 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 d08aa67f67..65bfc7999e 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 0160/2085] [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 408ac8bceb..b912a78f19 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 0161/2085] [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 4b26e04619..1e363f8769 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 0162/2085] [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 2c4f975d08..2f2f071df9 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 26e474493b..3e6a86bbf2 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 0163/2085] [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 cb51edb364..1054bbcc0a 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 aff01b8d35..45c3024aba 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 0164/2085] [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 092bc6f356..18c65e90dd 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 0165/2085] [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 18c65e90dd..17f05045c9 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 0166/2085] 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 65bfc7999e..0d34b8521a 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 0167/2085] 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 b912a78f19..8da9cbb2a3 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 0d34b8521a..b36b3697fa 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 0168/2085] Remove debug statement
---
lib/codemirror.js | 1 -
1 file changed, 1 deletion(-)
diff --git a/lib/codemirror.js b/lib/codemirror.js
index b36b3697fa..1702dbe9b8 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 0169/2085] [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 80de52da93..3487d75e5a 100644
--- a/doc/compress.html
+++ b/doc/compress.html
@@ -166,7 +166,6 @@ Script compression helper
htmlmixed.js
http.js
idl.js
- jade.js
javascript.js
jinja2.js
julia.js
@@ -193,6 +192,7 @@ Script compression helper
properties.js
protobuf.js
python.js
+ pug.js
puppet.js
q.js
r.js
diff --git a/mode/index.html b/mode/index.html
index 732e0e52cd..3a2fe5513e 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 eb25e242dd..2e9ac7ce4b 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 e534981b20..1765853a1f 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 51ed105aca..401823365c 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 0170/2085] 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 1702dbe9b8..0d3b7ba597 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 0171/2085] [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 2121de417b..73ba0e0537 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 0172/2085] [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 b7b65608e6..d0dee8a70e 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 0173/2085] 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 0d3b7ba597..aa125b66e2 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 0174/2085] 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 aa125b66e2..292eda966d 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 0175/2085] 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 292eda966d..58c45f4579 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 0176/2085] [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 8da9cbb2a3..40892d92ef 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 0177/2085] [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 7bcbf4a057..36d7dadf98 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 0178/2085] [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 36d7dadf98..d7088c191b 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 0179/2085] [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 be65ad7687..4873a3e3a6 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 0180/2085] [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 fe114827c0..e88744be27 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 0181/2085] [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 4873a3e3a6..5142f97077 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 0182/2085] [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 17f05045c9..df5f714f7a 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 0183/2085] [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 5142f97077..efeed7f154 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 0184/2085] [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 e88744be27..9ec22d5fa5 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 0185/2085] [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 9ec22d5fa5..a6109eb11c 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 0186/2085] [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 df5f714f7a..d7c5716b08 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 8916b7558f..11a13568db 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 0aed50f7dc..f706c4f5c6 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 0187/2085] 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 6a96b87c0a..0f2bc1bbf3 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 55ace8ffa3..5e1973fbf7 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 3487d75e5a..1cded19a7e 100644
--- a/doc/compress.html
+++ b/doc/compress.html
@@ -36,6 +36,7 @@ Script compression helper
Version:
HEAD
+ 5.18.0
5.17.0
5.16.0
5.15.2
diff --git a/doc/manual.html b/doc/manual.html
index 40892d92ef..9d5c7b9cd2 100644
--- a/doc/manual.html
+++ b/doc/manual.html
@@ -69,7 +69,7 @@
User manual and reference guide
- version 5.17.1
+ version 5.18.0
CodeMirror is a code-editor component that can be embedded in
diff --git a/doc/releases.html b/doc/releases.html
index 17ec88a96d..9a615030e4 100644
--- a/doc/releases.html
+++ b/doc/releases.html
@@ -30,6 +30,21 @@
Release notes and version history
Version 5.x
+ 22-08-2016: Version 5.18.0 :
+
+
+ Make sure gutter backgrounds stick to the rest of the gutter during horizontal scrolling.
+ The contenteditable inputStyle now properly supports pasting on pre-Edge IE versions.
+ javascript mode : Fix some small parsing bugs and improve TypeScript support.
+ matchbrackets addon : Fix bug where active highlighting was left in editor when the addon was disabled.
+ match-highlighter addon : Only start highlighting things when the editor gains focus.
+ javascript-hint addon : Also complete non-enumerable properties.
+ The 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.
+ The mode formerly known as Jade was renamed to Pug .
+ The Python mode now defaults to Python 3 (rather than 2) syntax.
+
+
19-07-2016: Version 5.17.0 :
diff --git a/index.html b/index.html
index 8860f8b4e3..ab03ca7521 100644
--- a/index.html
+++ b/index.html
@@ -96,7 +96,7 @@ This is CodeMirror
- Get the current version:
5.17.0 .
+ Get the current version:
5.18.0 .
You can see the
code or
read the
release notes .
There is a
minification helper .
diff --git a/lib/codemirror.js b/lib/codemirror.js
index 58c45f4579..68b4e953fa 100644
--- a/lib/codemirror.js
+++ b/lib/codemirror.js
@@ -8955,7 +8955,7 @@
// THE END
- CodeMirror.version = "5.17.1";
+ CodeMirror.version = "5.18.0";
return CodeMirror;
});
diff --git a/package.json b/package.json
index 135a9608c1..5f02281f4d 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "codemirror",
- "version":"5.17.1",
+ "version":"5.18.0",
"main": "lib/codemirror.js",
"description": "Full-featured in-browser code editor",
"license": "MIT",
From 29722b14f06a849d70ee1622e58f52c0b797d963 Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Mon, 22 Aug 2016 15:46:55 +0200
Subject: [PATCH 0188/2085] Bump version post-5.18
---
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 9d5c7b9cd2..f1162f186b 100644
--- a/doc/manual.html
+++ b/doc/manual.html
@@ -69,7 +69,7 @@
User manual and reference guide
- version 5.18.0
+ version 5.18.1
CodeMirror is a code-editor component that can be embedded in
diff --git a/lib/codemirror.js b/lib/codemirror.js
index 68b4e953fa..763034d2b3 100644
--- a/lib/codemirror.js
+++ b/lib/codemirror.js
@@ -8955,7 +8955,7 @@
// THE END
- CodeMirror.version = "5.18.0";
+ CodeMirror.version = "5.18.1";
return CodeMirror;
});
diff --git a/package.json b/package.json
index 5f02281f4d..76ebc3ee27 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "codemirror",
- "version":"5.18.0",
+ "version":"5.18.1",
"main": "lib/codemirror.js",
"description": "Full-featured in-browser code editor",
"license": "MIT",
From f0cb87d4cde902e2e64b2d66e793ba76327e9367 Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Tue, 23 Aug 2016 14:31:18 +0200
Subject: [PATCH 0189/2085] =?UTF-8?q?[vue=20mode]=20Fix=20dependency=20on?=
=?UTF-8?q?=20Jade=E2=86=92Pug=20mode?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Closes #4190
---
mode/vue/index.html | 2 +-
mode/vue/vue.js | 10 +++++-----
2 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/mode/vue/index.html b/mode/vue/index.html
index cccb9764f3..e0b45b9452 100644
--- a/mode/vue/index.html
+++ b/mode/vue/index.html
@@ -14,7 +14,7 @@
-
+
diff --git a/mode/vue/vue.js b/mode/vue/vue.js
index d89a552387..f8089af501 100644
--- a/mode/vue/vue.js
+++ b/mode/vue/vue.js
@@ -12,7 +12,7 @@
require("../css/css"),
require("../sass/sass"),
require("../stylus/stylus"),
- require("../jade/jade"),
+ require("../pug/pug"),
require("../handlebars/handlebars"));
} else if (typeof define === "function" && define.amd) { // AMD
define(["../../lib/codemirror",
@@ -23,7 +23,7 @@
"../css/css",
"../sass/sass",
"../stylus/stylus",
- "../jade/jade",
+ "../pug/pug",
"../handlebars/handlebars"], mod);
} else { // Plain browser env
mod(CodeMirror);
@@ -42,9 +42,9 @@
],
template: [
["lang", /^vue-template$/i, "vue"],
- ["lang", /^jade$/i, "jade"],
+ ["lang", /^pug$/i, "pug"],
["lang", /^handlebars$/i, "handlebars"],
- ["type", /^(text\/)?(x-)?jade$/i, "jade"],
+ ["type", /^(text\/)?(x-)?pug$/i, "pug"],
["type", /^text\/x-handlebars-template$/i, "handlebars"],
[null, null, "vue-template"]
]
@@ -63,7 +63,7 @@
CodeMirror.defineMode("vue", function (config) {
return CodeMirror.getMode(config, {name: "htmlmixed", tags: tagLanguages});
- }, "htmlmixed", "xml", "javascript", "coffeescript", "css", "sass", "stylus", "jade", "handlebars");
+ }, "htmlmixed", "xml", "javascript", "coffeescript", "css", "sass", "stylus", "pug", "handlebars");
CodeMirror.defineMIME("script/x-vue", "vue");
});
From 136324e0b7c8f3c163d633161fb01ed2066d57b8 Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Tue, 23 Aug 2016 17:02:53 +0200
Subject: [PATCH 0190/2085] Mark release 5.18.2
---
CHANGELOG.md | 6 ++++++
doc/compress.html | 1 +
doc/manual.html | 2 +-
doc/releases.html | 6 ++++++
index.html | 2 +-
lib/codemirror.js | 2 +-
package.json | 2 +-
7 files changed, 17 insertions(+), 4 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5e1973fbf7..9470deb79f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,9 @@
+## 5.18.2 (2016-08-23)
+
+### Bugfixes
+
+[vue mode](http://codemirror.net/mode/vue): Fix outdated references to renamed Pug mode dependency.
+
## 5.18.0 (2016-08-22)
### Bugfixes
diff --git a/doc/compress.html b/doc/compress.html
index 1cded19a7e..7f12f2c430 100644
--- a/doc/compress.html
+++ b/doc/compress.html
@@ -36,6 +36,7 @@ Script compression helper
Version:
HEAD
+ 5.18.2
5.18.0
5.17.0
5.16.0
diff --git a/doc/manual.html b/doc/manual.html
index f1162f186b..d6f5c521c4 100644
--- a/doc/manual.html
+++ b/doc/manual.html
@@ -69,7 +69,7 @@
User manual and reference guide
- version 5.18.1
+ version 5.18.2
CodeMirror is a code-editor component that can be embedded in
diff --git a/doc/releases.html b/doc/releases.html
index 9a615030e4..fa1dd69ac8 100644
--- a/doc/releases.html
+++ b/doc/releases.html
@@ -30,6 +30,12 @@
Release notes and version history
Version 5.x
+ 23-08-2016: Version 5.18.2 :
+
+
+ vue mode : Fix outdated references to renamed Pug mode dependency.
+
+
22-08-2016: Version 5.18.0 :
diff --git a/index.html b/index.html
index ab03ca7521..f3aba01bdf 100644
--- a/index.html
+++ b/index.html
@@ -96,7 +96,7 @@ This is CodeMirror
- Get the current version:
5.18.0 .
+ Get the current version:
5.18.2 .
You can see the
code or
read the
release notes .
There is a
minification helper .
diff --git a/lib/codemirror.js b/lib/codemirror.js
index 763034d2b3..1071bb9c90 100644
--- a/lib/codemirror.js
+++ b/lib/codemirror.js
@@ -8955,7 +8955,7 @@
// THE END
- CodeMirror.version = "5.18.1";
+ CodeMirror.version = "5.18.2";
return CodeMirror;
});
diff --git a/package.json b/package.json
index 76ebc3ee27..2fa2915918 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "codemirror",
- "version":"5.18.1",
+ "version":"5.18.2",
"main": "lib/codemirror.js",
"description": "Full-featured in-browser code editor",
"license": "MIT",
From 08b8b7b0d4a2168756829d421649ca149e3cc151 Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Tue, 23 Aug 2016 17:04:22 +0200
Subject: [PATCH 0191/2085] Bump version number post-5.18.2
---
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 d6f5c521c4..d7c15f653d 100644
--- a/doc/manual.html
+++ b/doc/manual.html
@@ -69,7 +69,7 @@
User manual and reference guide
- version 5.18.2
+ version 5.18.3
CodeMirror is a code-editor component that can be embedded in
diff --git a/lib/codemirror.js b/lib/codemirror.js
index 1071bb9c90..6e0b69c610 100644
--- a/lib/codemirror.js
+++ b/lib/codemirror.js
@@ -8955,7 +8955,7 @@
// THE END
- CodeMirror.version = "5.18.2";
+ CodeMirror.version = "5.18.3";
return CodeMirror;
});
diff --git a/package.json b/package.json
index 2fa2915918..86b65fbdfe 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "codemirror",
- "version":"5.18.2",
+ "version":"5.18.3",
"main": "lib/codemirror.js",
"description": "Full-featured in-browser code editor",
"license": "MIT",
From c44a084f4446b5120fa28ace99786c2a92b691a3 Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Fri, 26 Aug 2016 13:00:53 +0200
Subject: [PATCH 0192/2085] [python mode] Update docs to reflect new version
default
Closes #4201
---
mode/python/index.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/mode/python/index.html b/mode/python/index.html
index 6116a13b6b..0ac02a3333 100644
--- a/mode/python/index.html
+++ b/mode/python/index.html
@@ -176,7 +176,7 @@ Cython mode
Configuration Options for Python mode:
- version - 2/3 - The version of Python to recognize. Default is 2.
+ version - 2/3 - The version of Python to recognize. Default is 3.
singleLineStringErrors - true/false - If you have a single-line string that is not terminated at the end of the line, this will show subsequent lines as errors if true, otherwise it will consider the newline as the end of the string. Default is false.
hangingIndent - int - If you want to write long arguments to a function starting on a new line, how much that line should be indented. Defaults to one normal indentation unit.
From 2f814b2b8a5a9b1267e173bedcf45dddaf7196f0 Mon Sep 17 00:00:00 2001
From: Daniele Di Sarli
Date: Sun, 28 Aug 2016 20:05:22 +0200
Subject: [PATCH 0193/2085] [pastel-on-dark theme] Remove absolute font size
Closes #4206
---
theme/pastel-on-dark.css | 1 -
1 file changed, 1 deletion(-)
diff --git a/theme/pastel-on-dark.css b/theme/pastel-on-dark.css
index 7197509205..2603d36205 100644
--- a/theme/pastel-on-dark.css
+++ b/theme/pastel-on-dark.css
@@ -11,7 +11,6 @@
background: #2c2827;
color: #8F938F;
line-height: 1.5;
- font-size: 14px;
}
.cm-s-pastel-on-dark div.CodeMirror-selected { background: rgba(221,240,255,0.2); }
.cm-s-pastel-on-dark .CodeMirror-line::selection, .cm-s-pastel-on-dark .CodeMirror-line > span::selection, .cm-s-pastel-on-dark .CodeMirror-line > span > span::selection { background: rgba(221,240,255,0.2); }
From ba1b1e513901a18637bf41b74bb4a4136f672cce Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Sun, 28 Aug 2016 20:28:28 +0200
Subject: [PATCH 0194/2085] Move to phantom-prebuilt for headless testing
And hopefully get Travis to run our tests again
---
package.json | 64 ++++++++++++++++++++++++++++++++--------------------
test/run.js | 2 +-
test/test.js | 1 +
3 files changed, 41 insertions(+), 26 deletions(-)
diff --git a/package.json b/package.json
index 86b65fbdfe..23dfd5211f 100644
--- a/package.json
+++ b/package.json
@@ -1,28 +1,42 @@
{
- "name": "codemirror",
- "version":"5.18.3",
- "main": "lib/codemirror.js",
- "description": "Full-featured in-browser code editor",
- "license": "MIT",
- "directories": {"lib": "./lib"},
- "scripts": {
- "test": "node ./test/run.js",
- "lint": "bin/lint"
- },
- "devDependencies": {"node-static": "0.6.0",
- "phantomjs": "1.9.2-5",
- "blint": ">=0.1.1"},
- "bugs": "http://github.com/codemirror/CodeMirror/issues",
- "keywords": ["JavaScript", "CodeMirror", "Editor"],
- "homepage": "http://codemirror.net",
- "maintainers":[{"name": "Marijn Haverbeke",
- "email": "marijnh@gmail.com",
- "web": "http://marijnhaverbeke.nl"}],
- "repository": {"type": "git",
- "url": "https://github.com/codemirror/CodeMirror.git"},
- "jspm": {
- "directories": {},
- "dependencies": {},
- "devDependencies": {}
+ "name": "codemirror",
+ "version": "5.18.3",
+ "main": "lib/codemirror.js",
+ "description": "Full-featured in-browser code editor",
+ "license": "MIT",
+ "directories": {
+ "lib": "./lib"
+ },
+ "scripts": {
+ "test": "node ./test/run.js",
+ "lint": "bin/lint"
+ },
+ "devDependencies": {
+ "node-static": "0.6.0",
+ "phantomjs-prebuilt": "^2.1.12",
+ "blint": ">=0.1.1"
+ },
+ "bugs": "http://github.com/codemirror/CodeMirror/issues",
+ "keywords": [
+ "JavaScript",
+ "CodeMirror",
+ "Editor"
+ ],
+ "homepage": "http://codemirror.net",
+ "maintainers": [
+ {
+ "name": "Marijn Haverbeke",
+ "email": "marijnh@gmail.com",
+ "web": "http://marijnhaverbeke.nl"
}
+ ],
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/codemirror/CodeMirror.git"
+ },
+ "jspm": {
+ "directories": {},
+ "dependencies": {},
+ "devDependencies": {}
+ }
}
diff --git a/test/run.js b/test/run.js
index 24b835c37c..54984e73a5 100755
--- a/test/run.js
+++ b/test/run.js
@@ -17,7 +17,7 @@ var server = require('http').createServer(function (req, res) {
throw err;
}).listen(3000, function () {
var childProcess = require('child_process');
- var phantomjs = require("phantomjs");
+ var phantomjs = require("phantomjs-prebuilt");
var childArgs = [
require("path").join(__dirname, 'phantom_driver.js')
];
diff --git a/test/test.js b/test/test.js
index 82ee231e35..1b3721a647 100644
--- a/test/test.js
+++ b/test/test.js
@@ -1088,6 +1088,7 @@ testCM("wrappingAndResizing", function(cm) {
}, null, ie_lt8);
testCM("measureEndOfLine", function(cm) {
+ if (phantom) return;
cm.setSize(null, "auto");
var inner = byClassName(cm.getWrapperElement(), "CodeMirror-lines")[0].firstChild;
var lh = inner.offsetHeight;
From 607d0e8a9fbbd0c5faa2db318937d92337b3cf53 Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Mon, 29 Aug 2016 17:00:25 +0200
Subject: [PATCH 0195/2085] [javascript mode] Better support for
TypeScript-style class syntax
Closes #4188
---
mode/javascript/javascript.js | 14 +++++++-------
mode/javascript/test.js | 8 +++++++-
2 files changed, 14 insertions(+), 8 deletions(-)
diff --git a/mode/javascript/javascript.js b/mode/javascript/javascript.js
index d7c5716b08..8ac121644d 100644
--- a/mode/javascript/javascript.js
+++ b/mode/javascript/javascript.js
@@ -617,13 +617,14 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
}
function classBody(type, value) {
if (type == "variable" || cx.style == "keyword") {
- if (value == "static") {
+ if ((value == "static" || value == "get" || value == "set" ||
+ (isTS && (value == "public" || value == "private" || value == "protected"))) &&
+ cx.stream.match(/^\s+[\w$\xa1-\uffff]/, false)) {
cx.marked = "keyword";
return cont(classBody);
}
cx.marked = "property";
- if (value == "get" || value == "set") return cont(classGetterSetter, functiondef, classBody);
- return cont(functiondef, classBody);
+ return cont(isTS ? classfield : functiondef, classBody);
}
if (value == "*") {
cx.marked = "keyword";
@@ -632,10 +633,9 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
if (type == ";") return cont(classBody);
if (type == "}") return cont();
}
- function classGetterSetter(type) {
- if (type != "variable") return pass();
- cx.marked = "property";
- return cont();
+ function classfield(type) {
+ if (type == ":") return cont(typeexpr)
+ return pass(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 11a13568db..7e88837ed8 100644
--- a/mode/javascript/test.js
+++ b/mode/javascript/test.js
@@ -31,7 +31,7 @@
MT("class",
"[keyword class] [def Point] [keyword extends] [variable SuperThing] {",
- " [property get] [property prop]() { [keyword return] [number 24]; }",
+ " [keyword get] [property prop]() { [keyword return] [number 24]; }",
" [property constructor]([def x], [def y]) {",
" [keyword super]([string 'something']);",
" [keyword this].[property x] [operator =] [variable-2 x];",
@@ -178,6 +178,12 @@
TS("arrow_type",
"[keyword let] [def x]: ([variable arg]: [variable-3 Type]) [operator =>] [variable-3 ReturnType]")
+ TS("typescript_class",
+ "[keyword class] [def Foo] {",
+ " [keyword public] [keyword static] [property main]() {}",
+ " [keyword private] [property _foo]: [variable-3 string];",
+ "}")
+
var jsonld_mode = CodeMirror.getMode(
{indentUnit: 2},
{name: "javascript", jsonld: true}
From 45cf0aefe1681814567b1c6c8145f34544f630b5 Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Sun, 4 Sep 2016 21:43:22 +0200
Subject: [PATCH 0196/2085] [simplemode demo] Highlight strings even when
unclosed
Issue #4217
---
demo/simplemode.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/demo/simplemode.html b/demo/simplemode.html
index 49778ef1e2..d719b63d18 100644
--- a/demo/simplemode.html
+++ b/demo/simplemode.html
@@ -132,7 +132,7 @@ Simple Mode Demo
// The start state contains the rules that are intially used
start: [
// The regex matches the token, the token property contains the type
- {regex: /"(?:[^\\]|\\.)*?"/, token: "string"},
+ {regex: /"(?:[^\\]|\\.)*?(?:"|$)/, token: "string"},
// You can match multiple tokens at once. Note that the captured
// groups must span the whole string in this case
{regex: /(function)(\s+)([a-z$][\w$]*)/,
From 40210a5aa08cbb81b3b6646da5509fb20be4a258 Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Sun, 4 Sep 2016 21:48:38 +0200
Subject: [PATCH 0197/2085] [javascript mode] Support typescript's type keyword
Closes #4220
---
mode/javascript/javascript.js | 2 ++
1 file changed, 2 insertions(+)
diff --git a/mode/javascript/javascript.js b/mode/javascript/javascript.js
index 8ac121644d..15ffe03e67 100644
--- a/mode/javascript/javascript.js
+++ b/mode/javascript/javascript.js
@@ -54,6 +54,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
"namespace": C,
"module": kw("module"),
"enum": kw("module"),
+ "type": kw("type"),
// scope modifiers
"public": kw("modifier"),
@@ -365,6 +366,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 == "type") return cont(typeexpr, expect("operator"), typeexpr, expect(";"));
if (type == "async") return cont(statement)
return pass(pushlex("stat"), expression, expect(";"), poplex);
}
From 62dd0f089077feeac03befcd9108908553517c68 Mon Sep 17 00:00:00 2001
From: nightwing
Date: Fri, 2 Sep 2016 23:52:51 +0400
Subject: [PATCH 0198/2085] [vim] fix issues with repeating after mutltiselect
edit
---
keymap/vim.js | 20 ++++++++++++++++----
test/vim_test.js | 41 +++++++++++++++++++++++++++++++----------
2 files changed, 47 insertions(+), 14 deletions(-)
diff --git a/keymap/vim.js b/keymap/vim.js
index afed132e36..a166f72b10 100644
--- a/keymap/vim.js
+++ b/keymap/vim.js
@@ -780,8 +780,12 @@
if (lastInsertModeKeyTimer) { window.clearTimeout(lastInsertModeKeyTimer); }
if (keysAreChars) {
- var here = cm.getCursor();
- cm.replaceRange('', offsetCursor(here, 0, -(keys.length - 1)), here, '+input');
+ var selections = cm.listSelections();
+ for (var i = 0; i < selections.length; i++) {
+ var here = selections[i].head;
+ cm.replaceRange('', offsetCursor(here, 0, -(keys.length - 1)), here, '+input');
+ }
+ vimGlobalState.macroModeState.lastInsertModeChanges.changes.pop();
}
clearInputState(cm);
return match.command;
@@ -818,7 +822,7 @@
// TODO: Look into using CodeMirror's multi-key handling.
// Return no-op since we are caching the key. Counts as handled, but
// don't want act on it just yet.
- return function() {};
+ return function() { return true; };
} else {
return function() {
return cm.operation(function() {
@@ -4874,6 +4878,10 @@
if (changeObj.origin == '+input' || changeObj.origin == 'paste'
|| changeObj.origin === undefined /* only in testing */) {
var text = changeObj.text.join('\n');
+ if (lastChange.maybeReset) {
+ lastChange.changes = [];
+ lastChange.maybeReset = false;
+ }
lastChange.changes.push(text);
}
// Change objects may be chained with next.
@@ -4896,7 +4904,7 @@
lastChange.expectCursorActivityForChange = false;
} else {
// Cursor moved outside the context of an edit. Reset the change.
- lastChange.changes = [];
+ lastChange.maybeReset = true;
}
} else if (!cm.curOp.isVimOp) {
handleExternalSelection(cm, vim);
@@ -4960,6 +4968,10 @@
var keyName = CodeMirror.keyName(e);
if (!keyName) { return; }
function onKeyFound() {
+ if (lastChange.maybeReset) {
+ lastChange.changes = [];
+ lastChange.maybeReset = false;
+ }
lastChange.changes.push(new InsertModeKey(keyName));
return true;
}
diff --git a/test/vim_test.js b/test/vim_test.js
index 159114cecd..6eea5553db 100644
--- a/test/vim_test.js
+++ b/test/vim_test.js
@@ -127,7 +127,10 @@ function testVim(name, run, opts, expectedFail) {
arguments = args;
}
for (var i = 0; i < arguments.length; i++) {
- CodeMirror.Vim.handleKey(cm, arguments[i]);
+ var result = CodeMirror.Vim.handleKey(cm, arguments[i]);
+ if (!result && cm.state.vim.insertMode) {
+ cm.replaceSelections(fillArray(arguments[i], cm.listSelections().length));
+ }
}
}
}
@@ -149,7 +152,7 @@ function testVim(name, run, opts, expectedFail) {
// Record for insert mode.
if (handled == "handled" && cm.state.vim.insertMode && arguments[i] != 'Esc') {
var lastChange = CodeMirror.Vim.getVimGlobalState_().macroModeState.lastInsertModeChanges;
- if (lastChange) {
+ if (lastChange && (key.indexOf('Delete') != -1 || key.indexOf('Backspace') != -1)) {
lastChange.changes.push(new CodeMirror.Vim.InsertModeKey(key));
}
}
@@ -1980,7 +1983,7 @@ testVim('visual_block_mode_switch', function(cm, vim, helpers) {
cm.setCursor(1, 1);
// blockwise to characterwise visual
helpers.doKeys('', 'j', 'l', 'v');
- selections = cm.getSelections();
+ var selections = cm.getSelections();
eq('7891\nabc', selections.join(''));
// characterwise to blockwise
helpers.doKeys('');
@@ -2841,6 +2844,14 @@ testVim('._insert', function(cm, vim, helpers) {
helpers.doKeys('.');
eq('testestt', cm.getValue());
helpers.assertCursorAt(0, 6);
+ helpers.doKeys('O');
+ cm.replaceRange('xyz', cm.getCursor());
+ helpers.doInsertModeKeys('Backspace');
+ helpers.doInsertModeKeys('Down');
+ helpers.doKeys('');
+ helpers.doKeys('.');
+ eq('xy\nxy\ntestestt', cm.getValue());
+ helpers.assertCursorAt(1, 1);
}, { value: ''});
testVim('._insert_repeat', function(cm, vim, helpers) {
helpers.doKeys('i');
@@ -3767,7 +3778,7 @@ testVim('set_boolean', function(cm, vim, helpers) {
// Test fail to set to non-boolean
CodeMirror.Vim.setOption('testoption', '5');
fail();
- } catch (expected) {};
+ } catch (expected) {}
// Test setOption
CodeMirror.Vim.setOption('testoption', false);
is(!CodeMirror.Vim.getOption('testoption'));
@@ -3780,7 +3791,7 @@ testVim('ex_set_boolean', function(cm, vim, helpers) {
// Test fail to set to non-boolean
helpers.doEx('set testoption=22');
fail();
- } catch (expected) {};
+ } catch (expected) {}
// Test setOption
helpers.doEx('set notestoption');
is(!CodeMirror.Vim.getOption('testoption'));
@@ -3793,12 +3804,12 @@ testVim('set_string', function(cm, vim, helpers) {
// Test fail to set non-string.
CodeMirror.Vim.setOption('testoption', true);
fail();
- } catch (expected) {};
+ } catch (expected) {}
try {
// Test fail to set 'notestoption'
CodeMirror.Vim.setOption('notestoption', 'b');
fail();
- } catch (expected) {};
+ } catch (expected) {}
// Test setOption
CodeMirror.Vim.setOption('testoption', 'c');
eq('c', CodeMirror.Vim.getOption('testoption'));
@@ -3811,7 +3822,7 @@ testVim('ex_set_string', function(cm, vim, helpers) {
// Test fail to set 'notestopt'
helpers.doEx('set notestopt=b');
fail();
- } catch (expected) {};
+ } catch (expected) {}
// Test setOption
helpers.doEx('set testopt=c')
eq('c', CodeMirror.Vim.getOption('testopt'));
@@ -3861,7 +3872,7 @@ testVim('ex_set_callback', function(cm, vim, helpers) {
// Test fail to set 'notestopt'
helpers.doEx('set notestopt=b');
fail();
- } catch (expected) {};
+ } catch (expected) {}
// 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
@@ -3998,7 +4009,17 @@ testVim('ex_imap', function(cm, vim, helpers) {
is(vim.insertMode);
helpers.doKeys('j', 'k');
is(!vim.insertMode);
-});
+ cm.setCursor(0, 1);
+ CodeMirror.Vim.map('jj', '', 'insert');
+ helpers.doKeys('', '2', 'j', 'l', 'c');
+ var replacement = fillArray('fo', 3);
+ cm.replaceSelections(replacement);
+ eq('1fo4\n5fo8\nafodefg', cm.getValue());
+ helpers.doKeys('j', 'j');
+ cm.setCursor(0, 0);
+ helpers.doKeys('.');
+ eq('foo4\nfoo8\nfoodefg', cm.getValue());
+}, { value: '1234\n5678\nabcdefg' });
testVim('ex_unmap_api', function(cm, vim, helpers) {
CodeMirror.Vim.map('', 'gg', 'normal');
is(CodeMirror.Vim.handleKey(cm, "", "normal"), "Alt-X key is mapped");
From 15dbafc8c5f200b5c4e2362a93d91e5675ddb3b5 Mon Sep 17 00:00:00 2001
From: Mike Kobit
Date: Sun, 4 Sep 2016 22:44:34 -0500
Subject: [PATCH 0199/2085] [mode/meta] Add Groovy recognition for Jenkinsfile
`Jenkinsfile` is used in Jenkins 2.0 for defining your pipeline as code.
See https://jenkins.io/doc/pipeline/jenkinsfile
---
mode/meta.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/mode/meta.js b/mode/meta.js
index 2e9ac7ce4b..1e078eedee 100644
--- a/mode/meta.js
+++ b/mode/meta.js
@@ -56,7 +56,7 @@
{name: "Gherkin", mime: "text/x-feature", mode: "gherkin", ext: ["feature"]},
{name: "GitHub Flavored Markdown", mime: "text/x-gfm", mode: "gfm", file: /^(readme|contributing|history).md$/i},
{name: "Go", mime: "text/x-go", mode: "go", ext: ["go"]},
- {name: "Groovy", mime: "text/x-groovy", mode: "groovy", ext: ["groovy", "gradle"]},
+ {name: "Groovy", mime: "text/x-groovy", mode: "groovy", ext: ["groovy", "gradle"], file: /^Jenkinsfile$/},
{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"]},
From a6c9a59395cebd1ce58deecc3b9b922d36f23f6b Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Wed, 7 Sep 2016 10:25:58 +0200
Subject: [PATCH 0200/2085] [panda-syntax theme] Fix rule for matching brackets
Closes #4230
---
theme/panda-syntax.css | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/theme/panda-syntax.css b/theme/panda-syntax.css
index 0e03500328..5d47fe5063 100644
--- a/theme/panda-syntax.css
+++ b/theme/panda-syntax.css
@@ -65,8 +65,7 @@
color: #6FC1FF;
}
-.cm-s-panda-syntax .cm-matchingbracket,
-.CodeMirror .CodeMirror-matchingbracket {
+.cm-s-panda-syntax .CodeMirror-matchingbracket {
color: #E6E6E6 !important;
border-bottom: 1px dotted #19f9d8;
padding-bottom: 2px;
From 3d89b71b955b72e61430f7f38e2350489cad9e15 Mon Sep 17 00:00:00 2001
From: Remi Nyborg
Date: Tue, 13 Sep 2016 09:59:39 +0200
Subject: [PATCH 0201/2085] Add passing the FocusEvent to blur and focus
---
doc/manual.html | 4 ++--
lib/codemirror.js | 12 ++++++------
2 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/doc/manual.html b/doc/manual.html
index d7c15f653d..4d20f50f2e 100644
--- a/doc/manual.html
+++ b/doc/manual.html
@@ -626,10 +626,10 @@ Events
You can preventDefault the event, to signal that
CodeMirror should do no further handling.
- "focus" (instance: CodeMirror)
+ "focus" (instance: CodeMirror, event: Event)
Fires whenever the editor is focused.
- "blur" (instance: CodeMirror)
+ "blur" (instance: CodeMirror, event: Event)
Fires whenever the editor is unfocused.
"scroll" (instance: CodeMirror)
diff --git a/lib/codemirror.js b/lib/codemirror.js
index 6e0b69c610..a44bf0e531 100644
--- a/lib/codemirror.js
+++ b/lib/codemirror.js
@@ -3533,8 +3533,8 @@
on(inp, "keyup", function(e) { onKeyUp.call(cm, e); });
on(inp, "keydown", operation(cm, onKeyDown));
on(inp, "keypress", operation(cm, onKeyPress));
- on(inp, "focus", bind(onFocus, cm));
- on(inp, "blur", bind(onBlur, cm));
+ on(inp, "focus", function (e) { onFocus(cm, e); });
+ on(inp, "blur", function (e) { onBlur(cm, e); });
}
function dragDropChanged(cm, value, old) {
@@ -4262,12 +4262,12 @@
}, 100);
}
- function onFocus(cm) {
+ function onFocus(cm, e) {
if (cm.state.delayingBlurEvent) cm.state.delayingBlurEvent = false;
if (cm.options.readOnly == "nocursor") return;
if (!cm.state.focused) {
- signal(cm, "focus", cm);
+ signal(cm, "focus", cm, e);
cm.state.focused = true;
addClass(cm.display.wrapper, "CodeMirror-focused");
// This test prevents this from firing when a context
@@ -4281,11 +4281,11 @@
}
restartBlink(cm);
}
- function onBlur(cm) {
+ function onBlur(cm, e) {
if (cm.state.delayingBlurEvent) return;
if (cm.state.focused) {
- signal(cm, "blur", cm);
+ signal(cm, "blur", cm, e);
cm.state.focused = false;
rmClass(cm.display.wrapper, "CodeMirror-focused");
}
From 7db967f97565335c6e028b6f72617cac84316170 Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Thu, 15 Sep 2016 15:58:58 +0200
Subject: [PATCH 0202/2085] [xml-fold addon] Clip iterator range to editor
range
Closes #4018
---
addon/fold/xml-fold.js | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/addon/fold/xml-fold.js b/addon/fold/xml-fold.js
index f8c67b8976..75a9e305bd 100644
--- a/addon/fold/xml-fold.js
+++ b/addon/fold/xml-fold.js
@@ -21,8 +21,8 @@
function Iter(cm, line, ch, range) {
this.line = line; this.ch = ch;
this.cm = cm; this.text = cm.getLine(line);
- this.min = range ? range.from : cm.firstLine();
- this.max = range ? range.to - 1 : cm.lastLine();
+ this.min = range ? Math.max(range.from, cm.firstLine()) : cm.firstLine();
+ this.max = range ? Math.min(range.to - 1, cm.lastLine()) : cm.lastLine();
}
function tagAt(iter, ch) {
From 02d4ab46ce9d0b5acadbd6b3c3d39bfaf5e76815 Mon Sep 17 00:00:00 2001
From: Siamak Mokhtari
Date: Fri, 16 Sep 2016 20:46:46 +0430
Subject: [PATCH 0203/2085] [panda-syntax theme] Adjust colors
---
theme/panda-syntax.css | 92 +++++++++++++++++++-----------------------
1 file changed, 42 insertions(+), 50 deletions(-)
diff --git a/theme/panda-syntax.css b/theme/panda-syntax.css
index 5d47fe5063..8c0c754082 100644
--- a/theme/panda-syntax.css
+++ b/theme/panda-syntax.css
@@ -1,93 +1,85 @@
/*
-
Name: Panda Syntax
Author: Siamak Mokhtari (http://github.com/siamak/)
-
CodeMirror template by Siamak Mokhtari (https://github.com/siamak/atom-panda-syntax)
-
*/
.cm-s-panda-syntax {
- /*font-family: 'Operator Mono', 'Source Sans Pro', Helvetica, Arial, sans-serif;*/
- font-family: 'Operator Mono', 'Source Sans Pro', Menlo, Monaco, Consolas, Courier New, monospace;
background: #292A2B;
color: #E6E6E6;
+ line-height: 1.5;
+ font-family: 'Operator Mono', 'Source Sans Pro', Menlo, Monaco, Consolas, Courier New, monospace;
}
+.cm-s-panda-syntax .CodeMirror-cursor { border-color: #ff2c6d; }
.cm-s-panda-syntax .CodeMirror-activeline-background {
- background: #404954;
+ background: rgba(99, 123, 156, 0.1);
+}
+.cm-s-panda-syntax .CodeMirror-selected {
+ background: #FFF;
}
-
.cm-s-panda-syntax .cm-comment {
font-style: italic;
color: #676B79;
}
-.cm-s-panda-syntax .cm-string,
-.cm-s-panda-syntax .cm-string-2 {
+.cm-s-panda-syntax .cm-operator {
+ color: #f3f3f3;
+}
+.cm-s-panda-syntax .cm-string {
color: #19F9D8;
}
+.cm-s-panda-syntax .cm-string-2 {
+ color: #FFB86C;
+}
+
+.cm-s-panda-syntax .cm-tag {
+ color: #ff2c6d;
+}
+.cm-s-panda-syntax .cm-meta {
+ color: #b084eb;
+}
+
.cm-s-panda-syntax .cm-number {
color: #FFB86C;
}
.cm-s-panda-syntax .cm-atom {
- color: #FFB86C;
+ color: #ff2c6d;
}
-
.cm-s-panda-syntax .cm-keyword {
color: #FF75B5;
}
-.cm-s-panda-syntax .cm-keyword-2 {
- color: #FF75B5;
-}
-.cm-s-panda-syntax .cm-keyword-3 {
- color: #B084EB;
-}
-
.cm-s-panda-syntax .cm-variable {
- color: #FF9AC1;
+ color: #ffb86c;
}
.cm-s-panda-syntax .cm-variable-2 {
- color: #e6e6e6;
+ color: #ff9ac1;
}
.cm-s-panda-syntax .cm-variable-3 {
- color: #82B1FF;
+ color: #ff9ac1;
}
.cm-s-panda-syntax .cm-def {
- /*font-style: italic;*/
color: #e6e6e6;
}
-.cm-s-panda-syntax .cm-def-2 {
- font-style: italic;
- color: #ffcc95;
-}
-
-
.cm-s-panda-syntax .cm-property {
- color: #6FC1FF;
+ color: #f3f3f3;
}
-
-.cm-s-panda-syntax .CodeMirror-matchingbracket {
- color: #E6E6E6 !important;
- border-bottom: 1px dotted #19f9d8;
- padding-bottom: 2px;
+.cm-s-panda-syntax .cm-unit {
+ color: #ffb86c;
}
-.cm-s-panda-syntax .CodeMirror-gutters {
- background: #292A2B;
- color: #757575;
- border: none;
+.cm-s-panda-syntax .cm-attribute {
+ color: #ffb86c;
}
-.cm-s-panda-syntax .CodeMirror-guttermarker, .cm-s-panda-syntax .CodeMirror-guttermarker-subtle, .cm-s-panda-syntax .CodeMirror-linenumber {
- color: #757575;
+
+.cm-s-panda-syntax .CodeMirror-matchingbracket {
+ border-bottom: 1px dotted #19F9D8;
+ padding-bottom: 2px;
+ color: #e6e6e6;
}
-.cm-s-panda-syntax .CodeMirror-linenumber {
- padding-right: 10px;
+.CodeMirror-gutters {
+ background: #292a2b;
+ border-right-color: rgba(255, 255, 255, 0.1);
}
-.cm-s-panda-syntax .CodeMirror-cursor {
- border-left: 1px solid #757575;
+.CodeMirror-linenumber {
+ color: #e6e6e6;
+ opacity: 0.6;
}
-/*.cm-s-panda-syntax div.CodeMirror-selected { background: rgba(255, 255, 255, 0.5); }*/
-.cm-s-panda-syntax.CodeMirror-focused div.CodeMirror-selected { background: rgba(255, 255, 255, 0.25); }
-.cm-s-panda-syntax .CodeMirror-line::selection, .cm-s-panda-syntax .CodeMirror-line > span::selection, .cm-s-panda-syntax .CodeMirror-line > span > span::selection { background: rgba(255, 255, 255, 0.10); }
-.cm-s-panda-syntax .CodeMirror-line::-moz-selection, .cm-s-panda-syntax .CodeMirror-line > span::-moz-selection, .cm-s-panda-syntax .CodeMirror-line > span > span::-moz-selection { background: rgba(255, 255, 255, 0.10); }
-
-.cm-s-panda-syntax .CodeMirror-activeline-background { background: rgba(99, 123, 156, 0.125); }
From 953a5dca4e2e8ffa251069bd46a8da29ab977f74 Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Mon, 19 Sep 2016 21:09:51 +0200
Subject: [PATCH 0204/2085] [erlang mode] Fix problem with dereferencing empty
context array
Closes #4244
---
mode/erlang/erlang.js | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/mode/erlang/erlang.js b/mode/erlang/erlang.js
index 5aed76a526..9528e19f93 100644
--- a/mode/erlang/erlang.js
+++ b/mode/erlang/erlang.js
@@ -433,15 +433,16 @@ CodeMirror.defineMode("erlang", function(cmCfg) {
}
function maybe_drop_post(s) {
+ if (!s.length) return s
var last = s.length-1;
if (s[last].type === "dot") {
return [];
}
- if (s[last].type === "fun" && s[last-1].token === "fun") {
+ if (last > 1 && s[last].type === "fun" && s[last-1].token === "fun") {
return s.slice(0,last-1);
}
- switch (s[s.length-1].token) {
+ switch (s[last].token) {
case "}": return d(s,{g:["{"]});
case "]": return d(s,{i:["["]});
case ")": return d(s,{i:["("]});
From e865bdef6ce087db02686267d57c34478da8621b Mon Sep 17 00:00:00 2001
From: Pontus Melke
Date: Wed, 24 Aug 2016 14:41:27 +0200
Subject: [PATCH 0205/2085] [cypher mode] Added CALL and YIELD as keywords
---
mode/cypher/cypher.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/mode/cypher/cypher.js b/mode/cypher/cypher.js
index 107e4f6d21..f99abe2385 100644
--- a/mode/cypher/cypher.js
+++ b/mode/cypher/cypher.js
@@ -62,7 +62,7 @@
var curPunc;
var funcs = wordRegexp(["abs", "acos", "allShortestPaths", "asin", "atan", "atan2", "avg", "ceil", "coalesce", "collect", "cos", "cot", "count", "degrees", "e", "endnode", "exp", "extract", "filter", "floor", "haversin", "head", "id", "keys", "labels", "last", "left", "length", "log", "log10", "lower", "ltrim", "max", "min", "node", "nodes", "percentileCont", "percentileDisc", "pi", "radians", "rand", "range", "reduce", "rel", "relationship", "relationships", "replace", "reverse", "right", "round", "rtrim", "shortestPath", "sign", "sin", "size", "split", "sqrt", "startnode", "stdev", "stdevp", "str", "substring", "sum", "tail", "tan", "timestamp", "toFloat", "toInt", "toString", "trim", "type", "upper"]);
var preds = wordRegexp(["all", "and", "any", "contains", "exists", "has", "in", "none", "not", "or", "single", "xor"]);
- var keywords = wordRegexp(["as", "asc", "ascending", "assert", "by", "case", "commit", "constraint", "create", "csv", "cypher", "delete", "desc", "descending", "detach", "distinct", "drop", "else", "end", "ends", "explain", "false", "fieldterminator", "foreach", "from", "headers", "in", "index", "is", "join", "limit", "load", "match", "merge", "null", "on", "optional", "order", "periodic", "profile", "remove", "return", "scan", "set", "skip", "start", "starts", "then", "true", "union", "unique", "unwind", "using", "when", "where", "with"]);
+ var keywords = wordRegexp(["as", "asc", "ascending", "assert", "by", "case", "commit", "constraint", "create", "csv", "cypher", "delete", "desc", "descending", "detach", "distinct", "drop", "else", "end", "ends", "explain", "false", "fieldterminator", "foreach", "from", "headers", "in", "index", "is", "join", "limit", "load", "match", "merge", "null", "on", "optional", "order", "periodic", "profile", "remove", "return", "scan", "set", "skip", "start", "starts", "then", "true", "union", "unique", "unwind", "using", "when", "where", "with", "call", "yield"]);
var operatorChars = /[*+\-<>=&|~%^]/;
return {
From 45f97d2f83f766108b13f52594b74071b11698f7 Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Tue, 20 Sep 2016 10:47:45 +0200
Subject: [PATCH 0206/2085] [comment addon] Use first closing token when
uncommenting
And don't block-comment inside a block comment
Closes #4208
---
addon/comment/comment.js | 11 +++++++----
test/comment_test.js | 9 +++++++++
2 files changed, 16 insertions(+), 4 deletions(-)
diff --git a/addon/comment/comment.js b/addon/comment/comment.js
index 2f2f071df9..d71cf43603 100644
--- a/addon/comment/comment.js
+++ b/addon/comment/comment.js
@@ -103,6 +103,7 @@
self.lineComment(from, to, options);
return;
}
+ if (/\bcomment\b/.test(self.getTokenTypeAt(Pos(from.line, 0)))) return
var end = Math.min(to.line, self.lastLine());
if (end != from.line && to.ch == 0 && nonWS.test(self.getLine(end))) --end;
@@ -162,13 +163,15 @@
var endString = options.blockCommentEnd || mode.blockCommentEnd;
if (!startString || !endString) return false;
var lead = options.blockCommentLead || mode.blockCommentLead;
- var startLine = self.getLine(start), endLine = end == start ? startLine : self.getLine(end);
- var open = startLine.indexOf(startString), close = endLine.lastIndexOf(endString);
+ var startLine = self.getLine(start), open = startLine.indexOf(startString)
+ 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.lastIndexOf(endString);
+ close = endLine.indexOf(endString);
}
- if (open == -1 || close == -1 ||
+ if (close == -1 ||
!/comment/.test(self.getTokenTypeAt(Pos(start, open + 1))) ||
!/comment/.test(self.getTokenTypeAt(Pos(end, close + 1))))
return false;
diff --git a/test/comment_test.js b/test/comment_test.js
index 3e6a86bbf2..c6b9fe8109 100644
--- a/test/comment_test.js
+++ b/test/comment_test.js
@@ -102,4 +102,13 @@ namespace = "comment_";
cm.execCommand("selectAll")
cm.execCommand("toggleComment")
}, "// foo\n// bar\nbaz", "// // foo\n// // bar\n// baz")
+
+ test("uncommentWithTrailingBlockEnd", "xml", function(cm) {
+ cm.execCommand("toggleComment")
+ }, " -->", "foo -->")
+
+ test("dontCommentInComment", "xml", function(cm) {
+ cm.setCursor(1, 0)
+ cm.execCommand("toggleComment")
+ }, "", "")
})();
From 653f64cc744cf2b1e5658cc818d4d3ff52417d47 Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Tue, 20 Sep 2016 10:51:06 +0200
Subject: [PATCH 0207/2085] [javascript mode] Recognize TypeScript-style
optional arguments
Closes #4212
---
mode/javascript/javascript.js | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/mode/javascript/javascript.js b/mode/javascript/javascript.js
index 15ffe03e67..c27b01f7a6 100644
--- a/mode/javascript/javascript.js
+++ b/mode/javascript/javascript.js
@@ -521,8 +521,11 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
if (type == "}") return cont();
return pass(statement, block);
}
- function maybetype(type) {
- if (isTS && type == ":") return cont(typeexpr);
+ function maybetype(type, value) {
+ if (isTS) {
+ if (type == ":") return cont(typeexpr);
+ if (value == "?") return cont(maybetype);
+ }
}
function maybedefault(_, value) {
if (value == "=") return cont(expressionNoComma);
From 88c54d1d8daf2cba70d49c06b9e142ab2917a067 Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Tue, 20 Sep 2016 11:03:34 +0200
Subject: [PATCH 0208/2085] Remove debug statement
---
test/mode_test.js | 1 -
1 file changed, 1 deletion(-)
diff --git a/test/mode_test.js b/test/mode_test.js
index f706c4f5c6..0aed50f7dc 100644
--- a/test/mode_test.js
+++ b/test/mode_test.js
@@ -61,7 +61,6 @@
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 14c8a436efa943060682c1b9845c63bbcee491cf Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Tue, 20 Sep 2016 11:23:10 +0200
Subject: [PATCH 0209/2085] [javascript mode] Improve indentation with missing
semicolons
Closes #4214
---
mode/javascript/javascript.js | 18 +++++++++++++-----
mode/javascript/test.js | 13 +++++++++++++
2 files changed, 26 insertions(+), 5 deletions(-)
diff --git a/mode/javascript/javascript.js b/mode/javascript/javascript.js
index c27b01f7a6..e23560746d 100644
--- a/mode/javascript/javascript.js
+++ b/mode/javascript/javascript.js
@@ -344,19 +344,19 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
function statement(type, value) {
if (type == "var") return cont(pushlex("vardef", value.length), vardef, expect(";"), poplex);
- if (type == "keyword a") return cont(pushlex("form"), expression, statement, poplex);
+ if (type == "keyword a") return cont(pushlex("form"), parenExpr, statement, poplex);
if (type == "keyword b") return cont(pushlex("form"), statement, poplex);
if (type == "{") return cont(pushlex("}"), block, poplex);
if (type == ";") return cont();
if (type == "if") {
if (cx.state.lexical.info == "else" && cx.state.cc[cx.state.cc.length - 1] == poplex)
cx.state.cc.pop()();
- return cont(pushlex("form"), expression, statement, poplex, maybeelse);
+ return cont(pushlex("form"), parenExpr, statement, poplex, maybeelse);
}
if (type == "function") return cont(functiondef);
if (type == "for") return cont(pushlex("form"), forspec, statement, poplex);
if (type == "variable") return cont(pushlex("stat"), maybelabel);
- if (type == "switch") return cont(pushlex("form"), expression, pushlex("}", "switch"), expect("{"),
+ if (type == "switch") return cont(pushlex("form"), parenExpr, pushlex("}", "switch"), expect("{"),
block, poplex, poplex);
if (type == "case") return cont(expression, expect(":"));
if (type == "default") return cont(expect(":"));
@@ -376,6 +376,10 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
function expressionNoComma(type) {
return expressionInner(type, true);
}
+ function parenExpr(type) {
+ if (type != "(") return pass()
+ return cont(pushlex(")"), expression, expect(")"), poplex)
+ }
function expressionInner(type, noComma) {
if (cx.state.fatArrowAt == cx.stream.start) {
var body = noComma ? arrowBodyNoComma : arrowBody;
@@ -709,14 +713,18 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
indent: function(state, textAfter) {
if (state.tokenize == tokenComment) return CodeMirror.Pass;
if (state.tokenize != tokenBase) return 0;
- var firstChar = textAfter && textAfter.charAt(0), lexical = state.lexical;
+ var firstChar = textAfter && textAfter.charAt(0), lexical = state.lexical, top
// Kludge to prevent 'maybelse' from blocking lexical scope pops
if (!/^\s*else\b/.test(textAfter)) for (var i = state.cc.length - 1; i >= 0; --i) {
var c = state.cc[i];
if (c == poplex) lexical = lexical.prev;
else if (c != maybeelse) break;
}
- if (lexical.type == "stat" && firstChar == "}") lexical = lexical.prev;
+ while ((lexical.type == "stat" || lexical.type == "form") &&
+ (firstChar == "}" || ((top = state.cc[state.cc.length - 1]) &&
+ (top == maybeoperatorComma || top == maybeoperatorNoComma) &&
+ !/^[,\.=+\-*:?[\(]/.test(textAfter))))
+ lexical = lexical.prev;
if (statementIndent && lexical.type == ")" && lexical.prev.type == "stat")
lexical = lexical.prev;
var type = lexical.type, closing = firstChar == type;
diff --git a/mode/javascript/test.js b/mode/javascript/test.js
index 7e88837ed8..91c8b7434a 100644
--- a/mode/javascript/test.js
+++ b/mode/javascript/test.js
@@ -140,6 +140,19 @@
" [number 1];",
"[number 2];");
+ MT("indent_semicolonless_if",
+ "[keyword function] [def foo]() {",
+ " [keyword if] ([variable x])",
+ " [variable foo]()",
+ "}")
+
+ MT("indent_semicolonless_if_with_statement",
+ "[keyword function] [def foo]() {",
+ " [keyword if] ([variable x])",
+ " [variable foo]()",
+ " [variable bar]()",
+ "}")
+
MT("multilinestring",
"[keyword var] [def x] [operator =] [string 'foo\\]",
"[string bar'];");
From e59d75ad99d5499bb78e0a3103ad4ec51a184542 Mon Sep 17 00:00:00 2001
From: Joel Einbinder
Date: Wed, 31 Aug 2016 17:17:55 -0700
Subject: [PATCH 0210/2085] Always move in the right direction for
PageUp/PageDown
---
lib/codemirror.js | 3 ++-
test/test.js | 35 +++++++++++++++++++++++++++++++++--
2 files changed, 35 insertions(+), 3 deletions(-)
diff --git a/lib/codemirror.js b/lib/codemirror.js
index a44bf0e531..251469c909 100644
--- a/lib/codemirror.js
+++ b/lib/codemirror.js
@@ -4907,7 +4907,8 @@
var doc = cm.doc, x = pos.left, y;
if (unit == "page") {
var pageSize = Math.min(cm.display.wrapper.clientHeight, window.innerHeight || document.documentElement.clientHeight);
- y = pos.top + dir * (pageSize - (dir < 0 ? 1.5 : .5) * textHeight(cm.display));
+ 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;
}
diff --git a/test/test.js b/test/test.js
index 1b3721a647..a7658311e7 100644
--- a/test/test.js
+++ b/test/test.js
@@ -1336,6 +1336,37 @@ testCM("verticalMovementCommandsWrapping", function(cm) {
}, {value: "a very long line that wraps around somehow so that we can test cursor movement\nshortone\nk",
lineWrapping: true});
+testCM("verticalMovementCommandsSingleLine", function(cm) {
+ cm.display.wrapper.style.height = "auto";
+ cm.refresh();
+ cm.execCommand("goLineUp");
+ eqPos(cm.getCursor(), Pos(0, 0));
+ cm.execCommand("goLineDown");
+ eqPos(cm.getCursor(), Pos(0, 11));
+ cm.setCursor(Pos(0, 5));
+ cm.execCommand("goLineDown");
+ eqPos(cm.getCursor(), Pos(0, 11));
+ cm.execCommand("goLineDown");
+ eqPos(cm.getCursor(), Pos(0, 11));
+ cm.execCommand("goLineUp");
+ eqPos(cm.getCursor(), Pos(0, 0));
+ cm.execCommand("goLineUp");
+ eqPos(cm.getCursor(), Pos(0, 0));
+ cm.execCommand("goPageDown");
+ eqPos(cm.getCursor(), Pos(0, 11));
+ cm.execCommand("goPageDown"); cm.execCommand("goLineDown");
+ eqPos(cm.getCursor(), Pos(0, 11));
+ cm.execCommand("goPageUp");
+ eqPos(cm.getCursor(), Pos(0, 0));
+ cm.setCursor(Pos(0, 5));
+ cm.execCommand("goPageUp");
+ eqPos(cm.getCursor(), Pos(0, 0));
+ cm.setCursor(Pos(0, 5));
+ cm.execCommand("goPageDown");
+ eqPos(cm.getCursor(), Pos(0, 11));
+}, {value: "single line"});
+
+
testCM("rtlMovement", function(cm) {
if (cm.getOption("inputStyle") != "textarea") return;
forEach(["خحج", "خحabcخحج", "abخحخحجcd", "abخde", "abخح2342خ1حج", "خ1ح2خح3حxج",
@@ -1487,7 +1518,7 @@ testCM("lineWidgetChanged", function(cm) {
// Good:
// | ------------- display width ------------- |
// | ------- widget-width when measured ------ |
- // | | -- under-half -- | | -- under-half -- | |
+ // | | -- under-half -- | | -- under-half -- | |
// | | --- over-half --- | |
// | | --- over-half --- | |
// Height: measured as 3 lines, same as it will be when actually displayed
@@ -1504,7 +1535,7 @@ testCM("lineWidgetChanged", function(cm) {
// Bad (too wide):
// | ------------- display width ------------- |
// | -------- widget-width when measured ------- | < -- uh oh
- // | | -- under-half -- | | -- under-half -- | |
+ // | | -- under-half -- | | -- under-half -- | |
// | | --- over-half --- | | --- over-half --- | | < -- when measured, combined on one line
// Height: measured as 2 lines, less than expected. Will be displayed as 3 lines!
From cc817cd9281fcc0e9a69fd8c7f0965313ea014ff Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Tue, 20 Sep 2016 12:00:45 +0200
Subject: [PATCH 0211/2085] Lower max token size to 5000
Closes #4233
---
lib/codemirror.js | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/lib/codemirror.js b/lib/codemirror.js
index 251469c909..17c3112236 100644
--- a/lib/codemirror.js
+++ b/lib/codemirror.js
@@ -6861,7 +6861,7 @@
}
if (!flattenSpans || curStyle != style) {
while (curStart < stream.start) {
- curStart = Math.min(stream.start, curStart + 50000);
+ curStart = Math.min(stream.start, curStart + 5000);
f(curStart, curStyle);
}
curStyle = style;
@@ -6869,8 +6869,10 @@
stream.start = stream.pos;
}
while (curStart < stream.pos) {
- // Webkit seems to refuse to render text nodes longer than 57444 characters
- var pos = Math.min(stream.pos, curStart + 50000);
+ // 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;
}
From a350d03f15df2892e41b094faa85a08459942845 Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Tue, 20 Sep 2016 12:26:58 +0200
Subject: [PATCH 0212/2085] Mark release 5.19.0
---
AUTHORS | 3 +++
CHANGELOG.md | 20 ++++++++++++++++++++
doc/compress.html | 1 +
doc/manual.html | 2 +-
doc/releases.html | 12 ++++++++++++
index.html | 2 +-
lib/codemirror.js | 2 +-
package.json | 2 +-
8 files changed, 40 insertions(+), 4 deletions(-)
diff --git a/AUTHORS b/AUTHORS
index 0f2bc1bbf3..ada8c3f9cf 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -405,6 +405,7 @@ Mike Brevoort
Mike Diaz
Mike Ivanov
Mike Kadin
+Mike Kobit
MinRK
Miraculix87
misfo
@@ -464,6 +465,7 @@ Philipp A
Philip Stadermann
Pierre Gerold
Piët Delport
+Pontus Melke
prasanthj
Prasanth J
Prayag Verma
@@ -477,6 +479,7 @@ Randy Edmunds
Rasmus Erik Voel Jensen
ray ratchup
Ray Ratchup
+Remi Nyborg
Richard Denton
Richard van der Meer
Richard Z.H. Wang
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9470deb79f..f368485cfe 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,23 @@
+## 5.19.0 (2016-09-20)
+
+### Bugfixes
+
+[erlang mode](http://codemirror.net/mode/erlang): Fix mode crash when trying to read an empty context.
+
+[comment addon](http://codemirror.net/doc/manual.html#addon_comment): Fix broken behavior when toggling comments inside a comment.
+
+xml-fold addon: Fix a null-dereference bug.
+
+Page up and page down now do something even in single-line documents.
+
+Fix an issue where the cursor position could be off in really long (~8000 character) tokens.
+
+### New features
+
+[javascript mode](http://codemirror.net/mode/javascript): Better indentation when semicolons are missing. Better support for TypeScript classes, optional parameters, and the `type` keyword.
+
+The [`blur`](http://codemirror.net/doc/manual.html#event_blur) and [`focus`](http://codemirror.net/doc/manual.html#event_focus) events now pass the DOM event to their handlers.
+
## 5.18.2 (2016-08-23)
### Bugfixes
diff --git a/doc/compress.html b/doc/compress.html
index 7f12f2c430..c013d3b744 100644
--- a/doc/compress.html
+++ b/doc/compress.html
@@ -36,6 +36,7 @@ Script compression helper
Version:
HEAD
+ 5.19.0
5.18.2
5.18.0
5.17.0
diff --git a/doc/manual.html b/doc/manual.html
index 4d20f50f2e..5bdca1e0f7 100644
--- a/doc/manual.html
+++ b/doc/manual.html
@@ -69,7 +69,7 @@
User manual and reference guide
- version 5.18.3
+ version 5.19.0
CodeMirror is a code-editor component that can be embedded in
diff --git a/doc/releases.html b/doc/releases.html
index fa1dd69ac8..5a0c6b715c 100644
--- a/doc/releases.html
+++ b/doc/releases.html
@@ -30,6 +30,18 @@
Release notes and version history
Version 5.x
+ 20-09-2016: Version 5.19.0 :
+
+
+ erlang mode : Fix mode crash when trying to read an empty context.
+ comment addon : Fix broken behavior when toggling comments inside a comment.
+ xml-fold addon: Fix a null-dereference bug.
+ Page up and page down now do something even in single-line documents.
+ Fix an issue where the cursor position could be off in really long (~8000 character) tokens.
+ javascript mode : Better indentation when semicolons are missing. Better support for TypeScript classes, optional parameters, and the type keyword.
+ The blur and focus events now pass the DOM event to their handlers.
+
+
23-08-2016: Version 5.18.2 :
diff --git a/index.html b/index.html
index f3aba01bdf..636639ec08 100644
--- a/index.html
+++ b/index.html
@@ -96,7 +96,7 @@ This is CodeMirror
- Get the current version:
5.18.2 .
+ Get the current version:
5.19.0 .
You can see the
code or
read the
release notes .
There is a
minification helper .
diff --git a/lib/codemirror.js b/lib/codemirror.js
index 17c3112236..32d9743981 100644
--- a/lib/codemirror.js
+++ b/lib/codemirror.js
@@ -8958,7 +8958,7 @@
// THE END
- CodeMirror.version = "5.18.3";
+ CodeMirror.version = "5.19.0";
return CodeMirror;
});
diff --git a/package.json b/package.json
index 23dfd5211f..6744d254fa 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "codemirror",
- "version": "5.18.3",
+ "version": "5.19.0",
"main": "lib/codemirror.js",
"description": "Full-featured in-browser code editor",
"license": "MIT",
From de605105f9bab51f110ddd3f69444d5fde0da10b Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Tue, 20 Sep 2016 12:33:11 +0200
Subject: [PATCH 0213/2085] Fix release script to handle spaces in JSON
---
bin/release | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/bin/release b/bin/release
index 18c5cdafc7..5b31da8efd 100755
--- a/bin/release
+++ b/bin/release
@@ -21,7 +21,7 @@ rewrite("lib/codemirror.js", function(lib) {
"CodeMirror.version = \"" + number + "\"");
});
function rewriteJSON(pack) {
- return pack.replace(/"version":"\d+\.\d+\.\d+"/, "\"version\":\"" + number + "\"");
+ return pack.replace(/"version":\s*"\d+\.\d+\.\d+"/, "\"version\": \"" + number + "\"");
}
rewrite("package.json", rewriteJSON);
rewrite("doc/manual.html", function(manual) {
From 8f5974021f791b0d1d80f20c76ef1223bc23316e Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Tue, 20 Sep 2016 12:33:34 +0200
Subject: [PATCH 0214/2085] Bump version number post-5.19.0
---
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 5bdca1e0f7..8a0e2dcb81 100644
--- a/doc/manual.html
+++ b/doc/manual.html
@@ -69,7 +69,7 @@
User manual and reference guide
- version 5.19.0
+ version 5.19.1
CodeMirror is a code-editor component that can be embedded in
diff --git a/lib/codemirror.js b/lib/codemirror.js
index 32d9743981..48ebdf141a 100644
--- a/lib/codemirror.js
+++ b/lib/codemirror.js
@@ -8958,7 +8958,7 @@
// THE END
- CodeMirror.version = "5.19.0";
+ CodeMirror.version = "5.19.1";
return CodeMirror;
});
diff --git a/package.json b/package.json
index 6744d254fa..1e4c7e0b06 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "codemirror",
- "version": "5.19.0",
+ "version": "5.19.1",
"main": "lib/codemirror.js",
"description": "Full-featured in-browser code editor",
"license": "MIT",
From 6f0fe4da3440bb9c90c9ac7edd3f97c7a5831c5d Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Wed, 21 Sep 2016 11:36:16 +0200
Subject: [PATCH 0215/2085] Make newlineAndIndent work with multiple cursors on
same line
Closes #4248
---
lib/codemirror.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lib/codemirror.js b/lib/codemirror.js
index 48ebdf141a..9fb06561c3 100644
--- a/lib/codemirror.js
+++ b/lib/codemirror.js
@@ -5796,7 +5796,7 @@
newlineAndIndent: function(cm) {
runInOp(cm, function() {
var len = cm.listSelections().length;
- for (var i = 0; i < len; i++) {
+ for (var i = len - 1; i >= 0; i--) {
var range = cm.listSelections()[i];
cm.replaceRange(cm.doc.lineSeparator(), range.anchor, range.head, "+input");
cm.indentLine(range.from().line + 1, null, true);
From d3e8160274f1b4662ce9f7052651d8e4af836372 Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Wed, 21 Sep 2016 11:52:36 +0200
Subject: [PATCH 0216/2085] Amend previous patch
Indentation should happen top-to-bottom so that subsequent
indents are based on the updated code above
Issue #4248
---
lib/codemirror.js | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/lib/codemirror.js b/lib/codemirror.js
index 9fb06561c3..3798e5ec6c 100644
--- a/lib/codemirror.js
+++ b/lib/codemirror.js
@@ -5795,12 +5795,12 @@
},
newlineAndIndent: function(cm) {
runInOp(cm, function() {
- var len = cm.listSelections().length;
- for (var i = len - 1; i >= 0; i--) {
- var range = cm.listSelections()[i];
- cm.replaceRange(cm.doc.lineSeparator(), range.anchor, range.head, "+input");
- cm.indentLine(range.from().line + 1, null, true);
- }
+ var sels = cm.listSelections()
+ for (var 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++)
+ cm.indentLine(sels[i].from().line, null, true)
ensureCursorVisible(cm);
});
},
From 8f5ff2b2435c8408f447876c1f5436bb96549832 Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Wed, 21 Sep 2016 20:35:38 +0200
Subject: [PATCH 0217/2085] Revert "[lint demo] Remove css lint example"
This reverts commit 8a1dd1604974840fe69163f958ad204010442aea.
Issue #4123
---
demo/lint.html | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 68 insertions(+)
diff --git a/demo/lint.html b/demo/lint.html
index ea0297b27d..96009b4e1f 100644
--- a/demo/lint.html
+++ b/demo/lint.html
@@ -11,9 +11,11 @@
+
+
@@ -83,6 +85,66 @@ Linter Demo
]
+ @charset "UTF-8";
+
+@import url("booya.css") print, screen;
+@import "whatup.css" screen;
+@import "wicked.css";
+
+/*Error*/
+@charset "UTF-8";
+
+
+@namespace "http://www.w3.org/1999/xhtml";
+@namespace svg "http://www.w3.org/2000/svg";
+
+/*Warning: empty ruleset */
+.foo {
+}
+
+h1 {
+ font-weight: bold;
+}
+
+/*Warning: qualified heading */
+.foo h1 {
+ font-weight: bold;
+}
+
+/*Warning: adjoining classes */
+.foo.bar {
+ zoom: 1;
+}
+
+li.inline {
+ width: 100%; /*Warning: 100% can be problematic*/
+}
+
+li.last {
+ display: inline;
+ padding-left: 3px !important;
+ padding-right: 3px;
+ border-right: 0px;
+}
+
+@media print {
+ li.inline {
+ color: black;
+ }
+}
+
+@page {
+ margin: 10%;
+ counter-increment: page;
+
+ @top-center {
+ font-family: sans-serif;
+ font-weight: bold;
+ font-size: 2em;
+ content: counter(page);
+ }
+}
+
From 562e8eff5b0916d3b63fc59eda9540f8f455c6ed Mon Sep 17 00:00:00 2001
From: Luke Browning
Date: Thu, 22 Sep 2016 14:55:40 +0100
Subject: [PATCH 0218/2085] [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 01ebd80ae1..e3cbae54cf 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 0219/2085] 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 3798e5ec6c..ececc94c23 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 0220/2085] [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 2cfc5381fe..1f26d7fe1b 100644
--- a/mode/javascript/typescript.html
+++ b/mode/javascript/typescript.html
@@ -28,13 +28,13 @@ TypeScript mode
class Greeter {
- greeting: string;
- constructor (message: string) {
- this.greeting = message;
- }
- greet() {
- return "Hello, " + this.greeting;
- }
+ greeting: string;
+ constructor (message: string) {
+ this.greeting = message;
+ }
+ greet() {
+ return "Hello, " + this.greeting;
+ }
}
var greeter = new Greeter("world");
@@ -42,7 +42,7 @@ TypeScript mode
var button = document.createElement('button')
button.innerText = "Say Hello"
button.onclick = function() {
- alert(greeter.greet())
+ alert(greeter.greet())
}
document.body.appendChild(button)
From 1716aec3b2a1a5ec87d088773041e7c95b5d61f1 Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Mon, 26 Sep 2016 13:00:35 +0200
Subject: [PATCH 0221/2085] [javascript mode] Support TypeScript-style optional
class properties
Closes #4254
---
mode/javascript/javascript.js | 12 +++++-------
1 file changed, 5 insertions(+), 7 deletions(-)
diff --git a/mode/javascript/javascript.js b/mode/javascript/javascript.js
index e23560746d..2267f443d3 100644
--- a/mode/javascript/javascript.js
+++ b/mode/javascript/javascript.js
@@ -531,9 +531,6 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
if (value == "?") return cont(maybetype);
}
}
- function maybedefault(_, value) {
- if (value == "=") return cont(expressionNoComma);
- }
function typeexpr(type) {
if (type == "variable") {cx.marked = "variable-3"; return cont(afterType);}
if (type == "{") return cont(commasep(typeprop, "}"))
@@ -615,7 +612,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
}
function funarg(type) {
if (type == "spread") return cont(funarg);
- return pass(pattern, maybetype, maybedefault);
+ return pass(pattern, maybetype, maybeAssign);
}
function className(type, value) {
if (type == "variable") {register(value); return cont(classNameAfter);}
@@ -627,7 +624,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
function classBody(type, value) {
if (type == "variable" || cx.style == "keyword") {
if ((value == "static" || value == "get" || value == "set" ||
- (isTS && (value == "public" || value == "private" || value == "protected"))) &&
+ (isTS && (value == "public" || value == "private" || value == "protected" || value == "readonly"))) &&
cx.stream.match(/^\s+[\w$\xa1-\uffff]/, false)) {
cx.marked = "keyword";
return cont(classBody);
@@ -642,8 +639,9 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
if (type == ";") return cont(classBody);
if (type == "}") return cont();
}
- function classfield(type) {
- if (type == ":") return cont(typeexpr)
+ function classfield(type, value) {
+ if (value == "?") return cont(classfield)
+ if (type == ":") return cont(typeexpr, maybeAssign)
return pass(functiondef)
}
function afterExport(_type, value) {
From 189e3ca7354b90047c34618774ced3df69c403e4 Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Mon, 26 Sep 2016 13:04:05 +0200
Subject: [PATCH 0222/2085] [javascript mode] Handle TypeScript keyword
'abstract' better
Closes #4255
---
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 2267f443d3..65f85f3a98 100644
--- a/mode/javascript/javascript.js
+++ b/mode/javascript/javascript.js
@@ -618,13 +618,13 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
if (type == "variable") {register(value); return cont(classNameAfter);}
}
function classNameAfter(type, value) {
- if (value == "extends") return cont(isTS ? typeexpr : expression, classNameAfter);
+ if (value == "extends" || value == "implements") return cont(isTS ? typeexpr : expression, classNameAfter);
if (type == "{") return cont(pushlex("}"), classBody, poplex);
}
function classBody(type, value) {
if (type == "variable" || cx.style == "keyword") {
if ((value == "static" || value == "get" || value == "set" ||
- (isTS && (value == "public" || value == "private" || value == "protected" || value == "readonly"))) &&
+ (isTS && (value == "public" || value == "private" || value == "protected" || value == "readonly" || value == "abstract"))) &&
cx.stream.match(/^\s+[\w$\xa1-\uffff]/, false)) {
cx.marked = "keyword";
return cont(classBody);
From aa6e9903a786477a789f431c39a8fd030b8046d5 Mon Sep 17 00:00:00 2001
From: Adrian Heine
Date: Tue, 23 Aug 2016 08:41:51 +0200
Subject: [PATCH 0223/2085] Split into ES6 modules (Closes #747)
---
lib/codemirror.js | 8966 -----------------------
package.json | 7 +-
src/banner.js | 8 +
src/codemirror.js | 3 +
src/display/Display.js | 105 +
src/display/focus.js | 49 +
src/display/gutters.js | 33 +
src/display/highlight_worker.js | 51 +
src/display/line_numbers.js | 48 +
src/display/mode_state.js | 22 +
src/display/operations.js | 215 +
src/display/scroll_events.js | 138 +
src/display/scrollbars.js | 188 +
src/display/scrolling.js | 132 +
src/display/selection.js | 137 +
src/display/update_display.js | 227 +
src/display/update_lines.js | 62 +
src/display/view_tracking.js | 152 +
src/edit/CodeMirror.js | 213 +
src/edit/commands.js | 199 +
src/edit/deleteNearSelection.js | 30 +
src/edit/drop_events.js | 118 +
src/edit/fromTextArea.js | 59 +
src/edit/global_events.js | 46 +
src/edit/key_events.js | 153 +
src/edit/legacy.js | 62 +
src/edit/main.js | 71 +
src/edit/methods.js | 573 ++
src/edit/mouse_events.js | 307 +
src/edit/options.js | 196 +
src/edit/utils.js | 7 +
src/input/ContentEditableInput.js | 468 ++
src/input/TextareaInput.js | 359 +
src/input/indent.js | 71 +
src/input/input.js | 133 +
src/input/keymap.js | 139 +
src/input/keynames.js | 17 +
src/line/highlight.js | 204 +
src/line/line_data.js | 330 +
src/line/pos.js | 35 +
src/line/saw_special_spans.js | 10 +
src/line/spans.js | 362 +
src/line/utils_line.js | 83 +
src/measurement/position_measurement.js | 580 ++
src/measurement/update_line.js | 189 +
src/measurement/widgets.js | 26 +
src/model/Doc.js | 384 +
src/model/change_measurement.js | 61 +
src/model/changes.js | 329 +
src/model/chunk.js | 157 +
src/model/document_data.js | 97 +
src/model/history.js | 224 +
src/model/line_widget.js | 67 +
src/model/mark_text.js | 288 +
src/model/selection.js | 79 +
src/model/selection_updates.js | 205 +
src/modes.js | 95 +
src/util/StringStream.js | 80 +
src/util/bidi.js | 274 +
src/util/browser.js | 31 +
src/util/dom.js | 89 +
src/util/event.js | 102 +
src/util/feature_detection.js | 83 +
src/util/misc.js | 126 +
src/util/operation_group.js | 73 +
65 files changed, 9460 insertions(+), 8967 deletions(-)
delete mode 100644 lib/codemirror.js
create mode 100644 src/banner.js
create mode 100644 src/codemirror.js
create mode 100644 src/display/Display.js
create mode 100644 src/display/focus.js
create mode 100644 src/display/gutters.js
create mode 100644 src/display/highlight_worker.js
create mode 100644 src/display/line_numbers.js
create mode 100644 src/display/mode_state.js
create mode 100644 src/display/operations.js
create mode 100644 src/display/scroll_events.js
create mode 100644 src/display/scrollbars.js
create mode 100644 src/display/scrolling.js
create mode 100644 src/display/selection.js
create mode 100644 src/display/update_display.js
create mode 100644 src/display/update_lines.js
create mode 100644 src/display/view_tracking.js
create mode 100644 src/edit/CodeMirror.js
create mode 100644 src/edit/commands.js
create mode 100644 src/edit/deleteNearSelection.js
create mode 100644 src/edit/drop_events.js
create mode 100644 src/edit/fromTextArea.js
create mode 100644 src/edit/global_events.js
create mode 100644 src/edit/key_events.js
create mode 100644 src/edit/legacy.js
create mode 100644 src/edit/main.js
create mode 100644 src/edit/methods.js
create mode 100644 src/edit/mouse_events.js
create mode 100644 src/edit/options.js
create mode 100644 src/edit/utils.js
create mode 100644 src/input/ContentEditableInput.js
create mode 100644 src/input/TextareaInput.js
create mode 100644 src/input/indent.js
create mode 100644 src/input/input.js
create mode 100644 src/input/keymap.js
create mode 100644 src/input/keynames.js
create mode 100644 src/line/highlight.js
create mode 100644 src/line/line_data.js
create mode 100644 src/line/pos.js
create mode 100644 src/line/saw_special_spans.js
create mode 100644 src/line/spans.js
create mode 100644 src/line/utils_line.js
create mode 100644 src/measurement/position_measurement.js
create mode 100644 src/measurement/update_line.js
create mode 100644 src/measurement/widgets.js
create mode 100644 src/model/Doc.js
create mode 100644 src/model/change_measurement.js
create mode 100644 src/model/changes.js
create mode 100644 src/model/chunk.js
create mode 100644 src/model/document_data.js
create mode 100644 src/model/history.js
create mode 100644 src/model/line_widget.js
create mode 100644 src/model/mark_text.js
create mode 100644 src/model/selection.js
create mode 100644 src/model/selection_updates.js
create mode 100644 src/modes.js
create mode 100644 src/util/StringStream.js
create mode 100644 src/util/bidi.js
create mode 100644 src/util/browser.js
create mode 100644 src/util/dom.js
create mode 100644 src/util/event.js
create mode 100644 src/util/feature_detection.js
create mode 100644 src/util/misc.js
create mode 100644 src/util/operation_group.js
diff --git a/lib/codemirror.js b/lib/codemirror.js
deleted file mode 100644
index ececc94c23..0000000000
--- a/lib/codemirror.js
+++ /dev/null
@@ -1,8966 +0,0 @@
-// 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
-// implemented in JavaScript on top of the browser's DOM.
-//
-// You can find some technical background for some of the code below
-// at http://marijnhaverbeke.nl/blog/#cm-internals .
-
-(function(mod) {
- if (typeof exports == "object" && typeof module == "object") // CommonJS
- module.exports = mod();
- else if (typeof define == "function" && define.amd) // AMD
- return define([], mod);
- else // Plain browser env
- (this || window).CodeMirror = mod();
-})(function() {
- "use strict";
-
- // BROWSER SNIFFING
-
- // 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 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);
- var ie = ie_upto10 || ie_11up;
- var ie_version = ie && (ie_upto10 ? document.documentMode || 6 : ie_11up[1]);
- var webkit = /WebKit\//.test(userAgent);
- var qtwebkit = webkit && /Qt\/\d+\.\d+/.test(userAgent);
- var chrome = /Chrome\//.test(userAgent);
- var presto = /Opera\//.test(userAgent);
- var safari = /Apple Computer/.test(navigator.vendor);
- var mac_geMountainLion = /Mac OS X 1\d\D([8-9]|\d\d)\D/.test(userAgent);
- var phantom = /PhantomJS/.test(userAgent);
-
- var ios = /AppleWebKit/.test(userAgent) && /Mobile\/\w+/.test(userAgent);
- // This is woefully incomplete. Suggestions for alternative methods welcome.
- var mobile = ios || /Android|webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(userAgent);
- var mac = ios || /Mac/.test(platform);
- var chromeOS = /\bCrOS\b/.test(userAgent);
- var windows = /win/i.test(platform);
-
- var presto_version = presto && userAgent.match(/Version\/(\d*\.\d*)/);
- 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
- var flipCtrlCmd = mac && (qtwebkit || presto && (presto_version == null || presto_version < 12.11));
- var captureRightClick = gecko || (ie && ie_version >= 9);
-
- // Optimize some code when these features are not used.
- var sawReadOnlySpans = false, sawCollapsedSpans = false;
-
- // EDITOR CONSTRUCTOR
-
- // A CodeMirror instance represents an editor. This is the object
- // that user code is usually dealing with.
-
- function CodeMirror(place, options) {
- if (!(this instanceof CodeMirror)) return new CodeMirror(place, 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);
- if (options.lineWrapping)
- this.display.wrapper.className += " CodeMirror-wrap";
- if (options.autofocus && !mobile) display.input.focus();
- initScrollbars(this);
-
- this.state = {
- keyMaps: [], // stores maps added by addKeyMap
- overlays: [], // highlighting overlays, as added by addOverlay
- modeGen: 0, // bumped when mode/overlay changes, used to invalidate highlighting info
- overwrite: false,
- 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
- selectingText: false,
- draggingText: false,
- highlight: new Delayed(), // stores highlight worker timeout
- keySeq: null, // Unfinished key sequence
- specialChars: null
- };
-
- 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);
-
- registerEventHandlers(this);
- ensureGlobalHandlers();
-
- startOperation(this);
- this.curOp.forceUpdate = true;
- attachDoc(this, doc);
-
- if ((options.autofocus && !mobile) || cm.hasFocus())
- setTimeout(bind(onFocus, this), 20);
- else
- 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);
- // 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 CONSTRUCTOR
-
- // The display handles the DOM integration, both for input reading
- // and content drawing. It holds references to DOM nodes and
- // display-related state.
-
- function Display(place, doc, 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");
- // 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");
- // Will contain the actual code, positioned to cover the viewport.
- 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");
- // A visibility: hidden element used to find the size of things.
- 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");
- // 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");
- // Moved around its parent to cover visible view.
- 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;
- // 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;");
- // Will contain the gutters, if any.
- 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");
- // The element in which the editor lives.
- 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 (place) {
- 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;
- // Information about the rendered lines.
- 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;
- // Empty space (in pixels) above the view
- d.viewOffset = 0;
- d.lastWrapHeight = d.lastWrapWidth = 0;
- d.updateLineNumbers = null;
-
- 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;
- // 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.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;
-
- // Used for measuring wheel scrolling granularity
- d.wheelDX = d.wheelDY = d.wheelStartX = d.wheelStartY = null;
-
- // True when shift is held down.
- d.shift = false;
-
- // Used to track whether anything happened since the context menu
- // was opened.
- d.selForContextMenu = null;
-
- d.activeTouch = null;
-
- input.init(d);
- }
-
- // STATE UPDATES
-
- // Used to get the editor into a consistent state again when options change.
-
- function loadMode(cm) {
- cm.doc.mode = CodeMirror.getMode(cm.options, cm.doc.modeOption);
- resetModeState(cm);
- }
-
- 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);
- }
-
- function wrappingChanged(cm) {
- if (cm.options.lineWrapping) {
- addClass(cm.display.wrapper, "CodeMirror-wrap");
- cm.display.sizer.style.minWidth = "";
- cm.display.sizerWidth = null;
- } else {
- rmClass(cm.display.wrapper, "CodeMirror-wrap");
- findMaxLine(cm);
- }
- estimateLineHeights(cm);
- regChange(cm);
- clearCaches(cm);
- setTimeout(function(){updateScrollbars(cm);}, 100);
- }
-
- // 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).
- function estimateHeight(cm) {
- var th = textHeight(cm.display), wrapping = cm.options.lineWrapping;
- var perLine = wrapping && Math.max(5, cm.display.scroller.clientWidth / charWidth(cm.display) - 3);
- return function(line) {
- if (lineIsHidden(cm.doc, line)) return 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 (wrapping)
- return widgetsHeight + (Math.ceil(line.text.length / perLine) || 1) * th;
- else
- return widgetsHeight + th;
- };
- }
-
- function estimateLineHeights(cm) {
- var doc = cm.doc, est = estimateHeight(cm);
- doc.iter(function(line) {
- var estHeight = est(line);
- if (estHeight != line.height) updateLineHeight(line, estHeight);
- });
- }
-
- function themeChanged(cm) {
- cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-s-\S+/g, "") +
- cm.options.theme.replace(/(^|\s)\s*/g, " cm-s-");
- clearCaches(cm);
- }
-
- function guttersChanged(cm) {
- updateGutters(cm);
- regChange(cm);
- setTimeout(function(){alignHorizontally(cm);}, 20);
- }
-
- // Rebuild the gutter elements, ensure the margin to the left of the
- // code matches their width.
- function updateGutters(cm) {
- var gutters = cm.display.gutters, specs = cm.options.gutters;
- removeChildren(gutters);
- for (var i = 0; i < specs.length; ++i) {
- var gutterClass = specs[i];
- var gElt = gutters.appendChild(elt("div", null, "CodeMirror-gutter " + gutterClass));
- if (gutterClass == "CodeMirror-linenumbers") {
- cm.display.lineGutter = gElt;
- gElt.style.width = (cm.display.lineNumWidth || 1) + "px";
- }
- }
- gutters.style.display = i ? "" : "none";
- updateGutterSpace(cm);
- }
-
- function updateGutterSpace(cm) {
- var width = cm.display.gutters.offsetWidth;
- cm.display.sizer.style.marginLeft = width + "px";
- }
-
- // Compute the character length of a line, taking into account
- // collapsed ranges (see markText) that might hide parts, and join
- // other lines onto it.
- function lineLength(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;
- }
- 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;
- }
- return len;
- }
-
- // Find the longest line in the document.
- function findMaxLine(cm) {
- 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);
- if (len > d.maxLineLength) {
- d.maxLineLength = len;
- d.maxLine = line;
- }
- });
- }
-
- // Make sure the gutters options contains the element
- // "CodeMirror-linenumbers" when the lineNumbers option is true.
- function setGuttersForLineNumbers(options) {
- var found = indexOf(options.gutters, "CodeMirror-linenumbers");
- if (found == -1 && options.lineNumbers) {
- options.gutters = options.gutters.concat(["CodeMirror-linenumbers"]);
- } else if (found > -1 && !options.lineNumbers) {
- options.gutters = options.gutters.slice(0);
- options.gutters.splice(found, 1);
- }
- }
-
- // SCROLLBARS
-
- // Prepare DOM reads needed to update the scrollbars. Done in one
- // shot to minimize update/measure roundtrips.
- function measureForScrollbars(cm) {
- 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,
- scrollWidth: d.scroller.scrollWidth, clientWidth: d.scroller.clientWidth,
- viewWidth: d.wrapper.clientWidth,
- barLeft: cm.options.fixedGutter ? gutterW : 0,
- docHeight: docH,
- 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);
-
- on(vert, "scroll", function() {
- if (vert.clientHeight) scroll(vert.scrollTop, "vertical");
- });
- on(horiz, "scroll", function() {
- 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) {
- 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);
- // 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";
- } else {
- 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.firstChild.style.width =
- (measure.scrollWidth - measure.clientWidth + totalWidth) + "px";
- } else {
- this.horiz.style.display = "";
- this.horiz.firstChild.style.width = "0";
- }
-
- if (!this.checkedZeroWidth && measure.clientHeight > 0) {
- if (sWidth == 0) this.zeroWidthHack();
- this.checkedZeroWidth = true;
- }
-
- 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);
- },
- setScrollTop: function(pos) {
- 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;
- },
- enableZeroWidthBar: function(bar, delay) {
- 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
- // left corner of the scrollbar box is the scrollbar box
- // 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);
- }
- delay.set(1000, maybeDisable);
- },
- clear: function() {
- var 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);
-
- CodeMirror.scrollbarModel = {"native": NativeScrollbars, "null": NullScrollbars};
-
- function initScrollbars(cm) {
- if (cm.display.scrollbars) {
- cm.display.scrollbars.clear();
- if (cm.display.scrollbars.addClass)
- rmClass(cm.display.wrapper, cm.display.scrollbars.addClass);
- }
-
- cm.display.scrollbars = new CodeMirror.scrollbarModel[cm.options.scrollbarStyle](function(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);
- });
- node.setAttribute("cm-not-content", "true");
- }, function(pos, axis) {
- if (axis == "horizontal") setScrollLeft(cm, pos);
- else setScrollTop(cm, pos);
- }, cm);
- if (cm.display.scrollbars.addClass)
- addClass(cm.display.wrapper, cm.display.scrollbars.addClass);
- }
-
- function updateScrollbars(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;
- }
- }
-
- // 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);
-
- 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 = "";
- 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 = "";
- }
-
- // 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.
- 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 from = lineAtHeight(doc, top), to = lineAtHeight(doc, bottom);
- // Ensure is a {from: {line, ch}, to: {line, ch}} object, and
- // forces those lines into the viewport (if possible).
- if (viewport && viewport.ensure) {
- var ensureFrom = viewport.ensure.from.line, ensureTo = viewport.ensure.to.line;
- if (ensureFrom < from) {
- 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;
- }
- }
- return {from: from, to: Math.max(to, from + 1)};
- }
-
- // LINE NUMBERS
-
- // Re-align line numbers and gutter marks to compensate for
- // horizontal scrolling.
- 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";
- 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;
- 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;
- }
- if (cm.options.fixedGutter)
- 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.
- 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 (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;
- }
- return false;
- }
-
- function lineNumberFor(options, i) {
- return String(options.lineNumberFormatter(i + options.firstLineNumber));
- }
-
- // Computes display.scroller.scrollLeft + display.gutters.offsetWidth,
- // but using getBoundingClientRect to get a sub-pixel-accurate
- // result.
- function compensateForHScroll(display) {
- return display.scroller.getBoundingClientRect().left - display.sizer.getBoundingClientRect().left;
- }
-
- // DISPLAY DRAWING
-
- function DisplayUpdate(cm, viewport, force) {
- var 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 (var i = 0; i < this.events.length; i++)
- signal.apply(null, this.events[i]);
- };
-
- function maybeClipScrollbars(cm) {
- 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;
- }
- }
-
- // Does the actual updating of the line display. Bails out
- // (returning false) when there is nothing to be done and forced is
- // false.
- function updateDisplayIfNeeded(cm, update) {
- var display = cm.display, doc = cm.doc;
-
- if (update.editorIsHidden) {
- resetView(cm);
- return false;
- }
-
- // Bail out if the visible area is already rendered and nothing changed.
- if (!update.force &&
- 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;
-
- if (maybeUpdateLineNumberWidth(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);
- if (sawCollapsedSpans) {
- 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.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";
-
- var 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();
- 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();
-
- // 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;
-
- if (different) {
- display.lastWrapHeight = update.wrapperHeight;
- display.lastWrapWidth = update.wrapperWidth;
- startWorker(cm, 400);
- }
-
- display.updateLineNumbers = null;
-
- return true;
- }
-
- 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.
- if (viewport && viewport.top != null)
- 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);
- if (update.visible.from >= cm.display.viewFrom && update.visible.to <= cm.display.viewTo)
- break;
- }
- if (!updateDisplayIfNeeded(cm, update)) break;
- updateHeightsInViewport(cm);
- var barMeasure = measureForScrollbars(cm);
- updateSelection(cm);
- updateScrollbars(cm, barMeasure);
- setDocumentHeight(cm, barMeasure);
- }
-
- 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;
- }
- }
-
- function updateDisplaySimple(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();
- }
- }
-
- 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";
- }
-
- // Read the actual heights of the rendered lines, and update their
- // stored heights to match.
- 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;
- if (cur.hidden) continue;
- if (ie && ie_version < 8) {
- 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 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++)
- updateWidgetHeight(cur.rest[j]);
- }
- }
- }
-
- // 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)
- line.widgets[i].height = line.widgets[i].node.parentNode.offsetHeight;
- }
-
- // 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.
- 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) {
- 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};
- }
-
- // Sync the actual display DOM structure with display.view, removing
- // nodes for lines that are no longer in view, and creating the ones
- // 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;
-
- function rm(node) {
- 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";
- else
- node.parentNode.removeChild(node);
- return next;
- }
-
- 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];
- 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);
- } else { // Already drawn
- while (cur != lineView.node) cur = rm(cur);
- var updateNumber = lineNumbers && updateNumbersFrom != null &&
- updateNumbersFrom <= lineN && lineView.lineNumber;
- if (lineView.changes) {
- 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)));
- }
- cur = lineView.node.nextSibling;
- }
- lineN += lineView.size;
- }
- while (cur) cur = rm(cur);
- }
-
- // 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.
- 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);
- }
- 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");
- 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;
- }
- return lineView.node;
- }
-
- function updateLineBackground(lineView) {
- 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; }
- } else if (cls) {
- 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;
- if (ext && ext.line == lineView.line) {
- cm.display.externalMeasured = null;
- lineView.measure = ext.measure;
- return ext.built;
- }
- 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;
- if (built.bgClass != lineView.bgClass || built.textClass != lineView.textClass) {
- lineView.bgClass = built.bgClass;
- lineView.textClass = built.textClass;
- updateLineClasses(lineView);
- } else if (cls) {
- lineView.text.className = cls;
- }
- }
-
- function updateLineClasses(lineView) {
- updateLineBackground(lineView);
- if (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 || "";
- }
-
- function updateLineGutter(cm, lineView, lineN, dims) {
- if (lineView.gutter) {
- lineView.node.removeChild(lineView.gutter);
- lineView.gutter = null;
- }
- if (lineView.gutterBackground) {
- lineView.node.removeChild(lineView.gutterBackground);
- lineView.gutterBackground = null;
- }
- if (lineView.line.gutterClass) {
- 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);
- }
- var markers = lineView.line.gutterMarkers;
- if (cm.options.lineNumbers || markers) {
- 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);
- if (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"));
- if (markers) for (var k = 0; k < cm.options.gutters.length; ++k) {
- var id = cm.options.gutters[k], found = markers.hasOwnProperty(id) && markers[id];
- if (found)
- gutterWrap.appendChild(elt("div", [found], "CodeMirror-gutter-elt", "left: " +
- dims.gutterLeft[id] + "px; width: " + dims.gutterWidth[id] + "px"));
- }
- }
- }
-
- 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;
- if (node.className == "CodeMirror-linewidget")
- lineView.node.removeChild(node);
- }
- insertLineWidgets(cm, lineView, dims);
- }
-
- // Build a line's DOM representation from scratch
- 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;
-
- 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);
- if (lineView.rest) for (var 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");
- 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);
- else
- 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";
- if (!widget.coverGutter) {
- width -= dims.gutterTotalWidth;
- node.style.paddingLeft = dims.gutterTotalWidth + "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";
- }
- }
-
- // POSITION OBJECT
-
- // A Pos instance represents a position within the text.
- var Pos = CodeMirror.Pos = function(line, 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.
- var cmp = CodeMirror.cmpPos = function(a, b) { return a.line - b.line || a.ch - b.ch; };
-
- function copyPos(x) {return Pos(x.line, x.ch);}
- function maxPos(a, b) { return cmp(a, b) < 0 ? b : a; }
- function minPos(a, b) { return cmp(a, b) < 0 ? a : b; }
-
- // INPUT HANDLING
-
- function ensureFocus(cm) {
- if (!cm.state.focused) { cm.display.input.focus(); onFocus(cm); }
- }
-
- // 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;
-
- function applyTextInput(cm, inserted, deleted, sel, origin) {
- var 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
- // 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++)
- multiPaste.push(doc.splitLines(lastCopied.text[i]));
- }
- } else if (textLines.length == sel.ranges.length) {
- 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();
- if (range.empty()) {
- if (deleted && deleted > 0) // Handle deletion
- 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 (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,
- origin: origin || (paste ? "paste" : cm.state.cutIncoming ? "cut" : "+input")};
- makeChange(cm.doc, changeEvent);
- signalLater(cm, "inputRead", cm, changeEvent);
- }
- if (inserted && !paste)
- triggerElectric(cm, inserted);
-
- ensureCursorVisible(cm);
- cm.curOp.updateInput = updateInput;
- cm.curOp.typing = true;
- cm.state.pasteIncoming = cm.state.cutIncoming = false;
- }
-
- function handlePaste(e, cm) {
- var pasted = e.clipboardData && e.clipboardData.getData("Text");
- if (pasted) {
- e.preventDefault();
- if (!cm.isReadOnly() && !cm.options.disableInput)
- runInOp(cm, function() { applyTextInput(cm, pasted, 0, null, "paste"); });
- return true;
- }
- }
-
- 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;
-
- 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;
- 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;
- }
- } 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");
- }
- if (indented) signalLater(cm, "electricInput", cm, range.head.line);
- }
- }
-
- 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)};
- ranges.push(lineRange);
- text.push(cm.getRange(lineRange.anchor, lineRange.head));
- }
- return {text: text, ranges: ranges};
- }
-
- function disableBrowserMagic(field, spellcheck) {
- field.setAttribute("autocorrect", "off");
- field.setAttribute("autocapitalize", "off");
- field.setAttribute("spellcheck", !!spellcheck);
- }
-
- // TEXTAREA INPUT STYLE
-
- 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;
- };
-
- 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;");
- // 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 border: 0; -- iOS fails to open keyboard (issue #1287)
- if (ios) te.style.border = "1px solid black";
- disableBrowserMagic(te);
- return div;
- }
-
- TextareaInput.prototype = copyObj({
- init: function(display) {
- var input = this, cm = this.cm;
-
- // Wraps and hides input textarea
- 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);
-
- // 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() {
- 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();
- });
-
- function prepareCopyCut(e) {
- if (signalDOMEvent(cm, e)) return
- if (cm.somethingSelected()) {
- lastCopied = {lineWise: false, text: cm.getSelections()};
- if (input.inaccurateSelection) {
- input.prevInput = "";
- input.inaccurateSelection = false;
- te.value = lastCopied.text.join("\n");
- selectInput(te);
- }
- } else if (!cm.options.lineWiseCopyCut) {
- return;
- } else {
- var ranges = copyableRanges(cm);
- lastCopied = {lineWise: true, text: ranges.text};
- if (e.type == "cut") {
- cm.setSelections(ranges.ranges, null, sel_dontScroll);
- } else {
- input.prevInput = "";
- te.value = ranges.text.join("\n");
- selectInput(te);
- }
- }
- if (e.type == "cut") cm.state.cutIncoming = true;
- }
- 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();
- });
-
- // Prevent normal selection in the editor (we handle our own)
- on(display.lineSpace, "selectstart", function(e) {
- if (!eventInWidget(display, e)) e_preventDefault(e);
- });
-
- on(te, "compositionstart", function() {
- 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;
- }
- });
- },
-
- prepareSelection: function() {
- // Redraw the selection and/or cursor
- 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();
- result.teTop = Math.max(0, Math.min(display.wrapper.clientHeight - 10,
- headPos.top + lineOff.top - wrapOff.top));
- result.teLeft = Math.max(0, Math.min(display.wrapper.clientWidth - 10,
- headPos.left + lineOff.left - wrapOff.left));
- }
-
- return result;
- },
-
- showSelection: function(drawn) {
- 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";
- }
- },
-
- // 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 (cm.somethingSelected()) {
- 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;
- } else if (!typing) {
- this.prevInput = this.textarea.value = "";
- if (ie && ie_version >= 9) this.hasSelection = null;
- }
- this.inaccurateSelection = minimal;
- },
-
- getField: function() { return this.textarea; },
-
- supportsTouch: function() { return false; },
-
- focus: function() {
- 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(); },
-
- resetPosition: function() {
- this.wrapper.style.top = this.wrapper.style.left = 0;
- },
-
- 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;
- input.polling.set(this.cm.options.pollInterval, function() {
- 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;
- function p() {
- 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);
- },
-
- // Read input from the textarea, and update the document to match.
- // When something is selected, it is present in the textarea, and
- // selected (unless it is huge, in which case a placeholder is
- // 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() {
- 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,
- // in which case reading its value would be expensive.
- if (this.contextMenuPending || !cm.state.focused ||
- (hasSelection(input) && !prevInput && !this.composing) ||
- cm.isReadOnly() || cm.options.disableInput || cm.state.keySeq)
- return false;
-
- var text = input.value;
- // If nothing changed, bail.
- 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;
- }
-
- 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"); }
- }
- // 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 self = this;
- runInOp(cm, function() {
- applyTextInput(cm, text.slice(same), prevInput.length - same,
- 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 (self.composing) {
- self.composing.range.clear();
- self.composing.range = cm.markText(self.composing.start, cm.getCursor("to"),
- {className: "CodeMirror-composing"});
- }
- });
- return true;
- },
-
- ensurePolled: function() {
- if (this.pollingFast && this.poll()) this.pollingFast = false;
- },
-
- onKeyPress: function() {
- 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.
-
- // 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;
- 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;
- 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();
- // 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);
-
- // 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;
- // Re-set this, in case some other handler touched the
- // selection in the meantime.
- display.selForContextMenu = cm.doc.sel;
- }
- }
- function rehide() {
- input.contextMenuPending = false;
- input.wrapper.style.cssText = oldWrapperCSS
- 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();
- var i = 0, poll = function() {
- if (display.selForContextMenu == cm.doc.sel && te.selectionStart == 0 &&
- te.selectionEnd > 0 && input.prevInput == "\u200b")
- operation(cm, commands.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 (captureRightClick) {
- e_stop(e);
- var mouseup = function() {
- off(window, "mouseup", mouseup);
- setTimeout(rehide, 20);
- };
- on(window, "mouseup", mouseup);
- } else {
- setTimeout(rehide, 50);
- }
- },
-
- readOnlyChanged: function(val) {
- if (!val) this.reset();
- },
-
- setUneditable: nothing,
-
- needsContentAttribute: false
- }, TextareaInput.prototype);
-
- // CONTENTEDITABLE INPUT STYLE
-
- function ContentEditableInput(cm) {
- 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);
-
- 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);
- }), 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));
- 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, "compositionupdate", function(e) {
- input.composing.data = e.data;
- });
- on(div, "compositionend", function(e) {
- var 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(function() {
- if (!ours.handled)
- input.applyComposition(ours);
- if (input.composing == ours)
- input.composing = null;
- }, 50);
- });
-
- on(div, "touchstart", function() {
- input.forceCompositionEnd();
- });
-
- on(div, "input", function() {
- if (input.composing) return;
- if (cm.isReadOnly() || !input.pollContent())
- runInOp(input.cm, function() {regChange(cm);});
- });
-
- function onCopyCut(e) {
- if (signalDOMEvent(cm, e)) return
- if (cm.somethingSelected()) {
- 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 = {lineWise: true, text: ranges.text};
- if (e.type == "cut") {
- cm.operation(function() {
- cm.setSelections(ranges.ranges, 0, sel_dontScroll);
- cm.replaceSelection("", null, "cut");
- });
- }
- }
- if (e.clipboardData) {
- 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);
- 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();
- if (hadFocus == div) input.showPrimarySelection()
- }, 50);
- }
- on(div, "copy", onCopyCut);
- on(div, "cut", onCopyCut);
- },
-
- prepareSelection: function() {
- 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);
- },
-
- 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);
- 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());
- if (!start && !end) return;
-
- var view = this.cm.display.view;
- var 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;
- 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); }
- 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);
- } else {
- sel.removeAllRanges();
- sel.addRange(rng);
- }
- if (old && sel.anchorNode == null) sel.addRange(old);
- else if (gecko) this.startGracePeriod();
- }
- this.rememberSelection();
- },
-
- startGracePeriod: function() {
- var input = this;
- clearTimeout(this.gracePeriod);
- this.gracePeriod = setTimeout(function() {
- input.gracePeriod = false;
- if (input.selectionChanged())
- 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);
- },
-
- rememberSelection: function() {
- 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);
- },
-
- focus: function() {
- if (this.cm.options.readOnly != "nocursor") this.div.focus();
- },
- blur: function() { this.div.blur(); },
- getField: function() { return this.div; },
-
- supportsTouch: function() { return true; },
-
- receivedFocus: function() {
- var input = this;
- if (this.selectionInEditor())
- this.pollSelection();
- else
- 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);
- }
- }
- this.polling.set(this.cm.options.pollInterval, poll);
- },
-
- selectionChanged: function() {
- var 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;
- 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;
- });
- }
- },
-
- 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 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;
- } else {
- var fromLine = lineNo(display.view[fromIndex].line);
- var fromNode = display.view[fromIndex - 1].node.nextSibling;
- }
- var toIndex = findViewIndex(cm, to.line);
- if (toIndex == display.view.length - 1) {
- 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 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;
- }
-
- 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);
- var 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))
- ++cutEnd;
-
- 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);
- if (newText.length > 1 || newText[0] || cmp(chFrom, chTo)) {
- replaceRange(cm.doc, newText, chFrom, chTo, "+input");
- return true;
- }
- },
-
- ensurePolled: function() {
- this.forceCompositionEnd();
- },
- reset: function() {
- 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();
- },
- 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);
- },
-
- setUneditable: function(node) {
- node.contentEditable = "false"
- },
-
- onKeyPress: function(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) {
- this.div.contentEditable = String(val != "nocursor")
- },
-
- onContextMenu: nothing,
- resetPosition: nothing,
-
- needsContentAttribute: true
- }, 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 order = getOrder(line), side = "left";
- if (order) {
- 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;
- }
-
- function badPos(pos, bad) { if (bad) pos.bad = true; return pos; }
-
- function domToPos(cm, node, offset) {
- 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;
- } else {
- for (lineNode = node;; lineNode = lineNode.parentNode) {
- 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];
- if (lineView.node == lineNode)
- 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);
- if (node == wrapper) {
- 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 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;
-
- 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];
- 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 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);
- 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) {
- found = find(before, before.firstChild, -1);
- if (found)
- return badPos(Pos(found.line, found.ch + dist), bad);
- else
- dist += before.textContent.length;
- }
- }
-
- 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; }; }
- function walk(node) {
- if (node.nodeType == 1) {
- var 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;
- if (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;
- }
- if (node.getAttribute("contenteditable") == "false") return;
- for (var 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;
- if (!val) return;
- if (closing) {
- text += lineSep;
- closing = false;
- }
- text += val;
- }
- }
- for (;;) {
- walk(from);
- if (from == to) break;
- from = from.nextSibling;
- }
- return text;
- }
-
- CodeMirror.inputStyles = {"textarea": TextareaInput, "contenteditable": ContentEditableInput};
-
- // SELECTION / CURSOR
-
- // Selection objects are immutable. A new one is created every time
- // the selection changes. A selection is one or more non-overlapping
- // (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).
- function Selection(ranges, primIndex) {
- this.ranges = ranges;
- this.primIndex = primIndex;
- }
-
- Selection.prototype = {
- 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;
- 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;
- }
- 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);
- },
- somethingSelected: function() {
- for (var 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];
- if (cmp(end, range.from()) >= 0 && cmp(pos, range.to()) <= 0)
- return i;
- }
- return -1;
- }
- };
-
- 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;
- }
- };
-
- // Take an unsorted, potentially overlapping set of ranges, and
- // build a selection out of it. 'Consumes' ranges array (modifying
- // it).
- function normalizeSelection(ranges, primIndex) {
- 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];
- 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));
- }
- }
- return new Selection(ranges, primIndex);
- }
-
- function simpleSelection(anchor, head) {
- return new Selection([new Range(anchor, head || anchor)], 0);
- }
-
- // Most of the external API clips given positions to make sure they
- // actually exist within the document.
- function clipLine(doc, n) {return Math.max(doc.first, Math.min(n, doc.first + doc.size - 1));}
- function clipPos(doc, pos) {
- if (pos.line < doc.first) return 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;
- }
- function isLine(doc, l) {return l >= doc.first && l < doc.first + doc.size;}
- function clipPosArray(doc, array) {
- for (var out = [], i = 0; i < array.length; i++) out[i] = clipPos(doc, array[i]);
- return out;
- }
-
- // SELECTION UPDATES
-
- // The 'scroll' parameter given to many of these indicated whether
- // the new cursor position should be scrolled into view after
- // modifying the selection.
-
- // If shift is held or the extend flag is set, extends a range to
- // include a given position (and optionally a second position).
- // Otherwise, simply returns the range between the given positions.
- // Used for cursor motion and such.
- function extendRange(doc, range, head, other) {
- if (doc.cm && doc.cm.display.shift || doc.extend) {
- var anchor = range.anchor;
- if (other) {
- var posBefore = cmp(head, anchor) < 0;
- if (posBefore != (cmp(other, anchor) < 0)) {
- anchor = head;
- head = other;
- } else if (posBefore != (cmp(head, other) < 0)) {
- head = other;
- }
- }
- return new Range(anchor, head);
- } else {
- return new Range(other || head, head);
- }
- }
-
- // Extend the primary selection range, discard the rest.
- function extendSelection(doc, head, other, 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)
- 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);
- }
-
- // Updates a single range in the selection.
- function replaceOneSelection(doc, i, range, 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.
- function setSimpleSelection(doc, anchor, head, options) {
- setSelection(doc, simpleSelection(anchor, head), options);
- }
-
- // Give beforeSelectionChange handlers a change to influence a
- // selection update.
- function filterSelectionChange(doc, sel, options) {
- var obj = {
- ranges: sel.ranges,
- update: function(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));
- },
- 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;
- }
-
- function setSelectionReplaceHistory(doc, sel, options) {
- var done = doc.history.done, last = lst(done);
- if (last && last.ranges) {
- done[done.length - 1] = sel;
- setSelectionNoUndo(doc, sel, options);
- } else {
- setSelection(doc, sel, options);
- }
- }
-
- // Set a new selection.
- function setSelection(doc, sel, options) {
- setSelectionNoUndo(doc, sel, options);
- addSelectionToHistory(doc, doc.sel, doc.cm ? doc.cm.curOp.id : NaN, options);
- }
-
- 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 ||
- (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);
- }
-
- function setSelectionInner(doc, sel) {
- if (sel.equals(doc.sel)) return;
-
- doc.sel = sel;
-
- if (doc.cm) {
- doc.cm.curOp.updateInput = doc.cm.curOp.selectionChanged = true;
- signalCursorActivity(doc.cm);
- }
- signalLater(doc, "cursorActivity", doc);
- }
-
- // Verify that the selection does not partially select any atomic
- // marked ranges.
- function reCheckSelection(doc) {
- 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;
- 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);
- if (out || newAnchor != range.anchor || newHead != range.head) {
- if (!out) out = sel.ranges.slice(0, i);
- out[i] = new Range(newAnchor, newHead);
- }
- }
- return out ? normalizeSelection(out, sel.primIndex) : sel;
- }
-
- 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;
- 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");
- if (m.explicitlyCleared) {
- if (!line.markedSpans) break;
- else {--i; continue;}
- }
- }
- if (!m.atomic) continue;
-
- if (oldPos) {
- 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);
- 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);
- 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;
- }
- }
- return pos;
- }
-
- // Ensure a given position is not inside an atomic range.
- function skipAtomic(doc, pos, oldPos, bias, mayClear) {
- 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));
- if (!found) {
- doc.cantEdit = true;
- return Pos(doc.first, 0);
- }
- 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;
- } 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;
- } else {
- return new Pos(pos.line, pos.ch + dir);
- }
- }
-
- // SELECTION DRAWING
-
- function updateSelection(cm) {
- cm.display.input.showSelection(cm.display.input.prepareSelection());
- }
-
- function prepareSelection(cm, primary) {
- 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 (collapsed || cm.options.showCursorWhenSelecting)
- drawSelectionCursor(cm, range.head, curFragment);
- if (!collapsed)
- drawSelectionRange(cm, range, selFragment);
- }
- return result;
- }
-
- // Draws a cursor for the given range
- function drawSelectionCursor(cm, head, output) {
- 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";
-
- 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";
- }
- }
-
- // 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;
-
- function add(left, top, width, 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"));
- }
-
- function drawForLine(line, fromArg, toArg) {
- 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);
- }
-
- iterateBidiSections(getOrder(lineObj), fromArg || 0, toArg == null ? lineLen : toArg, function(from, to, dir) {
- var 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; }
- left = leftPos.left;
- right = rightPos.right;
- }
- 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);
- }
- if (toArg == null && to == lineLen) right = rightSide;
- if (!start || leftPos.top < start.top || leftPos.top == start.top && leftPos.left < start.left)
- 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};
- }
-
- var 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;
- if (singleVLine) {
- if (leftEnd.top < rightStart.top - 2) {
- 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);
- }
- }
- if (leftEnd.bottom < rightStart.top)
- add(leftSide, leftEnd.bottom, null, rightStart.top);
- }
-
- output.appendChild(fragment);
- }
-
- // Cursor-blinking
- function restartBlink(cm) {
- 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);
- else if (cm.options.cursorBlinkRate < 0)
- display.cursorDiv.style.visibility = "hidden";
- }
-
- // HIGHLIGHT WORKER
-
- function startWorker(cm, time) {
- if (cm.doc.mode.startState && cm.doc.frontier < cm.display.viewTo)
- 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 = [];
-
- 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 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);
- } else {
- if (line.text.length <= cm.options.maxHighlightLength)
- processLine(cm, line.text, state);
- line.stateAfter = doc.frontier % 5 == 0 ? copyState(doc.mode, state) : null;
- }
- ++doc.frontier;
- if (+new Date > end) {
- 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");
- });
- }
-
- // Finds the line to start with when starting a parse. Tries to
- // find a line with a stateAfter, so that it can start with a
- // valid state. If that fails, it returns the line with the
- // smallest indentation, which tends to need the least context to
- // parse correctly.
- function findStartLine(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) {
- 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;
- }
- }
- return minline;
- }
-
- 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);
- 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;
- }
-
- // POSITION MEASUREMENT
-
- function paddingTop(display) {return display.lineSpace.offsetTop;}
- function paddingVert(display) {return display.mover.offsetHeight - display.lineSpace.offsetHeight;}
- 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;
- }
-
- function scrollGap(cm) { return scrollerGap - cm.display.nativeBarWidth; }
- function displayWidth(cm) {
- return cm.display.scroller.clientWidth - scrollGap(cm) - cm.display.barWidth;
- }
- function displayHeight(cm) {
- return cm.display.scroller.clientHeight - scrollGap(cm) - cm.display.barHeight;
- }
-
- // Ensure the lineView.wrapping.heights array is populated. This is
- // an array of bottom offsets for the lines that make up a drawn
- // 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);
- if (!lineView.measure.heights || wrapping && lineView.measure.width != curWidth) {
- var 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];
- if (Math.abs(cur.bottom - next.bottom) > 2)
- heights.push((cur.bottom + next.top) / 2 - rect.top);
- }
- }
- heights.push(rect.bottom - rect.top);
- }
- }
-
- // Find a line map (mapping character offsets to text nodes) and a
- // measurement cache for the given line number. (A line view might
- // contain multiple lines when collapsed ranges are present.)
- 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++)
- 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++)
- if (lineNo(lineView.rest[i]) > lineN)
- 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;
- }
-
- // Get a {top, bottom, left, right} box (in line-local coordinates)
- // for a given character.
- function measureChar(cm, line, ch, bias) {
- return measureCharPrepared(cm, prepareMeasureForLine(cm, line), ch, bias);
- }
-
- // Find a line view that corresponds to the given line number.
- 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;
- if (ext && lineN >= ext.lineN && lineN < ext.lineN + ext.size)
- return ext;
- }
-
- // Measurement can be split in two steps, the set-up work that
- // applies to the whole line, and the measurement of the actual
- // 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) {
- var lineN = lineNo(line);
- var view = findViewForLine(cm, lineN);
- if (view && !view.text) {
- view = null;
- } else if (view && view.changes) {
- updateLineForChanges(cm, view, lineN, getDimensions(cm));
- cm.curOp.forceUpdate = true;
- }
- if (!view)
- view = updateExternalMeasurement(cm, line);
-
- 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.cache.hasOwnProperty(key)) {
- found = prepared.cache[key];
- } else {
- if (!prepared.rect)
- prepared.rect = prepared.view.text.getBoundingClientRect();
- if (!prepared.hasHeights) {
- ensureLineHeights(cm, prepared.view, prepared.rect);
- prepared.hasHeights = true;
- }
- 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};
- }
-
- var nullRect = {left: 0, right: 0, top: 0, bottom: 0};
-
- function nodeAndOffsetInLineMap(map, ch, bias) {
- 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];
- if (ch < mStart) {
- start = 0; end = 1;
- collapse = "left";
- } else if (ch < mEnd) {
- 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";
- }
- if (start != null) {
- node = map[i + 2];
- if (mStart == mEnd && bias == (node.insertLeft ? "left" : "right"))
- 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";
- }
- 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";
- }
- break;
- }
- }
- return {node: node, start: start, end: end, collapse: collapse, coverStart: mStart, coverEnd: mEnd};
- }
-
- function getUsefulRect(rects, bias) {
- var rect = nullRect
- if (bias == "left") for (var i = 0; i < rects.length; i++) {
- if ((rect = rects[i]).left != rect.right) break
- } else for (var 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;
-
- 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;
- if (ie && ie_version < 9 && start == 0 && end == place.coverEnd - place.coverStart)
- 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 (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 (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];
- 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++)
- 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; }
-
- return result;
- }
-
- // Work around problem with bounding client rects on ranges being
- // returned incorrectly when zoomed on IE10 and below.
- function maybeUpdateRectForZooming(measure, rect) {
- if (!window.screen || screen.logicalXDPI == null ||
- screen.logicalXDPI == screen.deviceXDPI || !hasBadZoomedRects(measure))
- return rect;
- var scaleX = screen.logicalXDPI / screen.deviceXDPI;
- var scaleY = screen.logicalYDPI / screen.deviceYDPI;
- return {left: rect.left * scaleX, right: rect.right * scaleX,
- top: rect.top * scaleY, bottom: rect.bottom * scaleY};
- }
-
- function clearLineMeasurementCacheFor(lineView) {
- if (lineView.measure) {
- lineView.measure.cache = {};
- lineView.measure.heights = null;
- if (lineView.rest) for (var i = 0; i < lineView.rest.length; i++)
- lineView.measure.caches[i] = {};
- }
- }
-
- function clearLineMeasurementCache(cm) {
- cm.display.externalMeasure = null;
- removeChildren(cm.display.lineMeasure);
- for (var i = 0; i < cm.display.view.length; i++)
- clearLineMeasurementCacheFor(cm.display.view[i]);
- }
-
- 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;
- }
-
- 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
- // "line", "div" (display.lineDiv), "local"/null (editor), "window",
- // or "page".
- function intoCoordSystem(cm, lineObj, rect, context) {
- if (lineObj.widgets) for (var i = 0; i < lineObj.widgets.length; ++i) if (lineObj.widgets[i].above) {
- var size = 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 == "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;
- }
- 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.
- function fromCoordSystem(cm, coords, context) {
- if (context == "div") return coords;
- var left = coords.left, top = coords.top;
- // First move into "page" coordinate system
- if (context == "page") {
- left -= pageScrollX();
- top -= pageScrollY();
- } else if (context == "local" || !context) {
- 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};
- }
-
- 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);
- }
-
- // 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.
- function cursorCoords(cm, pos, context, lineObj, preparedMeasure, varHeight) {
- 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);
- }
- function getBidi(ch, partPos) {
- 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;
- } 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);
- }
- 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.
- 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};
- }
-
- // Positions returned by coordsChar contain some extra information.
- // xRel is the relative x position of the input coordinates compared
- // to the found position (so xRel > 0 means the coordinates are to
- // 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) {
- 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).
- 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;
- 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);
- for (;;) {
- 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);
- else
- 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);
-
- 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 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);
- // 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);
- // 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");
- 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;
- }
- var step = Math.ceil(dist / 2), middle = from + step;
- if (bidi) {
- middle = from;
- for (var i = 0; i < step; ++i) middle = moveVisually(lineObj, middle, 1);
- }
- var middleX = getX(middle);
- if (middleX > x) {to = middle; toX = middleX; if (toOutside = wrongLine) toX += 1000; dist = step;}
- else {from = middle; fromX = middleX; fromOutside = wrongLine; dist -= step;}
- }
- }
-
- var measureText;
- // Compute the default text height.
- function textHeight(display) {
- if (display.cachedTextHeight != null) return display.cachedTextHeight;
- if (measureText == null) {
- measureText = elt("pre");
- // Measure a bunch of lines, for browsers that compute
- // fractional heights.
- for (var i = 0; i < 49; ++i) {
- measureText.appendChild(document.createTextNode("x"));
- measureText.appendChild(elt("br"));
- }
- measureText.appendChild(document.createTextNode("x"));
- }
- removeChildrenAndAdd(display.measure, measureText);
- var height = measureText.offsetHeight / 50;
- if (height > 3) display.cachedTextHeight = height;
- removeChildren(display.measure);
- return height || 1;
- }
-
- // Compute the default character width.
- 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;
- }
-
- // OPERATIONS
-
- // 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
- // cursor and display (which would be awkward, slow, and
- // error-prone). Instead, display updates are batched and then all
- // combined and executed at once.
-
- var operationGroup = null;
-
- var nextOpId = 0;
- // Start a new operation.
- function startOperation(cm) {
- cm.curOp = {
- cm: cm,
- viewChanged: false, // Flag that indicates that lines might need to be redrawn
- startHeight: cm.doc.height, // Used to detect need to update scrollbar
- forceUpdate: false, // Used to force a redraw
- updateInput: null, // 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
- cursorActivityCalled: 0, // Tracks which cursorActivity handlers have been called already
- selectionChanged: false, // Whether the selection needs to be redrawn
- updateMaxLine: false, // Set when the widest line needs to be determined anew
- scrollLeft: null, scrollTop: null, // Intermediate scroll position, not pushed to DOM yet
- scrollToPos: null, // Used to scroll to a specific position
- focus: false,
- id: ++nextOpId // Unique ID
- };
- if (operationGroup) {
- operationGroup.ops.push(cm.curOp);
- } else {
- cm.curOp.ownsGroup = operationGroup = {
- ops: [cm.curOp],
- delayedCallbacks: []
- };
- }
- }
-
- function fireCallbacksForOps(group) {
- // Calls delayed callbacks and cursorActivity handlers until no
- // new ones appear
- var callbacks = group.delayedCallbacks, i = 0;
- do {
- for (; i < callbacks.length; i++)
- callbacks[i].call(null);
- for (var j = 0; j < group.ops.length; j++) {
- var op = group.ops[j];
- if (op.cursorActivityHandlers)
- while (op.cursorActivityCalled < op.cursorActivityHandlers.length)
- op.cursorActivityHandlers[op.cursorActivityCalled++].call(null, op.cm);
- }
- } while (i < callbacks.length);
- }
-
- // Finish an operation, updating the display and signalling delayed events
- function endOperation(cm) {
- var op = cm.curOp, group = op.ownsGroup;
- if (!group) return;
-
- try { fireCallbacksForOps(group); }
- finally {
- operationGroup = null;
- for (var i = 0; i < group.ops.length; i++)
- 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;
- for (var i = 0; i < ops.length; i++) // Read DOM
- endOperation_R1(ops[i]);
- for (var i = 0; i < ops.length; i++) // Write DOM (maybe)
- endOperation_W1(ops[i]);
- for (var i = 0; i < ops.length; i++) // Read DOM
- endOperation_R2(ops[i]);
- for (var i = 0; i < ops.length; i++) // Write DOM (maybe)
- endOperation_W2(ops[i]);
- for (var i = 0; i < ops.length; i++) // Read DOM
- endOperation_finish(ops[i]);
- }
-
- function endOperation_R1(op) {
- var cm = op.cm, display = cm.display;
- 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;
- op.update = op.mustUpdate &&
- new DisplayUpdate(cm, op.mustUpdate && {top: op.scrollTop, ensure: op.scrollToPos}, op.forceUpdate);
- }
-
- function endOperation_W1(op) {
- op.updatedDisplay = op.mustUpdate && updateDisplayIfNeeded(op.cm, op.update);
- }
-
- function endOperation_R2(op) {
- var cm = op.cm, display = cm.display;
- if (op.updatedDisplay) updateHeightsInViewport(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.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));
- }
-
- if (op.updatedDisplay || op.selectionChanged)
- op.preparedSelection = display.input.prepareSelection(op.focus);
- }
-
- function endOperation_W2(op) {
- var cm = op.cm;
-
- if (op.adjustWidthTo != null) {
- cm.display.sizer.style.minWidth = op.adjustWidthTo + "px";
- if (op.maxScrollLeft < cm.doc.scrollLeft)
- setScrollLeft(cm, Math.min(cm.display.scroller.scrollLeft, op.maxScrollLeft), true);
- 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);
- if (op.updatedDisplay || op.startHeight != cm.doc.height)
- updateScrollbars(cm, op.barMeasure);
- if (op.updatedDisplay)
- setDocumentHeight(cm, op.barMeasure);
-
- if (op.selectionChanged) restartBlink(cm);
-
- if (cm.state.focused && op.updateInput)
- 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;
-
- 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;
-
- // 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;
- }
- 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);
- }
- // 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);
- }
-
- // 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)
- 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 (display.wrapper.offsetHeight)
- doc.scrollTop = cm.display.scroller.scrollTop;
-
- // Fire change events, and delayed event handlers
- if (op.changeObjs)
- signal(cm, "changes", cm, op.changeObjs);
- if (op.update)
- op.update.finish();
- }
-
- // Run the given function in an operation
- function runInOp(cm, f) {
- if (cm.curOp) return f();
- startOperation(cm);
- try { return f(); }
- finally { endOperation(cm); }
- }
- // Wraps a function in an operation. Returns the wrapped function.
- 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); }
- };
- }
- // Used to add methods to editor and doc instances, wrapping them in
- // operations.
- function methodOp(f) {
- return function() {
- if (this.curOp) return f.apply(this, arguments);
- startOperation(this);
- try { return f.apply(this, arguments); }
- finally { endOperation(this); }
- };
- }
- 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); }
- };
- }
-
- // VIEW TRACKING
-
- // These objects are used to represent the visible (currently drawn)
- // part of the document. A LineView may correspond to multiple
- // logical lines, if those are connected by collapsed ranges.
- function LineView(doc, line, lineN) {
- // The starting line
- this.line = line;
- // Continuing lines, if any
- 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);
- }
-
- // Create a range of LineView objects for the given lines.
- 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);
- nextPos = pos + view.size;
- array.push(view);
- }
- return array;
- }
-
- // Updates the display.view data structure for a given change to the
- // document. From and to are in pre-change coordinates. Lendiff is
- // the amount of lines added or subtracted by the change. This is
- // used for changes that span multiple lines, or change the way
- // lines are divided into visual lines. regLineChange (below)
- // registers single-line changes.
- 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;
-
- var display = cm.display;
- if (lendiff && to < display.viewTo &&
- (display.updateLineNumbers == null || display.updateLineNumbers > from))
- display.updateLineNumbers = from;
-
- cm.curOp.viewChanged = true;
-
- if (from >= display.viewTo) { // Change after
- if (sawCollapsedSpans && visualLineNo(cm.doc, from) < display.viewTo)
- resetView(cm);
- } else if (to <= display.viewFrom) { // Change before
- if (sawCollapsedSpans && visualLineEndNo(cm.doc, to + lendiff) > display.viewFrom) {
- resetView(cm);
- } else {
- display.viewFrom += lendiff;
- display.viewTo += 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);
- if (cut) {
- display.view = display.view.slice(cut.index);
- display.viewFrom = cut.lineN;
- display.viewTo += lendiff;
- } else {
- resetView(cm);
- }
- } else if (to >= display.viewTo) { // Bottom overlap
- var cut = viewCuttingPoint(cm, from, from, -1);
- if (cut) {
- display.view = display.view.slice(0, cut.index);
- display.viewTo = cut.lineN;
- } else {
- resetView(cm);
- }
- } else { // Gap in the middle
- 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;
- } else {
- resetView(cm);
- }
- }
-
- var ext = display.externalMeasured;
- if (ext) {
- if (to < ext.lineN)
- ext.lineN += lendiff;
- else if (from < ext.lineN + ext.size)
- display.externalMeasured = null;
- }
- }
-
- // Register a change to a single line. Type must be one of "text",
- // "gutter", "class", "widget"
- function regLineChange(cm, line, type) {
- 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;
-
- 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.
- function resetView(cm) {
- cm.display.viewFrom = cm.display.viewTo = cm.doc.first;
- cm.display.view = [];
- cm.display.viewOffset = 0;
- }
-
- // Find the view element corresponding to a given line. Return null
- // when the line isn't visible.
- 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++) {
- n -= view[i].size;
- if (n < 0) return i;
- }
- }
-
- function viewCuttingPoint(cm, oldN, newN, dir) {
- var 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++)
- n += view[i].size;
- if (n != oldN) {
- if (dir > 0) {
- if (index == view.length - 1) return null;
- diff = (n + view[index].size) - oldN;
- index++;
- } else {
- diff = n - oldN;
- }
- 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;
- }
- return {index: index, lineN: newN};
- }
-
- // Force the view to cover a given range, adding empty view element
- // or clipping off existing ones as needed.
- function adjustView(cm, from, to) {
- 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;
- } else {
- if (display.viewFrom > from)
- 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;
- if (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.viewTo = to;
- }
-
- // Count the number of lines in the view whose DOM representation is
- // out of date (or nonexistent).
- function countDirtyView(cm) {
- 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;
- }
- return dirty;
- }
-
- // EVENT HANDLERS
-
- // Attach the necessary event handlers when initializing the editor
- function registerEventHandlers(cm) {
- 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);
- }));
- else
- 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);});
-
- // Used to suppress mouse event handling when a touch happens
- var touchFinished, prevTouch = {end: 0};
- function finishTouch() {
- if (d.activeTouch) {
- 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;
- }
- 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;
- }
- on(d.scroller, "touchstart", function(e) {
- if (!signalDOMEvent(cm, e) && !isMouseLikeTouchEvent(e)) {
- clearTimeout(touchFinished);
- var now = +new Date;
- d.activeTouch = {start: now, moved: false,
- 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;
- }
- }
- });
- on(d.scroller, "touchmove", function() {
- if (d.activeTouch) d.activeTouch.moved = true;
- });
- on(d.scroller, "touchend", function(e) {
- 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;
- 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
- 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);
- }
- 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);
- }
- });
-
- // 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);});
-
- // Prevent wrapper from ever scrolling
- 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);},
- 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); });
- }
-
- function dragDropChanged(cm, value, old) {
- var wasOn = old && old != CodeMirror.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);
- }
- }
-
- // Called when the window resizes
- function onResize(cm) {
- var d = cm.display;
- if (d.lastWrapHeight == d.wrapper.clientHeight && d.lastWrapWidth == d.wrapper.clientWidth)
- return;
- // Might be a text scaling operation, clear size caches.
- d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null;
- d.scrollbarsClipped = false;
- cm.setSize();
- }
-
- // MOUSE EVENTS
-
- // Return true when the given mouse event happened in a widget
- 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;
- }
- }
-
- // Given a mouse event, find the corresponding position. If liberal
- // is false, it checks whether a gutter or scrollbar was clicked,
- // and returns null if it was. forRect is used by rectangular
- // selections, and tries to estimate a character position even for
- // coordinates beyond the right of the text.
- function posFromMouse(cm, e, liberal, forRect) {
- var display = cm.display;
- if (!liberal && e_target(e).getAttribute("cm-not-content") == "true") return null;
-
- 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;
- 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));
- }
- return coords;
- }
-
- // A mouse down can be a single click, double click, triple click,
- // start of selection drag, start of text drag, new cursor
- // (ctrl-click), rectangle drag (alt-drag), or xwin
- // middle-click-paste. Or it might be a click on something we should
- // not interfere with, such as a scrollbar or widget.
- function onMouseDown(e) {
- 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);
- }
- return;
- }
- 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);
- else if (start)
- leftButtonDown(cm, e, start);
- else if (e_target(e) == display.scroller)
- 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;
- case 3:
- if (captureRightClick) onContextMenu(cm, e);
- else delayBlurEvent(cm);
- break;
- }
- }
-
- var lastClick, lastDoubleClick;
- function leftButtonDown(cm, e, start) {
- if (ie) setTimeout(bind(ensureFocus, cm), 0);
- else cm.curOp.focus = activeElt();
-
- var 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) {
- type = "double";
- lastDoubleClick = {time: now, pos: start};
- } else {
- type = "single";
- lastClick = {time: now, pos: start};
- }
-
- 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);
- else
- 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 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 (Math.abs(e.clientX - e2.clientX) + Math.abs(e.clientY - e2.clientY) < 10) {
- e_preventDefault(e2);
- if (!modifier && +new Date - 200 < startTime)
- 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);
- else
- display.input.focus();
- }
- });
- // Let the drag handler handle this.
- 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);
- }
-
- // 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 ourRange, ourIndex, startSel = doc.sel, ranges = startSel.ranges;
- if (addNew && !e.shiftKey) {
- ourIndex = doc.sel.contains(start);
- if (ourIndex > -1)
- ourRange = ranges[ourIndex];
- else
- ourRange = new Range(start, start);
- } else {
- 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;
- } else if (type == "double") {
- var 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)));
- if (cm.display.shift || doc.extend)
- ourRange = extendRange(doc, ourRange, line.anchor, line.head);
- else
- ourRange = line;
- } else {
- ourRange = extendRange(doc, ourRange, start);
- }
-
- if (!addNew) {
- ourIndex = 0;
- setSelection(doc, new Selection([ourRange], 0), sel_mouse);
- startSel = doc.sel;
- } else if (ourIndex == -1) {
- ourIndex = ranges.length;
- setSelection(doc, normalizeSelection(ranges.concat([ourRange]), ourIndex),
- {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;
- } else {
- replaceOneSelection(doc, ourIndex, ourRange, sel_mouse);
- }
-
- var 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));
- line <= end; line++) {
- var 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)
- ranges.push(new Range(Pos(line, leftPos), Pos(line, findColumn(text, right, tabSize))));
- }
- 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);
- } else {
- var oldRange = ourRange;
- var anchor = oldRange.anchor, head = pos;
- if (type != "single") {
- if (type == "double")
- var range = cm.findWordAt(pos);
- else
- var range = new Range(Pos(pos.line, 0), clipPos(doc, Pos(pos.line + 1, 0)));
- if (cmp(range.anchor, anchor) > 0) {
- head = range.head;
- anchor = minPos(oldRange.from(), range.anchor);
- } else {
- 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 editorSize = display.wrapper.getBoundingClientRect();
- // Used to ensure timeout re-tries don't fire when another extend
- // happened in the meantime (clearTimeout isn't reliable -- at
- // least on Chrome, the timeouts still happen even when cleared,
- // if the clear happens after their scheduled firing time).
- var counter = 0;
-
- function extend(e) {
- var curCount = ++counter;
- var cur = posFromMouse(cm, e, true, type == "rect");
- if (!cur) return;
- if (cmp(cur, lastPos) != 0) {
- 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);
- } else {
- var outside = e.clientY < editorSize.top ? -20 : e.clientY > editorSize.bottom ? 20 : 0;
- if (outside) setTimeout(operation(cm, function() {
- if (counter != curCount) return;
- display.scroller.scrollTop += outside;
- extend(e);
- }), 50);
- }
- }
-
- function done(e) {
- 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);
- }
-
- // 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);
-
- 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;
-
- for (var i = 0; i < cm.options.gutters.length; ++i) {
- var g = display.gutters.childNodes[i];
- if (g && g.getBoundingClientRect().right >= mX) {
- var line = lineAtHeight(cm.doc, mY);
- var gutter = cm.options.gutters[i];
- signal(cm, type, cm, line, gutter, e);
- return e_defaultPrevented(e);
- }
- }
- }
-
- function clickInGutter(cm, e) {
- return gutterEvent(cm, e, "gutterClick", true);
- }
-
- // Kludge to work around strange IE behavior where it'll sometimes
- // re-fire a series of drag-related events right after the drop (#1551)
- var lastDrop = 0;
-
- function onDrop(e) {
- var cm = this;
- 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;
- // 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) {
- if (cm.options.allowDropFileTypes &&
- indexOf(cm.options.allowDropFileTypes, file.type) == -1)
- return;
-
- 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;
- if (++read == n) {
- 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)));
- }
- });
- 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);
- // Ensure the editor is re-focused
- setTimeout(function() {cm.display.input.focus();}, 20);
- return;
- }
- try {
- 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));
- 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();
- }
- }
- catch(e){}
- }
- }
-
- 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;
-
- 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==";
- if (presto) {
- img.width = img.height = 1;
- cm.display.wrapper.appendChild(img);
- // Force a relayout, or Opera won't use our image for some obscure reason
- img._top = img.offsetTop;
- }
- e.dataTransfer.setDragImage(img, 0, 0);
- if (presto) img.parentNode.removeChild(img);
- }
- }
-
- function onDragOver(cm, e) {
- 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);
- }
- removeChildrenAndAdd(cm.display.dragCursor, frag);
- }
-
- function clearDragCursor(cm) {
- if (cm.display.dragCursor) {
- cm.display.lineSpace.removeChild(cm.display.dragCursor);
- cm.display.dragCursor = null;
- }
- }
-
- // SCROLL EVENTS
-
- // Sync the scrollable area and scrollbars, ensure the viewport
- // covers the visible area.
- 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);
- }
- // Sync scroller and scrollbar, ensure the gutter elements are
- // aligned.
- 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);
- }
-
- // Since the delta values reported on mouse wheel events are
- // unstandardized between browsers and even browser versions, and
- // generally horribly unpredictable, this code starts by measuring
- // the scroll effect that the first few mouse wheel events have,
- // and, from that, detects the way it can convert deltas to pixel
- // offsets afterwards.
- //
- // The reason we want to know the amount a wheel event will scroll
- // is that it gives us a chance to update the display before the
- // actual scrolling happens, reducing flickering.
-
- 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;
-
- 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};
- };
- CodeMirror.wheelEventPixels = function(e) {
- var delta = wheelEventDelta(e);
- delta.x *= wheelPixelsPerUnit;
- delta.y *= wheelPixelsPerUnit;
- return delta;
- };
-
- function onScrollWheel(cm, e) {
- var delta = wheelEventDelta(e), dx = delta.x, dy = delta.y;
-
- 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;
-
- // Webkit browsers on OS X abort momentum scrolls when the target
- // of the scroll event is removed from the scrollable element.
- // 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++) {
- if (view[i].node == cur) {
- cm.display.currentWheelTarget = cur;
- break outer;
- }
- }
- }
- }
-
- // On some browsers, horizontal scrolling will cause redraws to
- // happen before the gutter has been realigned, causing it to
- // wriggle around in a most unseemly way. When we have an
- // estimated pixels/delta value, we just handle horizontal
- // scrolling entirely here. It'll be slightly off from native, but
- // 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)));
- // 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;
- }
-
- // '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});
- }
-
- if (wheelSamples < 20) {
- if (display.wheelStartX == null) {
- 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;
- var sample = (movedY && display.wheelDY && movedY / display.wheelDY) ||
- (movedX && display.wheelDX && movedX / display.wheelDX);
- display.wheelStartX = display.wheelStartY = null;
- if (!sample) return;
- wheelPixelsPerUnit = (wheelPixelsPerUnit * wheelSamples + sample) / (wheelSamples + 1);
- ++wheelSamples;
- }, 200);
- } else {
- display.wheelDX += dx; display.wheelDY += dy;
- }
- }
- }
-
- // KEY EVENTS
-
- // 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;
- }
- // 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;
- try {
- 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;
- }
- 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;
- }
- return (cm.options.extraKeys && lookupKey(name, cm.options.extraKeys, handle, cm))
- || lookupKey(name, cm.options.keyMap, handle, cm);
- }
-
- var stopSeq = new Delayed;
- function dispatchKey(cm, name, e, handle) {
- var seq = cm.state.keySeq;
- if (seq) {
- if (isModifierKey(name)) return "handled";
- stopSeq.set(50, function() {
- if (cm.state.keySeq == seq) {
- cm.state.keySeq = null;
- cm.display.input.reset();
- }
- });
- name = seq + " " + name;
- }
- var result = lookupKeyForEditor(cm, name, handle);
-
- if (result == "multi")
- cm.state.keySeq = name;
- if (result == "handled")
- signalLater(cm, "keyHandled", cm, name, e);
-
- if (result == "handled" || result == "multi") {
- e_preventDefault(e);
- restartBlink(cm);
- }
-
- if (seq && !result && /\'$/.test(name)) {
- e_preventDefault(e);
- return true;
- }
- return !!result;
- }
-
- // Handle a key from the keydown event.
- function handleKeyBinding(cm, e) {
- 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);})
- || dispatchKey(cm, name, e, function(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); });
- }
- }
-
- // Handle a key from the keypress event
- function handleCharBinding(cm, e, ch) {
- return dispatchKey(cm, "'" + ch + "'", e,
- function(b) { return doHandleBinding(cm, b, true); });
- }
-
- var lastStoppedKey = null;
- function onKeyDown(e) {
- 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 (presto) {
- 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");
- }
-
- // Turn mouse into crosshair when Alt is held on Mac.
- if (code == 18 && !/\bCodeMirror-crosshair\b/.test(cm.display.lineDiv.className))
- showCrossHair(cm);
- }
-
- function showCrossHair(cm) {
- 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);
- }
- }
- on(document, "keyup", up);
- on(document, "mouseover", up);
- }
-
- function onKeyUp(e) {
- if (e.keyCode == 16) this.doc.sel.shift = false;
- signalDOMEvent(this, e);
- }
-
- 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);
- // Some browsers fire keypress events for backspace
- if (ch == "\x08") return;
- if (handleCharBinding(cm, e, ch)) return;
- cm.display.input.onKeyPress(e);
- }
-
- // FOCUS/BLUR EVENTS
-
- function delayBlurEvent(cm) {
- cm.state.delayingBlurEvent = true;
- setTimeout(function() {
- if (cm.state.delayingBlurEvent) {
- cm.state.delayingBlurEvent = false;
- onBlur(cm);
- }
- }, 100);
- }
-
- function onFocus(cm, e) {
- if (cm.state.delayingBlurEvent) cm.state.delayingBlurEvent = false;
-
- 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");
- // 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.receivedFocus();
- }
- restartBlink(cm);
- }
- function onBlur(cm, e) {
- if (cm.state.delayingBlurEvent) return;
-
- if (cm.state.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);
- }
-
- // CONTEXT MENU HANDLING
-
- // To make the context menu work, we need to briefly unhide the
- // textarea (making it as unobtrusive as possible) to let the
- // right-click take effect on it.
- function onContextMenu(cm, 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);
- }
-
- // UPDATING
-
- // Compute the position of the end of a change (its 'to' property
- // refers to the pre-change end).
- var changeEnd = CodeMirror.changeEnd = function(change) {
- 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));
- };
-
- // 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);
-
- 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);
- }
-
- function computeSelAfterChange(doc, change) {
- var out = [];
- for (var i = 0; i < doc.sel.ranges.length; i++) {
- var range = doc.sel.ranges[i];
- out.push(new Range(adjustForChange(range.anchor, change),
- adjustForChange(range.head, change)));
- }
- 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);
- else
- 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".
- 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);
- 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);
- } else {
- out[i] = new Range(from, from);
- }
- }
- return new Selection(out, doc.sel.primIndex);
- }
-
- // Allow "beforeChange" event handlers to influence a change
- function filterChange(doc, change, update) {
- var obj = {
- canceled: false,
- from: change.from,
- to: change.to,
- text: change.text,
- origin: change.origin,
- cancel: function() { this.canceled = true; }
- };
- if (update) obj.update = function(from, to, text, origin) {
- if (from) this.from = clipPos(doc, from);
- if (to) this.to = clipPos(doc, to);
- if (text) this.text = text;
- if (origin !== undefined) this.origin = origin;
- };
- signal(doc, "beforeChange", doc, obj);
- if (doc.cm) signal(doc.cm, "beforeChange", doc.cm, obj);
-
- 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.
- 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 (hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange")) {
- 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);
- 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});
- } else {
- 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);
-
- 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);
- }
- makeChangeSingleDoc(doc, change, null, stretchSpansOverChange(doc, change));
- });
- }
-
- // Revert a change stored in a document's history.
- 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;
-
- // 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];
- if (allowSelectionOnly ? event.ranges && !event.equals(doc.sel) : !event.ranges)
- break;
- }
- if (i == source.length) return;
- hist.lastOrigin = hist.lastSelOrigin = null;
-
- for (;;) {
- event = source.pop();
- if (event.ranges) {
- pushSelectionToHistory(event, dest);
- if (allowSelectionOnly && !event.equals(doc.sel)) {
- setSelection(doc, event, {clearRedo: false});
- return;
- }
- selAfter = event;
- }
- 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 filter = hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange");
-
- for (var i = event.changes.length - 1; i >= 0; --i) {
- var change = event.changes[i];
- change.origin = type;
- if (filter && !filterChange(doc, change, false)) {
- source.length = 0;
- return;
- }
-
- 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 = [];
-
- // 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);
- }
- 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;
- 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);
- 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++)
- regLineChange(doc.cm, l, "gutter");
- }
- }
-
- // More lower-level change function, handling only a single document
- // (not linked ones).
- function makeChangeSingleDoc(doc, change, selAfter, spans) {
- if (doc.cm && !doc.cm.curOp)
- return operation(doc.cm, makeChangeSingleDoc)(doc, change, selAfter, spans);
-
- if (change.to.line < doc.first) {
- shiftDoc(doc, change.text.length - 1 - (change.to.line - change.from.line));
- return;
- }
- if (change.from.line > doc.lastLine()) return;
-
- // Clip the change to the size of this doc
- if (change.from.line < doc.first) {
- var shift = change.text.length - 1 - (doc.first - change.from.line);
- shiftDoc(doc, shift);
- change = {from: Pos(doc.first, 0), to: Pos(change.to.line + shift, change.to.ch),
- text: [lst(change.text)], origin: change.origin};
- }
- var last = doc.lastLine();
- if (change.to.line > last) {
- change = {from: change.from, to: Pos(last, getLine(doc, last).text.length),
- text: [change.text[0]], origin: change.origin};
- }
-
- 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);
- }
-
- // 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 recomputeMaxLength = false, checkWidthStart = from.line;
- if (!cm.options.lineWrapping) {
- checkWidthStart = lineNo(visualLine(getLine(doc, from.line)));
- doc.iter(checkWidthStart, to.line + 1, function(line) {
- if (line == display.maxLine) {
- recomputeMaxLength = true;
- return true;
- }
- });
- }
-
- if (doc.sel.contains(change.from, change.to) > -1)
- signalCursorActivity(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);
- if (len > display.maxLineLength) {
- display.maxLine = line;
- display.maxLineLength = len;
- display.maxLineChanged = true;
- recomputeMaxLength = false;
- }
- });
- if (recomputeMaxLength) cm.curOp.updateMaxLine = true;
- }
-
- // Adjust frontier, schedule worker
- doc.frontier = Math.min(doc.frontier, from.line);
- startWorker(cm, 400);
-
- var lendiff = change.text.length - (to.line - from.line) - 1;
- // Remember that these lines changed, for updating the display
- if (change.full)
- regChange(cm);
- else if (from.line == to.line && change.text.length == 1 && !isWholeLineUpdate(cm.doc, change))
- regLineChange(cm, from.line, "text");
- else
- regChange(cm, from.line, to.line + 1, lendiff);
-
- 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);
- }
- cm.display.selForContextMenu = null;
- }
-
- 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});
- }
-
- // 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.
- function maybeScrollWindow(cm, coords) {
- 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;
- 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);
- }
- }
-
- // Scroll a given position into view (immediately), verifying that
- // it actually became visible (as line heights are accurately
- // measured, the position of something may 'drift' during drawing).
- 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),
- 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;
- if (scrollPos.scrollTop != null) {
- 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;
- }
- if (!changed) break;
- }
- return coords;
- }
-
- // Scroll a given set of coordinates into view (immediately).
- 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);
- }
-
- // Calculate a new scroll position needed to scroll the given
- // rectangle into view. Returns an object with scrollTop and
- // scrollLeft properties. When these are undefined, the
- // vertical/horizontal position does not need to be adjusted.
- 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;
- if (y1 < screentop) {
- result.scrollTop = atTop ? 0 : y1;
- } else if (y2 > screentop + screen) {
- var newTop = Math.min(y1, (atBottom ? docBottom : y2) - screen);
- if (newTop != screentop) result.scrollTop = newTop;
- }
-
- var 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;
- else if (x1 < screenleft)
- result.scrollLeft = Math.max(0, x1 - (tooWide ? 0 : 10));
- else if (x2 > screenw + screenleft - 3)
- 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).
- function addToScrollPos(cm, left, top) {
- if (left != null || top != null) resolveScrollToPos(cm);
- if (left != null)
- 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;
- }
-
- // Make sure that at the end of the operation the current cursor is
- // shown.
- function ensureCursorVisible(cm) {
- 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);
- }
- cm.curOp.scrollToPos = {from: from, to: to, margin: cm.options.cursorScrollMargin, isCursor: true};
- }
-
- // When an operation has its scrollToPos property set, and another
- // scroll action is applied before the end of the operation, this
- // 'simulates' scrolling that position into view in a cheap way, so
- // that the effect of intermediate scroll commands is not ignored.
- function resolveScrollToPos(cm) {
- var 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),
- 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);
- }
- }
-
- // API UTILITIES
-
- // Indent the given line. The how parameter can be "smart",
- // "add"/null, "subtract", or "prev". When aggressive is false
- // (typically set to true for forced single-line indents), empty
- // lines are not indented, and places where the mode returns Pass
- // are left alone.
- function indentLine(cm, n, how, aggressive) {
- 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);
- }
-
- 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";
- } else if (how == "smart") {
- indentation = doc.mode.indent(state, line.text.slice(curSpaceString.length), line.text);
- if (indentation == Pass || indentation > 150) {
- if (!aggressive) return;
- how = "prev";
- }
- }
- if (how == "prev") {
- if (n > doc.first) indentation = countColumn(getLine(doc, n-1).text, null, tabSize);
- else indentation = 0;
- } else if (how == "add") {
- indentation = curSpace + cm.options.indentUnit;
- } else if (how == "subtract") {
- indentation = curSpace - cm.options.indentUnit;
- } else if (typeof how == "number") {
- indentation = curSpace + how;
- }
- indentation = Math.max(0, indentation);
-
- 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);
-
- if (indentString != curSpaceString) {
- 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];
- if (range.head.line == n && range.head.ch < curSpaceString.length) {
- var pos = Pos(n, curSpaceString.length);
- replaceOneSelection(doc, i, new Range(pos, pos));
- break;
- }
- }
- }
- }
-
- // Utility for applying a change to a line by handle or number,
- // returning the number and optionally registering the line as
- // changed.
- 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;
- }
-
- // Helper for deleting text near the selection(s), used to implement
- // backspace, delete, and similar functionality.
- function deleteNearSelection(cm, compute) {
- 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]);
- while (kill.length && cmp(toKill.from, lst(kill).to) <= 0) {
- var replaced = kill.pop();
- if (cmp(replaced.from, toKill.from) < 0) {
- toKill.from = replaced.from;
- break;
- }
- }
- 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);
- });
- }
-
- // Used for horizontal relative motion. Dir is -1 or 1 (left or
- // right), unit can be "char", "column" (like char, but doesn't
- // cross line boundaries), "word" (across next word), or "group" (to
- // the start of next group of word or non-word-non-whitespace
- // chars). The visually param controls whether, in right-to-left
- // text, direction 1 means to move towards the next index in the
- // string, or towards the character to the right of the current
- // 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);
- function findNextLine() {
- var 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);
- if (next == null) {
- if (!boundToLine && findNextLine()) {
- if (visually) ch = (dir < 0 ? lineRight : lineLeft)(lineObj);
- else ch = dir < 0 ? lineObj.text.length : 0;
- } else return false
- } else ch = next;
- return true;
- }
-
- if (unit == "char") {
- moveOnce()
- } else if (unit == "column") {
- moveOnce(true)
- } else if (unit == "word" || unit == "group") {
- 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";
- var 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();}
- 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;
- }
-
- // 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;
- 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;
- } else if (unit == "line") {
- y = dir > 0 ? pos.bottom + 3 : pos.top - 3;
- }
- for (;;) {
- var target = coordsChar(cm, x, y);
- if (!target.outside) break;
- if (dir < 0 ? y <= 0 : y >= doc.height) { target.hitSide = true; break; }
- y += dir * 5;
- }
- return target;
- }
-
- // EDITOR METHODS
-
- // The publicly visible API. Note that methodOp(f) means
- // 'wrap f in an operation, performed on its `this` parameter'.
-
- // This is not the complete set of editor methods. Most of the
- // methods defined on the Doc type are also injected into
- // CodeMirror.prototype, for backwards compatibility and
- // convenience.
-
- CodeMirror.prototype = {
- constructor: CodeMirror,
- 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;
- if (optionHandlers.hasOwnProperty(option))
- operation(this, optionHandlers[option])(this, value, old);
- },
-
- getOption: function(option) {return this.options[option];},
- getDoc: function() {return this.doc;},
-
- addKeyMap: function(map, bottom) {
- this.state.keyMaps[bottom ? "push" : "unshift"](getKeyMap(map));
- },
- removeKeyMap: function(map) {
- 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;
- }
- },
-
- 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.");
- 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);
- }),
- removeOverlay: methodOp(function(spec) {
- var overlays = this.state.overlays;
- for (var i = 0; i < overlays.length; ++i) {
- var cur = overlays[i].modeSpec;
- if (cur == spec || typeof spec == "string" && cur.name == spec) {
- 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 (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];
- 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;
- for (var j = start; j < end; ++j)
- 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);
- } 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);
- }
- }
- }),
-
- // 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);
- },
-
- getLineTokens: function(line, precise) {
- 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];
- 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 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;
- },
-
- getHelper: function(pos, type) {
- 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);
- 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]];
- if (val) found.push(val);
- }
- } else if (mode.helperType && help[mode.helperType]) {
- found.push(help[mode.helperType]);
- } else if (help[mode.name]) {
- found.push(help[mode.name]);
- }
- for (var i = 0; i < help._global.length; i++) {
- var cur = help._global[i];
- if (cur.pred(mode, this) && indexOf(found, cur.val) == -1)
- found.push(cur.val);
- }
- 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);
- },
-
- 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");
- },
-
- charCoords: function(pos, mode) {
- 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);
- },
-
- lineAtHeight: function(height, mode) {
- 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;
- 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);
- } else {
- lineObj = line;
- }
- return intoCoordSystem(this, lineObj, {top: 0, left: 0}, mode || "page").top +
- (end ? this.doc.height - heightAtLine(lineObj) : 0);
- },
-
- 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;
- });
- }),
-
- clearGutter: methodOp(function(gutterID) {
- 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;
- }
- ++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;
- } else {
- 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};
- },
-
- 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);
- if (vert == "over") {
- 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);
- // 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;
- else if (pos.bottom + node.offsetHeight <= vspace)
- top = pos.bottom;
- if (left + node.offsetWidth > hspace)
- left = hspace - node.offsetWidth;
- }
- node.style.top = top + "px";
- node.style.left = node.style.right = "";
- if (horiz == "right") {
- left = display.sizer.clientWidth - node.offsetWidth;
- node.style.right = "0px";
- } else {
- if (horiz == "left") left = 0;
- else if (horiz == "middle") left = (display.sizer.clientWidth - node.offsetWidth) / 2;
- node.style.left = left + "px";
- }
- if (scroll)
- scrollIntoView(this, left, top, left + node.offsetWidth, top + node.offsetHeight);
- },
-
- triggerOnKeyDown: methodOp(onKeyDown),
- triggerOnKeyPress: methodOp(onKeyPress),
- triggerOnKeyUp: onKeyUp,
-
- execCommand: function(cmd) {
- if (commands.hasOwnProperty(cmd))
- return commands[cmd].call(null, this);
- },
-
- triggerElectric: methodOp(function(text) { triggerElectric(this, text); }),
-
- findPosH: function(from, amount, unit, visually) {
- var dir = 1;
- if (amount < 0) { dir = -1; amount = -amount; }
- for (var i = 0, cur = clipPos(this.doc, from); i < amount; ++i) {
- cur = findPosH(this.doc, cur, dir, unit, visually);
- if (cur.hitSide) break;
- }
- return cur;
- },
-
- moveH: methodOp(function(dir, unit) {
- 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);
- else
- return dir < 0 ? range.from() : range.to();
- }, sel_move);
- }),
-
- deleteH: methodOp(function(dir, unit) {
- var 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);
- 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; }
- 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;
- }
- return cur;
- },
-
- moveV: methodOp(function(dir, unit) {
- 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);
- 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++)
- doc.sel.ranges[i].goalColumn = goals[i];
- }),
-
- // Find the word at the given position (as returned by coordsChar).
- findWordAt: function(pos) {
- var doc = this.doc, line = getLine(doc, pos.line).text;
- var start = pos.ch, end = pos.ch;
- if (line) {
- var helper = this.getHelper(pos, "wordChars");
- if ((pos.xRel < 0 || end == line.length) && start) --start; else ++end;
- var startChar = line.charAt(start);
- var check = isWordChar(startChar, helper)
- ? function(ch) { return isWordChar(ch, helper); }
- : /\s/.test(startChar) ? function(ch) {return /\s/.test(ch);}
- : function(ch) {return !/\s/.test(ch) && !isWordChar(ch);};
- while (start > 0 && check(line.charAt(start - 1))) --start;
- while (end < line.length && check(line.charAt(end))) ++end;
- }
- return new Range(Pos(pos.line, start), Pos(pos.line, end));
- },
-
- toggleOverwrite: function(value) {
- if (value != null && value == this.state.overwrite) return;
- if (this.state.overwrite = !this.state.overwrite)
- addClass(this.display.cursorDiv, "CodeMirror-overwrite");
- else
- rmClass(this.display.cursorDiv, "CodeMirror-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); },
-
- 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;
- }),
- getScrollInfo: function() {
- 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)};
- },
-
- scrollIntoView: methodOp(function(range, margin) {
- if (range == null) {
- 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};
- } else if (range.from == null) {
- range = {from: range, to: null};
- }
- if (!range.to) range.to = range.from;
- range.margin = margin || 0;
-
- if (range.from.line != null) {
- 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);
- }
- }),
-
- setSize: methodOp(function(width, height) {
- var 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;
- 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);
- }),
-
- 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);
- if (oldHeight == null || Math.abs(oldHeight - textHeight(this.display)) > .5)
- 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;
- }),
-
- 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);
-
- // OPTION DEFAULTS
-
- // The default configuration options.
- var defaults = CodeMirror.defaults = {};
- // Functions to run when options are changed.
- var optionHandlers = CodeMirror.optionHandlers = {};
-
- 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;
- }
-
- // Passed to option handlers when there is no old value.
- var Init = CodeMirror.Init = {toString: function(){return "CodeMirror.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);
- option("mode", null, function(cm, val) {
- cm.doc.modeOption = val;
- loadMode(cm);
- }, true);
-
- option("indentUnit", 2, loadMode, true);
- option("indentWithTabs", false);
- option("smartIndent", true);
- option("tabSize", 4, function(cm) {
- 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.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));
- }
- 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 != CodeMirror.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);
- option("spellcheck", false, function(cm, val) {
- cm.getInputField().spellcheck = val
- }, true);
- option("rtlMoveVisually", !windows);
- option("wholeLineUpdateBefore", true);
-
- option("theme", "default", function(cm) {
- themeChanged(cm);
- guttersChanged(cm);
- }, true);
- option("keyMap", "default", function(cm, val, old) {
- var next = getKeyMap(val);
- var prev = old != CodeMirror.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);
- 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);
- option("scrollbarStyle", "native", function(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) {
- 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("readOnly", false, function(cm, val) {
- if (val == "nocursor") {
- onBlur(cm);
- cm.display.input.blur();
- cm.display.disabled = true;
- } else {
- 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("moveInputWithCursor", true, function(cm, val) {
- if (!val) cm.display.input.resetPosition();
- });
-
- option("tabindex", null, function(cm, val) {
- cm.display.input.getField().tabIndex = val || "";
- });
- option("autofocus", null);
-
- // MODE DEFINITION AND QUERYING
-
- // Known modes, by name and by MIME
- var modes = CodeMirror.modes = {}, mimeModes = CodeMirror.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.)
- CodeMirror.defineMode = function(name, mode) {
- if (!CodeMirror.defaults.mode && name != "null") CodeMirror.defaults.mode = name;
- if (arguments.length > 2)
- mode.dependencies = Array.prototype.slice.call(arguments, 2);
- modes[name] = mode;
- };
-
- CodeMirror.defineMIME = function(mime, spec) {
- mimeModes[mime] = spec;
- };
-
- // Given a MIME type, a {name, ...options} config object, or a name
- // string, return a mode config object.
- CodeMirror.resolveMode = function(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];
- 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 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"};
- };
-
- // Given a mode spec (anything that resolveMode accepts), find and
- // initialize an actual mode object.
- CodeMirror.getMode = function(options, spec) {
- var spec = CodeMirror.resolveMode(spec);
- var mfactory = modes[spec.name];
- if (!mfactory) return CodeMirror.getMode(options, "text/plain");
- var modeObj = mfactory(options, spec);
- if (modeExtensions.hasOwnProperty(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];
- }
- }
- 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];
-
- return modeObj;
- };
-
- // Minimal default mode.
- CodeMirror.defineMode("null", function() {
- return {token: function(stream) {stream.skipToEnd();}};
- });
- CodeMirror.defineMIME("text/plain", "null");
-
- // This can be used to attach properties to mode objects from
- // outside the actual mode definition.
- var modeExtensions = CodeMirror.modeExtensions = {};
- CodeMirror.extendMode = function(mode, properties) {
- var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {});
- copyObj(properties, exts);
- };
-
- // EXTENSIONS
-
- CodeMirror.defineExtension = function(name, func) {
- CodeMirror.prototype[name] = func;
- };
- CodeMirror.defineDocExtension = function(name, func) {
- Doc.prototype[name] = func;
- };
- CodeMirror.defineOption = option;
-
- var initHooks = [];
- CodeMirror.defineInitHook = function(f) {initHooks.push(f);};
-
- var helpers = CodeMirror.helpers = {};
- CodeMirror.registerHelper = function(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});
- };
-
- // MODE STATE HANDLING
-
- // Utility functions for working with state. Exported because nested
- // modes need to do this for their inner modes.
-
- var copyState = CodeMirror.copyState = function(mode, state) {
- if (state === true) return state;
- if (mode.copyState) return mode.copyState(state);
- var nstate = {};
- for (var n in state) {
- var val = state[n];
- if (val instanceof Array) val = val.concat([]);
- nstate[n] = val;
- }
- return nstate;
- };
-
- var startState = CodeMirror.startState = function(mode, a1, a2) {
- return mode.startState ? mode.startState(a1, a2) : true;
- };
-
- // Given a mode and a state (for that mode), find the inner mode and
- // state at the position that the state refers to.
- CodeMirror.innerMode = function(mode, state) {
- while (mode.innerMode) {
- var info = mode.innerMode(state);
- if (!info || info.mode == mode) break;
- state = info.state;
- mode = info.mode;
- }
- return info || {mode: mode, state: state};
- };
-
- // STANDARD COMMANDS
-
- // Commands are parameter-less actions that can be performed on an
- // editor, mostly used for keybindings.
- var commands = CodeMirror.commands = {
- selectAll: function(cm) {cm.setSelection(Pos(cm.firstLine(), 0), Pos(cm.lastLine()), sel_dontScroll);},
- singleSelection: function(cm) {
- 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;
- 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) {
- 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()));},
- 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) {
- 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);
- },
- 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");},
- 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);
- spaces.push(spaceStr(tabSize - col % tabSize));
- }
- cm.replaceSelections(spaces);
- },
- defaultTab: function(cm) {
- if (cm.somethingSelected()) cm.indentSelection("add");
- else cm.execCommand("insertTab");
- },
- 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;
- 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) {
- 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");
- }
- }
- newSel.push(new Range(cur, cur));
- }
- cm.setSelections(newSel);
- });
- },
- newlineAndIndent: function(cm) {
- runInOp(cm, function() {
- var sels = cm.listSelections()
- for (var 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++)
- cm.indentLine(sels[i].from().line, null, true)
- ensureCursorVisible(cm);
- });
- },
- openLine: function(cm) {cm.replaceSelection("\n", "start")},
- toggleOverwrite: function(cm) {cm.toggleOverwrite();}
- };
-
-
- // STANDARD KEYMAPS
-
- var keyMap = CodeMirror.keyMap = {};
-
- keyMap.basic = {
- "Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown",
- "End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown",
- "Delete": "delCharAfter", "Backspace": "delCharBefore", "Shift-Backspace": "delCharBefore",
- "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.
- keyMap.pcDefault = {
- "Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo",
- "Ctrl-Home": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Up": "goLineUp", "Ctrl-Down": "goLineDown",
- "Ctrl-Left": "goGroupLeft", "Ctrl-Right": "goGroupRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd",
- "Ctrl-Backspace": "delGroupBefore", "Ctrl-Delete": "delGroupAfter", "Ctrl-S": "save", "Ctrl-F": "find",
- "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll",
- "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",
- "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",
- "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",
- "Alt-Right": "goGroupRight", "Cmd-Left": "goLineLeft", "Cmd-Right": "goLineRight", "Alt-Backspace": "delGroupBefore",
- "Ctrl-Alt-Backspace": "delGroupAfter", "Alt-Delete": "delGroupAfter", "Cmd-S": "save", "Cmd-F": "find",
- "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll",
- "Cmd-[": "indentLess", "Cmd-]": "indentMore", "Cmd-Backspace": "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 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];
- 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;
- }
-
- // This is a kludge to keep keymaps mostly working as raw objects
- // (backwards compatibility) while at the same time support features
- // like normalization and multi-stroke key bindings. It compiles a
- // new normalized keymap, and then updates the old object to reflect
- // this.
- CodeMirror.normalizeKeyMap = function(keymap) {
- 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 keys = map(keyname.split(" "), normalizeKeyName);
- for (var i = 0; i < keys.length; i++) {
- var val, name;
- if (i == keys.length - 1) {
- name = keys.join(" ");
- val = value;
- } else {
- 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);
- }
- delete keymap[keyname];
- }
- for (var prop in copy) keymap[prop] = copy[prop];
- return keymap;
- };
-
- var lookupKey = CodeMirror.lookupKey = function(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";
-
- 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);
- if (result) return result;
- }
- }
- };
-
- // Modifier key presses don't count as 'real' key presses for the
- // purpose of keymap fallthrough.
- var isModifierKey = CodeMirror.isModifierKey = function(value) {
- 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.
- var keyName = CodeMirror.keyName = function(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;
- };
-
- function getKeyMap(val) {
- return typeof val == "string" ? keyMap[val] : val;
- }
-
- // FROMTEXTAREA
-
- CodeMirror.fromTextArea = function(textarea, options) {
- options = options ? copyObj(options) : {};
- options.value = textarea.value;
- if (!options.tabindex && textarea.tabIndex)
- options.tabindex = textarea.tabIndex;
- if (!options.placeholder && textarea.placeholder)
- options.placeholder = textarea.placeholder;
- // Set autofocus to true if this textarea is focused, or if it has
- // autofocus and no other element is focused.
- if (options.autofocus == null) {
- var hasFocus = activeElt();
- options.autofocus = hasFocus == textarea ||
- textarea.getAttribute("autofocus") != null && hasFocus == document.body;
- }
-
- function save() {textarea.value = cm.getValue();}
- 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;
- try {
- var wrappedSubmit = form.submit = function() {
- save();
- form.submit = realSubmit;
- form.submit();
- form.submit = wrappedSubmit;
- };
- } catch(e) {}
- }
- }
-
- options.finishInit = function(cm) {
- 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 = "";
- if (textarea.form) {
- off(textarea.form, "submit", save);
- if (typeof textarea.form.submit == "function")
- textarea.form.submit = realSubmit;
- }
- };
- };
-
- textarea.style.display = "none";
- var cm = CodeMirror(function(node) {
- textarea.parentNode.insertBefore(node, textarea.nextSibling);
- }, options);
- return cm;
- };
-
- // STRING STREAM
-
- // Fed to the mode parsers, provides helper functions to make
- // parsers more succinct.
-
- var StringStream = CodeMirror.StringStream = function(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() {
- if (this.pos < this.string.length)
- return this.string.charAt(this.pos++);
- },
- eat: function(match) {
- var ch = this.string.charAt(this.pos);
- if (typeof match == "string") var ok = ch == match;
- else var ok = ch && (match.test ? match.test(ch) : match(ch));
- if (ok) {++this.pos; return ch;}
- },
- eatWhile: function(match) {
- var start = this.pos;
- while (this.eat(match)){}
- return this.pos > start;
- },
- eatSpace: function() {
- var start = this.pos;
- while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) ++this.pos;
- return this.pos > start;
- },
- skipToEnd: function() {this.pos = this.string.length;},
- skipTo: function(ch) {
- var found = this.string.indexOf(ch, this.pos);
- if (found > -1) {this.pos = found; return true;}
- },
- backUp: function(n) {this.pos -= n;},
- column: function() {
- 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() {
- return countColumn(this.string, null, this.tabSize) -
- (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);
- if (cased(substr) == cased(pattern)) {
- 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;
- }
- },
- current: function(){return this.string.slice(this.start, this.pos);},
- hideFirstChars: function(n, inner) {
- this.lineStart += n;
- try { return inner(); }
- finally { this.lineStart -= n; }
- }
- };
-
- // TEXTMARKERS
-
- // Created with markText and setBookmark methods. A TextMarker is a
- // handle that can be used to clear or find a marked position in the
- // document. Line objects hold arrays (markedSpans) containing
- // {from, to, marker} object pointing to such marker objects, and
- // indicating that such a marker is present on that line. Multiple
- // lines may point to the same marker when it spans across lines.
- // The spans will have null for their from/to properties when the
- // marker continues beyond the start/end of the line. Markers have
- // links back to the lines they currently touch.
-
- var nextMarkerId = 0;
-
- var TextMarker = CodeMirror.TextMarker = function(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;
- 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 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");
- 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 (var i = 0; i < this.lines.length; ++i) {
- 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;
- }
- }
-
- 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();
- };
-
- // 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;
- var from, to;
- for (var i = 0; i < this.lines.length; ++i) {
- var line = this.lines[i];
- var span = getMarkedSpanFor(line.markedSpans, this);
- if (span.from != null) {
- 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};
- };
-
- // 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;
- runInOp(cm, function() {
- var line = pos.line, lineN = lineNo(pos.line);
- var 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;
- widget.height = null;
- var dHeight = widgetHeight(widget) - oldHeight;
- if (dHeight)
- updateLineHeight(line, line.height + dHeight);
- }
- });
- };
-
- TextMarker.prototype.attachLine = function(line) {
- if (!this.lines.length && this.doc.cm) {
- var op = this.doc.cm.curOp;
- if (!op.maybeHiddenMarkers || indexOf(op.maybeHiddenMarkers, this) == -1)
- (op.maybeUnhiddenMarkers || (op.maybeUnhiddenMarkers = [])).push(this);
- }
- this.lines.push(line);
- };
- TextMarker.prototype.detachLine = function(line) {
- this.lines.splice(indexOf(this.lines, line), 1);
- if (!this.lines.length && this.doc.cm) {
- var op = this.doc.cm.curOp;
- (op.maybeHiddenMarkers || (op.maybeHiddenMarkers = [])).push(this);
- }
- };
-
- // 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
- 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);
- // 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);
- 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;
- 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;
- }
- 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");
- sawCollapsedSpans = true;
- }
-
- if (marker.addToHistory)
- addChangeToHistory(doc, {from: from, to: to, origin: "markText"}, doc.sel, NaN);
-
- 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);
- addMarkedSpan(line, new MarkedSpan(marker,
- curLine == from.line ? from.ch : null,
- 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 (marker.clearOnEnter) on(marker, "beforeCursorEnter", function() { marker.clear(); });
-
- if (marker.readOnly) {
- sawReadOnlySpans = true;
- if (doc.history.done.length || doc.history.undone.length)
- doc.clearHistory();
- }
- if (marker.collapsed) {
- marker.id = ++nextMarkerId;
- marker.atomic = true;
- }
- if (cm) {
- // Sync editor state
- 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)
- 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;
- }
-
- // SHARED TEXTMARKERS
-
- // A shared marker spans multiple linked documents. It is
- // implemented as a meta-marker-object controlling multiple normal
- // markers.
- var SharedTextMarker = CodeMirror.SharedTextMarker = function(markers, primary) {
- this.markers = markers;
- this.primary = primary;
- for (var i = 0; i < markers.length; ++i)
- markers[i].parent = this;
- };
- eventMixin(SharedTextMarker);
-
- SharedTextMarker.prototype.clear = function() {
- if (this.explicitlyCleared) return;
- this.explicitlyCleared = true;
- for (var 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);
- };
-
- 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;
- 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)
- if (doc.linked[i].isParent) return;
- primary = lst(markers);
- });
- return new SharedTextMarker(markers, primary);
- }
-
- function findSharedMarkers(doc) {
- return doc.findMarks(Pos(doc.first, 0), doc.clipPos(Pos(doc.lastLine())),
- function(m) { return m.parent; });
- }
-
- 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);
- if (cmp(mFrom, mTo)) {
- var subMark = markText(doc, mFrom, mTo, marker.primary, marker.primary.type);
- marker.markers.push(subMark);
- subMark.parent = marker;
- }
- }
- }
-
- 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); });
- for (var j = 0; j < marker.markers.length; j++) {
- var subMarker = marker.markers[j];
- if (indexOf(linked, subMarker.doc) == -1) {
- subMarker.parent = null;
- marker.markers.splice(j--, 1);
- }
- }
- }
- }
-
- // TEXTMARKER SPANS
-
- function MarkedSpan(marker, from, to) {
- this.marker = marker;
- this.from = from; this.to = to;
- }
-
- // Search an array of spans for a span matching the given marker.
- function getMarkedSpanFor(spans, marker) {
- if (spans) for (var i = 0; i < spans.length; ++i) {
- 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).
- function removeMarkedSpan(spans, span) {
- for (var r, i = 0; i < spans.length; ++i)
- if (spans[i] != span) (r || (r = [])).push(spans[i]);
- return r;
- }
- // Add a span to a line.
- function addMarkedSpan(line, span) {
- 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
- // document. These functions cut an array of spans at a given
- // 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);
- 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));
- }
- }
- 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);
- 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));
- }
- }
- return nw;
- }
-
- // Given a change object, compute the new set of marker spans that
- // cover the line in which the change took place. Removes spans
- // entirely within the change, reconnects spans belonging to the
- // same marker that appear on both sides of the change, and cuts off
- // spans partially within the change. Returns an array of span
- // arrays with one element for each line in (after) the change.
- 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;
-
- 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);
-
- // Next, merge those two ends
- 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];
- if (span.to == null) {
- var found = getMarkedSpanFor(last, span.marker);
- if (!found) span.to = startCh;
- else if (sameLine) span.to = found.to == null ? null : found.to + offset;
- }
- }
- }
- if (last) {
- // Fix up .from in last (or move them into first in case of sameLine)
- for (var i = 0; i < last.length; ++i) {
- var span = last[i];
- if (span.to != null) span.to += offset;
- if (span.from == null) {
- var found = getMarkedSpanFor(first, span.marker);
- if (!found) {
- span.from = offset;
- if (sameLine) (first || (first = [])).push(span);
- }
- } else {
- span.from += offset;
- if (sameLine) (first || (first = [])).push(span);
- }
- }
- }
- // Make sure we didn't create any zero-length spans
- if (first) first = clearEmptySpans(first);
- if (last && last != first) last = clearEmptySpans(last);
-
- var newMarkers = [first];
- if (!sameLine) {
- // Fill gap with whole-line-spans
- 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));
- for (var i = 0; i < gap; ++i)
- newMarkers.push(gapMarkers);
- newMarkers.push(last);
- }
- 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];
- if (span.from != null && span.from == span.to && span.marker.clearWhenEmpty !== false)
- spans.splice(i--, 1);
- }
- if (!spans.length) return null;
- return spans;
- }
-
- // Used for un/re-doing changes from the history. Combines the
- // result of computing the existing spans with the set of spans that
- // existed in the history (so that deleting around a span and then
- // undoing brings back the span).
- function mergeOldSpans(doc, change) {
- 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];
- if (oldCur && stretchCur) {
- spans: for (var j = 0; j < stretchCur.length; ++j) {
- var span = stretchCur[j];
- for (var k = 0; k < oldCur.length; ++k)
- if (oldCur[k].marker == span.marker) continue spans;
- oldCur.push(span);
- }
- } else if (stretchCur) {
- old[i] = stretchCur;
- }
- }
- return old;
- }
-
- // Used to 'clip' out readOnly ranges when making a change.
- function removeReadOnlyRanges(doc, from, to) {
- 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;
- 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];
- 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});
- if (dto > 0 || !mk.inclusiveRight && !dto)
- newParts.push({from: m.to, to: p.to});
- parts.splice.apply(parts, newParts);
- j += newParts.length - 1;
- }
- }
- return parts;
- }
-
- // Connect or disconnect spans from a line.
- function detachMarkedSpans(line) {
- var spans = line.markedSpans;
- if (!spans) return;
- for (var i = 0; i < spans.length; ++i)
- spans[i].marker.detachLine(line);
- line.markedSpans = null;
- }
- function attachMarkedSpans(line, spans) {
- if (!spans) return;
- for (var i = 0; i < spans.length; ++i)
- spans[i].marker.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; }
-
- // 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.
- 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;
- }
-
- // 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) {
- sp = sps[i];
- if (sp.marker.collapsed && (start ? sp.from : sp.to) == null &&
- (!found || compareCollapsedMarkers(found, sp.marker) < 0))
- found = sp.marker;
- }
- return found;
- }
- function collapsedSpanAtStart(line) { return collapsedSpanAtSide(line, true); }
- 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.
- 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];
- 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;
- }
- }
-
- // A visual line is a line as drawn on the screen. Folding, for
- // example, can cause multiple logical lines to appear on the same
- // visual line. This finds the start of the visual line that the
- // given line is part of (usually that is the line itself).
- function visualLine(line) {
- var merged;
- while (merged = collapsedSpanAtStart(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.
- function visualLineContinued(line) {
- var merged, lines;
- while (merged = collapsedSpanAtEnd(line)) {
- line = merged.find(1, true).line;
- (lines || (lines = [])).push(line);
- }
- return lines;
- }
-
- // Get the line number of the start of the visual line that the
- // given line number is part of.
- function visualLineNo(doc, lineN) {
- 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.
- function visualLineEndNo(doc, 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;
- }
-
- // 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.
- function lineIsHidden(doc, line) {
- 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;
- if (sp.from == 0 && sp.marker.inclusiveLeft && lineIsHiddenInner(doc, line, sp))
- 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));
- }
- if (span.marker.inclusiveRight && span.to == line.text.length)
- return true;
- for (var sp, i = 0; i < line.markedSpans.length; ++i) {
- sp = line.markedSpans[i];
- 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;
- }
- }
-
- // LINE WIDGETS
-
- // Line widgets are block elements displayed above or below a line.
-
- var LineWidget = CodeMirror.LineWidget = function(doc, node, options) {
- if (options) for (var opt in options) if (options.hasOwnProperty(opt))
- this[opt] = options[opt];
- this.doc = doc;
- this.node = node;
- };
- eventMixin(LineWidget);
-
- function adjustScrollWhenAboveVisible(cm, line, diff) {
- if (heightAtLine(line) < ((cm.curOp && cm.curOp.scrollTop) || cm.doc.scrollTop))
- 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));
- if (cm) runInOp(cm, function() {
- 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);
- if (cm) runInOp(cm, function() {
- cm.curOp.forceUpdate = true;
- adjustScrollWhenAboveVisible(cm, line, diff);
- });
- };
-
- function widgetHeight(widget) {
- 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;";
- if (widget.coverGutter)
- 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));
- }
- return widget.height = widget.node.parentNode.offsetHeight;
- }
-
- 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;
- 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;
- 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;
- }
- return true;
- });
- return widget;
- }
-
- // LINE DATA STRUCTURE
-
- // Line objects. These hold state related to a line, including
- // highlighting info (the styles array).
- var Line = CodeMirror.Line = function(text, markedSpans, estimateHeight) {
- this.text = text;
- attachMarkedSpans(this, markedSpans);
- this.height = estimateHeight ? estimateHeight(this) : 1;
- };
- 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.
- 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);
- }
-
- // Detach a line from the document tree and its markers.
- function cleanUpLine(line) {
- line.parent = null;
- detachMarkedSpans(line);
- }
-
- 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";
- if (output[prop] == null)
- output[prop] = lineClass[2];
- else if (!(new RegExp("(?:^|\s)" + lineClass[2] + "(?:$|\s)")).test(output[prop]))
- output[prop] += " " + lineClass[2];
- }
- return type;
- }
-
- function callBlankLine(mode, state) {
- if (mode.blankLine) return mode.blankLine(state);
- if (!mode.innerMode) return;
- var inner = CodeMirror.innerMode(mode, state);
- if (inner.mode.blankLine) return inner.mode.blankLine(inner.state);
- }
-
- function readToken(mode, stream, state, inner) {
- for (var i = 0; i < 10; i++) {
- if (inner) inner[0] = CodeMirror.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.");
- }
-
- // Utility for getTokenAt and getLineTokens
- 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};
- }
-
- 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));
- }
- return asArray ? tokens : getObj();
- }
-
- // 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);
- 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;
- } else {
- style = extractLineClasses(readToken(mode, stream, state, inner), lineClasses);
- }
- if (inner) {
- 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);
- }
- curStyle = style;
- }
- 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;
- }
- }
-
- // Compute a style array (an array starting with a mode generation
- // -- for invalidation -- followed by pairs of end positions and
- // style strings), which is used to highlight the tokens on the
- // line.
- 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 = {};
- // 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;
- runMode(cm, line.text, overlay.mode, true, function(end, style) {
- 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];
- if (i_end > end)
- st.splice(i, 1, end, st[i+1], i_end);
- i += 2;
- at = Math.min(end, i_end);
- }
- if (!style) return;
- if (overlay.opaque) {
- 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;
- }
- }
- }, lineClasses);
- }
-
- return {styles: st, classes: lineClasses.bgClass || lineClasses.textClass ? lineClasses : null};
- }
-
- 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++;
- }
- return line.styles;
- }
-
- // 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.
- 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);
- while (!stream.eol()) {
- readToken(mode, stream, state);
- stream.start = stream.pos;
- }
- }
-
- // 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 = {};
- function interpretTokenStyle(style, options) {
- if (!style || /^\s*$/.test(style)) return null;
- var cache = options.addModeClass ? styleToClassCacheWithMode : styleToClassCache;
- return cache[style] ||
- (cache[style] = style.replace(/\S+/g, "cm-$&"));
- }
-
- // Render the DOM representation of the text of a line. Also builds
- // up a 'line map', which points at the DOM nodes that represent
- // specific stretches of text, and is used by the measuring code.
- // The returned object contains the DOM node, this map, and
- // information about line-wide styles that were set by the mode.
- 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,
- 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;
- 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));
- if (line.styleClasses) {
- if (line.styleClasses.bgClass)
- builder.bgClass = joinClasses(line.styleClasses.bgClass, builder.bgClass || "");
- if (line.styleClasses.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)));
-
- // Store the map and a cache object for the current logical line
- if (i == 0) {
- 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({});
- }
- }
-
- // See issue #2901
- 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)
- builder.textClass = joinClasses(builder.pre.className, builder.textClass || "");
-
- return builder;
- }
-
- 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;
- }
-
- // 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;
- var displayText = builder.splitSpaces ? splitSpaces(text, builder.trailingSpace) : text
- 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;
- } else {
- 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;
- 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;
- }
- 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;
- } 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;
- } 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;
- }
- 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);
- }
- builder.content.appendChild(content);
- }
-
- 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)
- if (ch == " " && spaceBefore && (i == text.length - 1 || text.charCodeAt(i + 1) == 32))
- ch = "\u00a0"
- result += ch
- spaceBefore = ch == " "
- }
- return result
- }
-
- // 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) {
- 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;
- }
- 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);
- if (!ignoreWidget && builder.cm.display.input.needsContentAttribute) {
- if (!widget)
- 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.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;
- 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;
- }
-
- 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;
- 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) {
- 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 = "";
- }
- 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.collapsed && (!collapsed || compareCollapsedMarkers(collapsed.marker, m) < 0))
- collapsed = sp;
- } else if (sp.from > pos && 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]);
- 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 (pos >= len) break;
-
- var upto = Math.min(len, nextChange);
- while (true) {
- if (text) {
- var end = pos + text.length;
- if (!collapsed) {
- var tokenText = end > upto ? text.slice(0, upto - pos) : text;
- builder.addToken(builder, tokenText, style ? style + spanStyle : spanStyle,
- spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : "", title, css);
- }
- 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);
- }
- }
- }
-
- // DOCUMENT DATA STRUCTURE
-
- // By default, updates that start and end at the beginning of a line
- // are treated specially, in order to make the association of line
- // widgets and marker elements with the text behave more intuitive.
- function isWholeLineUpdate(doc, change) {
- return change.from.ch == 0 && change.to.ch == 0 && lst(change.text) == "" &&
- (!doc.cm || doc.cm.options.wholeLineUpdateBefore);
- }
-
- // Perform a change on the document data structure.
- function updateDoc(doc, change, markedSpans, estimateHeight) {
- function spansFor(n) {return markedSpans ? markedSpans[n] : null;}
- function update(line, text, spans) {
- 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;
- }
-
- 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);
- } 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);
- } else if (firstLine == lastLine) {
- 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);
- 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);
- } 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);
- }
-
- signalLater(doc, "change", doc, change);
- }
-
- // The document is represented as a BTree consisting of leaves, with
- // chunk of lines in them, and branches, with up to ten leaves or
- // other branch nodes below them. The top node is always a branch
- // node, and is the document object itself (meaning it has
- // additional methods and properties).
- //
- // All nodes have parent links. The tree is used both to go from
- // line numbers to line objects, and to go from objects to numbers.
- // It also indexes by height, and is used to convert between height
- // and line object, and to find the total height of the document.
- //
- // See also http://marijnhaverbeke.nl/blog/codemirror-line-tree.html
-
- function LeafChunk(lines) {
- this.lines = lines;
- this.parent = null;
- for (var i = 0, height = 0; i < lines.length; ++i) {
- lines[i].parent = this;
- height += lines[i].height;
- }
- this.height = height;
- }
-
- 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];
- this.height -= line.height;
- cleanUpLine(line);
- signalLater(line, "delete");
- }
- 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);
- },
- // 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;
- },
- // 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;
- }
- };
-
- function BranchChunk(children) {
- 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;
- }
- this.size = size;
- this.height = height;
- this.parent = null;
- }
-
- 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();
- 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;
- }
- // 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;
- }
- },
- collapse: function(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;
- for (var i = 0; i < this.children.length; ++i) {
- var 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));
- child.height -= leaf.height;
- this.children.splice(++i, 0, leaf);
- leaf.parent = this;
- }
- child.lines = child.lines.slice(0, remaining);
- this.maybeSpill();
- }
- break;
- }
- at -= sz;
- }
- },
- // 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();
- },
- iterN: function(at, n, op) {
- for (var i = 0; i < this.children.length; ++i) {
- 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 nextDocId = 0;
- var Doc = CodeMirror.Doc = function(text, mode, firstLine, lineSep) {
- 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;
-
- 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,
- // Iterate over the document. Supports two forms -- with only one
- // argument, it calls that for each line in the document. With
- // 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);
- },
-
- // 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);
- },
- 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());
- },
- setValue: docMethodOp(function(code) {
- 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));
- }),
- replaceRange: function(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());
- },
-
- 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);},
-
- getLineHandleVisualStart: function(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;},
-
- 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;
- },
- 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);
- }),
- setSelection: docMethodOp(function(anchor, head, 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);
- }),
- extendSelections: docMethodOp(function(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);
- }),
- setSelections: docMethodOp(function(ranges, primary, options) {
- 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);
- }),
- 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);
- }),
-
- 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());
- 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());
- 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++)
- 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];
- 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--)
- 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);}),
-
- 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};
- },
- clearHistory: function() {this.history = new History(this.history.maxGeneration);},
-
- markClean: function() {
- this.cleanGeneration = this.changeGeneration(true);
- },
- changeGeneration: function(forceSplit) {
- if (forceSplit)
- this.history.lastOp = this.history.lastSelOp = this.history.lastOrigin = null;
- return this.history.generation;
- },
- isClean: function (gen) {
- return this.history.generation == (gen || this.cleanGeneration);
- },
-
- getHistory: function() {
- return {done: copyHistoryArray(this.history.done),
- 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);
- },
-
- 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;
- });
- }),
- 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;
- 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;
- }
- return true;
- });
- }),
-
- addLineWidget: docMethodOp(function(handle, node, options) {
- return addLineWidget(this, handle, node, options);
- }),
- removeLineWidget: function(widget) { widget.clear(); },
-
- markText: function(from, to, options) {
- 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");
- },
- findMarksAt: function(pos) {
- pos = clipPos(this, pos);
- var markers = [], spans = getLine(this, pos.line).markedSpans;
- if (spans) for (var i = 0; i < spans.length; ++i) {
- var span = spans[i];
- if ((span.from == null || span.from <= pos.ch) &&
- (span.to == null || span.to >= pos.ch))
- markers.push(span.marker.parent || span.marker);
- }
- return markers;
- },
- findMarks: function(from, to, filter) {
- 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;
- 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 ||
- 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);
- }
- ++lineNo;
- });
- return found;
- },
- getAllMarks: function() {
- var markers = [];
- this.iter(function(line) {
- var sps = line.markedSpans;
- if (sps) for (var i = 0; i < sps.length; ++i)
- if (sps[i].from != null) markers.push(sps[i].marker);
- });
- return markers;
- },
-
- posFromIndex: function(off) {
- var ch, lineNo = this.first, 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));
- },
- 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;
- this.iter(this.first, coords.line, function (line) {
- 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;
- if (copyHistory) {
- doc.history.undoDepth = this.history.undoDepth;
- doc.setHistory(this.getHistory());
- }
- return doc;
- },
-
- linkedDoc: function(options) {
- if (!options) options = {};
- var from = this.first, to = this.first + this.size;
- if (options.from != null && options.from > from) from = options.from;
- if (options.to != null && options.to < to) to = options.to;
- var copy = new Doc(getLines(this, from, to), options.mode || this.modeOption, from, 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 (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;
- }
- // 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);
- }
- },
- iterLinkedDocs: function(f) {linkedDocs(this, f);},
-
- getMode: function() {return this.mode;},
- getEditor: function() {return this.cm;},
-
- splitLines: function(str) {
- if (this.lineSep) return str.split(this.lineSep);
- return splitLinesAuto(str);
- },
- lineSeparator: function() { return this.lineSep || "\n"; }
- });
-
- // Public alias.
- Doc.prototype.eachLine = Doc.prototype.iter;
-
- // 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)
- CodeMirror.prototype[prop] = (function(method) {
- return function() {return method.apply(this.doc, arguments);};
- })(Doc.prototype[prop]);
-
- eventMixin(Doc);
-
- // Call f for all linked documents.
- function linkedDocs(doc, f, sharedHistOnly) {
- function propagate(doc, skip, sharedHist) {
- if (doc.linked) for (var i = 0; i < doc.linked.length; ++i) {
- var rel = doc.linked[i];
- if (rel.doc == skip) continue;
- var shared = sharedHist && rel.sharedHist;
- if (sharedHistOnly && !shared) continue;
- f(rel.doc, shared);
- propagate(rel.doc, doc, shared);
- }
- }
- propagate(doc, null, true);
- }
-
- // Attach a document to an editor.
- 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);
- }
-
- // LINE UTILITIES
-
- // Find the line object corresponding to the given line number.
- 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();
- if (n < sz) { chunk = child; break; }
- n -= sz;
- }
- }
- return chunk.lines[n];
- }
-
- // Get the part of a document between two positions, as an array of
- // strings.
- function getBetween(doc, start, end) {
- var out = [], n = start.line;
- doc.iter(start.line, end.line + 1, function(line) {
- var text = line.text;
- if (n == end.line) text = text.slice(0, end.ch);
- if (n == start.line) text = text.slice(start.ch);
- out.push(text);
- ++n;
- });
- return out;
- }
- // Get the lines between from and to, as array of strings.
- function getLines(doc, from, to) {
- 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.
- function updateLineHeight(line, height) {
- 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.
- 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) {
- if (chunk.children[i] == cur) break;
- no += chunk.children[i].chunkSize();
- }
- }
- return no + cur.first;
- }
-
- // Find the line at the given vertical position, using the height
- // information in the document tree.
- function lineAtHeight(chunk, h) {
- 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();
- }
- 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;
- }
- return n + i;
- }
-
-
- // Find the height above the given line.
- 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];
- 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;
- }
- }
- return h;
- }
-
- // 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.
- function getOrder(line) {
- var order = line.order;
- if (order == null) order = line.order = bidiOrdering(line.text);
- return order;
- }
-
- // HISTORY
-
- 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;
- // 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;
- // Used by the isClean() method
- this.generation = this.maxGeneration = startGen || 1;
- }
-
- // Create a history change event from an updateDoc-style change
- // object.
- 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;
- }
-
- // 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;
- }
- }
-
- // Find the top change event in the history. Pop off selection
- // events that are in the way.
- function lastChangeEvent(hist, force) {
- if (force) {
- clearSelectionEvents(hist.done);
- return lst(hist.done);
- } else if (hist.done.length && !lst(hist.done).ranges) {
- return lst(hist.done);
- } else if (hist.done.length > 1 && !hist.done[hist.done.length - 2].ranges) {
- hist.done.pop();
- return lst(hist.done);
- }
- }
-
- // Register a change in the history. Merges changes that are within
- // 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;
- hist.undone.length = 0;
- var time = +new Date, cur;
-
- if ((hist.lastOp == opId ||
- hist.lastOrigin == change.origin && change.origin &&
- ((change.origin.charAt(0) == "+" && doc.cm && hist.lastModTime > time - doc.cm.options.historyEventDelay) ||
- change.origin.charAt(0) == "*")) &&
- (cur = lastChangeEvent(hist, hist.lastOp == opId))) {
- // Merge this change into the last event
- 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);
- } else {
- // Add new sub-event
- cur.changes.push(historyChangeFromChange(doc, change));
- }
- } else {
- // Can not be merged, start a new event.
- var before = lst(hist.done);
- if (!before || !before.ranges)
- pushSelectionToHistory(doc.sel, hist.done);
- cur = {changes: [historyChangeFromChange(doc, change)],
- 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.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");
- }
-
- function selectionEventCanBeMerged(doc, origin, prev, sel) {
- 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);
- }
-
- // Called whenever the selection changes, sets the new selection as
- // the pending selection in the history, and pushes the old pending
- // selection into the 'done' array when it was significantly
- // different (in number of selected ranges, emptiness, or time).
- function addSelectionToHistory(doc, sel, opId, options) {
- 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
- // starting with * are always merged, those starting with + are
- // merged when similar and close together in time.
- if (opId == hist.lastSelOp ||
- (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;
- else
- pushSelectionToHistory(sel, hist.done);
-
- hist.lastSelTime = +new Date;
- hist.lastSelOrigin = origin;
- hist.lastSelOp = opId;
- if (options && options.clearRedo !== false)
- clearSelectionEvents(hist.undone);
- }
-
- function pushSelectionToHistory(sel, dest) {
- var 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;
- 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;
- });
- }
-
- // 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;
- for (var i = 0, out; i < spans.length; ++i) {
- if (spans[i].marker.explicitlyCleared) { if (!out) out = spans.slice(0, i); }
- else if (out) out.push(spans[i]);
- }
- return !out ? spans : out.length ? out : null;
- }
-
- // 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;
- for (var i = 0, nw = []; i < change.text.length; ++i)
- nw.push(removeClearedSpans(found[i]));
- return nw;
- }
-
- // Used both to provide a JSON-safe object in .getHistory, and, when
- // detaching a document, to split the history in two
- function copyHistoryArray(events, newGroup, instantiateSel) {
- for (var i = 0, copy = []; i < events.length; ++i) {
- var event = events[i];
- if (event.ranges) {
- copy.push(instantiateSel ? Selection.prototype.deepCopy.call(event) : event);
- continue;
- }
- 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});
- if (newGroup) for (var prop in change) if (m = prop.match(/^spans_(\d+)$/)) {
- if (indexOf(newGroup, Number(m[1])) > -1) {
- lst(newChanges)[prop] = change[prop];
- delete change[prop];
- }
- }
- }
- }
- return copy;
- }
-
- // Rebasing/resetting history to deal with externally-sourced changes
-
- function rebaseHistSelSingle(pos, from, to, diff) {
- if (to < pos.line) {
- pos.line += diff;
- } else if (from < pos.line) {
- pos.line = from;
- pos.ch = 0;
- }
- }
-
- // Tries to rebase an array of history events given a change in the
- // document. If the change touches the same lines as the event, the
- // event, and everything 'behind' it, is discarded. If the change is
- // before the event, the event's positions are updated. Uses a
- // copy-on-write scheme for the positions, to avoid having to
- // reallocate them all on every rebase, but also avoid problems with
- // shared position objects being unsafely updated.
- function rebaseHistArray(array, from, to, diff) {
- for (var i = 0; i < array.length; ++i) {
- var sub = array[i], ok = true;
- if (sub.ranges) {
- 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);
- }
- continue;
- }
- for (var j = 0; j < sub.changes.length; ++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);
- } else if (from <= cur.to.line) {
- ok = false;
- break;
- }
- }
- if (!ok) {
- 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);
- }
-
- // EVENT UTILITIES
-
- // Due to the fact that we still support jurassic IE versions, some
- // compatibility wrappers are needed.
-
- var e_preventDefault = CodeMirror.e_preventDefault = function(e) {
- if (e.preventDefault) e.preventDefault();
- else e.returnValue = false;
- };
- var e_stopPropagation = CodeMirror.e_stopPropagation = function(e) {
- if (e.stopPropagation) e.stopPropagation();
- else e.cancelBubble = true;
- };
- function e_defaultPrevented(e) {
- return e.defaultPrevented != null ? e.defaultPrevented : e.returnValue == false;
- }
- var e_stop = CodeMirror.e_stop = function(e) {e_preventDefault(e); e_stopPropagation(e);};
-
- function e_target(e) {return e.target || e.srcElement;}
- function e_button(e) {
- 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 (mac && e.ctrlKey && b == 1) b = 3;
- return b;
- }
-
- // EVENT HANDLING
-
- // Lightweight event framework. on/off also work on DOM nodes,
- // registering native DOM handlers.
-
- var on = CodeMirror.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] = []);
- arr.push(f);
- }
- };
-
- var noHandlers = []
- function getHandlers(emitter, type, copy) {
- var arr = emitter._handlers && emitter._handlers[type]
- if (copy) return arr && arr.length > 0 ? arr.slice() : noHandlers
- else return arr || noHandlers
- }
-
- var off = CodeMirror.off = function(emitter, type, f) {
- if (emitter.removeEventListener)
- emitter.removeEventListener(type, f, false);
- else if (emitter.detachEvent)
- 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; }
- }
- };
-
- var signal = CodeMirror.signal = function(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);
- };
-
- var orphanDelayedCallbacks = null;
-
- // Often, we want to signal events at a point where we are in the
- // middle of some work, but don't want the handler to start calling
- // other methods on the editor, which might be in an inconsistent
- // state or simply not expect any other events to happen.
- // signalLater looks whether there are any handlers, and schedules
- // them to be executed when the last operation ends, or, if no
- // operation is active, when a timeout fires.
- 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 (operationGroup) {
- list = operationGroup.delayedCallbacks;
- } else if (orphanDelayedCallbacks) {
- list = orphanDelayedCallbacks;
- } else {
- list = orphanDelayedCallbacks = [];
- setTimeout(fireOrphanDelayed, 0);
- }
- function bnd(f) {return function(){f.apply(null, args);};};
- for (var i = 0; i < arr.length; ++i)
- list.push(bnd(arr[i]));
- }
-
- function fireOrphanDelayed() {
- var delayed = orphanDelayedCallbacks;
- orphanDelayedCallbacks = null;
- for (var i = 0; i < delayed.length; ++i) delayed[i]();
- }
-
- // The DOM events that CodeMirror handles can be overridden by
- // registering a (non-DOM) handler on the editor for the event name,
- // and preventDefault-ing the event in that handler.
- 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;
- }
-
- function signalCursorActivity(cm) {
- 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]);
- }
-
- function hasHandler(emitter, type) {
- return getHandlers(emitter, type).length > 0
- }
-
- // Add on and off methods to a constructor's prototype, to make
- // registering events on such objects more convenient.
- function eventMixin(ctor) {
- ctor.prototype.on = function(type, f) {on(this, type, f);};
- ctor.prototype.off = function(type, f) {off(this, type, f);};
- }
-
- // MISC UTILITIES
-
- // Number of pixels added to scroller and sizer to hide scrollbar
- var scrollerGap = 30;
-
- // Returned or thrown by various protocols to signal 'I'm not
- // handling this'.
- var Pass = CodeMirror.Pass = {toString: function(){return "CodeMirror.Pass";}};
-
- // Reused option objects for setSelection & friends
- var sel_dontScroll = {scroll: false}, sel_mouse = {origin: "*mouse"}, sel_move = {origin: "+move"};
-
- function Delayed() {this.id = null;}
- Delayed.prototype.set = function(ms, f) {
- clearTimeout(this.id);
- this.id = setTimeout(f, ms);
- };
-
- // Counts the column offset in a string, taking tabs into account.
- // Used mostly to find indentation.
- var countColumn = CodeMirror.countColumn = function(string, end, tabSize, startIndex, startValue) {
- if (end == null) {
- 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);
- if (nextTab < 0 || nextTab >= end)
- return n + (end - i);
- n += nextTab - i;
- n += tabSize - (n % tabSize);
- i = nextTab + 1;
- }
- };
-
- // The inverse of countColumn -- find the offset that corresponds to
- // a particular column.
- var findColumn = CodeMirror.findColumn = function(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;
- 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;
- }
- }
-
- var spaceStrs = [""];
- function spaceStr(n) {
- while (spaceStrs.length <= n)
- spaceStrs.push(lst(spaceStrs) + " ");
- return spaceStrs[n];
- }
-
- function lst(arr) { return arr[arr.length-1]; }
-
- 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; };
- else if (ie) // Suppress mysterious IE10 errors
- selectInput = function(node) { try { node.select(); } catch(_e) {} };
-
- function indexOf(array, elt) {
- for (var i = 0; i < array.length; ++i)
- if (array[i] == elt) return i;
- return -1;
- }
- function map(array, f) {
- var out = [];
- for (var i = 0; i < array.length; i++) out[i] = f(array[i], i);
- return out;
- }
-
- function insertSorted(array, value, score) {
- 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) {
- var inst;
- if (Object.create) {
- inst = Object.create(base);
- } else {
- nothing.prototype = base;
- inst = new nothing();
- }
- if (props) copyObj(props, inst);
- return inst;
- };
-
- function copyObj(obj, target, overwrite) {
- if (!target) target = {};
- for (var prop in obj)
- if (obj.hasOwnProperty(prop) && (overwrite !== false || !target.hasOwnProperty(prop)))
- target[prop] = obj[prop];
- return target;
- }
-
- function bind(f) {
- var args = Array.prototype.slice.call(arguments, 1);
- return function(){return f.apply(null, args);};
- }
-
- var nonASCIISingleCaseWordChar = /[\u00df\u0587\u0590-\u05f4\u0600-\u06ff\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/;
- var isWordCharBasic = CodeMirror.isWordChar = function(ch) {
- return /\w/.test(ch) || ch > "\x80" &&
- (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch));
- };
- function isWordChar(ch, helper) {
- if (!helper) return isWordCharBasic(ch);
- if (helper.source.indexOf("\\w") > -1 && isWordCharBasic(ch)) return true;
- return helper.test(ch);
- }
-
- function isEmpty(obj) {
- 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 +
- // any number of extending chars is treated as a single unit as far
- // 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]/;
- function isExtendingChar(ch) { return ch.charCodeAt(0) >= 768 && extendingChars.test(ch); }
-
- // DOM UTILITIES
-
- 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 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;
- };
- 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;
- };
-
- function removeChildren(e) {
- for (var count = e.childNodes.length; count > 0; --count)
- e.removeChild(e.firstChild);
- return e;
- }
-
- function removeChildrenAndAdd(parent, e) {
- return removeChildren(parent).appendChild(e);
- }
-
- var contains = CodeMirror.contains = function(parent, child) {
- if (child.nodeType == 3) // Android browser always returns false when child is a textnode
- child = child.parentNode;
- if (parent.contains)
- return parent.contains(child);
- do {
- if (child.nodeType == 11) child = child.host;
- if (child == parent) return true;
- } while (child = child.parentNode);
- };
-
- function activeElt() {
- var activeElement = document.activeElement;
- 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; }
- };
-
- function classTest(cls) { return new RegExp("(^|\\s)" + cls + "(?:$|\\s)\\s*"); }
- var rmClass = CodeMirror.rmClass = function(node, cls) {
- 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 addClass = CodeMirror.addClass = function(node, cls) {
- var current = node.className;
- if (!classTest(cls).test(current)) node.className += (current ? " " : "") + cls;
- };
- function joinClasses(a, b) {
- 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;
- }
-
- // WINDOW-WIDE EVENTS
-
- // 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");
- for (var i = 0; i < byClass.length; i++) {
- var cm = byClass[i].CodeMirror;
- if (cm) f(cm);
- }
- }
-
- var globalsRegistered = false;
- function ensureGlobalHandlers() {
- if (globalsRegistered) return;
- registerGlobalHandlers();
- globalsRegistered = true;
- }
- function registerGlobalHandlers() {
- // When the window resizes, we need to refresh active editors.
- var resizeTimer;
- on(window, "resize", function() {
- if (resizeTimer == null) resizeTimer = setTimeout(function() {
- resizeTimer = null;
- forEachCodeMirror(onResize);
- }, 100);
- });
- // When the window loses focus, we want to show the editor as blurred
- on(window, "blur", function() {
- forEachCodeMirror(onBlur);
- });
- }
-
- // FEATURE DETECTION
-
- // Detect drag-and-drop
- var dragAndDrop = function() {
- // There is *some* kind of drag-and-drop support in IE6-8, but I
- // couldn't get it to work yet.
- if (ie && ie_version < 9) return false;
- var div = elt('div');
- return "draggable" in div || "dragDrop" in div;
- }();
-
- var zwspSupported;
- function zeroWidthElement(measure) {
- if (zwspSupported == null) {
- 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);
- }
- var 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;
- 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);
- }
-
- // See if "".split is the broken IE version, if so, provide an
- // alternative way to split lines.
- var splitLinesAuto = CodeMirror.splitLines = "\n\nb".split(/\n/).length != 3 ? function(string) {
- 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");
- if (rt != -1) {
- result.push(line.slice(0, rt));
- pos += rt + 1;
- } else {
- result.push(line);
- pos = nl + 1;
- }
- }
- return result;
- } : function(string){return string.split(/\r\n?|\n/);};
-
- var hasSelection = window.getSelection ? function(te) {
- try { return te.selectionStart != te.selectionEnd; }
- catch(e) { return false; }
- } : function(te) {
- try {var range = te.ownerDocument.selection.createRange();}
- catch(e) {}
- if (!range || range.parentElement() != te) return false;
- return range.compareEndPoints("StartToEnd", range) != 0;
- };
-
- var hasCopyEvent = (function() {
- var e = elt("div");
- if ("oncopy" in e) return true;
- e.setAttribute("oncopy", "return;");
- return typeof e.oncopy == "function";
- })();
-
- var badZoomedRects = null;
- function hasBadZoomedRects(measure) {
- if (badZoomedRects != null) return badZoomedRects;
- var node = removeChildrenAndAdd(measure, elt("span", "x"));
- var normal = node.getBoundingClientRect();
- var fromRange = range(node, 0, 1).getBoundingClientRect();
- return badZoomedRects = Math.abs(normal.left - fromRange.left) > 1;
- }
-
- // KEY NAMES
-
- var keyNames = CodeMirror.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",
- 46: "Delete", 59: ";", 61: "=", 91: "Mod", 92: "Mod", 93: "Mod",
- 106: "*", 107: "=", 109: "-", 110: ".", 111: "/", 127: "Delete",
- 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"
- };
- (function() {
- // Number keys
- 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);
- // Function keys
- for (var i = 1; i <= 12; i++) keyNames[i + 111] = keyNames[i + 63235] = "F" + i;
- })();
-
- // BIDI HELPERS
-
- function iterateBidiSections(order, from, to, f) {
- if (!order) return f(from, to, "ltr");
- var found = false;
- for (var i = 0; i < order.length; ++i) {
- var part = order[i];
- if (part.from < to && part.to > from || from == to && part.to == from) {
- f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "rtl" : "ltr");
- found = true;
- }
- }
- if (!found) f(from, to, "ltr");
- }
-
- function bidiLeft(part) { return part.level % 2 ? part.to : part.from; }
- function bidiRight(part) { return part.level % 2 ? part.from : part.to; }
-
- function lineLeft(line) { var order = getOrder(line); return order ? bidiLeft(order[0]) : 0; }
- function lineRight(line) {
- var order = getOrder(line);
- if (!order) return line.text.length;
- return bidiRight(lst(order));
- }
-
- 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);
- }
- function lineEnd(cm, lineN) {
- var 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);
- 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);
- 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);
- }
- return start;
- }
-
- function compareBidiLevel(order, a, b) {
- var linedir = order[0].level;
- if (a == linedir) return true;
- if (b == linedir) return false;
- return a < b;
- }
- var bidiOther;
- function getBidiPartAt(order, pos) {
- bidiOther = null;
- for (var i = 0, found; i < order.length; ++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;
- } 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;
- }
- }
- }
- 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;
- }
-
- // 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.
- 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);
-
- 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);
- }
- }
- }
-
- 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;
- }
-
- // Bidirectional ordering algorithm
- // See http://unicode.org/reports/tr9/tr9-13.html for the algorithm
- // that this (partially) implements.
-
- // One-char codes used for character types:
- // L (L): Left-to-Right
- // R (R): Right-to-Left
- // r (AL): Right-to-Left Arabic
- // 1 (EN): European Number
- // + (ES): European Number Separator
- // % (ET): European Number Terminator
- // n (AN): Arabic Number
- // , (CS): Common Number Separator
- // m (NSM): Non-Spacing Mark
- // b (BN): Boundary Neutral
- // s (B): Paragraph Separator
- // t (S): Segment Separator
- // w (WS): Whitespace
- // N (ON): Other Neutrals
-
- // Returns null if characters are ordered as they appear
- // (left-to-right), or an array of sections ({from, to, level}
- // objects) in the order in which they occur visually.
- var bidiOrdering = (function() {
- // Character types for codepoints 0 to 0xff
- var lowTypes = "bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLN";
- // Character types for codepoints 0x600 to 0x6ff
- 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";
- }
-
- 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";
-
- function BidiSpan(level, from, to) {
- this.level = level;
- this.from = from; this.to = to;
- }
-
- 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)));
-
- // 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;
- }
-
- // W2. Search backwards from each instance of a European number
- // until the first strong type (R, L, AL, or sor) is found. If an
- // AL is found, change the type of the European number to Arabic
- // number.
- // W3. Change all ALs to R.
- for (var i = 0, cur = 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"; }
- }
-
- // W4. A single European separator between two European numbers
- // changes to a European number. A single common separator between
- // two numbers of the same type changes to that type.
- for (var i = 1, prev = types[0]; i < len - 1; ++i) {
- var type = types[i];
- if (type == "+" && prev == "1" && types[i+1] == "1") types[i] = "1";
- else if (type == "," && prev == types[i+1] &&
- (prev == "1" || prev == "n")) types[i] = prev;
- prev = type;
- }
-
- // W5. A sequence of European terminators adjacent to European
- // numbers changes to all European numbers.
- // W6. Otherwise, separators and terminators change to Other
- // Neutral.
- for (var i = 0; i < len; ++i) {
- var type = types[i];
- if (type == ",") types[i] = "N";
- else if (type == "%") {
- for (var end = i + 1; end < len && types[end] == "%"; ++end) {}
- var replace = (i && types[i-1] == "!") || (end < len && types[end] == "1") ? "1" : "N";
- for (var j = i; j < end; ++j) types[j] = replace;
- i = end - 1;
- }
- }
-
- // W7. Search backwards from each instance of a European number
- // until the first strong type (R, L, or sor) is found. If an L is
- // found, then change the type of the European number to L.
- for (var i = 0, cur = outerType; i < len; ++i) {
- var type = types[i];
- if (cur == "L" && type == "1") types[i] = "L";
- else if (isStrong.test(type)) cur = type;
- }
-
- // N1. A sequence of neutrals takes the direction of the
- // surrounding strong text if the text on both sides has the same
- // direction. European and Arabic numbers act as if they were R in
- // terms of their influence on neutrals. Start-of-level-run (sor)
- // and end-of-level-run (eor) are used at level run boundaries.
- // N2. Any remaining neutrals take the embedding direction.
- for (var i = 0; i < len; ++i) {
- if (isNeutral.test(types[i])) {
- for (var end = i + 1; end < len && isNeutral.test(types[end]); ++end) {}
- var before = (i ? types[i-1] : 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;
- }
- }
-
- // Here we depart from the documented algorithm, in order to avoid
- // building up an actual levels array. Since there are only three
- // levels (0, 1, 2) in an implementation that doesn't take
- // explicit embedding into account, we can build up the order on
- // the fly, without following the level-based algorithm.
- var order = [], m;
- for (var i = 0; i < len;) {
- if (countsAsLeft.test(types[i])) {
- var start = i;
- for (++i; i < len && countsAsLeft.test(types[i]); ++i) {}
- order.push(new BidiSpan(0, start, i));
- } else {
- var pos = i, at = order.length;
- for (++i; i < len && types[i] != "L"; ++i) {}
- for (var j = pos; j < i;) {
- if (countsAsNum.test(types[j])) {
- if (pos < j) order.splice(at, 0, 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;
- }
- 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));
- }
- 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));
- }
- 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;
- };
- })();
-
- // THE END
-
- CodeMirror.version = "5.19.1";
-
- return CodeMirror;
-});
diff --git a/package.json b/package.json
index 1e4c7e0b06..1f4b036e12 100644
--- a/package.json
+++ b/package.json
@@ -8,13 +8,18 @@
"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",
+ "prepublish": "npm run-script build",
"test": "node ./test/run.js",
"lint": "bin/lint"
},
"devDependencies": {
+ "blint": ">=0.1.1",
"node-static": "0.6.0",
"phantomjs-prebuilt": "^2.1.12",
- "blint": ">=0.1.1"
+ "rollup": "^0.34.10",
+ "rollup-watch": "^2.5.0"
},
"bugs": "http://github.com/codemirror/CodeMirror/issues",
"keywords": [
diff --git a/src/banner.js b/src/banner.js
new file mode 100644
index 0000000000..6bd6553f29
--- /dev/null
+++ b/src/banner.js
@@ -0,0 +1,8 @@
+// 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
+// implemented in JavaScript on top of the browser's DOM.
+//
+// You can find some technical background for some of the code below
+// at http://marijnhaverbeke.nl/blog/#cm-internals .
diff --git a/src/codemirror.js b/src/codemirror.js
new file mode 100644
index 0000000000..4b75d8cbaf
--- /dev/null
+++ b/src/codemirror.js
@@ -0,0 +1,3 @@
+import { CodeMirror } from "./edit/main";
+
+export default CodeMirror;
diff --git a/src/display/Display.js b/src/display/Display.js
new file mode 100644
index 0000000000..e1cb6a0923
--- /dev/null
+++ b/src/display/Display.js
@@ -0,0 +1,105 @@
+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;
+
+ // Covers bottom-right square when both scrollbars are present.
+ 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");
+ // Will contain the actual code, positioned to cover the viewport.
+ 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");
+ // A visibility: hidden element used to find the size of things.
+ 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");
+ // 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");
+ // Moved around its parent to cover visible view.
+ 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;
+ // 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;");
+ // Will contain the gutters, if any.
+ 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");
+ // The element in which the editor lives.
+ 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 (place) {
+ 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;
+ // Information about the rendered lines.
+ 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;
+ // Empty space (in pixels) above the view
+ d.viewOffset = 0;
+ d.lastWrapHeight = d.lastWrapWidth = 0;
+ d.updateLineNumbers = null;
+
+ 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;
+ // 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.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;
+
+ // Used for measuring wheel scrolling granularity
+ d.wheelDX = d.wheelDY = d.wheelStartX = d.wheelStartY = null;
+
+ // True when shift is held down.
+ d.shift = false;
+
+ // Used to track whether anything happened since the context menu
+ // was opened.
+ d.selForContextMenu = null;
+
+ d.activeTouch = null;
+
+ input.init(d);
+}
diff --git a/src/display/focus.js b/src/display/focus.js
new file mode 100644
index 0000000000..abe70f09d2
--- /dev/null
+++ b/src/display/focus.js
@@ -0,0 +1,49 @@
+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); }
+}
+
+export function delayBlurEvent(cm) {
+ cm.state.delayingBlurEvent = true;
+ setTimeout(function() {
+ if (cm.state.delayingBlurEvent) {
+ cm.state.delayingBlurEvent = false;
+ onBlur(cm);
+ }
+ }, 100);
+}
+
+export function onFocus(cm, e) {
+ if (cm.state.delayingBlurEvent) cm.state.delayingBlurEvent = false;
+
+ 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");
+ // 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.receivedFocus();
+ }
+ restartBlink(cm);
+}
+export function onBlur(cm, e) {
+ if (cm.state.delayingBlurEvent) return;
+
+ if (cm.state.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);
+}
diff --git a/src/display/gutters.js b/src/display/gutters.js
new file mode 100644
index 0000000000..0a0f591a94
--- /dev/null
+++ b/src/display/gutters.js
@@ -0,0 +1,33 @@
+import { elt, removeChildren } from "../util/dom";
+import { indexOf } from "../util/misc";
+
+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);
+ for (var i = 0; i < specs.length; ++i) {
+ var gutterClass = specs[i];
+ var gElt = gutters.appendChild(elt("div", null, "CodeMirror-gutter " + gutterClass));
+ if (gutterClass == "CodeMirror-linenumbers") {
+ cm.display.lineGutter = gElt;
+ gElt.style.width = (cm.display.lineNumWidth || 1) + "px";
+ }
+ }
+ gutters.style.display = i ? "" : "none";
+ 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");
+ 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);
+ }
+}
diff --git a/src/display/highlight_worker.js b/src/display/highlight_worker.js
new file mode 100644
index 0000000000..101b9acb30
--- /dev/null
+++ b/src/display/highlight_worker.js
@@ -0,0 +1,51 @@
+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";
+
+// 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));
+}
+
+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 = [];
+
+ 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 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);
+ } else {
+ if (line.text.length <= cm.options.maxHighlightLength)
+ processLine(cm, line.text, state);
+ line.stateAfter = doc.frontier % 5 == 0 ? copyState(doc.mode, state) : null;
+ }
+ ++doc.frontier;
+ if (+new Date > end) {
+ 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");
+ });
+}
diff --git a/src/display/line_numbers.js b/src/display/line_numbers.js
new file mode 100644
index 0000000000..85964db7ba
--- /dev/null
+++ b/src/display/line_numbers.js
@@ -0,0 +1,48 @@
+import { lineNumberFor } from "../line/utils_line";
+import { compensateForHScroll } from "../measurement/position_measurement";
+import { elt } from "../util/dom";
+
+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";
+ 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;
+ 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;
+ }
+ if (cm.options.fixedGutter)
+ 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 (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;
+ }
+ return false;
+}
diff --git a/src/display/mode_state.js b/src/display/mode_state.js
new file mode 100644
index 0000000000..005e60e3b0
--- /dev/null
+++ b/src/display/mode_state.js
@@ -0,0 +1,22 @@
+import { getMode } from "../modes";
+
+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);
+}
+
+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);
+}
diff --git a/src/display/operations.js b/src/display/operations.js
new file mode 100644
index 0000000000..a3439d0056
--- /dev/null
+++ b/src/display/operations.js
@@ -0,0 +1,215 @@
+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
+// cursor and display (which would be awkward, slow, and
+// error-prone). Instead, display updates are batched and then all
+// combined and executed at once.
+
+var nextOpId = 0;
+// Start a new operation.
+export function startOperation(cm) {
+ cm.curOp = {
+ cm: cm,
+ viewChanged: false, // Flag that indicates that lines might need to be redrawn
+ startHeight: cm.doc.height, // Used to detect need to update scrollbar
+ forceUpdate: false, // Used to force a redraw
+ updateInput: null, // 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
+ cursorActivityCalled: 0, // Tracks which cursorActivity handlers have been called already
+ selectionChanged: false, // Whether the selection needs to be redrawn
+ updateMaxLine: false, // Set when the widest line needs to be determined anew
+ scrollLeft: null, scrollTop: null, // Intermediate scroll position, not pushed to DOM yet
+ scrollToPos: null, // Used to scroll to a specific position
+ focus: false,
+ id: ++nextOpId // Unique ID
+ };
+ pushOperation(cm.curOp);
+}
+
+// Finish an operation, updating the display and signalling delayed events
+export function endOperation(cm) {
+ 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);
+ });
+}
+
+// The DOM updates done when an operation finishes are batched so
+// that the minimum number of relayouts are required.
+function endOperations(group) {
+ var ops = group.ops;
+ for (var i = 0; i < ops.length; i++) // Read DOM
+ endOperation_R1(ops[i]);
+ for (var i = 0; i < ops.length; i++) // Write DOM (maybe)
+ endOperation_W1(ops[i]);
+ for (var i = 0; i < ops.length; i++) // Read DOM
+ endOperation_R2(ops[i]);
+ for (var i = 0; i < ops.length; i++) // Write DOM (maybe)
+ endOperation_W2(ops[i]);
+ for (var i = 0; i < ops.length; i++) // Read DOM
+ endOperation_finish(ops[i]);
+}
+
+function endOperation_R1(op) {
+ var cm = op.cm, display = cm.display;
+ 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;
+ op.update = op.mustUpdate &&
+ new DisplayUpdate(cm, op.mustUpdate && {top: op.scrollTop, ensure: op.scrollToPos}, op.forceUpdate);
+}
+
+function endOperation_W1(op) {
+ op.updatedDisplay = op.mustUpdate && updateDisplayIfNeeded(op.cm, op.update);
+}
+
+function endOperation_R2(op) {
+ var cm = op.cm, display = cm.display;
+ if (op.updatedDisplay) updateHeightsInViewport(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.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));
+ }
+
+ if (op.updatedDisplay || op.selectionChanged)
+ op.preparedSelection = display.input.prepareSelection(op.focus);
+}
+
+function endOperation_W2(op) {
+ var cm = op.cm;
+
+ if (op.adjustWidthTo != null) {
+ cm.display.sizer.style.minWidth = op.adjustWidthTo + "px";
+ if (op.maxScrollLeft < cm.doc.scrollLeft)
+ setScrollLeft(cm, Math.min(cm.display.scroller.scrollLeft, op.maxScrollLeft), true);
+ 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);
+ if (op.updatedDisplay || op.startHeight != cm.doc.height)
+ updateScrollbars(cm, op.barMeasure);
+ if (op.updatedDisplay)
+ setDocumentHeight(cm, op.barMeasure);
+
+ if (op.selectionChanged) restartBlink(cm);
+
+ if (cm.state.focused && op.updateInput)
+ 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;
+
+ 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;
+
+ // 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;
+ }
+ 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);
+ }
+ // 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);
+ }
+
+ // 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)
+ 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 (display.wrapper.offsetHeight)
+ doc.scrollTop = cm.display.scroller.scrollTop;
+
+ // Fire change events, and delayed event handlers
+ if (op.changeObjs)
+ signal(cm, "changes", cm, op.changeObjs);
+ if (op.update)
+ 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); }
+}
+// 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); }
+ };
+}
+// 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); }
+ };
+}
+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); }
+ };
+}
diff --git a/src/display/scroll_events.js b/src/display/scroll_events.js
new file mode 100644
index 0000000000..3c5a666e28
--- /dev/null
+++ b/src/display/scroll_events.js
@@ -0,0 +1,138 @@
+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";
+
+// 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);
+}
+// 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);
+}
+
+// Since the delta values reported on mouse wheel events are
+// unstandardized between browsers and even browser versions, and
+// generally horribly unpredictable, this code starts by measuring
+// the scroll effect that the first few mouse wheel events have,
+// and, from that, detects the way it can convert deltas to pixel
+// offsets afterwards.
+//
+// The reason we want to know the amount a wheel event will scroll
+// is that it gives us a chance to update the display before the
+// actual scrolling happens, reducing flickering.
+
+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;
+
+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};
+};
+export function wheelEventPixels(e) {
+ 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 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;
+
+ // Webkit browsers on OS X abort momentum scrolls when the target
+ // of the scroll event is removed from the scrollable element.
+ // 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++) {
+ if (view[i].node == cur) {
+ cm.display.currentWheelTarget = cur;
+ break outer;
+ }
+ }
+ }
+ }
+
+ // On some browsers, horizontal scrolling will cause redraws to
+ // happen before the gutter has been realigned, causing it to
+ // wriggle around in a most unseemly way. When we have an
+ // estimated pixels/delta value, we just handle horizontal
+ // scrolling entirely here. It'll be slightly off from native, but
+ // 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)));
+ // 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;
+ }
+
+ // '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});
+ }
+
+ if (wheelSamples < 20) {
+ if (display.wheelStartX == null) {
+ 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;
+ var sample = (movedY && display.wheelDY && movedY / display.wheelDY) ||
+ (movedX && display.wheelDX && movedX / display.wheelDX);
+ display.wheelStartX = display.wheelStartY = null;
+ if (!sample) return;
+ wheelPixelsPerUnit = (wheelPixelsPerUnit * wheelSamples + sample) / (wheelSamples + 1);
+ ++wheelSamples;
+ }, 200);
+ } else {
+ display.wheelDX += dx; display.wheelDY += dy;
+ }
+ }
+}
diff --git a/src/display/scrollbars.js b/src/display/scrollbars.js
new file mode 100644
index 0000000000..a29444b5d3
--- /dev/null
+++ b/src/display/scrollbars.js
@@ -0,0 +1,188 @@
+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";
+
+// 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));
+ return {
+ clientHeight: d.scroller.clientHeight,
+ viewHeight: d.wrapper.clientHeight,
+ scrollWidth: d.scroller.scrollWidth, clientWidth: d.scroller.clientWidth,
+ viewWidth: d.wrapper.clientWidth,
+ barLeft: cm.options.fixedGutter ? gutterW : 0,
+ docHeight: docH,
+ 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);
+
+ on(vert, "scroll", function() {
+ if (vert.clientHeight) scroll(vert.scrollTop, "vertical");
+ });
+ on(horiz, "scroll", function() {
+ 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) {
+ 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);
+ // 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";
+ } else {
+ 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.firstChild.style.width =
+ (measure.scrollWidth - measure.clientWidth + totalWidth) + "px";
+ } else {
+ this.horiz.style.display = "";
+ this.horiz.firstChild.style.width = "0";
+ }
+
+ if (!this.checkedZeroWidth && measure.clientHeight > 0) {
+ if (sWidth == 0) this.zeroWidthHack();
+ this.checkedZeroWidth = true;
+ }
+
+ 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);
+ },
+ setScrollTop: function(pos) {
+ 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;
+ },
+ enableZeroWidthBar: function(bar, delay) {
+ 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
+ // left corner of the scrollbar box is the scrollbar box
+ // 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);
+ }
+ delay.set(1000, maybeDisable);
+ },
+ clear: function() {
+ var 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);
+
+export function updateScrollbars(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;
+ }
+}
+
+// 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);
+
+ 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 = "";
+ 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 = "";
+}
+
+export var scrollbarModel = {"native": NativeScrollbars, "null": NullScrollbars};
+
+export function initScrollbars(cm) {
+ if (cm.display.scrollbars) {
+ cm.display.scrollbars.clear();
+ if (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);
+ // 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");
+ }, function(pos, axis) {
+ if (axis == "horizontal") setScrollLeft(cm, pos);
+ else setScrollTop(cm, pos);
+ }, cm);
+ if (cm.display.scrollbars.addClass)
+ addClass(cm.display.wrapper, cm.display.scrollbars.addClass);
+}
diff --git a/src/display/scrolling.js b/src/display/scrolling.js
new file mode 100644
index 0000000000..50ea401262
--- /dev/null
+++ b/src/display/scrolling.js
@@ -0,0 +1,132 @@
+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";
+
+// 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;
+
+ 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);
+ }
+}
+
+// Scroll a given position into view (immediately), verifying that
+// 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;
+ 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),
+ 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;
+ if (scrollPos.scrollTop != null) {
+ 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;
+ }
+ if (!changed) break;
+ }
+ 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);
+}
+
+// Calculate a new scroll position needed to scroll the given
+// rectangle into view. Returns an object with scrollTop and
+// 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;
+ if (y1 < screentop) {
+ result.scrollTop = atTop ? 0 : y1;
+ } else if (y2 > screentop + screen) {
+ var newTop = Math.min(y1, (atBottom ? docBottom : y2) - screen);
+ if (newTop != screentop) result.scrollTop = newTop;
+ }
+
+ var 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;
+ else if (x1 < screenleft)
+ result.scrollLeft = Math.max(0, x1 - (tooWide ? 0 : 10));
+ else if (x2 > screenw + screenleft - 3)
+ 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)
+ 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;
+}
+
+// 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;
+ if (!cm.options.lineWrapping) {
+ 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};
+}
+
+// When an operation has its scrollToPos property set, and another
+// scroll action is applied before the end of the operation, this
+// '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;
+ 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),
+ 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);
+ }
+}
diff --git a/src/display/selection.js b/src/display/selection.js
new file mode 100644
index 0000000000..b6ca8b6f91
--- /dev/null
+++ b/src/display/selection.js
@@ -0,0 +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";
+
+export function updateSelection(cm) {
+ 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();
+
+ 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 (collapsed || cm.options.showCursorWhenSelecting)
+ drawSelectionCursor(cm, range.head, curFragment);
+ if (!collapsed)
+ drawSelectionRange(cm, range, selFragment);
+ }
+ 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 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";
+ }
+}
+
+// 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;
+
+ function add(left, top, width, 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"));
+ }
+
+ function drawForLine(line, fromArg, toArg) {
+ 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);
+ }
+
+ iterateBidiSections(getOrder(lineObj), fromArg || 0, toArg == null ? lineLen : toArg, function(from, to, dir) {
+ var 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; }
+ left = leftPos.left;
+ right = rightPos.right;
+ }
+ 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);
+ }
+ if (toArg == null && to == lineLen) right = rightSide;
+ if (!start || leftPos.top < start.top || leftPos.top == start.top && leftPos.left < start.left)
+ 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};
+ }
+
+ var 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;
+ if (singleVLine) {
+ if (leftEnd.top < rightStart.top - 2) {
+ 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);
+ }
+ }
+ if (leftEnd.bottom < rightStart.top)
+ add(leftSide, leftEnd.bottom, null, rightStart.top);
+ }
+
+ 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.options.cursorBlinkRate > 0)
+ display.blinker = setInterval(function() {
+ 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/display/update_display.js b/src/display/update_display.js
new file mode 100644
index 0000000000..c69f75f9d4
--- /dev/null
+++ b/src/display/update_display.js
@@ -0,0 +1,227 @@
+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;
+
+ 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 (var i = 0; i < this.events.length; i++)
+ signal.apply(null, this.events[i]);
+};
+
+export function maybeClipScrollbars(cm) {
+ 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;
+ }
+}
+
+// Does the actual updating of the line display. Bails out
+// (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;
+
+ if (update.editorIsHidden) {
+ resetView(cm);
+ return false;
+ }
+
+ // Bail out if the visible area is already rendered and nothing changed.
+ if (!update.force &&
+ 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;
+
+ if (maybeUpdateLineNumberWidth(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);
+ if (sawCollapsedSpans) {
+ 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.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";
+
+ var 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();
+ 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();
+
+ // 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;
+
+ if (different) {
+ display.lastWrapHeight = update.wrapperHeight;
+ display.lastWrapWidth = update.wrapperWidth;
+ startWorker(cm, 400);
+ }
+
+ display.updateLineNumbers = null;
+
+ return true;
+}
+
+export 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.
+ if (viewport && viewport.top != null)
+ 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);
+ if (update.visible.from >= cm.display.viewFrom && update.visible.to <= cm.display.viewTo)
+ break;
+ }
+ if (!updateDisplayIfNeeded(cm, update)) break;
+ updateHeightsInViewport(cm);
+ var barMeasure = measureForScrollbars(cm);
+ updateSelection(cm);
+ updateScrollbars(cm, barMeasure);
+ setDocumentHeight(cm, barMeasure);
+ }
+
+ 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;
+ }
+}
+
+export function updateDisplaySimple(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();
+ }
+}
+
+// Sync the actual display DOM structure with display.view, removing
+// nodes for lines that are no longer in view, and creating the ones
+// 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;
+
+ function rm(node) {
+ 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";
+ else
+ node.parentNode.removeChild(node);
+ return next;
+ }
+
+ 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];
+ 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);
+ } else { // Already drawn
+ while (cur != lineView.node) cur = rm(cur);
+ var updateNumber = lineNumbers && updateNumbersFrom != null &&
+ updateNumbersFrom <= lineN && lineView.lineNumber;
+ if (lineView.changes) {
+ 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)));
+ }
+ cur = lineView.node.nextSibling;
+ }
+ lineN += lineView.size;
+ }
+ while (cur) cur = rm(cur);
+}
+
+export function updateGutterSpace(cm) {
+ 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";
+}
diff --git a/src/display/update_lines.js b/src/display/update_lines.js
new file mode 100644
index 0000000000..e10d8b9c1a
--- /dev/null
+++ b/src/display/update_lines.js
@@ -0,0 +1,62 @@
+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;
+ for (var i = 0; i < display.view.length; i++) {
+ 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;
+ } else {
+ var box = cur.node.getBoundingClientRect();
+ height = box.bottom - box.top;
+ }
+ var 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++)
+ updateWidgetHeight(cur.rest[j]);
+ }
+ }
+}
+
+// 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)
+ 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 from = lineAtHeight(doc, top), to = lineAtHeight(doc, bottom);
+ // Ensure is a {from: {line, ch}, to: {line, ch}} object, and
+ // forces those lines into the viewport (if possible).
+ if (viewport && viewport.ensure) {
+ var ensureFrom = viewport.ensure.from.line, ensureTo = viewport.ensure.to.line;
+ if (ensureFrom < from) {
+ 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;
+ }
+ }
+ return {from: from, to: Math.max(to, from + 1)};
+}
diff --git a/src/display/view_tracking.js b/src/display/view_tracking.js
new file mode 100644
index 0000000000..fd535178ce
--- /dev/null
+++ b/src/display/view_tracking.js
@@ -0,0 +1,152 @@
+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
+// the amount of lines added or subtracted by the change. This is
+// used for changes that span multiple lines, or change the way
+// 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;
+
+ var display = cm.display;
+ if (lendiff && to < display.viewTo &&
+ (display.updateLineNumbers == null || display.updateLineNumbers > from))
+ display.updateLineNumbers = from;
+
+ cm.curOp.viewChanged = true;
+
+ if (from >= display.viewTo) { // Change after
+ if (sawCollapsedSpans && visualLineNo(cm.doc, from) < display.viewTo)
+ resetView(cm);
+ } else if (to <= display.viewFrom) { // Change before
+ if (sawCollapsedSpans && visualLineEndNo(cm.doc, to + lendiff) > display.viewFrom) {
+ resetView(cm);
+ } else {
+ display.viewFrom += lendiff;
+ display.viewTo += 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);
+ if (cut) {
+ display.view = display.view.slice(cut.index);
+ display.viewFrom = cut.lineN;
+ display.viewTo += lendiff;
+ } else {
+ resetView(cm);
+ }
+ } else if (to >= display.viewTo) { // Bottom overlap
+ var cut = viewCuttingPoint(cm, from, from, -1);
+ if (cut) {
+ display.view = display.view.slice(0, cut.index);
+ display.viewTo = cut.lineN;
+ } else {
+ resetView(cm);
+ }
+ } else { // Gap in the middle
+ 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;
+ } else {
+ resetView(cm);
+ }
+ }
+
+ var ext = display.externalMeasured;
+ if (ext) {
+ if (to < ext.lineN)
+ ext.lineN += lendiff;
+ else if (from < ext.lineN + ext.size)
+ 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;
+ 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)];
+ 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;
+}
+
+function viewCuttingPoint(cm, oldN, newN, dir) {
+ var 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++)
+ n += view[i].size;
+ if (n != oldN) {
+ if (dir > 0) {
+ if (index == view.length - 1) return null;
+ diff = (n + view[index].size) - oldN;
+ index++;
+ } else {
+ diff = n - oldN;
+ }
+ 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;
+ }
+ 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;
+ if (view.length == 0 || from >= display.viewTo || to <= display.viewFrom) {
+ display.view = buildViewArray(cm, from, to);
+ display.viewFrom = from;
+ } else {
+ if (display.viewFrom > from)
+ 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;
+ if (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.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;
+ for (var i = 0; i < view.length; i++) {
+ var 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
new file mode 100644
index 0000000000..7bd6a60a50
--- /dev/null
+++ b/src/edit/CodeMirror.js
@@ -0,0 +1,213 @@
+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);
+
+ 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);
+ if (options.lineWrapping)
+ this.display.wrapper.className += " CodeMirror-wrap";
+ if (options.autofocus && !mobile) display.input.focus();
+ initScrollbars(this);
+
+ this.state = {
+ keyMaps: [], // stores maps added by addKeyMap
+ overlays: [], // highlighting overlays, as added by addOverlay
+ modeGen: 0, // bumped when mode/overlay changes, used to invalidate highlighting info
+ overwrite: false,
+ 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
+ selectingText: false,
+ draggingText: false,
+ highlight: new Delayed(), // stores highlight worker timeout
+ keySeq: null, // Unfinished key sequence
+ specialChars: null
+ };
+
+ 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);
+
+ registerEventHandlers(this);
+ ensureGlobalHandlers();
+
+ startOperation(this);
+ this.curOp.forceUpdate = true;
+ attachDoc(this, doc);
+
+ if ((options.autofocus && !mobile) || cm.hasFocus())
+ setTimeout(bind(onFocus, this), 20);
+ else
+ 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);
+ // 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";
+}
+
+// The default configuration options.
+CodeMirror.defaults = defaults;
+// Functions to run when options are changed.
+CodeMirror.optionHandlers = optionHandlers;
+
+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));
+ // 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);
+ }));
+ else
+ 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);});
+
+ // Used to suppress mouse event handling when a touch happens
+ var touchFinished, prevTouch = {end: 0};
+ function finishTouch() {
+ if (d.activeTouch) {
+ 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;
+ }
+ 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;
+ }
+ on(d.scroller, "touchstart", function(e) {
+ if (!signalDOMEvent(cm, e) && !isMouseLikeTouchEvent(e)) {
+ clearTimeout(touchFinished);
+ var now = +new Date;
+ d.activeTouch = {start: now, moved: false,
+ 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;
+ }
+ }
+ });
+ on(d.scroller, "touchmove", function() {
+ if (d.activeTouch) d.activeTouch.moved = true;
+ });
+ on(d.scroller, "touchend", function(e) {
+ 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;
+ 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
+ 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);
+ }
+ 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);
+ }
+ });
+
+ // 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);});
+
+ // Prevent wrapper from ever scrolling
+ 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);},
+ 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); });
+}
+
+var initHooks = [];
+CodeMirror.defineInitHook = function(f) {initHooks.push(f);};
diff --git a/src/edit/commands.js b/src/edit/commands.js
new file mode 100644
index 0000000000..3e8735ec13
--- /dev/null
+++ b/src/edit/commands.js
@@ -0,0 +1,199 @@
+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);
+ },
+ killLine: function(cm) {
+ deleteNearSelection(cm, function(range) {
+ if (range.empty()) {
+ 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)};
+ 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) {
+ 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()));},
+ 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) {
+ 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);
+ },
+ 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");},
+ 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);
+ spaces.push(spaceStr(tabSize - col % tabSize));
+ }
+ cm.replaceSelections(spaces);
+ },
+ defaultTab: function(cm) {
+ if (cm.somethingSelected()) cm.indentSelection("add");
+ else cm.execCommand("insertTab");
+ },
+ 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;
+ 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) {
+ 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");
+ }
+ }
+ newSel.push(new Range(cur, cur));
+ }
+ cm.setSelections(newSel);
+ });
+ },
+ newlineAndIndent: function(cm) {
+ runInOp(cm, function() {
+ var sels = cm.listSelections()
+ for (var 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++)
+ cm.indentLine(sels[i].from().line, null, true)
+ ensureCursorVisible(cm);
+ });
+ },
+ openLine: function(cm) {cm.replaceSelection("\n", "start")},
+ 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);
+}
+function lineEnd(cm, lineN) {
+ var 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);
+ 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);
+ 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);
+ }
+ return start;
+}
diff --git a/src/edit/deleteNearSelection.js b/src/edit/deleteNearSelection.js
new file mode 100644
index 0000000000..dd1369026c
--- /dev/null
+++ b/src/edit/deleteNearSelection.js
@@ -0,0 +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";
+
+// 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 = [];
+ // 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]);
+ while (kill.length && cmp(toKill.from, lst(kill).to) <= 0) {
+ var replaced = kill.pop();
+ if (cmp(replaced.from, toKill.from) < 0) {
+ toKill.from = replaced.from;
+ break;
+ }
+ }
+ 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);
+ });
+}
diff --git a/src/edit/drop_events.js b/src/edit/drop_events.js
new file mode 100644
index 0000000000..fac58a266e
--- /dev/null
+++ b/src/edit/drop_events.js
@@ -0,0 +1,118 @@
+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;
+
+export function onDrop(e) {
+ 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;
+ // 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) {
+ if (cm.options.allowDropFileTypes &&
+ indexOf(cm.options.allowDropFileTypes, file.type) == -1)
+ return;
+
+ 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;
+ if (++read == n) {
+ 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)));
+ }
+ });
+ 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);
+ // Ensure the editor is re-focused
+ setTimeout(function() {cm.display.input.focus();}, 20);
+ return;
+ }
+ try {
+ 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));
+ 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();
+ }
+ }
+ catch(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;
+
+ 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==";
+ if (presto) {
+ img.width = img.height = 1;
+ cm.display.wrapper.appendChild(img);
+ // Force a relayout, or Opera won't use our image for some obscure reason
+ img._top = img.offsetTop;
+ }
+ e.dataTransfer.setDragImage(img, 0, 0);
+ if (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);
+ if (!cm.display.dragCursor) {
+ 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);
+}
+
+export function clearDragCursor(cm) {
+ if (cm.display.dragCursor) {
+ cm.display.lineSpace.removeChild(cm.display.dragCursor);
+ cm.display.dragCursor = null;
+ }
+}
diff --git a/src/edit/fromTextArea.js b/src/edit/fromTextArea.js
new file mode 100644
index 0000000000..aa1a040fa9
--- /dev/null
+++ b/src/edit/fromTextArea.js
@@ -0,0 +1,59 @@
+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;
+ if (!options.tabindex && textarea.tabIndex)
+ options.tabindex = textarea.tabIndex;
+ if (!options.placeholder && textarea.placeholder)
+ options.placeholder = textarea.placeholder;
+ // Set autofocus to true if this textarea is focused, or if it has
+ // autofocus and no other element is focused.
+ if (options.autofocus == null) {
+ var hasFocus = activeElt();
+ options.autofocus = hasFocus == textarea ||
+ textarea.getAttribute("autofocus") != null && hasFocus == document.body;
+ }
+
+ function save() {textarea.value = cm.getValue();}
+ 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;
+ try {
+ var wrappedSubmit = form.submit = function() {
+ save();
+ form.submit = realSubmit;
+ form.submit();
+ form.submit = wrappedSubmit;
+ };
+ } catch(e) {}
+ }
+ }
+
+ options.finishInit = function(cm) {
+ 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 = "";
+ if (textarea.form) {
+ off(textarea.form, "submit", save);
+ if (typeof textarea.form.submit == "function")
+ textarea.form.submit = realSubmit;
+ }
+ };
+ };
+
+ textarea.style.display = "none";
+ var 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
new file mode 100644
index 0000000000..b219285dfb
--- /dev/null
+++ b/src/edit/global_events.js
@@ -0,0 +1,46 @@
+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");
+ for (var i = 0; i < byClass.length; i++) {
+ var cm = byClass[i].CodeMirror;
+ if (cm) f(cm);
+ }
+}
+
+var globalsRegistered = false;
+export function ensureGlobalHandlers() {
+ if (globalsRegistered) return;
+ registerGlobalHandlers();
+ globalsRegistered = true;
+}
+function registerGlobalHandlers() {
+ // When the window resizes, we need to refresh active editors.
+ var resizeTimer;
+ on(window, "resize", function() {
+ if (resizeTimer == null) resizeTimer = setTimeout(function() {
+ resizeTimer = null;
+ forEachCodeMirror(onResize);
+ }, 100);
+ });
+ // When the window loses focus, we want to show the editor as blurred
+ on(window, "blur", function() {
+ forEachCodeMirror(onBlur);
+ });
+}
+// Called when the window resizes
+function onResize(cm) {
+ var d = cm.display;
+ if (d.lastWrapHeight == d.wrapper.clientHeight && d.lastWrapWidth == d.wrapper.clientWidth)
+ return;
+ // Might be a text scaling operation, clear size caches.
+ 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
new file mode 100644
index 0000000000..92763037c9
--- /dev/null
+++ b/src/edit/key_events.js
@@ -0,0 +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";
+
+// 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;
+ }
+ // 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;
+ try {
+ 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;
+ }
+ 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;
+ }
+ return (cm.options.extraKeys && lookupKey(name, cm.options.extraKeys, handle, cm))
+ || lookupKey(name, cm.options.keyMap, handle, cm);
+}
+
+var stopSeq = new Delayed;
+function dispatchKey(cm, name, e, handle) {
+ var seq = cm.state.keySeq;
+ if (seq) {
+ if (isModifierKey(name)) return "handled";
+ stopSeq.set(50, function() {
+ if (cm.state.keySeq == seq) {
+ cm.state.keySeq = null;
+ cm.display.input.reset();
+ }
+ });
+ name = seq + " " + name;
+ }
+ var result = lookupKeyForEditor(cm, name, handle);
+
+ if (result == "multi")
+ cm.state.keySeq = name;
+ if (result == "handled")
+ signalLater(cm, "keyHandled", cm, name, e);
+
+ if (result == "handled" || result == "multi") {
+ e_preventDefault(e);
+ restartBlink(cm);
+ }
+
+ if (seq && !result && /\'$/.test(name)) {
+ e_preventDefault(e);
+ return true;
+ }
+ return !!result;
+}
+
+// Handle a key from the keydown event.
+function handleKeyBinding(cm, e) {
+ 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);})
+ || dispatchKey(cm, name, e, function(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); });
+ }
+}
+
+// Handle a key from the keypress event
+function handleCharBinding(cm, e, ch) {
+ return dispatchKey(cm, "'" + ch + "'", e,
+ function(b) { return doHandleBinding(cm, b, true); });
+}
+
+var lastStoppedKey = null;
+export function onKeyDown(e) {
+ 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 (presto) {
+ 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");
+ }
+
+ // Turn mouse into crosshair when Alt is held on Mac.
+ if (code == 18 && !/\bCodeMirror-crosshair\b/.test(cm.display.lineDiv.className))
+ showCrossHair(cm);
+}
+
+function showCrossHair(cm) {
+ 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);
+ }
+ }
+ on(document, "keyup", up);
+ on(document, "mouseover", up);
+}
+
+export function onKeyUp(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);
+ // Some browsers fire keypress events for backspace
+ 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
new file mode 100644
index 0000000000..150f8a7225
--- /dev/null
+++ b/src/edit/legacy.js
@@ -0,0 +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 { 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;
+}
diff --git a/src/edit/main.js b/src/edit/main.js
new file mode 100644
index 0000000000..a955aaa197
--- /dev/null
+++ b/src/edit/main.js
@@ -0,0 +1,71 @@
+// EDITOR CONSTRUCTOR
+
+import { CodeMirror } from "./CodeMirror";
+export { CodeMirror } from "./CodeMirror";
+
+import { eventMixin } from "../util/event";
+import { indexOf } from "../util/misc";
+
+import { defineOptions } from "./options";
+
+defineOptions(CodeMirror);
+
+import addEditorMethods from "./methods";
+
+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)
+ CodeMirror.prototype[prop] = (function(method) {
+ return function() {return method.apply(this.doc, arguments);};
+ })(Doc.prototype[prop]);
+
+eventMixin(Doc);
+
+// INPUT HANDLING
+
+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";
+
+// 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);
+};
+
+CodeMirror.defineMIME = defineMIME;
+
+// Minimal default mode.
+CodeMirror.defineMode("null", function() {
+ return {token: function(stream) {stream.skipToEnd();}};
+});
+CodeMirror.defineMIME("text/plain", "null");
+
+// EXTENSIONS
+
+CodeMirror.defineExtension = function(name, func) {
+ CodeMirror.prototype[name] = func;
+};
+CodeMirror.defineDocExtension = function(name, func) {
+ Doc.prototype[name] = func;
+};
+
+import { fromTextArea } from "./fromTextArea";
+
+CodeMirror.fromTextArea = fromTextArea;
+
+import { addLegacyProps } from "./legacy";
+
+addLegacyProps(CodeMirror);
+
+CodeMirror.version = "5.19.1";
diff --git a/src/edit/methods.js b/src/edit/methods.js
new file mode 100644
index 0000000000..84aaf397b0
--- /dev/null
+++ b/src/edit/methods.js
@@ -0,0 +1,573 @@
+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'.
+
+// This is not the complete set of editor methods. Most of the
+// methods defined on the Doc type are also injected into
+// CodeMirror.prototype, for backwards compatibility and
+// convenience.
+
+export default function(CodeMirror) {
+ var optionHandlers = CodeMirror.optionHandlers;
+
+ var 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];
+ if (options[option] == value && option != "mode") return;
+ options[option] = value;
+ if (optionHandlers.hasOwnProperty(option))
+ operation(this, optionHandlers[option])(this, value, old);
+ },
+
+ getOption: function(option) {return this.options[option];},
+ getDoc: function() {return this.doc;},
+
+ addKeyMap: function(map, bottom) {
+ this.state.keyMaps[bottom ? "push" : "unshift"](getKeyMap(map));
+ },
+ removeKeyMap: function(map) {
+ 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;
+ }
+ },
+
+ 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.");
+ 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);
+ }),
+ removeOverlay: methodOp(function(spec) {
+ var overlays = this.state.overlays;
+ for (var i = 0; i < overlays.length; ++i) {
+ var cur = overlays[i].modeSpec;
+ if (cur == spec || typeof spec == "string" && cur.name == spec) {
+ 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 (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];
+ 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;
+ for (var j = start; j < end; ++j)
+ 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);
+ } 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);
+ }
+ }
+ }),
+
+ // 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);
+ },
+
+ getLineTokens: function(line, precise) {
+ 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];
+ 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 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;
+ },
+
+ getHelper: function(pos, type) {
+ 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);
+ 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]];
+ if (val) found.push(val);
+ }
+ } else if (mode.helperType && help[mode.helperType]) {
+ found.push(help[mode.helperType]);
+ } else if (help[mode.name]) {
+ found.push(help[mode.name]);
+ }
+ for (var i = 0; i < help._global.length; i++) {
+ var cur = help._global[i];
+ if (cur.pred(mode, this) && indexOf(found, cur.val) == -1)
+ found.push(cur.val);
+ }
+ 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);
+ },
+
+ 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");
+ },
+
+ charCoords: function(pos, mode) {
+ 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);
+ },
+
+ lineAtHeight: function(height, mode) {
+ 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;
+ 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);
+ } else {
+ lineObj = line;
+ }
+ return intoCoordSystem(this, lineObj, {top: 0, left: 0}, mode || "page").top +
+ (end ? this.doc.height - heightAtLine(lineObj) : 0);
+ },
+
+ 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;
+ });
+ }),
+
+ clearGutter: methodOp(function(gutterID) {
+ 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;
+ }
+ ++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;
+ } else {
+ 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};
+ },
+
+ 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);
+ if (vert == "over") {
+ 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);
+ // 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;
+ else if (pos.bottom + node.offsetHeight <= vspace)
+ top = pos.bottom;
+ if (left + node.offsetWidth > hspace)
+ left = hspace - node.offsetWidth;
+ }
+ node.style.top = top + "px";
+ node.style.left = node.style.right = "";
+ if (horiz == "right") {
+ left = display.sizer.clientWidth - node.offsetWidth;
+ node.style.right = "0px";
+ } else {
+ if (horiz == "left") left = 0;
+ else if (horiz == "middle") left = (display.sizer.clientWidth - node.offsetWidth) / 2;
+ node.style.left = left + "px";
+ }
+ if (scroll)
+ scrollIntoView(this, left, top, left + node.offsetWidth, top + node.offsetHeight);
+ },
+
+ triggerOnKeyDown: methodOp(onKeyDown),
+ triggerOnKeyPress: methodOp(onKeyPress),
+ triggerOnKeyUp: onKeyUp,
+
+ execCommand: function(cmd) {
+ if (commands.hasOwnProperty(cmd))
+ return commands[cmd].call(null, this);
+ },
+
+ triggerElectric: methodOp(function(text) { triggerElectric(this, text); }),
+
+ findPosH: function(from, amount, unit, visually) {
+ var dir = 1;
+ if (amount < 0) { dir = -1; amount = -amount; }
+ for (var i = 0, cur = clipPos(this.doc, from); i < amount; ++i) {
+ cur = findPosH(this.doc, cur, dir, unit, visually);
+ if (cur.hitSide) break;
+ }
+ return cur;
+ },
+
+ moveH: methodOp(function(dir, unit) {
+ 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);
+ else
+ return dir < 0 ? range.from() : range.to();
+ }, sel_move);
+ }),
+
+ deleteH: methodOp(function(dir, unit) {
+ var 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);
+ 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; }
+ 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;
+ }
+ return cur;
+ },
+
+ moveV: methodOp(function(dir, unit) {
+ 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);
+ 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++)
+ doc.sel.ranges[i].goalColumn = goals[i];
+ }),
+
+ // Find the word at the given position (as returned by coordsChar).
+ findWordAt: function(pos) {
+ var doc = this.doc, line = getLine(doc, pos.line).text;
+ var start = pos.ch, end = pos.ch;
+ if (line) {
+ var helper = this.getHelper(pos, "wordChars");
+ if ((pos.xRel < 0 || end == line.length) && start) --start; else ++end;
+ var startChar = line.charAt(start);
+ var check = isWordChar(startChar, helper)
+ ? function(ch) { return isWordChar(ch, helper); }
+ : /\s/.test(startChar) ? function(ch) {return /\s/.test(ch);}
+ : function(ch) {return !/\s/.test(ch) && !isWordChar(ch);};
+ while (start > 0 && check(line.charAt(start - 1))) --start;
+ while (end < line.length && check(line.charAt(end))) ++end;
+ }
+ return new Range(Pos(pos.line, start), Pos(pos.line, end));
+ },
+
+ toggleOverwrite: function(value) {
+ if (value != null && value == this.state.overwrite) return;
+ if (this.state.overwrite = !this.state.overwrite)
+ addClass(this.display.cursorDiv, "CodeMirror-overwrite");
+ else
+ rmClass(this.display.cursorDiv, "CodeMirror-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); },
+
+ 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;
+ }),
+ getScrollInfo: function() {
+ 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)};
+ },
+
+ scrollIntoView: methodOp(function(range, margin) {
+ if (range == null) {
+ 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};
+ } else if (range.from == null) {
+ range = {from: range, to: null};
+ }
+ if (!range.to) range.to = range.from;
+ range.margin = margin || 0;
+
+ if (range.from.line != null) {
+ 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);
+ }
+ }),
+
+ setSize: methodOp(function(width, height) {
+ var 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;
+ 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);
+ }),
+
+ 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);
+ if (oldHeight == null || Math.abs(oldHeight - textHeight(this.display)) > .5)
+ 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;
+ }),
+
+ 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;
+ };
+ CodeMirror.registerGlobalHelper = function(type, name, predicate, 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
+// right), unit can be "char", "column" (like char, but doesn't
+// cross line boundaries), "word" (across next word), or "group" (to
+// the start of next group of word or non-word-non-whitespace
+// chars). The visually param controls whether, in right-to-left
+// text, direction 1 means to move towards the next index in the
+// string, or towards the character to the right of the current
+// 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);
+ function findNextLine() {
+ var 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);
+ if (next == null) {
+ if (!boundToLine && findNextLine()) {
+ if (visually) ch = (dir < 0 ? lineRight : lineLeft)(lineObj);
+ else ch = dir < 0 ? lineObj.text.length : 0;
+ } else return false
+ } else ch = next;
+ return true;
+ }
+
+ if (unit == "char") {
+ moveOnce()
+ } else if (unit == "column") {
+ moveOnce(true)
+ } else if (unit == "word" || unit == "group") {
+ 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";
+ var 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();}
+ 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;
+}
+
+// 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;
+ 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;
+
+ } else if (unit == "line") {
+ y = dir > 0 ? pos.bottom + 3 : pos.top - 3;
+ }
+ for (;;) {
+ var target = coordsChar(cm, x, y);
+ if (!target.outside) break;
+ if (dir < 0 ? y <= 0 : y >= doc.height) { target.hitSide = true; break; }
+ y += dir * 5;
+ }
+ return target;
+}
diff --git a/src/edit/mouse_events.js b/src/edit/mouse_events.js
new file mode 100644
index 0000000000..08a564bd2c
--- /dev/null
+++ b/src/edit/mouse_events.js
@@ -0,0 +1,307 @@
+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 } 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
+// (ctrl-click), rectangle drag (alt-drag), or xwin
+// 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;
+
+ 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);
+ }
+ return;
+ }
+ 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);
+ else if (start)
+ leftButtonDown(cm, e, start);
+ else if (e_target(e) == display.scroller)
+ 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;
+ case 3:
+ if (captureRightClick) onContextMenu(cm, e);
+ else delayBlurEvent(cm);
+ break;
+ }
+}
+
+var lastClick, lastDoubleClick;
+function leftButtonDown(cm, e, start) {
+ if (ie) setTimeout(bind(ensureFocus, cm), 0);
+ else cm.curOp.focus = activeElt();
+
+ var 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) {
+ type = "double";
+ lastDoubleClick = {time: now, pos: start};
+ } else {
+ type = "single";
+ lastClick = {time: now, pos: start};
+ }
+
+ 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);
+ else
+ 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 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 (Math.abs(e.clientX - e2.clientX) + Math.abs(e.clientY - e2.clientY) < 10) {
+ e_preventDefault(e2);
+ if (!modifier && +new Date - 200 < startTime)
+ 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);
+ else
+ display.input.focus();
+ }
+ });
+ // Let the drag handler handle this.
+ 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);
+}
+
+// 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 ourRange, ourIndex, startSel = doc.sel, ranges = startSel.ranges;
+ if (addNew && !e.shiftKey) {
+ ourIndex = doc.sel.contains(start);
+ if (ourIndex > -1)
+ ourRange = ranges[ourIndex];
+ else
+ ourRange = new Range(start, start);
+ } else {
+ 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;
+ } else if (type == "double") {
+ var 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)));
+ if (cm.display.shift || doc.extend)
+ ourRange = extendRange(doc, ourRange, line.anchor, line.head);
+ else
+ ourRange = line;
+ } else {
+ ourRange = extendRange(doc, ourRange, start);
+ }
+
+ if (!addNew) {
+ ourIndex = 0;
+ setSelection(doc, new Selection([ourRange], 0), sel_mouse);
+ startSel = doc.sel;
+ } else if (ourIndex == -1) {
+ ourIndex = ranges.length;
+ setSelection(doc, normalizeSelection(ranges.concat([ourRange]), ourIndex),
+ {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;
+ } else {
+ replaceOneSelection(doc, ourIndex, ourRange, sel_mouse);
+ }
+
+ var 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));
+ line <= end; line++) {
+ var 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)
+ ranges.push(new Range(Pos(line, leftPos), Pos(line, findColumn(text, right, tabSize))));
+ }
+ 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);
+ } else {
+ var oldRange = ourRange;
+ var anchor = oldRange.anchor, head = pos;
+ if (type != "single") {
+ if (type == "double")
+ var range = cm.findWordAt(pos);
+ else
+ var range = new Range(Pos(pos.line, 0), clipPos(doc, Pos(pos.line + 1, 0)));
+ if (cmp(range.anchor, anchor) > 0) {
+ head = range.head;
+ anchor = minPos(oldRange.from(), range.anchor);
+ } else {
+ 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 editorSize = display.wrapper.getBoundingClientRect();
+ // Used to ensure timeout re-tries don't fire when another extend
+ // happened in the meantime (clearTimeout isn't reliable -- at
+ // least on Chrome, the timeouts still happen even when cleared,
+ // if the clear happens after their scheduled firing time).
+ var counter = 0;
+
+ function extend(e) {
+ var curCount = ++counter;
+ var cur = posFromMouse(cm, e, true, type == "rect");
+ if (!cur) return;
+ if (cmp(cur, lastPos) != 0) {
+ 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);
+ } else {
+ var outside = e.clientY < editorSize.top ? -20 : e.clientY > editorSize.bottom ? 20 : 0;
+ if (outside) setTimeout(operation(cm, function() {
+ if (counter != curCount) return;
+ display.scroller.scrollTop += outside;
+ extend(e);
+ }), 50);
+ }
+ }
+
+ function done(e) {
+ 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);
+}
+
+
+// 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);
+
+ 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;
+
+ for (var i = 0; i < cm.options.gutters.length; ++i) {
+ var g = display.gutters.childNodes[i];
+ if (g && g.getBoundingClientRect().right >= mX) {
+ var line = lineAtHeight(cm.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);
+}
+
+// CONTEXT MENU HANDLING
+
+// To make the context menu work, we need to briefly unhide the
+// 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);
+}
+
+function contextMenuInGutter(cm, e) {
+ if (!hasHandler(cm, "gutterContextMenu")) return false;
+ return gutterEvent(cm, e, "gutterContextMenu", false);
+}
diff --git a/src/edit/options.js b/src/edit/options.js
new file mode 100644
index 0000000000..308eb574f2
--- /dev/null
+++ b/src/edit/options.js
@@ -0,0 +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 = {};
+
+export function defineOptions(CodeMirror) {
+ var optionHandlers = CodeMirror.optionHandlers;
+
+ 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;
+ }
+
+ CodeMirror.defineOption = option;
+
+ // Passed to option handlers when there is no old value.
+ 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);
+ option("mode", null, function(cm, val) {
+ cm.doc.modeOption = val;
+ loadMode(cm);
+ }, true);
+
+ option("indentUnit", 2, loadMode, true);
+ option("indentWithTabs", false);
+ option("smartIndent", true);
+ option("tabSize", 4, function(cm) {
+ 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.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));
+ }
+ 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);
+ 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);
+
+ option("theme", "default", function(cm) {
+ 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);
+ option("gutters", [], function(cm) {
+ 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);
+ option("scrollbarStyle", "native", function(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) {
+ 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("readOnly", false, function(cm, val) {
+ if (val == "nocursor") {
+ onBlur(cm);
+ cm.display.input.blur();
+ cm.display.disabled = true;
+ } else {
+ 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("moveInputWithCursor", true, function(cm, val) {
+ if (!val) cm.display.input.resetPosition();
+ });
+
+ option("tabindex", null, function(cm, val) {
+ cm.display.input.getField().tabIndex = val || "";
+ });
+ option("autofocus", null);
+}
+
+function guttersChanged(cm) {
+ updateGutters(cm);
+ regChange(cm);
+ setTimeout(function(){alignHorizontally(cm);}, 20);
+}
+
+function dragDropChanged(cm, value, old) {
+ 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);
+ }
+}
+
+function wrappingChanged(cm) {
+ if (cm.options.lineWrapping) {
+ addClass(cm.display.wrapper, "CodeMirror-wrap");
+ cm.display.sizer.style.minWidth = "";
+ cm.display.sizerWidth = null;
+ } else {
+ rmClass(cm.display.wrapper, "CodeMirror-wrap");
+ findMaxLine(cm);
+ }
+ estimateLineHeights(cm);
+ regChange(cm);
+ clearCaches(cm);
+ setTimeout(function(){updateScrollbars(cm);}, 100);
+}
diff --git a/src/edit/utils.js b/src/edit/utils.js
new file mode 100644
index 0000000000..cdef3156bb
--- /dev/null
+++ b/src/edit/utils.js
@@ -0,0 +1,7 @@
+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);
+}
diff --git a/src/input/ContentEditableInput.js b/src/input/ContentEditableInput.js
new file mode 100644
index 0000000000..69d53c0cbe
--- /dev/null
+++ b/src/input/ContentEditableInput.js
@@ -0,0 +1,468 @@
+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;
+}
+
+ContentEditableInput.prototype = copyObj({
+ init: function(display) {
+ 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);
+ }), 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));
+ 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, "compositionupdate", function(e) {
+ input.composing.data = e.data;
+ });
+ on(div, "compositionend", function(e) {
+ var 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(function() {
+ if (!ours.handled)
+ input.applyComposition(ours);
+ if (input.composing == ours)
+ input.composing = null;
+ }, 50);
+ });
+
+ on(div, "touchstart", function() {
+ input.forceCompositionEnd();
+ });
+
+ on(div, "input", function() {
+ if (input.composing) return;
+ if (cm.isReadOnly() || !input.pollContent())
+ 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");
+ } else if (!cm.options.lineWiseCopyCut) {
+ return;
+ } else {
+ 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");
+ });
+ }
+ }
+ if (e.clipboardData) {
+ 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);
+ 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();
+ if (hadFocus == div) input.showPrimarySelection()
+ }, 50);
+ }
+ on(div, "copy", onCopyCut);
+ on(div, "cut", onCopyCut);
+ },
+
+ prepareSelection: function() {
+ 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);
+ },
+
+ 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);
+ 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());
+ if (!start && !end) return;
+
+ var view = this.cm.display.view;
+ var 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;
+ 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); }
+ 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);
+ } else {
+ sel.removeAllRanges();
+ sel.addRange(rng);
+ }
+ if (old && sel.anchorNode == null) sel.addRange(old);
+ else if (gecko) this.startGracePeriod();
+ }
+ this.rememberSelection();
+ },
+
+ startGracePeriod: function() {
+ var input = this;
+ clearTimeout(this.gracePeriod);
+ this.gracePeriod = setTimeout(function() {
+ input.gracePeriod = false;
+ if (input.selectionChanged())
+ 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);
+ },
+
+ rememberSelection: function() {
+ 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);
+ },
+
+ focus: function() {
+ if (this.cm.options.readOnly != "nocursor") this.div.focus();
+ },
+ blur: function() { this.div.blur(); },
+ getField: function() { return this.div; },
+
+ supportsTouch: function() { return true; },
+
+ receivedFocus: function() {
+ var input = this;
+ if (this.selectionInEditor())
+ this.pollSelection();
+ else
+ 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);
+ }
+ }
+ this.polling.set(this.cm.options.pollInterval, poll);
+ },
+
+ selectionChanged: function() {
+ var 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;
+ 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;
+ });
+ }
+ },
+
+ 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 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;
+ } else {
+ var fromLine = lineNo(display.view[fromIndex].line);
+ var fromNode = display.view[fromIndex - 1].node.nextSibling;
+ }
+ var toIndex = findViewIndex(cm, to.line);
+ if (toIndex == display.view.length - 1) {
+ 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 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;
+ }
+
+ 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);
+ var 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))
+ ++cutEnd;
+
+ 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);
+ if (newText.length > 1 || newText[0] || cmp(chFrom, chTo)) {
+ replaceRange(cm.doc, newText, chFrom, chTo, "+input");
+ return true;
+ }
+ },
+
+ ensurePolled: function() {
+ this.forceCompositionEnd();
+ },
+ reset: function() {
+ 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();
+ },
+ 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);
+ },
+
+ setUneditable: function(node) {
+ node.contentEditable = "false"
+ },
+
+ onKeyPress: function(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) {
+ this.div.contentEditable = String(val != "nocursor")
+ },
+
+ onContextMenu: nothing,
+ resetPosition: nothing,
+
+ needsContentAttribute: true
+ }, 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 order = getOrder(line), side = "left";
+ if (order) {
+ 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;
+}
+
+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; }; }
+ function walk(node) {
+ if (node.nodeType == 1) {
+ var 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;
+ if (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;
+ }
+ if (node.getAttribute("contenteditable") == "false") return;
+ for (var 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;
+ if (!val) return;
+ if (closing) {
+ text += lineSep;
+ closing = false;
+ }
+ text += val;
+ }
+ }
+ for (;;) {
+ walk(from);
+ if (from == to) break;
+ from = from.nextSibling;
+ }
+ return text;
+}
+
+function domToPos(cm, node, offset) {
+ 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;
+ } else {
+ for (lineNode = node;; lineNode = lineNode.parentNode) {
+ 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];
+ if (lineView.node == lineNode)
+ 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);
+ if (node == wrapper) {
+ 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 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;
+
+ 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];
+ 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 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);
+ 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) {
+ found = find(before, before.firstChild, -1);
+ if (found)
+ return badPos(Pos(found.line, found.ch + dist), bad);
+ else
+ dist += before.textContent.length;
+ }
+}
diff --git a/src/input/TextareaInput.js b/src/input/TextareaInput.js
new file mode 100644
index 0000000000..421ce17a06
--- /dev/null
+++ b/src/input/TextareaInput.js
@@ -0,0 +1,359 @@
+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;
+ // 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) {
+ var input = this, cm = this.cm;
+
+ // Wraps and hides input textarea
+ 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);
+
+ // 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() {
+ 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();
+ });
+
+ function prepareCopyCut(e) {
+ if (signalDOMEvent(cm, e)) return
+ if (cm.somethingSelected()) {
+ setLastCopied({lineWise: false, text: cm.getSelections()});
+ if (input.inaccurateSelection) {
+ input.prevInput = "";
+ input.inaccurateSelection = false;
+ te.value = lastCopied.text.join("\n");
+ selectInput(te);
+ }
+ } else if (!cm.options.lineWiseCopyCut) {
+ return;
+ } else {
+ var ranges = copyableRanges(cm);
+ setLastCopied({lineWise: true, text: ranges.text});
+ if (e.type == "cut") {
+ cm.setSelections(ranges.ranges, null, sel_dontScroll);
+ } else {
+ input.prevInput = "";
+ te.value = ranges.text.join("\n");
+ selectInput(te);
+ }
+ }
+ if (e.type == "cut") cm.state.cutIncoming = true;
+ }
+ 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();
+ });
+
+ // Prevent normal selection in the editor (we handle our own)
+ on(display.lineSpace, "selectstart", function(e) {
+ if (!eventInWidget(display, e)) e_preventDefault(e);
+ });
+
+ on(te, "compositionstart", function() {
+ 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;
+ }
+ });
+ },
+
+ prepareSelection: function() {
+ // Redraw the selection and/or cursor
+ 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();
+ result.teTop = Math.max(0, Math.min(display.wrapper.clientHeight - 10,
+ headPos.top + lineOff.top - wrapOff.top));
+ result.teLeft = Math.max(0, Math.min(display.wrapper.clientWidth - 10,
+ headPos.left + lineOff.left - wrapOff.left));
+ }
+
+ return result;
+ },
+
+ showSelection: function(drawn) {
+ 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";
+ }
+ },
+
+ // 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 (cm.somethingSelected()) {
+ 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;
+ } else if (!typing) {
+ this.prevInput = this.textarea.value = "";
+ if (ie && ie_version >= 9) this.hasSelection = null;
+ }
+ this.inaccurateSelection = minimal;
+ },
+
+ getField: function() { return this.textarea; },
+
+ supportsTouch: function() { return false; },
+
+ focus: function() {
+ 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(); },
+
+ resetPosition: function() {
+ this.wrapper.style.top = this.wrapper.style.left = 0;
+ },
+
+ 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;
+ input.polling.set(this.cm.options.pollInterval, function() {
+ 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;
+ function p() {
+ 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);
+ },
+
+ // Read input from the textarea, and update the document to match.
+ // When something is selected, it is present in the textarea, and
+ // selected (unless it is huge, in which case a placeholder is
+ // 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() {
+ 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,
+ // in which case reading its value would be expensive.
+ if (this.contextMenuPending || !cm.state.focused ||
+ (hasSelection(input) && !prevInput && !this.composing) ||
+ cm.isReadOnly() || cm.options.disableInput || cm.state.keySeq)
+ return false;
+
+ var text = input.value;
+ // If nothing changed, bail.
+ 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;
+ }
+
+ 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"); }
+ }
+ // 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 self = this;
+ runInOp(cm, function() {
+ applyTextInput(cm, text.slice(same), prevInput.length - same,
+ 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 (self.composing) {
+ self.composing.range.clear();
+ self.composing.range = cm.markText(self.composing.start, cm.getCursor("to"),
+ {className: "CodeMirror-composing"});
+ }
+ });
+ return true;
+ },
+
+ ensurePolled: function() {
+ if (this.pollingFast && this.poll()) this.pollingFast = false;
+ },
+
+ onKeyPress: function() {
+ 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.
+
+ // 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;
+ 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;
+ 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();
+ // 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);
+
+ // 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;
+ // Re-set this, in case some other handler touched the
+ // selection in the meantime.
+ display.selForContextMenu = cm.doc.sel;
+ }
+ }
+ function rehide() {
+ input.contextMenuPending = false;
+ input.wrapper.style.cssText = oldWrapperCSS
+ 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();
+ 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);
+ }
+ }
+
+ if (ie && ie_version >= 9) prepareSelectAllHack();
+ if (captureRightClick) {
+ e_stop(e);
+ var mouseup = function() {
+ off(window, "mouseup", mouseup);
+ setTimeout(rehide, 20);
+ };
+ on(window, "mouseup", mouseup);
+ } else {
+ setTimeout(rehide, 50);
+ }
+ },
+
+ readOnlyChanged: function(val) {
+ if (!val) this.reset();
+ },
+
+ setUneditable: nothing,
+
+ needsContentAttribute: false
+}, TextareaInput.prototype);
diff --git a/src/input/indent.js b/src/input/indent.js
new file mode 100644
index 0000000000..d814452e8b
--- /dev/null
+++ b/src/input/indent.js
@@ -0,0 +1,71 @@
+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
+// (typically set to true for forced single-line indents), empty
+// 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";
+ 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);
+ }
+
+ 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";
+ } else if (how == "smart") {
+ indentation = doc.mode.indent(state, line.text.slice(curSpaceString.length), line.text);
+ if (indentation == Pass || indentation > 150) {
+ if (!aggressive) return;
+ how = "prev";
+ }
+ }
+ if (how == "prev") {
+ if (n > doc.first) indentation = countColumn(getLine(doc, n-1).text, null, tabSize);
+ else indentation = 0;
+ } else if (how == "add") {
+ indentation = curSpace + cm.options.indentUnit;
+ } else if (how == "subtract") {
+ indentation = curSpace - cm.options.indentUnit;
+ } else if (typeof how == "number") {
+ indentation = curSpace + how;
+ }
+ indentation = Math.max(0, indentation);
+
+ 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);
+
+ if (indentString != curSpaceString) {
+ 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];
+ if (range.head.line == n && range.head.ch < curSpaceString.length) {
+ 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
new file mode 100644
index 0000000000..b017934a2c
--- /dev/null
+++ b/src/input/input.js
@@ -0,0 +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 { 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 function setLastCopied(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 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 = [];
+ 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]; });
+ }
+ }
+
+ // 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();
+ if (range.empty()) {
+ if (deleted && deleted > 0) // Handle deletion
+ 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 (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,
+ origin: origin || (paste ? "paste" : cm.state.cutIncoming ? "cut" : "+input")};
+ makeChange(cm.doc, changeEvent);
+ signalLater(cm, "inputRead", cm, changeEvent);
+ }
+ if (inserted && !paste)
+ triggerElectric(cm, inserted);
+
+ 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");
+ if (pasted) {
+ e.preventDefault();
+ if (!cm.isReadOnly() && !cm.options.disableInput)
+ 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;
+
+ 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;
+ 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;
+ }
+ } 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");
+ }
+ if (indented) signalLater(cm, "electricInput", cm, range.head.line);
+ }
+}
+
+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)};
+ ranges.push(lineRange);
+ text.push(cm.getRange(lineRange.anchor, lineRange.head));
+ }
+ return {text: text, ranges: ranges};
+}
+
+export function disableBrowserMagic(field, 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;");
+ // 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 border: 0; -- iOS fails to open keyboard (issue #1287)
+ if (ios) te.style.border = "1px solid black";
+ disableBrowserMagic(te);
+ return div;
+}
diff --git a/src/input/keymap.js b/src/input/keymap.js
new file mode 100644
index 0000000000..7b2879247e
--- /dev/null
+++ b/src/input/keymap.js
@@ -0,0 +1,139 @@
+import { flipCtrlCmd, mac, presto } from "../util/browser";
+import { map } from "../util/misc";
+
+import { keyNames } from "./keynames";
+
+export var keyMap = {};
+
+keyMap.basic = {
+ "Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown",
+ "End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown",
+ "Delete": "delCharAfter", "Backspace": "delCharBefore", "Shift-Backspace": "delCharBefore",
+ "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.
+keyMap.pcDefault = {
+ "Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo",
+ "Ctrl-Home": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Up": "goLineUp", "Ctrl-Down": "goLineDown",
+ "Ctrl-Left": "goGroupLeft", "Ctrl-Right": "goGroupRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd",
+ "Ctrl-Backspace": "delGroupBefore", "Ctrl-Delete": "delGroupAfter", "Ctrl-S": "save", "Ctrl-F": "find",
+ "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll",
+ "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",
+ "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",
+ "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",
+ "Alt-Right": "goGroupRight", "Cmd-Left": "goLineLeft", "Cmd-Right": "goLineRight", "Alt-Backspace": "delGroupBefore",
+ "Ctrl-Alt-Backspace": "delGroupAfter", "Alt-Delete": "delGroupAfter", "Cmd-S": "save", "Cmd-F": "find",
+ "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll",
+ "Cmd-[": "indentLess", "Cmd-]": "indentMore", "Cmd-Backspace": "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 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];
+ 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;
+}
+
+// This is a kludge to keep keymaps mostly working as raw objects
+// (backwards compatibility) while at the same time support features
+// like normalization and multi-stroke key bindings. It compiles a
+// 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];
+ 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;
+ if (i == keys.length - 1) {
+ name = keys.join(" ");
+ val = value;
+ } else {
+ 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);
+ }
+ delete keymap[keyname];
+ }
+ 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";
+
+ 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);
+ if (result) return result;
+ }
+ }
+}
+
+// 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";
+}
+
+// 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;
+}
+
+export function getKeyMap(val) {
+ return typeof val == "string" ? keyMap[val] : val;
+}
diff --git a/src/input/keynames.js b/src/input/keynames.js
new file mode 100644
index 0000000000..5956093e82
--- /dev/null
+++ b/src/input/keynames.js
@@ -0,0 +1,17 @@
+export var keyNames = {
+ 3: "Enter", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt",
+ 19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End",
+ 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",
+ 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);
+// Alphabetic keys
+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;
diff --git a/src/line/highlight.js b/src/line/highlight.js
new file mode 100644
index 0000000000..eada1a0671
--- /dev/null
+++ b/src/line/highlight.js
@@ -0,0 +1,204 @@
+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";
+
+// Compute a style array (an array starting with a mode generation
+// -- for invalidation -- followed by pairs of end positions and
+// style strings), which is used to highlight the tokens on the
+// line.
+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 = {};
+ // 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;
+ runMode(cm, line.text, overlay.mode, true, function(end, style) {
+ 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];
+ if (i_end > end)
+ st.splice(i, 1, end, st[i+1], i_end);
+ i += 2;
+ at = Math.min(end, i_end);
+ }
+ if (!style) return;
+ if (overlay.opaque) {
+ 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;
+ }
+ }
+ }, lineClasses);
+ }
+
+ 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++;
+ }
+ 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);
+ 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;
+}
+
+// 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);
+ while (!stream.eol()) {
+ 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);
+}
+
+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;
+ }
+ throw new Error("Mode " + mode.name + " failed to advance stream.");
+}
+
+// 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};
+ }
+
+ 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));
+ }
+ 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";
+ if (output[prop] == null)
+ output[prop] = lineClass[2];
+ else if (!(new RegExp("(?:^|\s)" + lineClass[2] + "(?:$|\s)")).test(output[prop]))
+ output[prop] += " " + lineClass[2];
+ }
+ 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);
+ 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;
+ } else {
+ style = extractLineClasses(readToken(mode, stream, state, inner), lineClasses);
+ }
+ if (inner) {
+ 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);
+ }
+ curStyle = style;
+ }
+ 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;
+ }
+}
+
+// Finds the line to start with when starting a parse. Tries to
+// find a line with a stateAfter, so that it can start with a
+// valid state. If that fails, it returns the line with the
+// smallest indentation, which tends to need the least context to
+// parse correctly.
+function findStartLine(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) {
+ 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;
+ }
+ }
+ return minline;
+}
diff --git a/src/line/line_data.js b/src/line/line_data.js
new file mode 100644
index 0000000000..018bd0e297
--- /dev/null
+++ b/src/line/line_data.js
@@ -0,0 +1,330 @@
+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";
+
+// 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;
+}
+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);
+}
+
+// Detach a line from the document tree and its markers.
+export function cleanUpLine(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 = {};
+function interpretTokenStyle(style, options) {
+ if (!style || /^\s*$/.test(style)) return null;
+ var cache = options.addModeClass ? styleToClassCacheWithMode : styleToClassCache;
+ return cache[style] ||
+ (cache[style] = style.replace(/\S+/g, "cm-$&"));
+}
+
+// Render the DOM representation of the text of a line. Also builds
+// up a 'line map', which points at the DOM nodes that represent
+// specific stretches of text, and is used by the measuring code.
+// The returned object contains the DOM node, this map, and
+// information about line-wide styles that were set by the mode.
+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,
+ 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;
+ 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));
+ if (line.styleClasses) {
+ if (line.styleClasses.bgClass)
+ builder.bgClass = joinClasses(line.styleClasses.bgClass, builder.bgClass || "");
+ if (line.styleClasses.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)));
+
+ // Store the map and a cache object for the current logical line
+ if (i == 0) {
+ 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({});
+ }
+ }
+
+ // See issue #2901
+ 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)
+ builder.textClass = joinClasses(builder.pre.className, builder.textClass || "");
+
+ 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;
+}
+
+// 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;
+ var displayText = builder.splitSpaces ? splitSpaces(text, builder.trailingSpace) : text
+ 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;
+ } else {
+ 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;
+ 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;
+ }
+ 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;
+ } 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;
+ } 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;
+ }
+ 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);
+ }
+ builder.content.appendChild(content);
+}
+
+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)
+ if (ch == " " && spaceBefore && (i == text.length - 1 || text.charCodeAt(i + 1) == 32))
+ ch = "\u00a0"
+ result += ch
+ spaceBefore = ch == " "
+ }
+ return result
+}
+
+// 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) {
+ 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;
+ }
+ 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);
+ if (!ignoreWidget && builder.cm.display.input.needsContentAttribute) {
+ if (!widget)
+ 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.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;
+ 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;
+ }
+
+ 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;
+ 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) {
+ 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 = "";
+ }
+ 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.collapsed && (!collapsed || compareCollapsedMarkers(collapsed.marker, m) < 0))
+ collapsed = sp;
+ } else if (sp.from > pos && 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]);
+ 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 (pos >= len) break;
+
+ var upto = Math.min(len, nextChange);
+ while (true) {
+ if (text) {
+ var end = pos + text.length;
+ if (!collapsed) {
+ var tokenText = end > upto ? text.slice(0, upto - pos) : text;
+ builder.addToken(builder, tokenText, style ? style + spanStyle : spanStyle,
+ spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : "", title, css);
+ }
+ 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);
+ }
+ }
+}
+
+
+// These objects are used to represent the visible (currently drawn)
+// part of the document. A LineView may correspond to multiple
+// logical lines, if those are connected by collapsed ranges.
+export function LineView(doc, line, lineN) {
+ // The starting line
+ this.line = line;
+ // Continuing lines, if any
+ 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);
+}
+
+// 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);
+ nextPos = pos + view.size;
+ array.push(view);
+ }
+ return array;
+}
diff --git a/src/line/pos.js b/src/line/pos.js
new file mode 100644
index 0000000000..e81eb58493
--- /dev/null
+++ b/src/line/pos.js
@@ -0,0 +1,35 @@
+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;
+}
+
+// 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 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 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);
+}
+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;
+}
+export function clipPosArray(doc, array) {
+ 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
new file mode 100644
index 0000000000..4c1f8e0bf0
--- /dev/null
+++ b/src/line/saw_special_spans.js
@@ -0,0 +1,10 @@
+// Optimize some code when these features are not used.
+export var sawReadOnlySpans = false, sawCollapsedSpans = false;
+
+export function seeReadOnlySpans() {
+ sawReadOnlySpans = true;
+}
+
+export function seeCollapsedSpans() {
+ sawCollapsedSpans = true;
+}
diff --git a/src/line/spans.js b/src/line/spans.js
new file mode 100644
index 0000000000..d114d8943f
--- /dev/null
+++ b/src/line/spans.js
@@ -0,0 +1,362 @@
+import { indexOf, lst } from "../util/misc";
+
+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;
+}
+
+// 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;
+ }
+}
+// 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;
+}
+// Add a span to a line.
+export function addMarkedSpan(line, span) {
+ 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
+// document. These functions cut an array of spans at a given
+// 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);
+ 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));
+ }
+ }
+ 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);
+ 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));
+ }
+ }
+ return nw;
+}
+
+// Given a change object, compute the new set of marker spans that
+// cover the line in which the change took place. Removes spans
+// entirely within the change, reconnects spans belonging to the
+// same marker that appear on both sides of the change, and cuts off
+// 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;
+
+ 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);
+
+ // Next, merge those two ends
+ 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];
+ if (span.to == null) {
+ var found = getMarkedSpanFor(last, span.marker);
+ if (!found) span.to = startCh;
+ else if (sameLine) span.to = found.to == null ? null : found.to + offset;
+ }
+ }
+ }
+ if (last) {
+ // Fix up .from in last (or move them into first in case of sameLine)
+ for (var i = 0; i < last.length; ++i) {
+ var span = last[i];
+ if (span.to != null) span.to += offset;
+ if (span.from == null) {
+ var found = getMarkedSpanFor(first, span.marker);
+ if (!found) {
+ span.from = offset;
+ if (sameLine) (first || (first = [])).push(span);
+ }
+ } else {
+ span.from += offset;
+ if (sameLine) (first || (first = [])).push(span);
+ }
+ }
+ }
+ // Make sure we didn't create any zero-length spans
+ if (first) first = clearEmptySpans(first);
+ if (last && last != first) last = clearEmptySpans(last);
+
+ var newMarkers = [first];
+ if (!sameLine) {
+ // Fill gap with whole-line-spans
+ 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));
+ for (var i = 0; i < gap; ++i)
+ newMarkers.push(gapMarkers);
+ newMarkers.push(last);
+ }
+ 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];
+ if (span.from != null && span.from == span.to && span.marker.clearWhenEmpty !== false)
+ spans.splice(i--, 1);
+ }
+ 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;
+ 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 (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];
+ 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});
+ if (dto > 0 || !mk.inclusiveRight && !dto)
+ newParts.push({from: m.to, to: p.to});
+ parts.splice.apply(parts, newParts);
+ j += newParts.length - 1;
+ }
+ }
+ return parts;
+}
+
+// Connect or disconnect spans from a line.
+export function detachMarkedSpans(line) {
+ var spans = line.markedSpans;
+ if (!spans) return;
+ for (var 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)
+ 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; }
+
+// 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;
+}
+
+// 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) {
+ sp = sps[i];
+ if (sp.marker.collapsed && (start ? sp.from : sp.to) == null &&
+ (!found || compareCollapsedMarkers(found, sp.marker) < 0))
+ found = sp.marker;
+ }
+ return found;
+}
+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;
+ 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;
+ 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;
+ }
+}
+
+// A visual line is a line as drawn on the screen. Folding, for
+// example, can cause multiple logical lines to appear on the same
+// 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;
+ while (merged = collapsedSpanAtStart(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;
+ while (merged = collapsedSpanAtEnd(line)) {
+ line = merged.find(1, true).line;
+ (lines || (lines = [])).push(line);
+ }
+ 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);
+}
+
+// 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;
+ while (merged = collapsedSpanAtEnd(line))
+ 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;
+ 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;
+ if (sp.from == 0 && sp.marker.inclusiveLeft && lineIsHiddenInner(doc, line, sp))
+ 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));
+ }
+ if (span.marker.inclusiveRight && span.to == line.text.length)
+ return true;
+ for (var sp, i = 0; i < line.markedSpans.length; ++i) {
+ sp = line.markedSpans[i];
+ 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;
+ }
+}
+
+// Find the height above the given line.
+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];
+ 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;
+ }
+ }
+ 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;
+ while (merged = collapsedSpanAtStart(cur)) {
+ var 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);
+ len -= cur.text.length - found.from.ch;
+ cur = found.to.line;
+ len += cur.text.length - found.to.ch;
+ }
+ 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;
+ doc.iter(function(line) {
+ var 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
new file mode 100644
index 0000000000..b194ff38b6
--- /dev/null
+++ b/src/line/utils_line.js
@@ -0,0 +1,83 @@
+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.");
+ 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;
+ }
+ }
+ 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;
+ 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;
+}
+// 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;
+}
+
+// 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;
+}
+
+// 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) {
+ if (chunk.children[i] == cur) break;
+ no += chunk.children[i].chunkSize();
+ }
+ }
+ 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;
+ 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();
+ }
+ 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;
+ }
+ return n + i;
+}
+
+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));
+}
diff --git a/src/measurement/position_measurement.js b/src/measurement/position_measurement.js
new file mode 100644
index 0000000000..b479e25090
--- /dev/null
+++ b/src/measurement/position_measurement.js
@@ -0,0 +1,580 @@
+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 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;
+}
+
+export function scrollGap(cm) { return scrollerGap - cm.display.nativeBarWidth; }
+export function displayWidth(cm) {
+ return cm.display.scroller.clientWidth - scrollGap(cm) - cm.display.barWidth;
+}
+export function displayHeight(cm) {
+ return cm.display.scroller.clientHeight - scrollGap(cm) - cm.display.barHeight;
+}
+
+// Ensure the lineView.wrapping.heights array is populated. This is
+// an array of bottom offsets for the lines that make up a drawn
+// 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);
+ if (!lineView.measure.heights || wrapping && lineView.measure.width != curWidth) {
+ var 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];
+ if (Math.abs(cur.bottom - next.bottom) > 2)
+ heights.push((cur.bottom + next.top) / 2 - rect.top);
+ }
+ }
+ heights.push(rect.bottom - rect.top);
+ }
+}
+
+// Find a line map (mapping character offsets to text nodes) and a
+// measurement cache for the given line number. (A line view might
+// 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};
+ 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]};
+ 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};
+}
+
+// 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;
+}
+
+// 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);
+}
+
+// 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;
+ if (ext && lineN >= ext.lineN && lineN < ext.lineN + ext.size)
+ return ext;
+}
+
+// Measurement can be split in two steps, the set-up work that
+// applies to the whole line, and the measurement of the actual
+// 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) {
+ var lineN = lineNo(line);
+ var view = findViewForLine(cm, lineN);
+ if (view && !view.text) {
+ view = null;
+ } else if (view && view.changes) {
+ updateLineForChanges(cm, view, lineN, getDimensions(cm));
+ cm.curOp.forceUpdate = true;
+ }
+ if (!view)
+ view = updateExternalMeasurement(cm, line);
+
+ 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.cache.hasOwnProperty(key)) {
+ found = prepared.cache[key];
+ } else {
+ if (!prepared.rect)
+ prepared.rect = prepared.view.text.getBoundingClientRect();
+ if (!prepared.hasHeights) {
+ ensureLineHeights(cm, prepared.view, prepared.rect);
+ prepared.hasHeights = true;
+ }
+ 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};
+}
+
+var nullRect = {left: 0, right: 0, top: 0, bottom: 0};
+
+export function nodeAndOffsetInLineMap(map, ch, bias) {
+ 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];
+ if (ch < mStart) {
+ start = 0; end = 1;
+ collapse = "left";
+ } else if (ch < mEnd) {
+ 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";
+ }
+ if (start != null) {
+ node = map[i + 2];
+ if (mStart == mEnd && bias == (node.insertLeft ? "left" : "right"))
+ 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";
+ }
+ 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";
+ }
+ break;
+ }
+ }
+ return {node: node, start: start, end: end, collapse: collapse, coverStart: mStart, coverEnd: mEnd};
+}
+
+function getUsefulRect(rects, bias) {
+ var rect = nullRect
+ if (bias == "left") for (var i = 0; i < rects.length; i++) {
+ if ((rect = rects[i]).left != rect.right) break
+ } else for (var 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;
+
+ 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;
+ if (ie && ie_version < 9 && start == 0 && end == place.coverEnd - place.coverStart)
+ 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 (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 (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];
+ 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++)
+ 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; }
+
+ return result;
+}
+
+// Work around problem with bounding client rects on ranges being
+// returned incorrectly when zoomed on IE10 and below.
+function maybeUpdateRectForZooming(measure, rect) {
+ if (!window.screen || screen.logicalXDPI == null ||
+ screen.logicalXDPI == screen.deviceXDPI || !hasBadZoomedRects(measure))
+ return rect;
+ var scaleX = screen.logicalXDPI / screen.deviceXDPI;
+ var scaleY = screen.logicalYDPI / screen.deviceYDPI;
+ return {left: rect.left * scaleX, right: rect.right * scaleX,
+ top: rect.top * scaleY, bottom: rect.bottom * scaleY};
+}
+
+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++)
+ lineView.measure.caches[i] = {};
+ }
+}
+
+export function clearLineMeasurementCache(cm) {
+ cm.display.externalMeasure = null;
+ removeChildren(cm.display.lineMeasure);
+ for (var i = 0; i < cm.display.view.length; 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;
+}
+
+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
+// "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]);
+ 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 == "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;
+ }
+ 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;
+ // First move into "page" coordinate system
+ if (context == "page") {
+ left -= pageScrollX();
+ top -= pageScrollY();
+ } else if (context == "local" || !context) {
+ 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};
+}
+
+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);
+}
+
+// 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);
+ 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);
+ }
+ function getBidi(ch, partPos) {
+ 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;
+ } 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);
+ }
+ 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};
+}
+
+// Positions returned by coordsChar contain some extra information.
+// xRel is the relative x position of the input coordinates compared
+// to the found position (so xRel > 0 means the coordinates are to
+// 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) {
+ 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;
+ 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);
+ for (;;) {
+ 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);
+ else
+ 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);
+
+ 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 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);
+ // 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);
+ // 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");
+ 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;
+ }
+ var step = Math.ceil(dist / 2), middle = from + step;
+ if (bidi) {
+ middle = from;
+ for (var i = 0; i < step; ++i) middle = moveVisually(lineObj, middle, 1);
+ }
+ var middleX = getX(middle);
+ if (middleX > x) {to = middle; toX = middleX; if (toOutside = wrongLine) toX += 1000; dist = step;}
+ else {from = middle; fromX = middleX; fromOutside = wrongLine; dist -= step;}
+ }
+}
+
+var measureText;
+// Compute the default text height.
+export function textHeight(display) {
+ if (display.cachedTextHeight != null) return display.cachedTextHeight;
+ if (measureText == null) {
+ measureText = elt("pre");
+ // Measure a bunch of lines, for browsers that compute
+ // fractional heights.
+ for (var i = 0; i < 49; ++i) {
+ measureText.appendChild(document.createTextNode("x"));
+ measureText.appendChild(elt("br"));
+ }
+ measureText.appendChild(document.createTextNode("x"));
+ }
+ removeChildrenAndAdd(display.measure, measureText);
+ var height = measureText.offsetHeight / 50;
+ if (height > 3) display.cachedTextHeight = height;
+ removeChildren(display.measure);
+ return height || 1;
+}
+
+// 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;
+}
+
+// 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) {
+ 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};
+}
+
+// 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;
+}
+
+// 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);
+ 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++) {
+ if (line.widgets[i].height) widgetsHeight += line.widgets[i].height;
+ }
+
+ if (wrapping)
+ return widgetsHeight + (Math.ceil(line.text.length / perLine) || 1) * th;
+ else
+ return widgetsHeight + th;
+ };
+}
+
+export function estimateLineHeights(cm) {
+ var doc = cm.doc, est = estimateHeight(cm);
+ doc.iter(function(line) {
+ var estHeight = est(line);
+ if (estHeight != line.height) updateLineHeight(line, estHeight);
+ });
+}
+
+// Given a mouse event, find the corresponding position. If liberal
+// is false, it checks whether a gutter or scrollbar was clicked,
+// and returns null if it was. forRect is used by rectangular
+// 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 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;
+ 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));
+ }
+ 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;
+ for (var 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
new file mode 100644
index 0000000000..5cac95db3e
--- /dev/null
+++ b/src/measurement/update_line.js
@@ -0,0 +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";
+
+// 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);
+ }
+ 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");
+ 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;
+ }
+ return lineView.node;
+}
+
+function updateLineBackground(lineView) {
+ 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; }
+ } else if (cls) {
+ 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;
+ if (ext && ext.line == lineView.line) {
+ cm.display.externalMeasured = null;
+ lineView.measure = ext.measure;
+ return ext.built;
+ }
+ 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;
+ if (built.bgClass != lineView.bgClass || built.textClass != lineView.textClass) {
+ lineView.bgClass = built.bgClass;
+ lineView.textClass = built.textClass;
+ updateLineClasses(lineView);
+ } else if (cls) {
+ lineView.text.className = cls;
+ }
+}
+
+function updateLineClasses(lineView) {
+ updateLineBackground(lineView);
+ if (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 || "";
+}
+
+function updateLineGutter(cm, lineView, lineN, dims) {
+ if (lineView.gutter) {
+ lineView.node.removeChild(lineView.gutter);
+ lineView.gutter = null;
+ }
+ if (lineView.gutterBackground) {
+ lineView.node.removeChild(lineView.gutterBackground);
+ lineView.gutterBackground = null;
+ }
+ if (lineView.line.gutterClass) {
+ 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);
+ }
+ var markers = lineView.line.gutterMarkers;
+ if (cm.options.lineNumbers || markers) {
+ 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);
+ if (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"));
+ if (markers) for (var k = 0; k < cm.options.gutters.length; ++k) {
+ var id = cm.options.gutters[k], found = markers.hasOwnProperty(id) && markers[id];
+ if (found)
+ gutterWrap.appendChild(elt("div", [found], "CodeMirror-gutter-elt", "left: " +
+ dims.gutterLeft[id] + "px; width: " + dims.gutterWidth[id] + "px"));
+ }
+ }
+}
+
+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;
+ if (node.className == "CodeMirror-linewidget")
+ lineView.node.removeChild(node);
+ }
+ 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;
+
+ 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);
+ if (lineView.rest) for (var 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");
+ 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);
+ else
+ 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";
+ if (!widget.coverGutter) {
+ width -= dims.gutterTotalWidth;
+ node.style.paddingLeft = dims.gutterTotalWidth + "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";
+ }
+}
diff --git a/src/measurement/widgets.js b/src/measurement/widgets.js
new file mode 100644
index 0000000000..283740e2c0
--- /dev/null
+++ b/src/measurement/widgets.js
@@ -0,0 +1,26 @@
+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 (!contains(document.body, widget.node)) {
+ var parentStyle = "position: relative;";
+ if (widget.coverGutter)
+ 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));
+ }
+ return widget.height = widget.node.parentNode.offsetHeight;
+}
+
+// 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) {
+ 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
new file mode 100644
index 0000000000..68d38c90d9
--- /dev/null
+++ b/src/model/Doc.js
@@ -0,0 +1,384 @@
+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";
+
+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;
+
+ 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);
+};
+
+Doc.prototype = createObj(BranchChunk.prototype, {
+ constructor: Doc,
+ // Iterate over the document. Supports two forms -- with only one
+ // argument, it calls that for each line in the document. With
+ // 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);
+ },
+
+ // 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);
+ },
+ 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());
+ },
+ setValue: docMethodOp(function(code) {
+ 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));
+ }),
+ replaceRange: function(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());
+ },
+
+ 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);},
+
+ getLineHandleVisualStart: function(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;},
+
+ 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;
+ },
+ 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);
+ }),
+ setSelection: docMethodOp(function(anchor, head, 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);
+ }),
+ extendSelections: docMethodOp(function(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);
+ }),
+ setSelections: docMethodOp(function(ranges, primary, options) {
+ 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);
+ }),
+ 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);
+ }),
+
+ 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());
+ 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());
+ 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++)
+ 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];
+ 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--)
+ 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);}),
+
+ 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};
+ },
+ clearHistory: function() {this.history = new History(this.history.maxGeneration);},
+
+ markClean: function() {
+ this.cleanGeneration = this.changeGeneration(true);
+ },
+ changeGeneration: function(forceSplit) {
+ if (forceSplit)
+ this.history.lastOp = this.history.lastSelOp = this.history.lastOrigin = null;
+ return this.history.generation;
+ },
+ isClean: function (gen) {
+ return this.history.generation == (gen || this.cleanGeneration);
+ },
+
+ getHistory: function() {
+ return {done: copyHistoryArray(this.history.done),
+ 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);
+ },
+
+ 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;
+ });
+ }),
+ 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;
+ 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;
+ }
+ return true;
+ });
+ }),
+
+ addLineWidget: docMethodOp(function(handle, node, options) {
+ return addLineWidget(this, handle, node, options);
+ }),
+ removeLineWidget: function(widget) { widget.clear(); },
+
+ markText: function(from, to, options) {
+ 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");
+ },
+ findMarksAt: function(pos) {
+ pos = clipPos(this, pos);
+ var markers = [], spans = getLine(this, pos.line).markedSpans;
+ if (spans) for (var i = 0; i < spans.length; ++i) {
+ var span = spans[i];
+ if ((span.from == null || span.from <= pos.ch) &&
+ (span.to == null || span.to >= pos.ch))
+ markers.push(span.marker.parent || span.marker);
+ }
+ return markers;
+ },
+ findMarks: function(from, to, filter) {
+ 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;
+ 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 ||
+ 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);
+ }
+ ++lineNo;
+ });
+ return found;
+ },
+ getAllMarks: function() {
+ var markers = [];
+ this.iter(function(line) {
+ var sps = line.markedSpans;
+ if (sps) for (var i = 0; i < sps.length; ++i)
+ if (sps[i].from != null) markers.push(sps[i].marker);
+ });
+ return markers;
+ },
+
+ posFromIndex: function(off) {
+ var ch, lineNo = this.first, 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));
+ },
+ 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;
+ this.iter(this.first, coords.line, function (line) {
+ 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;
+ if (copyHistory) {
+ doc.history.undoDepth = this.history.undoDepth;
+ doc.setHistory(this.getHistory());
+ }
+ return doc;
+ },
+
+ linkedDoc: function(options) {
+ if (!options) options = {};
+ var from = this.first, to = this.first + this.size;
+ if (options.from != null && options.from > from) from = options.from;
+ if (options.to != null && options.to < to) to = options.to;
+ var copy = new Doc(getLines(this, from, to), options.mode || this.modeOption, from, 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 (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;
+ }
+ // 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);
+ }
+ },
+ iterLinkedDocs: function(f) {linkedDocs(this, f);},
+
+ getMode: function() {return this.mode;},
+ getEditor: function() {return this.cm;},
+
+ splitLines: function(str) {
+ if (this.lineSep) return str.split(this.lineSep);
+ return splitLinesAuto(str);
+ },
+ lineSeparator: function() { return this.lineSep || "\n"; }
+});
+
+// Public alias.
+Doc.prototype.eachLine = Doc.prototype.iter;
+
+export default Doc;
diff --git a/src/model/change_measurement.js b/src/model/change_measurement.js
new file mode 100644
index 0000000000..cb2bb2d1d8
--- /dev/null
+++ b/src/model/change_measurement.js
@@ -0,0 +1,61 @@
+import { cmp, Pos } from "../line/pos";
+import { lst } from "../util/misc";
+
+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;
+ return Pos(change.from.line + change.text.length - 1,
+ 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);
+
+ 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 = [];
+ for (var i = 0; i < doc.sel.ranges.length; i++) {
+ var range = doc.sel.ranges[i];
+ out.push(new Range(adjustForChange(range.anchor, change),
+ adjustForChange(range.head, change)));
+ }
+ 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);
+ else
+ 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;
+ 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;
+ 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);
+ } else {
+ out[i] = new Range(from, from);
+ }
+ }
+ return new Selection(out, doc.sel.primIndex);
+}
diff --git a/src/model/changes.js b/src/model/changes.js
new file mode 100644
index 0000000000..01526d2943
--- /dev/null
+++ b/src/model/changes.js
@@ -0,0 +1,329 @@
+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
+
+// Allow "beforeChange" event handlers to influence a change
+function filterChange(doc, change, update) {
+ var obj = {
+ canceled: false,
+ from: change.from,
+ to: change.to,
+ text: change.text,
+ origin: change.origin,
+ cancel: function() { this.canceled = true; }
+ };
+ if (update) obj.update = function(from, to, text, origin) {
+ if (from) this.from = clipPos(doc, from);
+ if (to) this.to = clipPos(doc, to);
+ if (text) this.text = text;
+ if (origin !== undefined) this.origin = origin;
+ };
+ signal(doc, "beforeChange", doc, obj);
+ if (doc.cm) signal(doc.cm, "beforeChange", doc.cm, obj);
+
+ 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 (hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange")) {
+ 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);
+ 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});
+ } else {
+ 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);
+
+ 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);
+ }
+ 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;
+
+ 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];
+ if (allowSelectionOnly ? event.ranges && !event.equals(doc.sel) : !event.ranges)
+ break;
+ }
+ if (i == source.length) return;
+ hist.lastOrigin = hist.lastSelOrigin = null;
+
+ for (;;) {
+ event = source.pop();
+ if (event.ranges) {
+ pushSelectionToHistory(event, dest);
+ if (allowSelectionOnly && !event.equals(doc.sel)) {
+ setSelection(doc, event, {clearRedo: false});
+ return;
+ }
+ selAfter = event;
+ }
+ 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 filter = hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange");
+
+ for (var i = event.changes.length - 1; i >= 0; --i) {
+ var change = event.changes[i];
+ change.origin = type;
+ if (filter && !filterChange(doc, change, false)) {
+ source.length = 0;
+ return;
+ }
+
+ 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 = [];
+
+ // 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);
+ }
+ 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;
+ 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);
+ 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++)
+ regLineChange(doc.cm, l, "gutter");
+ }
+}
+
+// More lower-level change function, handling only a single document
+// (not linked ones).
+function makeChangeSingleDoc(doc, change, selAfter, spans) {
+ if (doc.cm && !doc.cm.curOp)
+ return operation(doc.cm, makeChangeSingleDoc)(doc, change, selAfter, spans);
+
+ if (change.to.line < doc.first) {
+ shiftDoc(doc, change.text.length - 1 - (change.to.line - change.from.line));
+ return;
+ }
+ if (change.from.line > doc.lastLine()) return;
+
+ // Clip the change to the size of this doc
+ if (change.from.line < doc.first) {
+ var shift = change.text.length - 1 - (doc.first - change.from.line);
+ shiftDoc(doc, shift);
+ change = {from: Pos(doc.first, 0), to: Pos(change.to.line + shift, change.to.ch),
+ text: [lst(change.text)], origin: change.origin};
+ }
+ var last = doc.lastLine();
+ if (change.to.line > last) {
+ change = {from: change.from, to: Pos(last, getLine(doc, last).text.length),
+ text: [change.text[0]], origin: change.origin};
+ }
+
+ 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);
+}
+
+// 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 recomputeMaxLength = false, checkWidthStart = from.line;
+ if (!cm.options.lineWrapping) {
+ checkWidthStart = lineNo(visualLine(getLine(doc, from.line)));
+ doc.iter(checkWidthStart, to.line + 1, function(line) {
+ if (line == display.maxLine) {
+ recomputeMaxLength = true;
+ return true;
+ }
+ });
+ }
+
+ if (doc.sel.contains(change.from, change.to) > -1)
+ signalCursorActivity(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);
+ if (len > display.maxLineLength) {
+ display.maxLine = line;
+ display.maxLineLength = len;
+ display.maxLineChanged = true;
+ recomputeMaxLength = false;
+ }
+ });
+ if (recomputeMaxLength) cm.curOp.updateMaxLine = true;
+ }
+
+ // Adjust frontier, schedule worker
+ doc.frontier = Math.min(doc.frontier, from.line);
+ startWorker(cm, 400);
+
+ var lendiff = change.text.length - (to.line - from.line) - 1;
+ // Remember that these lines changed, for updating the display
+ if (change.full)
+ regChange(cm);
+ else if (from.line == to.line && change.text.length == 1 && !isWholeLineUpdate(cm.doc, change))
+ regLineChange(cm, from.line, "text");
+ else
+ regChange(cm, from.line, to.line + 1, lendiff);
+
+ 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);
+ }
+ 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});
+}
+
+// Rebasing/resetting history to deal with externally-sourced changes
+
+function rebaseHistSelSingle(pos, from, to, diff) {
+ if (to < pos.line) {
+ pos.line += diff;
+ } else if (from < pos.line) {
+ pos.line = from;
+ pos.ch = 0;
+ }
+}
+
+// Tries to rebase an array of history events given a change in the
+// document. If the change touches the same lines as the event, the
+// event, and everything 'behind' it, is discarded. If the change is
+// before the event, the event's positions are updated. Uses a
+// copy-on-write scheme for the positions, to avoid having to
+// reallocate them all on every rebase, but also avoid problems with
+// shared position objects being unsafely updated.
+function rebaseHistArray(array, from, to, diff) {
+ for (var i = 0; i < array.length; ++i) {
+ var sub = array[i], ok = true;
+ if (sub.ranges) {
+ 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);
+ }
+ continue;
+ }
+ for (var j = 0; j < sub.changes.length; ++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);
+ } else if (from <= cur.to.line) {
+ ok = false;
+ break;
+ }
+ }
+ if (!ok) {
+ 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);
+}
+
+// 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;
+}
diff --git a/src/model/chunk.js b/src/model/chunk.js
new file mode 100644
index 0000000000..28fd4153b5
--- /dev/null
+++ b/src/model/chunk.js
@@ -0,0 +1,157 @@
+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
+// other branch nodes below them. The top node is always a branch
+// node, and is the document object itself (meaning it has
+// additional methods and properties).
+//
+// All nodes have parent links. The tree is used both to go from
+// line numbers to line objects, and to go from objects to numbers.
+// It also indexes by height, and is used to convert between height
+// and line object, and to find the total height of the document.
+//
+// See also http://marijnhaverbeke.nl/blog/codemirror-line-tree.html
+
+export function LeafChunk(lines) {
+ this.lines = lines;
+ this.parent = null;
+ for (var i = 0, height = 0; i < lines.length; ++i) {
+ lines[i].parent = this;
+ height += lines[i].height;
+ }
+ this.height = height;
+}
+
+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];
+ this.height -= line.height;
+ cleanUpLine(line);
+ signalLater(line, "delete");
+ }
+ 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);
+ },
+ // 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;
+ },
+ // 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;
+ }
+};
+
+export function BranchChunk(children) {
+ 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;
+ }
+ this.size = size;
+ this.height = height;
+ this.parent = null;
+}
+
+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();
+ 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;
+ }
+ // 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;
+ }
+ },
+ collapse: function(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;
+ for (var i = 0; i < this.children.length; ++i) {
+ var 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));
+ child.height -= leaf.height;
+ this.children.splice(++i, 0, leaf);
+ leaf.parent = this;
+ }
+ child.lines = child.lines.slice(0, remaining);
+ this.maybeSpill();
+ }
+ break;
+ }
+ at -= sz;
+ }
+ },
+ // 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();
+ },
+ iterN: function(at, n, op) {
+ for (var i = 0; i < this.children.length; ++i) {
+ 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;
+ }
+ }
+};
diff --git a/src/model/document_data.js b/src/model/document_data.js
new file mode 100644
index 0000000000..62ee034adf
--- /dev/null
+++ b/src/model/document_data.js
@@ -0,0 +1,97 @@
+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
+
+// By default, updates that start and end at the beginning of a line
+// are treated specially, in order to make the association of line
+// 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);
+}
+
+// Perform a change on the document data structure.
+export function updateDoc(doc, change, markedSpans, estimateHeight) {
+ function spansFor(n) {return markedSpans ? markedSpans[n] : null;}
+ function update(line, text, spans) {
+ 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;
+ }
+
+ 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);
+ } 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);
+ } else if (firstLine == lastLine) {
+ 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);
+ 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);
+ } 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);
+ }
+
+ 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);
+ }
+ }
+ 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);
+}
+
diff --git a/src/model/history.js b/src/model/history.js
new file mode 100644
index 0000000000..8e34427330
--- /dev/null
+++ b/src/model/history.js
@@ -0,0 +1,224 @@
+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";
+
+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;
+ // 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;
+ // Used by the isClean() method
+ 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;
+}
+
+// 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;
+ }
+}
+
+// Find the top change event in the history. Pop off selection
+// events that are in the way.
+function lastChangeEvent(hist, force) {
+ if (force) {
+ clearSelectionEvents(hist.done);
+ return lst(hist.done);
+ } else if (hist.done.length && !lst(hist.done).ranges) {
+ return lst(hist.done);
+ } else if (hist.done.length > 1 && !hist.done[hist.done.length - 2].ranges) {
+ hist.done.pop();
+ return lst(hist.done);
+ }
+}
+
+// Register a change in the history. Merges changes that are within
+// 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;
+
+ if ((hist.lastOp == opId ||
+ hist.lastOrigin == change.origin && change.origin &&
+ ((change.origin.charAt(0) == "+" && doc.cm && hist.lastModTime > time - doc.cm.options.historyEventDelay) ||
+ change.origin.charAt(0) == "*")) &&
+ (cur = lastChangeEvent(hist, hist.lastOp == opId))) {
+ // Merge this change into the last event
+ 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);
+ } else {
+ // Add new sub-event
+ cur.changes.push(historyChangeFromChange(doc, change));
+ }
+ } else {
+ // Can not be merged, start a new event.
+ var before = lst(hist.done);
+ if (!before || !before.ranges)
+ pushSelectionToHistory(doc.sel, hist.done);
+ cur = {changes: [historyChangeFromChange(doc, change)],
+ 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.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");
+}
+
+function selectionEventCanBeMerged(doc, origin, prev, sel) {
+ 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);
+}
+
+// Called whenever the selection changes, sets the new selection as
+// the pending selection in the history, and pushes the old pending
+// 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;
+
+ // A new event is started when the previous origin does not match
+ // the current, or the origins don't allow matching. Origins
+ // starting with * are always merged, those starting with + are
+ // merged when similar and close together in time.
+ if (opId == hist.lastSelOp ||
+ (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;
+ else
+ pushSelectionToHistory(sel, hist.done);
+
+ hist.lastSelTime = +new Date;
+ hist.lastSelOrigin = origin;
+ hist.lastSelOp = opId;
+ if (options && options.clearRedo !== false)
+ clearSelectionEvents(hist.undone);
+}
+
+export function pushSelectionToHistory(sel, dest) {
+ var 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;
+ 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;
+ });
+}
+
+// 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;
+ for (var i = 0, out; i < spans.length; ++i) {
+ if (spans[i].marker.explicitlyCleared) { if (!out) out = spans.slice(0, i); }
+ else if (out) out.push(spans[i]);
+ }
+ return !out ? spans : out.length ? out : null;
+}
+
+// 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;
+ for (var i = 0, nw = []; i < change.text.length; ++i)
+ nw.push(removeClearedSpans(found[i]));
+ return nw;
+}
+
+// Used for un/re-doing changes from the history. Combines the
+// result of computing the existing spans with the set of spans that
+// 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;
+
+ for (var i = 0; i < old.length; ++i) {
+ var 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)
+ if (oldCur[k].marker == span.marker) continue spans;
+ oldCur.push(span);
+ }
+ } else if (stretchCur) {
+ old[i] = stretchCur;
+ }
+ }
+ 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];
+ if (event.ranges) {
+ copy.push(instantiateSel ? Selection.prototype.deepCopy.call(event) : event);
+ continue;
+ }
+ 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});
+ if (newGroup) for (var prop in change) if (m = prop.match(/^spans_(\d+)$/)) {
+ if (indexOf(newGroup, Number(m[1])) > -1) {
+ lst(newChanges)[prop] = change[prop];
+ delete change[prop];
+ }
+ }
+ }
+ }
+ return copy;
+}
diff --git a/src/model/line_widget.js b/src/model/line_widget.js
new file mode 100644
index 0000000000..241327ce77
--- /dev/null
+++ b/src/model/line_widget.js
@@ -0,0 +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";
+
+// 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;
+}
+eventMixin(LineWidget);
+
+function adjustScrollWhenAboveVisible(cm, line, diff) {
+ if (heightAtLine(line) < ((cm.curOp && cm.curOp.scrollTop) || cm.doc.scrollTop))
+ 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));
+ if (cm) runInOp(cm, function() {
+ 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);
+ if (cm) runInOp(cm, function() {
+ 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;
+ 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;
+ 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;
+ }
+ return true;
+ });
+ return widget;
+}
diff --git a/src/model/mark_text.js b/src/model/mark_text.js
new file mode 100644
index 0000000000..7a334e5352
--- /dev/null
+++ b/src/model/mark_text.js
@@ -0,0 +1,288 @@
+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";
+
+// TEXTMARKERS
+
+// Created with markText and setBookmark methods. A TextMarker is a
+// handle that can be used to clear or find a marked position in the
+// document. Line objects hold arrays (markedSpans) containing
+// {from, to, marker} object pointing to such marker objects, and
+// indicating that such a marker is present on that line. Multiple
+// lines may point to the same marker when it spans across lines.
+// The spans will have null for their from/to properties when the
+// marker continues beyond the start/end of the line. Markers have
+// links back to the lines they currently touch.
+
+var 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;
+ 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 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");
+ 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 (var i = 0; i < this.lines.length; ++i) {
+ 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;
+ }
+ }
+
+ 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();
+};
+
+// 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;
+ var from, to;
+ for (var i = 0; i < this.lines.length; ++i) {
+ var line = this.lines[i];
+ var span = getMarkedSpanFor(line.markedSpans, this);
+ if (span.from != null) {
+ 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};
+};
+
+// 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;
+ runInOp(cm, function() {
+ var line = pos.line, lineN = lineNo(pos.line);
+ var 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;
+ widget.height = null;
+ var dHeight = widgetHeight(widget) - oldHeight;
+ if (dHeight)
+ updateLineHeight(line, line.height + dHeight);
+ }
+ });
+};
+
+TextMarker.prototype.attachLine = function(line) {
+ if (!this.lines.length && this.doc.cm) {
+ var op = this.doc.cm.curOp;
+ if (!op.maybeHiddenMarkers || indexOf(op.maybeHiddenMarkers, this) == -1)
+ (op.maybeUnhiddenMarkers || (op.maybeUnhiddenMarkers = [])).push(this);
+ }
+ this.lines.push(line);
+};
+TextMarker.prototype.detachLine = function(line) {
+ this.lines.splice(indexOf(this.lines, line), 1);
+ if (!this.lines.length && this.doc.cm) {
+ var op = this.doc.cm.curOp;
+ (op.maybeHiddenMarkers || (op.maybeHiddenMarkers = [])).push(this);
+ }
+};
+
+// 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
+ // (markTextShared will call out to this again, once per
+ // document).
+ 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);
+
+ 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;
+ 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;
+ }
+ 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();
+ }
+
+ if (marker.addToHistory)
+ addChangeToHistory(doc, {from: from, to: to, origin: "markText"}, doc.sel, NaN);
+
+ 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);
+ addMarkedSpan(line, new MarkedSpan(marker,
+ curLine == from.line ? from.ch : null,
+ 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 (marker.clearOnEnter) on(marker, "beforeCursorEnter", function() { marker.clear(); });
+
+ if (marker.readOnly) {
+ seeReadOnlySpans();
+ if (doc.history.done.length || doc.history.undone.length)
+ doc.clearHistory();
+ }
+ if (marker.collapsed) {
+ marker.id = ++nextMarkerId;
+ marker.atomic = true;
+ }
+ if (cm) {
+ // Sync editor state
+ 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)
+ 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;
+}
+
+// SHARED TEXTMARKERS
+
+// 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 (var i = 0; i < markers.length; ++i)
+ markers[i].parent = this;
+}
+eventMixin(SharedTextMarker);
+
+SharedTextMarker.prototype.clear = function() {
+ if (this.explicitlyCleared) return;
+ this.explicitlyCleared = true;
+ for (var 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);
+};
+
+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;
+ 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)
+ 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; });
+}
+
+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);
+ if (cmp(mFrom, mTo)) {
+ 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); });
+ for (var j = 0; j < marker.markers.length; j++) {
+ var 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
new file mode 100644
index 0000000000..ce3c228c8b
--- /dev/null
+++ b/src/model/selection.js
@@ -0,0 +1,79 @@
+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
+// (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;
+}
+
+Selection.prototype = {
+ 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;
+ 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;
+ }
+ 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);
+ },
+ somethingSelected: function() {
+ for (var 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];
+ if (cmp(end, range.from()) >= 0 && cmp(pos, range.to()) <= 0)
+ return i;
+ }
+ return -1;
+ }
+};
+
+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;
+ }
+};
+
+// 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);
+ for (var i = 1; i < ranges.length; i++) {
+ 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));
+ }
+ }
+ return new Selection(ranges, primIndex);
+}
+
+export function simpleSelection(anchor, head) {
+ return new Selection([new Range(anchor, head || anchor)], 0);
+}
diff --git a/src/model/selection_updates.js b/src/model/selection_updates.js
new file mode 100644
index 0000000000..e105afa9e0
--- /dev/null
+++ b/src/model/selection_updates.js
@@ -0,0 +1,205 @@
+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";
+
+// The 'scroll' parameter given to many of these indicated whether
+// the new cursor position should be scrolled into view after
+// modifying the selection.
+
+// If shift is held or the extend flag is set, extends a range to
+// include a given position (and optionally a second position).
+// Otherwise, simply returns the range between the given positions.
+// 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;
+ if (other) {
+ var posBefore = cmp(head, anchor) < 0;
+ if (posBefore != (cmp(other, anchor) < 0)) {
+ anchor = head;
+ head = other;
+ } else if (posBefore != (cmp(head, other) < 0)) {
+ head = other;
+ }
+ }
+ return new Range(anchor, head);
+ } else {
+ 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);
+}
+
+// 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);
+}
+
+// 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);
+}
+
+// Reset the selection to a single range.
+export function setSimpleSelection(doc, anchor, head, options) {
+ setSelection(doc, simpleSelection(anchor, head), options);
+}
+
+// Give beforeSelectionChange handlers a change to influence a
+// selection update.
+function filterSelectionChange(doc, sel, options) {
+ var obj = {
+ ranges: sel.ranges,
+ update: function(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));
+ },
+ 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;
+}
+
+export function setSelectionReplaceHistory(doc, sel, options) {
+ var done = doc.history.done, last = lst(done);
+ if (last && last.ranges) {
+ done[done.length - 1] = sel;
+ setSelectionNoUndo(doc, sel, options);
+ } else {
+ 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);
+}
+
+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 ||
+ (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);
+}
+
+function setSelectionInner(doc, sel) {
+ if (sel.equals(doc.sel)) return;
+
+ doc.sel = sel;
+
+ if (doc.cm) {
+ doc.cm.curOp.updateInput = doc.cm.curOp.selectionChanged = true;
+ signalCursorActivity(doc.cm);
+ }
+ 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);
+}
+
+// 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);
+ if (out || newAnchor != range.anchor || newHead != range.head) {
+ if (!out) out = sel.ranges.slice(0, i);
+ out[i] = new Range(newAnchor, newHead);
+ }
+ }
+ return out ? normalizeSelection(out, sel.primIndex) : sel;
+}
+
+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;
+ 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");
+ if (m.explicitlyCleared) {
+ if (!line.markedSpans) break;
+ else {--i; continue;}
+ }
+ }
+ if (!m.atomic) continue;
+
+ if (oldPos) {
+ 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);
+ 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);
+ 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;
+ }
+ }
+ 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 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));
+ if (!found) {
+ doc.cantEdit = true;
+ return Pos(doc.first, 0);
+ }
+ 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;
+ } 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;
+ } else {
+ return new Pos(pos.line, pos.ch + dir);
+ }
+}
+
+export function selectAll(cm) {
+ cm.setSelection(Pos(cm.firstLine(), 0), Pos(cm.lastLine()), sel_dontScroll);
+}
diff --git a/src/modes.js b/src/modes.js
new file mode 100644
index 0000000000..ed5e918039
--- /dev/null
+++ b/src/modes.js
@@ -0,0 +1,95 @@
+import { copyObj, createObj } from "./util/misc";
+
+// Known modes, by name and by MIME
+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;
+}
+
+export function defineMIME(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];
+ } 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;
+ } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec)) {
+ return resolveMode("application/xml");
+ } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+json$/.test(spec)) {
+ return resolveMode("application/json");
+ }
+ 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);
+ if (modeExtensions.hasOwnProperty(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];
+ }
+ }
+ 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];
+
+ return modeObj;
+}
+
+// This can be used to attach properties to mode objects from
+// outside the actual mode definition.
+export var modeExtensions = {};
+export function extendMode(mode, properties) {
+ 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 = {};
+ for (var n in state) {
+ var val = state[n];
+ if (val instanceof Array) val = val.concat([]);
+ nstate[n] = val;
+ }
+ 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;
+ }
+ return info || {mode: mode, state: state};
+}
+
+export function startState(mode, a1, a2) {
+ return mode.startState ? mode.startState(a1, a2) : true;
+}
diff --git a/src/util/StringStream.js b/src/util/StringStream.js
new file mode 100644
index 0000000000..f07357befc
--- /dev/null
+++ b/src/util/StringStream.js
@@ -0,0 +1,80 @@
+import { countColumn } from "./misc";
+
+// STRING STREAM
+
+// Fed to the mode parsers, provides helper functions to make
+// 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;
+};
+
+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() {
+ if (this.pos < this.string.length)
+ return this.string.charAt(this.pos++);
+ },
+ eat: function(match) {
+ var ch = this.string.charAt(this.pos);
+ if (typeof match == "string") var ok = ch == match;
+ else var ok = ch && (match.test ? match.test(ch) : match(ch));
+ if (ok) {++this.pos; return ch;}
+ },
+ eatWhile: function(match) {
+ var start = this.pos;
+ while (this.eat(match)){}
+ return this.pos > start;
+ },
+ eatSpace: function() {
+ var start = this.pos;
+ while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) ++this.pos;
+ return this.pos > start;
+ },
+ skipToEnd: function() {this.pos = this.string.length;},
+ skipTo: function(ch) {
+ var found = this.string.indexOf(ch, this.pos);
+ if (found > -1) {this.pos = found; return true;}
+ },
+ backUp: function(n) {this.pos -= n;},
+ column: function() {
+ 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() {
+ return countColumn(this.string, null, this.tabSize) -
+ (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);
+ if (cased(substr) == cased(pattern)) {
+ 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;
+ }
+ },
+ current: function(){return this.string.slice(this.start, this.pos);},
+ hideFirstChars: function(n, inner) {
+ this.lineStart += n;
+ try { return inner(); }
+ finally { this.lineStart -= n; }
+ }
+};
+
+export default StringStream;
diff --git a/src/util/bidi.js b/src/util/bidi.js
new file mode 100644
index 0000000000..776319d022
--- /dev/null
+++ b/src/util/bidi.js
@@ -0,0 +1,274 @@
+import { isExtendingChar, lst } from "./misc";
+
+// BIDI HELPERS
+
+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];
+ if (part.from < to && part.to > from || from == to && part.to == from) {
+ f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "rtl" : "ltr");
+ found = true;
+ }
+ }
+ if (!found) f(from, to, "ltr");
+}
+
+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 lineRight(line) {
+ 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;
+}
+
+export var bidiOther = null;
+export function getBidiPartAt(order, pos) {
+ bidiOther = null;
+ for (var i = 0, found; i < order.length; ++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;
+ } 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;
+ }
+ }
+ }
+ 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;
+}
+
+// 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) {
+ 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) {
+ 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, 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;
+}
+
+// Bidirectional ordering algorithm
+// See http://unicode.org/reports/tr9/tr9-13.html for the algorithm
+// that this (partially) implements.
+
+// One-char codes used for character types:
+// L (L): Left-to-Right
+// R (R): Right-to-Left
+// r (AL): Right-to-Left Arabic
+// 1 (EN): European Number
+// + (ES): European Number Separator
+// % (ET): European Number Terminator
+// n (AN): Arabic Number
+// , (CS): Common Number Separator
+// m (NSM): Non-Spacing Mark
+// b (BN): Boundary Neutral
+// s (B): Paragraph Separator
+// t (S): Segment Separator
+// w (WS): Whitespace
+// N (ON): Other Neutrals
+
+// Returns null if characters are ordered as they appear
+// (left-to-right), or an array of sections ({from, to, level}
+// objects) in the order in which they occur visually.
+export var bidiOrdering = (function() {
+ // Character types for codepoints 0 to 0xff
+ var lowTypes = "bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLN";
+ // Character types for codepoints 0x600 to 0x6ff
+ 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";
+ }
+
+ 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";
+
+ function BidiSpan(level, from, to) {
+ this.level = level;
+ this.from = from; this.to = to;
+ }
+
+ 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)));
+
+ // 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;
+ }
+
+ // W2. Search backwards from each instance of a European number
+ // until the first strong type (R, L, AL, or sor) is found. If an
+ // AL is found, change the type of the European number to Arabic
+ // number.
+ // W3. Change all ALs to R.
+ for (var i = 0, cur = 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"; }
+ }
+
+ // W4. A single European separator between two European numbers
+ // changes to a European number. A single common separator between
+ // two numbers of the same type changes to that type.
+ for (var i = 1, prev = types[0]; i < len - 1; ++i) {
+ var type = types[i];
+ if (type == "+" && prev == "1" && types[i+1] == "1") types[i] = "1";
+ else if (type == "," && prev == types[i+1] &&
+ (prev == "1" || prev == "n")) types[i] = prev;
+ prev = type;
+ }
+
+ // W5. A sequence of European terminators adjacent to European
+ // numbers changes to all European numbers.
+ // W6. Otherwise, separators and terminators change to Other
+ // Neutral.
+ for (var i = 0; i < len; ++i) {
+ var type = types[i];
+ if (type == ",") types[i] = "N";
+ else if (type == "%") {
+ for (var end = i + 1; end < len && types[end] == "%"; ++end) {}
+ var replace = (i && types[i-1] == "!") || (end < len && types[end] == "1") ? "1" : "N";
+ for (var j = i; j < end; ++j) types[j] = replace;
+ i = end - 1;
+ }
+ }
+
+ // W7. Search backwards from each instance of a European number
+ // until the first strong type (R, L, or sor) is found. If an L is
+ // found, then change the type of the European number to L.
+ for (var i = 0, cur = outerType; i < len; ++i) {
+ var type = types[i];
+ if (cur == "L" && type == "1") types[i] = "L";
+ else if (isStrong.test(type)) cur = type;
+ }
+
+ // N1. A sequence of neutrals takes the direction of the
+ // surrounding strong text if the text on both sides has the same
+ // direction. European and Arabic numbers act as if they were R in
+ // terms of their influence on neutrals. Start-of-level-run (sor)
+ // and end-of-level-run (eor) are used at level run boundaries.
+ // N2. Any remaining neutrals take the embedding direction.
+ for (var i = 0; i < len; ++i) {
+ if (isNeutral.test(types[i])) {
+ for (var end = i + 1; end < len && isNeutral.test(types[end]); ++end) {}
+ var before = (i ? types[i-1] : 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;
+ }
+ }
+
+ // Here we depart from the documented algorithm, in order to avoid
+ // building up an actual levels array. Since there are only three
+ // levels (0, 1, 2) in an implementation that doesn't take
+ // explicit embedding into account, we can build up the order on
+ // the fly, without following the level-based algorithm.
+ var order = [], m;
+ for (var i = 0; i < len;) {
+ if (countsAsLeft.test(types[i])) {
+ var start = i;
+ for (++i; i < len && countsAsLeft.test(types[i]); ++i) {}
+ order.push(new BidiSpan(0, start, i));
+ } else {
+ var pos = i, at = order.length;
+ for (++i; i < len && types[i] != "L"; ++i) {}
+ for (var j = pos; j < i;) {
+ if (countsAsNum.test(types[j])) {
+ if (pos < j) order.splice(at, 0, 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;
+ }
+ 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));
+ }
+ 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));
+ }
+ 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;
+ };
+})();
+
+// 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;
+}
diff --git a/src/util/browser.js b/src/util/browser.js
new file mode 100644
index 0000000000..7941f930d7
--- /dev/null
+++ b/src/util/browser.js
@@ -0,0 +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;
+
+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);
+// 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);
+
+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);
diff --git a/src/util/dom.js b/src/util/dom.js
new file mode 100644
index 0000000000..478402e668
--- /dev/null
+++ b/src/util/dom.js
@@ -0,0 +1,89 @@
+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);
+ if (match) {
+ 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;
+}
+
+export function removeChildrenAndAdd(parent, 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;
+}
+
+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;
+};
+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;
+};
+
+export function contains(parent, child) {
+ if (child.nodeType == 3) // Android browser always returns false when child is a textnode
+ child = child.parentNode;
+ if (parent.contains)
+ return parent.contains(child);
+ do {
+ if (child.nodeType == 11) child = child.host;
+ if (child == parent) return true;
+ } while (child = child.parentNode);
+}
+
+export var activeElt = function() {
+ var activeElement = document.activeElement;
+ 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) {
+ var 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++)
+ if (as[i] && !classTest(as[i]).test(b)) b += " " + as[i];
+ return b;
+}
+
+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; };
+else if (ie) // Suppress mysterious IE10 errors
+ selectInput = function(node) { try { node.select(); } catch(_e) {} };
diff --git a/src/util/event.js b/src/util/event.js
new file mode 100644
index 0000000000..4c479cbd25
--- /dev/null
+++ b/src/util/event.js
@@ -0,0 +1,102 @@
+import { mac } from "./browser";
+import { indexOf } from "./misc";
+
+// EVENT HANDLING
+
+// Lightweight event framework. on/off also work on DOM nodes,
+// registering native DOM handlers.
+
+export var 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] = []);
+ arr.push(f);
+ }
+};
+
+var noHandlers = []
+export function getHandlers(emitter, type, copy) {
+ var arr = emitter._handlers && emitter._handlers[type]
+ if (copy) return arr && arr.length > 0 ? arr.slice() : noHandlers
+ else return arr || noHandlers
+}
+
+export function off(emitter, type, f) {
+ if (emitter.removeEventListener)
+ emitter.removeEventListener(type, f, false);
+ else if (emitter.detachEvent)
+ 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; }
+ }
+}
+
+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);
+}
+
+// The DOM events that CodeMirror handles can be overridden by
+// registering a (non-DOM) handler on the editor for the event name,
+// 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;
+}
+
+export function signalCursorActivity(cm) {
+ 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]);
+}
+
+export function hasHandler(emitter, type) {
+ return getHandlers(emitter, type).length > 0
+}
+
+// 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);};
+}
+
+// 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;
+}
+export function e_stopPropagation(e) {
+ if (e.stopPropagation) e.stopPropagation();
+ else e.cancelBubble = true;
+}
+export function e_defaultPrevented(e) {
+ return e.defaultPrevented != null ? e.defaultPrevented : e.returnValue == false;
+}
+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;
+ if (b == null) {
+ 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;
+}
diff --git a/src/util/feature_detection.js b/src/util/feature_detection.js
new file mode 100644
index 0000000000..bce86ad220
--- /dev/null
+++ b/src/util/feature_detection.js
@@ -0,0 +1,83 @@
+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;
+}();
+
+var zwspSupported;
+export function zeroWidthElement(measure) {
+ if (zwspSupported == null) {
+ 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);
+ }
+ var 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;
+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);
+}
+
+// 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;
+ 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");
+ if (rt != -1) {
+ result.push(line.slice(0, rt));
+ pos += rt + 1;
+ } else {
+ result.push(line);
+ pos = nl + 1;
+ }
+ }
+ 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; }
+} : function(te) {
+ try {var 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");
+ if ("oncopy" in e) return true;
+ e.setAttribute("oncopy", "return;");
+ return typeof e.oncopy == "function";
+})();
+
+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;
+}
diff --git a/src/util/misc.js b/src/util/misc.js
new file mode 100644
index 0000000000..a98a18832b
--- /dev/null
+++ b/src/util/misc.js
@@ -0,0 +1,126 @@
+export function bind(f) {
+ var 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)
+ if (obj.hasOwnProperty(prop) && (overwrite !== false || !target.hasOwnProperty(prop)))
+ 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;
+ }
+ for (var i = startIndex || 0, n = startValue || 0;;) {
+ 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;
+ }
+}
+
+export function Delayed() {this.id = null;}
+Delayed.prototype.set = function(ms, f) {
+ 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;
+}
+
+// Number of pixels added to scroller and sizer to hide scrollbar
+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";}};
+
+// Reused option objects for setSelection & friends
+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;
+ 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;
+ }
+}
+
+var spaceStrs = [""];
+export function spaceStr(n) {
+ while (spaceStrs.length <= n)
+ spaceStrs.push(lst(spaceStrs) + " ");
+ return spaceStrs[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);
+ return out;
+}
+
+export function insertSorted(array, value, score) {
+ var pos = 0, priority = score(value)
+ while (pos < array.length && score(array[pos]) <= priority) pos++
+ array.splice(pos, 0, value)
+}
+
+export function nothing() {}
+
+export function createObj(base, props) {
+ var inst;
+ if (Object.create) {
+ inst = Object.create(base);
+ } else {
+ nothing.prototype = base;
+ inst = new nothing();
+ }
+ 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]/;
+export function isWordCharBasic(ch) {
+ return /\w/.test(ch) || ch > "\x80" &&
+ (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);
+}
+
+export function isEmpty(obj) {
+ 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 +
+// any number of extending chars is treated as a single unit as far
+// 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); }
diff --git a/src/util/operation_group.js b/src/util/operation_group.js
new file mode 100644
index 0000000000..478f07283e
--- /dev/null
+++ b/src/util/operation_group.js
@@ -0,0 +1,73 @@
+import { getHandlers } from "./event";
+
+var operationGroup = null;
+
+export function pushOperation(op) {
+ if (operationGroup) {
+ 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;
+ do {
+ for (; i < callbacks.length; i++)
+ callbacks[i].call(null);
+ for (var j = 0; j < group.ops.length; j++) {
+ var op = group.ops[j];
+ if (op.cursorActivityHandlers)
+ while (op.cursorActivityCalled < op.cursorActivityHandlers.length)
+ op.cursorActivityHandlers[op.cursorActivityCalled++].call(null, op.cm);
+ }
+ } while (i < callbacks.length);
+}
+
+export function finishOperation(op, endCb) {
+ var group = op.ownsGroup;
+ if (!group) return;
+
+ try { fireCallbacksForOps(group); }
+ finally {
+ operationGroup = null;
+ endCb(group);
+ }
+}
+
+var orphanDelayedCallbacks = null;
+
+// Often, we want to signal events at a point where we are in the
+// middle of some work, but don't want the handler to start calling
+// other methods on the editor, which might be in an inconsistent
+// state or simply not expect any other events to happen.
+// signalLater looks whether there are any handlers, and schedules
+// them to be executed when the last operation ends, or, if no
+// operation is active, when a timeout fires.
+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 (operationGroup) {
+ list = operationGroup.delayedCallbacks;
+ } else if (orphanDelayedCallbacks) {
+ list = orphanDelayedCallbacks;
+ } else {
+ list = orphanDelayedCallbacks = [];
+ setTimeout(fireOrphanDelayed, 0);
+ }
+ function bnd(f) {return function(){f.apply(null, args);};}
+ for (var i = 0; i < arr.length; ++i)
+ list.push(bnd(arr[i]));
+}
+
+function fireOrphanDelayed() {
+ var delayed = orphanDelayedCallbacks;
+ orphanDelayedCallbacks = null;
+ for (var i = 0; i < delayed.length; ++i) delayed[i]();
+}
From 4d0d2ffd0c41f931dee42dfd6e2ecbb4a8ef6485 Mon Sep 17 00:00:00 2001
From: Adrian Heine
Date: Mon, 19 Sep 2016 21:13:23 +0200
Subject: [PATCH 0224/2085] Add lint for src/
---
test/lint.js | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/test/lint.js b/test/lint.js
index 35777562ef..10b68a8deb 100644
--- a/test/lint.js
+++ b/test/lint.js
@@ -8,4 +8,12 @@ var blint = require("blint");
});
});
+["src"].forEach(function(dir) {
+ blint.checkDir(dir, {
+ browser: true,
+ blob: "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: http:\/\/codemirror.net\/LICENSE\n\n",
+ ecmaVersion: 6
+ });
+});
+
module.exports = {ok: blint.success()};
From a02dd4036b4fb6c6aaf7e3e4761496d892dfbc78 Mon Sep 17 00:00:00 2001
From: Adrian Heine
Date: Fri, 23 Sep 2016 22:26:53 +0200
Subject: [PATCH 0225/2085] Remove version blob check
---
test/lint.js | 1 -
1 file changed, 1 deletion(-)
diff --git a/test/lint.js b/test/lint.js
index 10b68a8deb..355b3e1c04 100644
--- a/test/lint.js
+++ b/test/lint.js
@@ -11,7 +11,6 @@ var blint = require("blint");
["src"].forEach(function(dir) {
blint.checkDir(dir, {
browser: true,
- blob: "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: http:\/\/codemirror.net\/LICENSE\n\n",
ecmaVersion: 6
});
});
From 518fb67418baeec4d3eef41976af79cc0ce45e93 Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Mon, 26 Sep 2016 14:12:08 +0200
Subject: [PATCH 0226/2085] Drop the compression helper
The kludge of pulling files from github no longer works now
that we have a build process, and since most people have their
own build pipeline now, I don't feel that supporting on-website
builds is that important anymore.
---
bin/release | 5 -
doc/compress.html | 360 ----------------------------------------------
doc/manual.html | 3 +-
index.html | 6 +-
4 files changed, 4 insertions(+), 370 deletions(-)
delete mode 100644 doc/compress.html
diff --git a/bin/release b/bin/release
index 5b31da8efd..df1fb269c3 100755
--- a/bin/release
+++ b/bin/release
@@ -32,11 +32,6 @@ if (bumpOnly) process.exit(0);
child.exec("bash bin/authors.sh", function(){});
-rewrite("doc/compress.html", function(cmp) {
- return cmp.replace(/HEAD<\/option>/,
- " HEAD \n " + number + " ");
-});
-
rewrite("index.html", function(index) {
return index.replace(/\.zip">\d+\.\d+\.\d+<\/a>/,
".zip\">" + number + "");
diff --git a/doc/compress.html b/doc/compress.html
deleted file mode 100644
index c013d3b744..0000000000
--- a/doc/compress.html
+++ /dev/null
@@ -1,360 +0,0 @@
-
-
-CodeMirror: Compression Helper
-
-
-
-
-
-
-
-
-
-
-Script compression helper
-
- To optimize loading CodeMirror, especially when including a
- bunch of different modes, it is recommended that you combine and
- minify (and preferably also gzip) the scripts. This page makes
- those first two steps very easy. Simply select the version and
- scripts you need in the form below, and
- click Compress to download the minified script
- file.
-
-
-
- Version:
- HEAD
- 5.19.0
- 5.18.2
- 5.18.0
- 5.17.0
- 5.16.0
- 5.15.2
- 5.15.0
- 5.14.2
- 5.14.0
- 5.13.2
- 5.13.0
- 5.12.0
- 5.11.0
- 5.10.0
- 5.9.0
- 5.8.0
- 5.7.0
- 5.6.0
- 5.5.0
- 5.4.0
- 5.3.0
- 5.2.0
- 5.1.0
- 5.0.0
- 4.13
- 4.12
- 4.11
- 4.10
- 4.9
- 4.8
- 4.7
- 4.6
- 4.5
- 4.4
- 4.3
- 4.2
- 4.2
- 4.1
- 4.0
- 3.23
- 3.22
- 3.21
- 3.20
- 3.19
- 3.18
- 3.16
- 3.15
- 3.14
- 3.13
- 3.12
- 3.11
- 3.1
- 3.02
- 3.01
- 3.0
- 2.38
- 2.37
- 2.36
- 2.35
- 2.34
- 2.33
- 2.32
- 2.31
- 2.3
- 2.25
- 2.24
- 2.23
- 2.22
- 2.21
- 2.2
- 2.18
- 2.16
- 2.15
- 2.13
- 2.12
- 2.11
- 2.1
- 2.02
- 2.01
- 2.0
- beta2
- beta1
-
-
-
-
- codemirror.js
-
-
- apl.js
- asn.1.js
- asterisk.js
- asciiarmor.js
- brainfuck.js
- clike.js
- clojure.js
- cmake.js
- cobol.js
- coffeescript.js
- commonlisp.js
- crystal.js
- css.js
- cypher.js
- d.js
- dart.js
- diff.js
- django.js
- dockerfile.js
- dtd.js
- dylan.js
- ebnf.js
- ecl.js
- eiffel.js
- eml.js
- erlang.js
- factor.js
- fcl.js
- forth.js
- fortran.js
- gfm.js
- gas.js
- gherkin.js
- go.js
- groovy.js
- haml.js
- handlebars.js
- haskell.js
- haskell-literate.js
- haxe.js
- htmlembedded.js
- htmlmixed.js
- http.js
- idl.js
- javascript.js
- jinja2.js
- julia.js
- jsx.js
- lua.js
- markdown.js
- mathematica.js
- mbox.js
- mirc.js
- mllike.js
- modelica.js
- mscgen.js
- mumps.js
- nginx.js
- ntriples.js
- octave.js
- oz.js
- pascal.js
- pegjs.js
- perl.js
- php.js
- pig.js
- powershell.js
- properties.js
- protobuf.js
- python.js
- pug.js
- puppet.js
- q.js
- r.js
- rpm.js
- rst.js
- ruby.js
- rust.js
- sass.js
- scala.js
- scheme.js
- shell.js
- sieve.js
- slim.js
- smalltalk.js
- smarty.js
- solr.js
- soy.js
- sparql.js
- swift.js
- spreadsheet.js
- stylus.js
- sql.js
- stex.js
- tcl.js
- textile.js
- tiddlywiki.js
- tiki.js
- toml.js
- tornado.js
- troff.js
- ttcn.js
- ttcn-cfg.js
- turtle.js
- twig.js
- vb.js
- vbscript.js
- velocity.js
- verilog.js
- vhdl.js
- vue.js
- webidl.js
- xml.js
- xquery.js
- yaml.js
- yaml-frontmatter.js
- z80.js
-
-
- active-line.js
- anyword-hint.js
- brace-fold.js
- closebrackets.js
- closetag.js
- colorize.js
- comment.js
- comment-fold.js
- continuecomment.js
- continuelist.js
- css-hint.js
- css-lint.js
- dialog.js
- foldcode.js
- foldgutter.js
- fullscreen.js
- hardwrap.js
- html-hint.js
- html-lint.js
- indent-fold.js
- javascript-hint.js
- javascript-lint.js
- json-lint.js
- jump-to-line.js
- lint.js
- loadmode.js
- markdown-fold.js
- mark-selection.js
- match-highlighter.js
- matchbrackets.js
- matchtags.js
- merge.js
- multiplex.js
- overlay.js
- placeholder.js
- rulers.js
- runmode.js
- runmode.node.js
- runmode-standalone.js
- search.js
- searchcursor.js
- show-hint.js
- simple.js
- simplescrollbars.js
- sql-hint.js
- trailingspace.js
- tern.js
- xml-fold.js
- xml-hint.js
- yaml-lint.js
-
-
- emacs.js
- sublime.js
- vim.js
-
-
-
-
- Compress with UglifyJS
-
-
- Custom code to add to the compressed file:
-
-
-
-
-
diff --git a/doc/manual.html b/doc/manual.html
index 8a0e2dcb81..e97ce07e14 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 636639ec08..3423eab209 100644
--- a/index.html
+++ b/index.html
@@ -97,9 +97,9 @@ This is CodeMirror
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 0227/2085] 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 08a564bd2c..ebda0c28a9 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 0228/2085] 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 1f4b036e12..a6e61d06dc 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 4b75d8cbaf..3c16cc875e 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 e1cb6a0923..fa4c52fef7 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 abe70f09d2..e6a9f5218d 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 0a0f591a94..3fe9a13447 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 101b9acb30..b8a48c0e91 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 85964db7ba..22bd32ffc4 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 005e60e3b0..7742c722a4 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 a3439d0056..2873e0e91d 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 3c5a666e28..99afe7ee50 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 a29444b5d3..075ae41a1b 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 50ea401262..ec8cec6715 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 b6ca8b6f91..3ee79fa358 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 c69f75f9d4..06551eec8a 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 e10d8b9c1a..8de0698c6f 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 fd535178ce..d7ca682b1e 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 7bd6a60a50..ee549d54c8 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 3e8735ec13..2d670ab516 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 dd1369026c..00b4aa08b9 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 fac58a266e..d2aeaa7cef 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 aa1a040fa9..dda26d4bff 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 b219285dfb..57542f2d32 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 92763037c9..9a4944414c 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 150f8a7225..bc3df6c8f1 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 a955aaa197..3f76d70207 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 84aaf397b0..828304d3dc 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 ebda0c28a9..408cc497d1 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 308eb574f2..cf0d4d8185 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 cdef3156bb..61f795572d 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 69d53c0cbe..51597d19fa 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 421ce17a06..4c08f37797 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 d814452e8b..f0fc78fd05 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 b017934a2c..a084e08f8b 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 7b2879247e..0f5941e81a 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 5956093e82..8cf11f38cd 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 eada1a0671..0632be6e3e 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 018bd0e297..688aa1bdd2 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 e81eb58493..b5d2513abe 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 4c1f8e0bf0..0cf5ff320b 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 d114d8943f..fd9bd55408 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 b194ff38b6..4e10a1d8d8 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 b479e25090..75011a34d3 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 5cac95db3e..a80d8c508c 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 283740e2c0..f20d313e6d 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 68d38c90d9..ba004631d3 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 cb2bb2d1d8..ac14fa5458 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 01526d2943..6a8606afcb 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 28fd4153b5..0aee95fe2f 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 62ee034adf..8e423bafd3 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 8e34427330..8cc77b1ab0 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 241327ce77..b3b422aafb 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 7a334e5352..c103a57481 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 ce3c228c8b..c75fcd6186 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 e105afa9e0..640af1f249 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 ed5e918039..15d16cefcb 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 f07357befc..d04807426c 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 776319d022..f12dccf50b 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 7941f930d7..ae48fb5c65 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 478402e668..df2cfd0aac 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 4c479cbd25..b2137583fb 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 bce86ad220..1e9baf41e5 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 a98a18832b..a6e3340db1 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 478f07283e..d48f6eb32a 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 0229/2085] 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 355b3e1c04..cc03ce64c0 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 0230/2085] 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 688aa1bdd2..8b247d5306 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 a80d8c508c..be94b00940 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 0231/2085] Document the refresh event
---
doc/manual.html | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/doc/manual.html b/doc/manual.html
index e97ce07e14..b1377928de 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 0232/2085] 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 b1377928de..65e030c0bf 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 0233/2085] [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 3c28ced329..9dcd822e91 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 0234/2085] 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 a6e61d06dc..4cbaa3096e 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 6bd6553f29..c1c8c11ae8 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 0235/2085] 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 4cbaa3096e..057280c569 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 c1c8c11ae8..584dfe1ec4 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 0236/2085] 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 fa4c52fef7..c1879e7b16 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 3fe9a13447..7ccf119e80 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 b8a48c0e91..5d535abe70 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 22bd32ffc4..c48f2204d5 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 2873e0e91d..cc6eef7bde 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 99afe7ee50..3c166b4855 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 075ae41a1b..a07f2a165d 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 ec8cec6715..d58efe0bcf 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 3ee79fa358..9fa8f6eefb 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 06551eec8a..a8d6a4de13 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 8de0698c6f..095cddef3f 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 d7ca682b1e..b9abd2fc40 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 ee549d54c8..90650ff059 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 2d670ab516..97a30d6d97 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 00b4aa08b9..38c5342ff2 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 d2aeaa7cef..b8024d920d 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 dda26d4bff..7d22101469 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 57542f2d32..a7ec22fe89 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 9a4944414c..8530aef315 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 3f76d70207..55432c0812 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 828304d3dc..04be851246 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 408cc497d1..5fe10ea39f 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 cf0d4d8185..a2ca1c6803 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 51597d19fa..80e91f847f 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 4c08f37797..e244a2d168 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 f0fc78fd05..2c1755eb9f 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 a084e08f8b..6cc3e9111f 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 0f5941e81a..1b9564ef4f 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 8cf11f38cd..66bc80010c 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 0632be6e3e..8fd6d47b99 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 8b247d5306..7a3a412211 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 b5d2513abe..73b2e0b8e8 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 0cf5ff320b..d315e7ba99 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 fd9bd55408..63acacfc83 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 4e10a1d8d8..647c07791e 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 75011a34d3..63e3771b1f 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 be94b00940..4a80a7666c 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 f20d313e6d..554cf80977 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 ba004631d3..c02fe062f1 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 ac14fa5458..881f39eb46 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 6a8606afcb..e39bc30392 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 0aee95fe2f..2a05d7c545 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 8e423bafd3..6ce2ae58e7 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 8cc77b1ab0..0c7cf5bba1 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 b3b422aafb..23b2381b95 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 c103a57481..f15cefb29a 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 c75fcd6186..63371954e1 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 640af1f249..0e310a5d2d 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 15d16cefcb..065a463b5b 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 d04807426c..cf7b8244a9 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 f12dccf50b..4c365f4c9b 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 ae48fb5c65..ce678a82a4 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 df2cfd0aac..465dbb5a5e 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 b2137583fb..e667a9f3c0 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 1e9baf41e5..d98393af90 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 a6e3340db1..c94de8bd2c 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 d48f6eb32a..d743f994e5 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 0237/2085] =?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 0c7cf5bba1..2a7502cb8e 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 0238/2085] 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 e6a9f5218d..ee52daffac 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 5d535abe70..d34db2b42e 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 7742c722a4..8c4c60b058 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 cc6eef7bde..30a63a613a 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 3c166b4855..e85d2a02fd 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 a07f2a165d..a85fffe9a5 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 9fa8f6eefb..8380de82e5 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 90650ff059..909ffcb185 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 97a30d6d97..fe39ed727f 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 38c5342ff2..5a9bd2cfd5 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 b8024d920d..43e996fb68 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 7d22101469..5d920830b3 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 a7ec22fe89..dd7d185308 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 8530aef315..8e695473ba 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 55432c0812..2831d2a03d 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 04be851246..260ffa5df2 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 5fe10ea39f..784f195b3e 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 a2ca1c6803..ea19d5ba2a 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 80e91f847f..59ef55fa9d 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 e244a2d168..17f6f599cd 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 6cc3e9111f..e51a90a119 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 8fd6d47b99..f54e7eaa56 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 7a3a412211..93b57577da 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 63acacfc83..267ee7182c 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 647c07791e..e4e6943f55 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 63e3771b1f..5688836ee0 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 c02fe062f1..fcb2c1e109 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 e39bc30392..c6692d4c5a 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 2a7502cb8e..83938cf4c1 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 23b2381b95..e832e8c483 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 f15cefb29a..4250288ef3 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 63371954e1..120af2a45c 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 cf7b8244a9..92dbc69062 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 d98393af90..e65881d4ca 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 d743f994e5..f50da343a9 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 0239/2085] 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 909ffcb185..7e17002d15 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 260ffa5df2..f609d912e0 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 59ef55fa9d..fbb327f034 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 17f6f599cd..3193aa6890 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 0240/2085] 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 d58efe0bcf..db9463cd04 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 8380de82e5..e3f912403a 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 3193aa6890..a150f05d16 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 4a80a7666c..cbfa657b38 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 0241/2085] 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 f54e7eaa56..dcf020adf7 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 0242/2085] 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 f609d912e0..b63f9bd26b 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 0243/2085] [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 1b272862b8..8db34cd9fa 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 0244/2085] [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 efeed7f154..30f1428e3a 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 0245/2085] [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 fe0bab67ed..4197666a44 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 0246/2085] Add lib/codemirror.js to .gitignore
---
.gitignore | 1 +
1 file changed, 1 insertion(+)
diff --git a/.gitignore b/.gitignore
index f91c241f20..01019cf150 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 0247/2085] [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 57f979ae69..b2ef62913c 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 0248/2085] 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 18b0bf70db..d7821d17df 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 0249/2085] 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 1b9564ef4f..3a107bb466 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 0250/2085] 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 a7658311e7..de2c66e830 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 0251/2085] 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 fe39ed727f..0b0125d1e9 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 124575c724..628651c786 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 0252/2085] [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 65f85f3a98..8ff38b81bf 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 0253/2085] [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 db9f54de16..fada04c038 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 0254/2085] [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 9dd44574fc..3dcce8d3b1 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 0255/2085] [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 3c9ef6b989..803a5ba27b 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 0256/2085] [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 a684e8c233..570b4e2419 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 0257/2085] [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 6dd9634659..01b6e664b7 100644
--- a/mode/r/index.html
+++ b/mode/r/index.html
@@ -31,47 +31,50 @@
R mode
-# Code from http://www.mayin.org/ajayshah/KB/R/
+X <- list(height = 5.4, weight = 54)
+cat("Printing objects: "); print(X)
+print("Accessing individual elements:")
+cat(sprintf("Your height is %s and your weight is %s\n", X$height, X$weight))
-# FIRST LEARN ABOUT LISTS --
-X = list(height=5.4, weight=54)
-print("Use default printing --")
-print(X)
-print("Accessing individual elements --")
-cat("Your height is ", X$height, " and your weight is ", X$weight, "\n")
-
-# FUNCTIONS --
+# Functions:
square <- function(x) {
- return(x*x)
+ return(x * x)
}
-cat("The square of 3 is ", square(3), "\n")
+cat(sprintf("The square of 3 is %s\n", square(3)))
- # default value of the arg is set to 5.
-cube <- function(x=5) {
- return(x*x*x);
+# In R, the last expression in a function is, by default, what is
+# returned. The idiomatic way to write the function is:
+square <- function(x) {
+ x * x
}
-cat("Calling cube with 2 : ", cube(2), "\n") # will give 2^3
-cat("Calling cube : ", cube(), "\n") # will default to 5^3.
+# or, for functions with short content:
+square <- function(x) x * x
-# LEARN ABOUT FUNCTIONS THAT RETURN MULTIPLE OBJECTS --
-powers <- function(x) {
- parcel = list(x2=x*x, x3=x*x*x, x4=x*x*x*x);
- return(parcel);
-}
+# Function arguments with default values:
+cube <- function(x = 5) x * x * x
+cat(sprintf("Calling cube with 2 : %s\n", cube(2)) # will give 2^3
+cat(sprintf("Calling cube : %s\n", cube()) # will default to 5^3.
-X = powers(3);
-print("Showing powers of 3 --"); print(X);
+powers <- function(x) list(x2 = x*x, x3 = x*x*x, x4 = x*x*x*x)
-# WRITING THIS COMPACTLY (4 lines instead of 7)
+cat("Powers of 3: "); print(powers(3))
-powerful <- function(x) {
- return(list(x2=x*x, x3=x*x*x, x4=x*x*x*x));
-}
-print("Showing powers of 3 --"); print(powerful(3));
+# Data frames
+df <- data.frame(letters = letters[1:5], '#letter' = 1:5)
+print(df$letters)
+print(df$`#letter`)
-# In R, the last expression in a function is, by default, what is
-# returned. So you could equally just say:
-powerful <- function(x) {list(x2=x*x, x3=x*x*x, x4=x*x*x*x)}
+# Operators:
+m1 <- matrix(1:6, 2, 3)
+m2 <- m1 %*% t(m1)
+cat("Matrix product: "); print(m2)
+
+# Assignments:
+a <- 1
+b <<- 2
+c = 3
+4 -> d
+5 ->> e
+
@@ -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 0270/2085] 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 a8d6a4de13..4e016a2515 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 5688836ee0..4ece080e49 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 0271/2085] [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 ea7bd01d84..b6be4cd508 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 0272/2085] [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 b6be4cd508..e56e3dd8c6 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 0273/2085] [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 af7fce2a82..7c47bcd096 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 0274/2085] 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 e51a90a119..57e4ea533b 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 0275/2085] 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 cc94cafb8c..2cfeabe426 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 65e030c0bf..baa8356fa0 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 0276/2085] 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 2cfeabe426..2f53406edc 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 baa8356fa0..f9e9946533 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 0277/2085] add quickstart to project readme
---
README.md | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/README.md b/README.md
index ff2622a2b8..84466293b4 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 0278/2085] [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 e3a452766d..c1f1702f92 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 0279/2085] 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 fbb327f034..cdaa825488 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 0280/2085] 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 ada8c3f9cf..e2cb74a557 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 f368485cfe..b243546569 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 f9e9946533..36354b71f3 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 5a0c6b715c..cfc366f3e2 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 3423eab209..f4e56a70c8 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 057280c569..74040d5658 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 0281/2085] 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 36354b71f3..0cff1be813 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 74040d5658..7dbf4751e1 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 0282/2085] 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 903d9f55f8..0000000000
--- 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 0283/2085] Fix installation instruction
Closes #4333
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 84466293b4..1b3309881c 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 0284/2085] 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 df1fb269c3..b31d511857 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 2831d2a03d..16dc032155 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 0285/2085] 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 b243546569..a7e995ab4b 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 0cff1be813..2944f0d5e5 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 f4e56a70c8..1d1bb3c8a2 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 7dbf4751e1..8a58fbded2 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 16dc032155..4335965626 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 0286/2085] 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 2944f0d5e5..6ef27689ce 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 8a58fbded2..1d07d681b8 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 4335965626..57fcffa04e 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 0287/2085] 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 2f53406edc..b0f78c87fd 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 0288/2085] [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 e56e3dd8c6..b75732034e 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 0289/2085] [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 b0f78c87fd..e9700821b1 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 0290/2085] [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 47e9a31ce8..cb0684b984 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 b7015e599c..59c0ecdbec 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 0291/2085] [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 cb0684b984..8faf7677df 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 f8089af501..c0eab6b82f 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 0292/2085] 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 e9700821b1..72526d0179 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 0293/2085] [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 10cad8d9f1..085f909f5f 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 0294/2085] [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 8c28ee6200..b1d7e7c2e6 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 d61b470652..2cd6f42703 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 e319a3997e..956c5758e1 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 80173de082..edf9da09af 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 f9a50f0af2..950aeca1f9 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 0295/2085] [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 7513dcede2..1f6ecb9f99 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 0296/2085] [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 72526d0179..e7772ace66 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 0297/2085] [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 e7772ace66..1d7a5cccb0 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 0298/2085] [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 1d7a5cccb0..9bd086580b 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 0299/2085] [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 730054473a..151cc8205f 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 0300/2085] 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 9bd086580b..0c54a66464 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 6ef27689ce..e74ec36200 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 b63f9bd26b..8aa2a437b0 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 4ece080e49..f62672c494 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 0301/2085] 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 0c54a66464..f0d746449d 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 0302/2085] [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 1f6ecb9f99..460cdb3b1c 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 0303/2085] [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 004de4431c..6c40bf20ee 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 0304/2085] [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 9dcd822e91..329470664c 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 0000000000..8c8ee2f65a
--- /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 8ac33c0913..cfa3bb71fb 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 0305/2085] [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 329470664c..43ab7c8fb4 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 8c8ee2f65a..786b89e299 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 0306/2085] [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 8db34cd9fa..e52c9f00f8 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 0307/2085] [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 e52c9f00f8..dc2a7f6a97 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 0308/2085] [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 6c40bf20ee..0174210b55 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 0309/2085] Simplify build instructions in README
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 1b3309881c..3328e3bdfb 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 0310/2085] [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 1d9ca4334a..9b2490014c 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 34cf96caff..76d0d08296 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 0311/2085] [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 e3cbae54cf..32ced3e9de 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 0312/2085] [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 b75732034e..985287f475 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 0313/2085] [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 cdaa825488..594f17f663 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 0314/2085] 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 594f17f663..524d571e5e 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 0315/2085] 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 e74ec36200..ecfe3071f7 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 8aa2a437b0..7efaf20e2c 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 fcb2c1e109..27b62d1945 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 0316/2085] [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 7e17002d15..a3dc622cf1 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 784f195b3e..0b96f4cfda 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 524d571e5e..a7254b12c2 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 0317/2085] 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 e667a9f3c0..29fd4c5981 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 f50da343a9..b8fa78ac48 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 0318/2085] [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 a7254b12c2..c385bea12e 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 0319/2085] =?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 465dbb5a5e..349fae07d3 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 0320/2085] [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 3dcce8d3b1..6aedc360b0 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 2f43a170ca..37ecb4bbfe 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 0321/2085] [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 a166f72b10..34570bb889 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 6eea5553db..703a07a779 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 0322/2085] 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 c385bea12e..d76058ffd5 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 0323/2085] 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 e2cb74a557..09fb4cc852 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 a7e995ab4b..2404815f88 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 ecfe3071f7..be834f0f10 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 cfc366f3e2..5880469189 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 1d1bb3c8a2..7164296018 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 1d07d681b8..3235a47d8d 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 57fcffa04e..78c6da49d3 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 0324/2085] 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 be834f0f10..05c49718c9 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 3235a47d8d..d2e45f2f87 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 78c6da49d3..64b647b5d2 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 0325/2085] [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 e1b9ed3753..4e96010a65 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 0326/2085] [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 4e96010a65..4a9f99a072 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 0327/2085] [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 fb1f99c631..5b407a9285 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 0328/2085] 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 4c365f4c9b..6812d4fdeb 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 0329/2085] 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 6812d4fdeb..6a84914035 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 0330/2085] [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 580c306f15..9fd75c6d27 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 0000000000..1a962de3e7
--- /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 ][tag h1][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 cfa3bb71fb..6ddf5b1021 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 0331/2085] [npmignore] add files that do nothing when
installed with npm
---
.npmignore | 2 ++
1 file changed, 2 insertions(+)
diff --git a/.npmignore b/.npmignore
index 5ed053f893..de3a24080b 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 0332/2085] [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 98fce4d302..171e692e67 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 0333/2085] 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 171e692e67..c5d2906bc0 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 0334/2085] [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 3eec1e5762..57cf6e8525 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 0335/2085] 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 ea19d5ba2a..97587f73e4 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 0336/2085] [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 b0b3f61af2..68db3f4d3b 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 741f6c45a4..7a273a77ee 100644
--- a/demo/activeline.html
+++ b/demo/activeline.html
@@ -65,14 +65,26 @@
Active Line Demo
Styling the current cursor line.
+
Enable styleActiveSelected option
+
diff --git a/doc/manual.html b/doc/manual.html
index 05c49718c9..b5b451ae5c 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 0337/2085] [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 68db3f4d3b..aa295d0d86 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 7a273a77ee..86c8c18e1d 100644
--- a/demo/activeline.html
+++ b/demo/activeline.html
@@ -65,26 +65,24 @@
Active Line Demo
Styling the current cursor line.
-
Enable styleActiveSelected option
+
Enable nonEmpty option
diff --git a/doc/manual.html b/doc/manual.html
index b5b451ae5c..0065790810 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 0338/2085] =?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 d2e45f2f87..e2b97a9cd1 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 584dfe1ec4..9a17b24ff7 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 0339/2085] [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 a717745897..b9f3925951 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 0340/2085] [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 6d15f1ae3f..0d6239c2bc 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 0341/2085] [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 985287f475..a1d5a388e5 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 662cd03c04..8d83a01807 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 0342/2085] [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 b9f3925951..2ad7a1e970 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 0343/2085] [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 8faf7677df..47364448f1 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 0344/2085] 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 0065790810..c6745e3ce6 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 7efaf20e2c..144e4773f6 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 0345/2085] [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 6aedc360b0..86c017c3f0 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 0346/2085] [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 86c017c3f0..4cc1dc6890 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 0347/2085] 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 97587f73e4..dffc577be6 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 0348/2085] [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 f0d746449d..352e27dc6f 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 0349/2085] 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 a3dc622cf1..3c482c599f 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 0350/2085] 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 a85fffe9a5..2026a3b0a1 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 4e016a2515..17d5a069e5 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 d76058ffd5..57114af681 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 a150f05d16..28b3327376 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 c94de8bd2c..2fb90914a8 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 0351/2085] 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 d7821d17df..2a6a262282 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 0352/2085] 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 41765e75dd..971829d938 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 0353/2085] 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 971829d938..a38865d4ae 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 0354/2085] [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 2ad7a1e970..10419bf9de 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 a38865d4ae..2caefea439 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 0355/2085] Link to elixir mode
---
mode/index.html | 1 +
1 file changed, 1 insertion(+)
diff --git a/mode/index.html b/mode/index.html
index 3a2fe5513e..c0001a53ee 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 0356/2085] 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 57114af681..4c7b58ebde 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 0357/2085] [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 10419bf9de..fe9b805f1f 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 0358/2085] [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 fe9b805f1f..10fc95bd8a 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 0359/2085] [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 10fc95bd8a..c185106ce4 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 0360/2085] 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 09fb4cc852..9b9b3355ba 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 2404815f88..f3eb79ff5c 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 c6745e3ce6..704984fc71 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 5880469189..2dfd1fe482 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 7164296018..06fdc1567f 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 e2b97a9cd1..c007b2a87c 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 64b647b5d2..8da8f48e3a 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 0361/2085] 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 704984fc71..98c160946f 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 06fdc1567f..dd4b1ded05 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 c007b2a87c..0c565e9b2b 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 8da8f48e3a..429cfe94e7 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 0362/2085] Remove link to google group
---
index.html | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/index.html b/index.html
index dd4b1ded05..9812829e52 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 0363/2085] 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 cc03ce64c0..12146ffd28 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 0364/2085] 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 93b57577da..7583c3424e 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 0365/2085] 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 4250288ef3..15ec28498f 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 0366/2085] 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 f94df9e21a..caa7546c1e 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 0367/2085] 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 27b62d1945..006598f1fe 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 0368/2085] [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 30f1428e3a..4310f9fb6b 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 0369/2085] [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 a1d5a388e5..90de4ee795 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 0370/2085] [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 721933b01c..daa798722e 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 0371/2085] [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 9fd75c6d27..3876333f94 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 1a962de3e7..1a4c6c934f 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 0372/2085] [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 91ed6cef29..01d867932a 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 0373/2085] [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 bf0b8a674f..4d0be609c4 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 0374/2085] [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 3876333f94..3e9c04a49e 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 0375/2085] [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 3e9c04a49e..b9eec59a4c 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 1a4c6c934f..9e265c1b0c 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 0376/2085] 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 47364448f1..bb60502416 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 0377/2085] [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 7c47bcd096..62b99c1ba8 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 98c160946f..3c252381ba 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 0378/2085] [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 2caefea439..56b90e3cfe 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 0379/2085] Update LICENSE
---
LICENSE | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/LICENSE b/LICENSE
index 766132177a..1bca6bfed4 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 0380/2085] [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 dc2a7f6a97..2429ba1655 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 0381/2085] [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 4cc1dc6890..1aeb34414c 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 0382/2085] [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 ba29484d6c..a6ac74f0ca 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 b3b0b7ca6b..1ce3d87c79 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 3c252381ba..7acc872b8b 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 0383/2085] 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 a6ac74f0ca..7da34a42da 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 0384/2085] 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 12146ffd28..502706de8e 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 0385/2085] [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 7da34a42da..74199ff059 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 0386/2085] [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 352e27dc6f..b3a9af405c 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 0387/2085] 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 de3a24080b..f23ca7195d 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 0388/2085] 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 f3eb79ff5c..9074c1b0c5 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 7acc872b8b..f765185eb3 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 0c565e9b2b..fa82dd11dc 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 429cfe94e7..c16f377119 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 0389/2085] [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 b3a9af405c..a8132b605d 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 0390/2085] 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 56b90e3cfe..c02eb06c31 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 0391/2085] [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 c185106ce4..17890dcc12 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 0392/2085] [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 b2ef62913c..53a660b521 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 0393/2085] [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 c5d2906bc0..3d112ab961 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 c5c19c0a23..57f16485e1 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 0394/2085] 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 9b9b3355ba..866c78f842 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 9074c1b0c5..07ed0295e5 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 f765185eb3..a5eb223355 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 2dfd1fe482..69c0e66b10 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 9812829e52..df98ffd096 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 fa82dd11dc..f7b617cd22 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 c16f377119..b6d3d488ad 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 0395/2085] [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 570b4e2419..a636387395 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 0396/2085] [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 4310f9fb6b..b539d84aa6 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 0397/2085] [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 9f4a790221..6305649e5c 100644
--- a/mode/sass/index.html
+++ b/mode/sass/index.html
@@ -7,6 +7,7 @@
+
@@ -58,7 +59,8 @@
Sass mode
diff --git a/mode/sass/sass.js b/mode/sass/sass.js
index 6973ece292..d2d2502968 100644
--- a/mode/sass/sass.js
+++ b/mode/sass/sass.js
@@ -3,19 +3,33 @@
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
- mod(require("../../lib/codemirror"));
+ mod(require("../../lib/codemirror"), require("../css/css"));
else if (typeof define == "function" && define.amd) // AMD
- define(["../../lib/codemirror"], mod);
+ define(["../../lib/codemirror", "../css/css"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
"use strict";
CodeMirror.defineMode("sass", function(config) {
+ var cssMode = CodeMirror.mimeModes["text/css"];
+ var propertyKeywords = cssMode.propertyKeywords || {},
+ colorKeywords = cssMode.colorKeywords || {},
+ valueKeywords = cssMode.valueKeywords || {};
+
function tokenRegexp(words) {
return new RegExp("^" + words.join("|"));
}
+ function propWithVendorPrefix(keyset, string) {
+ if (string.indexOf('-') !== 0) {
+ return false;
+ }
+
+ var unvendored = string.substring(string.indexOf("-", 1) + 1);
+ return keyset.hasOwnProperty(unvendored);
+ }
+
var keywords = ["true", "false", "null", "auto"];
var keywordsRegexp = new RegExp("^" + keywords.join("|"));
@@ -25,6 +39,8 @@ CodeMirror.defineMode("sass", function(config) {
var pseudoElementsRegexp = /^::?[a-zA-Z_][\w\-]*/;
+ var word;
+
function urlTokens(stream, state) {
var ch = stream.peek();
@@ -151,10 +167,10 @@ CodeMirror.defineMode("sass", function(config) {
stream.next();
if (stream.match(/^[\w-]+/)) {
indent(state);
- return "atom";
+ return "qualifier";
} else if (stream.peek() === "#") {
indent(state);
- return "atom";
+ return "tag";
}
}
@@ -163,11 +179,11 @@ CodeMirror.defineMode("sass", function(config) {
// ID selectors
if (stream.match(/^[\w-]+/)) {
indent(state);
- return "atom";
+ return "builtin";
}
if (stream.peek() === "#") {
indent(state);
- return "atom";
+ return "tag";
}
}
@@ -220,31 +236,42 @@ CodeMirror.defineMode("sass", function(config) {
// Indent Directives
if (stream.match(/^@(else if|if|media|else|for|each|while|mixin|function)/)) {
indent(state);
- return "meta";
+ return "def";
}
// Other Directives
if (ch === "@") {
stream.next();
stream.eatWhile(/[\w-]/);
- return "meta";
+ return "def";
}
if (stream.eatWhile(/[\w-]/)){
if(stream.match(/ *: *[\w-\+\$#!\("']/,false)){
- return "property";
+ word = stream.current().toLowerCase();
+ var prop = state.prevProp + "-" + word;
+ if (propertyKeywords.hasOwnProperty(prop)) {
+ return "property";
+ } else if (propertyKeywords.hasOwnProperty(word)) {
+ state.prevProp = word;
+ return "property";
+ } else if (propWithVendorPrefix(propertyKeywords, word)) {
+ return "property";
+ }
+ return "tag";
}
else if(stream.match(/ *:/,false)){
indent(state);
state.cursorHalf = 1;
- return "atom";
+ state.prevProp = stream.current().toLowerCase();
+ return "property";
}
else if(stream.match(/ *,/,false)){
- return "atom";
+ return "tag";
}
else{
indent(state);
- return "atom";
+ return "tag";
}
}
@@ -309,20 +336,18 @@ CodeMirror.defineMode("sass", function(config) {
if(!stream.peek()){
state.cursorHalf = 0;
}
- return "variable-3";
+ return "variable-2";
}
// bang character for !important, !default, etc.
if (ch === "!") {
stream.next();
- if(!stream.peek()){
- state.cursorHalf = 0;
- }
+ state.cursorHalf = 0;
return stream.match(/^[\w]+/) ? "keyword": "operator";
}
if (stream.match(opRegexp)){
- if(!stream.peek()){
+ if(!stream.peek() || stream.match(/\s+$/, false)){
state.cursorHalf = 0;
}
return "operator";
@@ -333,7 +358,17 @@ CodeMirror.defineMode("sass", function(config) {
if(!stream.peek()){
state.cursorHalf = 0;
}
- return "attribute";
+ word = stream.current().toLowerCase();
+ if (valueKeywords.hasOwnProperty(word)) {
+ return "atom";
+ } else if (colorKeywords.hasOwnProperty(word)) {
+ return "keyword";
+ } else if (propertyKeywords.hasOwnProperty(word)) {
+ state.prevProp = stream.current().toLowerCase();
+ return "property";
+ } else {
+ return "tag";
+ }
}
//stream.eatSpace();
diff --git a/mode/sass/test.js b/mode/sass/test.js
new file mode 100644
index 0000000000..56ba006415
--- /dev/null
+++ b/mode/sass/test.js
@@ -0,0 +1,103 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
+(function() {
+ var mode = CodeMirror.getMode({indentUnit: 2}, "sass");
+ // Since Sass has an indent-based syntax, is almost impossible to test correctly the indentation in all cases.
+ // So disable it for tests.
+ mode.indent = undefined;
+ function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)); }
+
+ MT("comment",
+ "[comment // this is a comment]",
+ "[comment also this is a comment]")
+
+ MT("comment_multiline",
+ "[comment /* this is a comment]",
+ "[comment also this is a comment]")
+
+ MT("variable",
+ "[variable-2 $page-width][operator :] [number 800][unit px]")
+
+ MT("global_attributes",
+ "[tag body]",
+ " [property font][operator :]",
+ " [property family][operator :] [atom sans-serif]",
+ " [property size][operator :] [number 30][unit em]",
+ " [property weight][operator :] [atom bold]")
+
+ MT("scoped_styles",
+ "[builtin #contents]",
+ " [property width][operator :] [variable-2 $page-width]",
+ " [builtin #sidebar]",
+ " [property float][operator :] [atom right]",
+ " [property width][operator :] [variable-2 $sidebar-width]",
+ " [builtin #main]",
+ " [property width][operator :] [variable-2 $page-width] [operator -] [variable-2 $sidebar-width]",
+ " [property background][operator :] [variable-2 $primary-color]",
+ " [tag h2]",
+ " [property color][operator :] [keyword blue]")
+
+ // Sass allows to write the colon as first char instead of a "separator".
+ // :color red
+ // Not supported
+ // MT("property_syntax",
+ // "[qualifier .foo]",
+ // " [operator :][property color] [keyword red]")
+
+ MT("import",
+ "[def @import] [string \"sass/variables\"]",
+ // Probably it should parsed as above: as a string even without the " or '
+ // "[def @import] [string sass/baz]"
+ "[def @import] [tag sass][operator /][tag baz]")
+
+ MT("def",
+ "[def @if] [variable-2 $foo] [def @else]")
+
+ MT("tag_on_more_lines",
+ "[tag td],",
+ "[tag th]",
+ " [property font-family][operator :] [string \"Arial\"], [atom serif]")
+
+ MT("important",
+ "[qualifier .foo]",
+ " [property text-decoration][operator :] [atom none] [keyword !important]",
+ "[tag h1]",
+ " [property font-size][operator :] [number 2.5][unit em]")
+
+ MT("selector",
+ // SCSS uses variable-3
+ // "[tag h1]:[variable-3 before],",
+ // "[tag h2]:[variable-3 before]",
+ "[tag h1][keyword :before],",
+ "[tag h2][keyword :before]",
+ " [property content][operator :] [string \"::\"]")
+
+ MT("definition_mixin_equal",
+ "[variable-2 $defined-bs-type][operator :] [atom border-box] [keyword !default]",
+ "[meta =bs][operator (][variable-2 $bs-type][operator :] [variable-2 $defined-bs-type][operator )]",
+ // The vendor prefix is highlighted as "meta" in CSS mode
+ // " [meta -webkit-][property box-sizing][operator :] [variable-2 $bs-type]",
+ " [property -webkit-box-sizing][operator :] [variable-2 $bs-type]",
+ " [property box-sizing][operator :] [variable-2 $bs-type]")
+
+ MT("definition_mixin_with_space",
+ "[variable-2 $defined-bs-type][operator :] [atom border-box] [keyword !default]",
+ "[def @mixin] [tag bs][operator (][variable-2 $bs-type][operator :] [variable-2 $defined-bs-type][operator )] ",
+ // The vendor prefix is highlighted as "meta" in CSS mode
+ // " [meta -moz-][property box-sizing][operator :] [variable-2 $bs-type]",
+ " [property -moz-box-sizing][operator :] [variable-2 $bs-type]",
+ " [property box-sizing][operator :] [variable-2 $bs-type]")
+
+ MT("numbers_start_dot_include_plus",
+ // The % is not highlighted correctly
+ // "[meta =button-links][operator (][variable-2 $button-base][operator :] [atom darken][operator (][variable-2 $color11], [number 10][unit %][operator )][operator )]",
+ "[meta =button-links][operator (][variable-2 $button-base][operator :] [atom darken][operator (][variable-2 $color11], [number 10][operator %))]",
+ " [property padding][operator :] [number .3][unit em] [number .6][unit em]",
+ " [variable-3 +border-radius][operator (][number 8][unit px][operator )]",
+ " [property background-color][operator :] [variable-2 $button-base]")
+
+ MT("include",
+ "[qualifier .bar]",
+ " [def @include] [tag border-radius][operator (][number 8][unit px][operator )]")
+})();
diff --git a/test/index.html b/test/index.html
index 6ddf5b1021..deeb7781fb 100644
--- a/test/index.html
+++ b/test/index.html
@@ -31,6 +31,7 @@
+
@@ -121,6 +122,7 @@
Test Suite
+
From 15aed69a5025cbda52960610eb54082f03f5a915 Mon Sep 17 00:00:00 2001
From: Geordie Hall
Date: Mon, 23 Jan 2017 15:35:59 +1100
Subject: [PATCH 0398/2085] [css mode] Support lineComment option, enable it
for LESS, SCSS
---
mode/css/css.js | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/mode/css/css.js b/mode/css/css.js
index 90de4ee795..16d6f05b9f 100644
--- a/mode/css/css.js
+++ b/mode/css/css.js
@@ -28,6 +28,7 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
colorKeywords = parserConfig.colorKeywords || {},
valueKeywords = parserConfig.valueKeywords || {},
allowNested = parserConfig.allowNested,
+ lineComment = parserConfig.lineComment,
supportsAtComponent = parserConfig.supportsAtComponent === true;
var type, override;
@@ -407,6 +408,7 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
electricChars: "}",
blockCommentStart: "/*",
blockCommentEnd: "*/",
+ lineComment: lineComment,
fold: "brace"
};
});
@@ -730,6 +732,7 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
valueKeywords: valueKeywords,
fontProperties: fontProperties,
allowNested: true,
+ lineComment: "//",
tokenHooks: {
"/": function(stream, state) {
if (stream.eat("/")) {
@@ -772,6 +775,7 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
valueKeywords: valueKeywords,
fontProperties: fontProperties,
allowNested: true,
+ lineComment: "//",
tokenHooks: {
"/": function(stream, state) {
if (stream.eat("/")) {
From 268cf9905509b87de33a00342ee1cce9a1bdc5cd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Oskar=20Segersv=C3=A4rd?=
Date: Mon, 23 Jan 2017 15:15:31 +0100
Subject: [PATCH 0399/2085] [java mode] Make @interface a defining keyword
---
mode/clike/clike.js | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/mode/clike/clike.js b/mode/clike/clike.js
index cad2d5145c..bba2ec9056 100644
--- a/mode/clike/clike.js
+++ b/mode/clike/clike.js
@@ -425,16 +425,19 @@ CodeMirror.defineMode("clike", function(config, parserConfig) {
"do else enum extends final finally float 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"),
+ "try volatile while @interface"),
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"),
+ defKeywords: words("class interface package 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,
hooks: {
"@": function(stream) {
+ // Don't match the @interface keyword.
+ if (stream.match('interface', false)) return false;
+
stream.eatWhile(/[\w\$_]/);
return "meta";
}
From 524f54a85c963f174cf4fc9c430da85b58b3e1cf Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Arthur=20M=C3=BCller?=
Date: Tue, 31 Jan 2017 15:43:59 +0100
Subject: [PATCH 0400/2085] [go mode] Add builtin delete function to atoms
---
mode/go/go.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/mode/go/go.js b/mode/go/go.js
index 803a5ba27b..c4e382db9e 100644
--- a/mode/go/go.js
+++ b/mode/go/go.js
@@ -28,7 +28,7 @@ CodeMirror.defineMode("go", function(config) {
var atoms = {
"true":true, "false":true, "iota":true, "nil":true, "append":true,
- "cap":true, "close":true, "complex":true, "copy":true, "imag":true,
+ "cap":true, "close":true, "complex":true, "copy":true, "delete":true, "imag":true,
"len":true, "make":true, "new":true, "panic":true, "print":true,
"println":true, "real":true, "recover":true
};
From 1ec2263e2f55f2c538a1b47301f74977e24f5bbd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Arthur=20M=C3=BCller?=
Date: Wed, 1 Feb 2017 10:05:28 +0100
Subject: [PATCH 0401/2085] [go mode] Added missing keyword 'rune'
Reference: https://golang.org/pkg/builtin/#rune
---
mode/go/go.js | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/mode/go/go.js b/mode/go/go.js
index c4e382db9e..8896b57fb9 100644
--- a/mode/go/go.js
+++ b/mode/go/go.js
@@ -23,7 +23,8 @@ CodeMirror.defineMode("go", function(config) {
"bool":true, "byte":true, "complex64":true, "complex128":true,
"float32":true, "float64":true, "int8":true, "int16":true, "int32":true,
"int64":true, "string":true, "uint8":true, "uint16":true, "uint32":true,
- "uint64":true, "int":true, "uint":true, "uintptr":true, "error": true
+ "uint64":true, "int":true, "uint":true, "uintptr":true, "error": true,
+ "rune":true
};
var atoms = {
From d0e3b2e727c41aa4fd89fbad0adfb3815339174c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Arthur=20M=C3=BCller?=
Date: Wed, 1 Feb 2017 10:17:02 +0100
Subject: [PATCH 0402/2085] Added license name
Added the name of the license in the first line inorder to let Github recognize the License.
---
LICENSE | 2 ++
1 file changed, 2 insertions(+)
diff --git a/LICENSE b/LICENSE
index 1bca6bfed4..ff7db4b99f 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,3 +1,5 @@
+MIT License
+
Copyright (C) 2017 by Marijn Haverbeke and others
Permission is hereby granted, free of charge, to any person obtaining a copy
From 73044ab7cc377d4e039400d9ca23d044169cde0b Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Wed, 1 Feb 2017 19:08:40 +0100
Subject: [PATCH 0403/2085] [julia mode] Properly indent elseif lines
Closes #4519
---
mode/julia/julia.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/mode/julia/julia.js b/mode/julia/julia.js
index 0174210b55..8fc999f5f9 100644
--- a/mode/julia/julia.js
+++ b/mode/julia/julia.js
@@ -388,7 +388,7 @@ CodeMirror.defineMode("julia", function(config, parserConf) {
indent: function(state, textAfter) {
var delta = 0;
if ( textAfter === ']' || textAfter === ')' || textAfter === "end" ||
- textAfter === "else" || textAfter === "catch" ||
+ textAfter === "else" || textAfter === "catch" || textAfter === "elseif" ||
textAfter === "finally" ) {
delta = -1;
}
From d8166067bed6935d1a1db158acf58cc1a595ae31 Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Thu, 2 Feb 2017 11:36:18 +0100
Subject: [PATCH 0404/2085] Document the fact that skipTo can skip to any
string
---
doc/manual.html | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/doc/manual.html b/doc/manual.html
index a5eb223355..496494c4fc 100644
--- a/doc/manual.html
+++ b/doc/manual.html
@@ -3141,11 +3141,11 @@ Writing CodeMirror Modes
white-space.
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 0405/2085] [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 1aeb34414c..fde258fef4 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 0406/2085] [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 fde258fef4..483422a90a 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 0407/2085] [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 1b46bc8284..4520c14e30 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 0408/2085] [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 4520c14e30..7d4c3ed2fd 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 0409/2085] 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 703a07a779..d6b0ad16b2 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 0410/2085] 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 de2c66e830..07d029fba8 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 0411/2085] 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 73b2e0b8e8..4f5e4c5594 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 f62672c494..4e8a3f839a 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 c61d4c1f36..01736450f3 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 07d029fba8..37894190db 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 0412/2085] 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 144e4773f6..caf62b19f1 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 120af2a45c..a47ede93fa 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 37894190db..8a064b6c97 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 0413/2085] 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 4e8a3f839a..49581e656e 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 6a84914035..7d08e36596 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 0414/2085] 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 caf62b19f1..1cce4f3d23 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 0000000000..0464361c9a
--- /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 49581e656e..d87ec7a0a5 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 2fb90914a8..dead8a6daf 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 8a064b6c97..00c0be25ec 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 0415/2085] 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 0b0125d1e9..ea14b9bccc 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 267ee7182c..b9da7de7b1 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 7d08e36596..ce694be481 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 0416/2085] 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 0464361c9a..bce3f5c276 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 ce694be481..4ec0578a92 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 0417/2085] 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 1cce4f3d23..52ce0aee47 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 bce3f5c276..95cc50fc90 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 4ec0578a92..4f0e5a37f5 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 628651c786..acb4ba2bfb 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 00c0be25ec..e74f11d863 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 0418/2085] 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 52ce0aee47..18412d31b1 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 d87ec7a0a5..531ab1d737 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 4f0e5a37f5..f10ee707a9 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 acb4ba2bfb..2e17642354 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 e74f11d863..2367de6a65 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 0419/2085] 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 5f242f658d..3af20ff98d 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 2e17642354..b73eedaa6a 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 a8e760d27f..49169b9900 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 04a1e685ab..9fe898bacd 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 39f2172e1f..31d0490f19 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 57f16485e1..e9cd342ff4 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 2367de6a65..38659f875d 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 d6b0ad16b2..acb5ba4be0 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 0420/2085] 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 38659f875d..c4941cfe3d 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 0421/2085] 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 c4941cfe3d..4853e6708d 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 0422/2085] 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 4853e6708d..1339e7f553 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 0423/2085] 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 95cc50fc90..a3f3eab830 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 0424/2085] 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 a3f3eab830..a97aa07458 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 1339e7f553..633190ac6e 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 0425/2085] 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 633190ac6e..7f6d5d860a 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 0426/2085] 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 7f6d5d860a..93bca15e3a 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 0427/2085] 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 531ab1d737..aa56336367 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 0428/2085] 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 aa56336367..dd2ec5497a 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 0429/2085] [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 59b8e6fca9..9c9aed0d5d 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 0430/2085] 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 2026a3b0a1..d03850be0f 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 0431/2085] 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 deeb7781fb..5a0b3d68bf 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 93bca15e3a..37abdd3fe3 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 0432/2085] 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 18412d31b1..2883909e82 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 0433/2085] 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 a97aa07458..80e8f25854 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 dd2ec5497a..10334443fb 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 0434/2085] 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 10334443fb..4d24cdf57c 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 0435/2085] 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 4d24cdf57c..cd7bbfe36b 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 0436/2085] 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 a47ede93fa..97084fbc15 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 0437/2085] 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 2a05d7c545..3879da293d 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 0438/2085] 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 92dbc69062..0f7af07ca1 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 0439/2085] 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 7583c3424e..552c56b152 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 e832e8c483..5854e43df7 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 15ec28498f..2e8ecf77bd 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 dead8a6daf..097eb050c6 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 0440/2085] [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 6574ea335e..5a845907d7 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 0441/2085] 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 2883909e82..e58a3b25b3 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 37abdd3fe3..ab23757d0f 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 0442/2085] 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 e58a3b25b3..6d69cfc250 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 80e8f25854..ebb1bdf239 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 cd7bbfe36b..4d2754b950 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 f10ee707a9..acc1ef8e63 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 097eb050c6..2ad1d0ed73 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 0443/2085] [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 34570bb889..c6608bf694 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 0444/2085] [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 c6608bf694..08f4374cac 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 acb5ba4be0..67cf8d8fea 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 0445/2085] [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 5b407a9285..8cd212d3a8 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 0446/2085] 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 4d2754b950..1faa02ba3f 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 0447/2085] 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 ab23757d0f..be970cd196 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 0448/2085] 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 1faa02ba3f..16cfbed9e5 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 0449/2085] 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 16cfbed9e5..ec72332e96 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 0450/2085] 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 ebb1bdf239..7f4643bec3 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 ec72332e96..92da0e809d 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 be970cd196..b9f5aa8c4c 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 0451/2085] 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 ce678a82a4..3891d13e9c 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 0452/2085] [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 e29f15e9da..743150c623 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 81cbf9894b..06d5830208 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}
+
CodeMirror
@@ -25,8 +26,8 @@
Bi-directional Text Demo
-
+
+
value (string or Doc)
@@ -53,13 +54,37 @@ Bi-directional Text Demo
سيتم تعيين كل من cm-s-foo و cm-s-bar
الطبقات إلى المحرر.
-
+
+
+ Editor default direction:
+ LTR
+ RTL
+
+
+ Use visual order for arrow key movement.
+
+
Demonstration of bi-directional text support. See
From bfbdd885989a868fae94a35786f5f5ff53961b4a Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Tue, 7 Mar 2017 14:31:59 +0100
Subject: [PATCH 0536/2085] [merge addon] Don't try to align lines in deleted
chunks
Issue #4627
---
addon/merge/merge.js | 12 ++++++++++--
1 file changed, 10 insertions(+), 2 deletions(-)
diff --git a/addon/merge/merge.js b/addon/merge/merge.js
index 35b992ea5d..76460dd864 100644
--- a/addon/merge/merge.js
+++ b/addon/merge/merge.js
@@ -372,14 +372,22 @@
// lines that need to be aligned with each other.
function mergeAlignable(result, origAlignable, chunks, setIndex) {
var rI = 0, origI = 0, chunkI = 0, diff = 0
- for (;; rI++) {
+ outer: for (;; rI++) {
var nextR = result[rI], nextO = origAlignable[origI]
if (!nextR && nextO == null) break
var rLine = nextR ? nextR[0] : 1e9, oLine = nextO == null ? 1e9 : nextO
while (chunkI < chunks.length) {
var chunk = chunks[chunkI]
- if (chunk.editTo > rLine) break
+ if (chunk.origFrom <= oLine && chunk.origTo > oLine) {
+ origI++
+ rI--
+ continue outer;
+ }
+ if (chunk.editTo > rLine) {
+ if (chunk.editFrom <= rLine) continue outer;
+ break
+ }
diff += (chunk.origTo - chunk.origFrom) - (chunk.editTo - chunk.editFrom)
chunkI++
}
From d3e68b6ed4f0dcdee6bbc37d55aa9c8d87b7d6ac Mon Sep 17 00:00:00 2001
From: Ami Fischman
Date: Tue, 7 Mar 2017 14:17:26 -0800
Subject: [PATCH 0537/2085] [emacs keymap] Update to match Emacs >= 24.4
RET/C-j mappings.
Starting with Emacs 24.4 the meanings of Enter and Ctrl+J was swapped, so that
RET does newline-and-indent while C-j is the simple non-electric newline. This
change makes codemirror4 match Emacs.
---
keymap/emacs.js | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/keymap/emacs.js b/keymap/emacs.js
index 57cf6e8525..2d5fe1b815 100644
--- a/keymap/emacs.js
+++ b/keymap/emacs.js
@@ -371,7 +371,9 @@
"Shift-Alt-,": "goDocStart", "Shift-Alt-.": "goDocEnd",
"Ctrl-S": "findNext", "Ctrl-R": "findPrev", "Ctrl-G": quit, "Shift-Alt-5": "replace",
"Alt-/": "autocomplete",
- "Ctrl-J": "newlineAndIndent", "Enter": false, "Tab": "indentAuto",
+ "Enter": "newlineAndIndent",
+ "Ctrl-J": repeated(function(cm) { cm.replaceSelection("\n", "end"); }),
+ "Tab": "indentAuto",
"Alt-G G": function(cm) {
var prefix = getPrefix(cm, true);
From e8f3146784844c41157b9c210b5df82fcda31cc7 Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Wed, 8 Mar 2017 10:15:39 +0100
Subject: [PATCH 0538/2085] [shell mode] Ignore escaped open parens in
parenthesized content
Closes #4635
---
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 944f64dc52..76c67cd2b7 100644
--- a/mode/shell/shell.js
+++ b/mode/shell/shell.js
@@ -96,7 +96,7 @@ CodeMirror.defineMode('shell', function() {
state.tokens.unshift(tokenDollar);
break;
}
- if (next === "(" && quote === "(") {
+ if (!escaped && next === "(" && quote === "(") {
state.tokens.unshift(tokenString(quote, style))
return tokenize(stream, state)
}
From 64cffa1f844f8d2a1a42e9185359cc414fa25045 Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Wed, 8 Mar 2017 12:23:20 +0100
Subject: [PATCH 0539/2085] Make sure line backgrounds aren't treated as
content in contentEditable mode
Issue #4194
Closes #4500
---
src/display/update_line.js | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/display/update_line.js b/src/display/update_line.js
index 007eba7ffe..e9a4ae7e66 100644
--- a/src/display/update_line.js
+++ b/src/display/update_line.js
@@ -96,6 +96,7 @@ function updateLineGutter(cm, lineView, lineN, dims) {
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`)
+ cm.display.input.setUneditable(gutterWrap)
wrap.insertBefore(lineView.gutterBackground, lineView.text)
}
let markers = lineView.line.gutterMarkers
From f4913408198cdfd21d537121500de9beeac06180 Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Wed, 8 Mar 2017 12:27:08 +0100
Subject: [PATCH 0540/2085] Fix copy-paste mistake in previous patch
Issue #4194
Issue #4500
---
src/display/update_line.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/display/update_line.js b/src/display/update_line.js
index e9a4ae7e66..28a0edb5d4 100644
--- a/src/display/update_line.js
+++ b/src/display/update_line.js
@@ -96,7 +96,7 @@ function updateLineGutter(cm, lineView, lineN, dims) {
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`)
- cm.display.input.setUneditable(gutterWrap)
+ cm.display.input.setUneditable(lineView.gutterBackground)
wrap.insertBefore(lineView.gutterBackground, lineView.text)
}
let markers = lineView.line.gutterMarkers
From bad02ce6ea740b8415955ab21301dd5a4af5260f Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Wed, 8 Mar 2017 12:29:04 +0100
Subject: [PATCH 0541/2085] Make sure whole-line background is also uneditable
Issue #4194
Issue #4500
---
src/display/update_line.js | 13 +++++++------
1 file changed, 7 insertions(+), 6 deletions(-)
diff --git a/src/display/update_line.js b/src/display/update_line.js
index 28a0edb5d4..15a2394257 100644
--- a/src/display/update_line.js
+++ b/src/display/update_line.js
@@ -12,7 +12,7 @@ export function updateLineForChanges(cm, lineView, lineN, dims) {
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)
+ else if (type == "class") updateLineClasses(cm, lineView)
else if (type == "widget") updateLineWidgets(cm, lineView, dims)
}
lineView.changes = null
@@ -31,7 +31,7 @@ function ensureLineWrapped(lineView) {
return lineView.node
}
-function updateLineBackground(lineView) {
+function updateLineBackground(cm, lineView) {
let cls = lineView.bgClass ? lineView.bgClass + " " + (lineView.line.bgClass || "") : lineView.line.bgClass
if (cls) cls += " CodeMirror-linebackground"
if (lineView.background) {
@@ -40,6 +40,7 @@ function updateLineBackground(lineView) {
} else if (cls) {
let wrap = ensureLineWrapped(lineView)
lineView.background = wrap.insertBefore(elt("div", null, cls), wrap.firstChild)
+ cm.display.input.setUneditable(lineView.background)
}
}
@@ -67,14 +68,14 @@ function updateLineText(cm, lineView) {
if (built.bgClass != lineView.bgClass || built.textClass != lineView.textClass) {
lineView.bgClass = built.bgClass
lineView.textClass = built.textClass
- updateLineClasses(lineView)
+ updateLineClasses(cm, lineView)
} else if (cls) {
lineView.text.className = cls
}
}
-function updateLineClasses(lineView) {
- updateLineBackground(lineView)
+function updateLineClasses(cm, lineView) {
+ updateLineBackground(cm, lineView)
if (lineView.line.wrapClass)
ensureLineWrapped(lineView).className = lineView.line.wrapClass
else if (lineView.node != lineView.text)
@@ -138,7 +139,7 @@ export function buildLineElement(cm, lineView, lineN, dims) {
if (built.bgClass) lineView.bgClass = built.bgClass
if (built.textClass) lineView.textClass = built.textClass
- updateLineClasses(lineView)
+ updateLineClasses(cm, lineView)
updateLineGutter(cm, lineView, lineN, dims)
insertLineWidgets(cm, lineView, dims)
return lineView.node
From 3842167fbb9a30a1dcc17e319e340a5dbd4c248e Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Wed, 8 Mar 2017 16:25:27 +0100
Subject: [PATCH 0542/2085] Add first contenteditable tests
---
src/input/ContentEditableInput.js | 14 ++++---
test/contenteditable_test.js | 68 +++++++++++++++++++++++++++++++
test/index.html | 1 +
3 files changed, 77 insertions(+), 6 deletions(-)
create mode 100644 test/contenteditable_test.js
diff --git a/src/input/ContentEditableInput.js b/src/input/ContentEditableInput.js
index 08debaa22a..252d6a871b 100644
--- a/src/input/ContentEditableInput.js
+++ b/src/input/ContentEditableInput.js
@@ -34,9 +34,7 @@ export default class ContentEditableInput {
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, () => {
- if (!input.pollContent()) regChange(cm)
- }), 20)
+ if (ie_version <= 11) setTimeout(operation(cm, () => this.updateFromDOM()), 20)
})
on(div, "compositionstart", e => {
@@ -303,7 +301,7 @@ export default class ContentEditableInput {
if (!this.composing) return
clearTimeout(this.readDOMTimeout)
this.composing = null
- if (!this.pollContent()) regChange(this.cm)
+ this.updateFromDOM()
this.div.blur()
this.div.focus()
}
@@ -315,11 +313,15 @@ export default class ContentEditableInput {
if (this.composing.done) this.composing = null
else return
}
- if (this.cm.isReadOnly() || !this.pollContent())
- runInOp(this.cm, () => regChange(this.cm))
+ this.updateFromDOM()
}, 80)
}
+ updateFromDOM() {
+ if (this.cm.isReadOnly() || !this.pollContent())
+ runInOp(this.cm, () => regChange(this.cm))
+ }
+
setUneditable(node) {
node.contentEditable = "false"
}
diff --git a/test/contenteditable_test.js b/test/contenteditable_test.js
new file mode 100644
index 0000000000..e9c2e09880
--- /dev/null
+++ b/test/contenteditable_test.js
@@ -0,0 +1,68 @@
+(function() {
+ "use strict";
+
+ namespace = "contenteditable_";
+ var Pos = CodeMirror.Pos
+
+ function findTextNode(dom, text) {
+ if (dom instanceof CodeMirror) dom = dom.getInputField()
+ if (dom.nodeType == 1) {
+ for (var ch = dom.firstChild; ch; ch = ch.nextSibling) {
+ var found = findTextNode(ch, text)
+ if (found) return found
+ }
+ } else if (dom.nodeType == 3 && dom.nodeValue == text) {
+ return dom
+ }
+ }
+
+ function lineElt(node) {
+ for (;;) {
+ var parent = node.parentNode
+ if (/CodeMirror-code/.test(parent.className)) return node
+ node = parent
+ }
+ }
+
+ testCM("insert_text", function(cm) {
+ findTextNode(cm, "foobar").nodeValue = "foo bar"
+ cm.display.input.updateFromDOM()
+ eq(cm.getValue(), "foo bar")
+ }, {inputStyle: "contenteditable", value: "foobar"})
+
+ testCM("split_line", function(cm) {
+ cm.setSelection(Pos(2, 3))
+ var node = findTextNode(cm, "foobar")
+ node.nodeValue = "foo"
+ var lineNode = lineElt(node)
+ lineNode.parentNode.insertBefore(document.createElement("pre"), lineNode.nextSibling).textContent = "bar"
+ cm.display.input.updateFromDOM()
+ eq(cm.getValue(), "one\ntwo\nfoo\nbar\nthree\nfour\n")
+ }, {inputStyle: "contenteditable", value: "one\ntwo\nfoobar\nthree\nfour\n"})
+
+ testCM("join_line", function(cm) {
+ cm.setSelection(Pos(2, 3))
+ var node = findTextNode(cm, "foo")
+ node.nodeValue = "foobar"
+ var lineNode = lineElt(node)
+ lineNode.parentNode.removeChild(lineNode.nextSibling)
+ cm.display.input.updateFromDOM()
+ eq(cm.getValue(), "one\ntwo\nfoobar\nthree\nfour\n")
+ }, {inputStyle: "contenteditable", value: "one\ntwo\nfoo\nbar\nthree\nfour\n"})
+
+ testCM("delete_multiple", function(cm) {
+ cm.setSelection(Pos(1, 3), Pos(4, 0))
+ var text = findTextNode(cm, "two"), startLine = lineElt(text)
+ for (var i = 0; i < 3; i++)
+ startLine.parentNode.removeChild(startLine.nextSibling)
+ text.nodeValue = "twothree"
+ cm.display.input.updateFromDOM()
+ eq(cm.getValue(), "one\ntwothree\nfour\n")
+ }, {inputStyle: "contenteditable", value: "one\ntwo\nfoo\nbar\nthree\nfour\n"})
+
+ testCM("force_redraw", function(cm) {
+ findTextNode(cm, "foo").parentNode.appendChild(document.createElement("hr")).className = "inserted"
+ cm.display.input.updateFromDOM()
+ eq(byClassName(cm.getInputField(), "inserted").length, 0)
+ }, {inputStyle: "contenteditable", value: "foo"})
+})();
diff --git a/test/index.html b/test/index.html
index 5a0b3d68bf..4146a4d45d 100644
--- a/test/index.html
+++ b/test/index.html
@@ -103,6 +103,7 @@ Test Suite
+
From c15090d1d47a7d15c67d9ddade9356fbcbbb2bec Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Wed, 8 Mar 2017 16:41:19 +0100
Subject: [PATCH 0543/2085] Improve handling of ambiguous diffs in
contenteditable input mode
---
src/input/ContentEditableInput.js | 8 ++++++++
test/contenteditable_test.js | 21 +++++++++++++++++++++
2 files changed, 29 insertions(+)
diff --git a/src/input/ContentEditableInput.js b/src/input/ContentEditableInput.js
index 252d6a871b..34e5226821 100644
--- a/src/input/ContentEditableInput.js
+++ b/src/input/ContentEditableInput.js
@@ -279,6 +279,14 @@ export default class ContentEditableInput {
while (cutEnd < maxCutEnd &&
newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1))
++cutEnd
+ // Try to move start of change to start of selection if ambiguous
+ if (newText.length == 1 && oldText.length == 1 && fromLine == from.line) {
+ while (cutFront && cutFront > from.ch &&
+ newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1)) {
+ cutFront--
+ cutEnd++
+ }
+ }
newText[newText.length - 1] = newBot.slice(0, newBot.length - cutEnd).replace(/^\u200b+/, "")
newText[0] = newText[0].slice(cutFront).replace(/\u200b+$/, "")
diff --git a/test/contenteditable_test.js b/test/contenteditable_test.js
index e9c2e09880..bc41c5a065 100644
--- a/test/contenteditable_test.js
+++ b/test/contenteditable_test.js
@@ -60,6 +60,27 @@
eq(cm.getValue(), "one\ntwothree\nfour\n")
}, {inputStyle: "contenteditable", value: "one\ntwo\nfoo\nbar\nthree\nfour\n"})
+ testCM("ambiguous_diff_middle", function(cm) {
+ cm.setSelection(Pos(0, 2))
+ findTextNode(cm, "baah").nodeValue = "baaah"
+ cm.display.input.updateFromDOM()
+ eqCharPos(cm.getCursor(), Pos(0, 3))
+ }, {inputStyle: "contenteditable", value: "baah"})
+
+ testCM("ambiguous_diff_start", function(cm) {
+ cm.setSelection(Pos(0, 1))
+ findTextNode(cm, "baah").nodeValue = "baaah"
+ cm.display.input.updateFromDOM()
+ eqCharPos(cm.getCursor(), Pos(0, 2))
+ }, {inputStyle: "contenteditable", value: "baah"})
+
+ testCM("ambiguous_diff_end", function(cm) {
+ cm.setSelection(Pos(0, 3))
+ findTextNode(cm, "baah").nodeValue = "baaah"
+ cm.display.input.updateFromDOM()
+ eqCharPos(cm.getCursor(), Pos(0, 4))
+ }, {inputStyle: "contenteditable", value: "baah"})
+
testCM("force_redraw", function(cm) {
findTextNode(cm, "foo").parentNode.appendChild(document.createElement("hr")).className = "inserted"
cm.display.input.updateFromDOM()
From 438079cb951de946266be37af4487a8cd8719fe6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jakub=20Vr=C3=A1na?=
Date: Wed, 8 Mar 2017 18:18:20 +0100
Subject: [PATCH 0544/2085] [soy mode] Treat {@inject} same as {@param}
`{@inject}` is used for injected parameters instead of normal
parameters. It behaves the same as `{@param}`.
---
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 1c1f74d1dc..21f9f28303 100644
--- a/mode/soy/soy.js
+++ b/mode/soy/soy.js
@@ -256,7 +256,7 @@
state.scopes = prepend(state.scopes, state.variables);
state.soyState.push("var-def");
}
- if (state.tag.match(/^@param\??/)) {
+ if (state.tag.match(/^@(?:param\??|inject)/)) {
state.soyState.push("param-def");
}
return "keyword";
From 168d2286d15419031be1cdba14d8b53facefe8e5 Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Thu, 9 Mar 2017 14:56:50 +0100
Subject: [PATCH 0545/2085] Upgrade build tools
---
package.json | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/package.json b/package.json
index e4d3adc15a..b08d4c9950 100644
--- a/package.json
+++ b/package.json
@@ -18,9 +18,9 @@
"blint": "^0.5.1",
"node-static": "0.6.0",
"phantomjs-prebuilt": "^2.1.12",
- "rollup": "^0.34.10",
+ "rollup": "^0.41.0",
"rollup-plugin-buble": "^0.15.0",
- "rollup-watch": "^2.5.0"
+ "rollup-watch": "^3.2.0"
},
"bugs": "http://github.com/codemirror/CodeMirror/issues",
"keywords": [
From b470f2452d0fb514ed2101f9f86f4657669ad07c Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Thu, 9 Mar 2017 14:58:11 +0100
Subject: [PATCH 0546/2085] Clear contentEditable selection when it can't be
drawn
So that we don't leave a stray selection that'll be reset
to some nonsense position by the DOM changes.
Closes #4625
---
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 34e5226821..7d73602a97 100644
--- a/src/input/ContentEditableInput.js
+++ b/src/input/ContentEditableInput.js
@@ -122,7 +122,10 @@ export default class ContentEditableInput {
let start = posToDOM(this.cm, prim.from())
let end = posToDOM(this.cm, prim.to())
- if (!start && !end) return
+ if (!start && !end) {
+ sel.removeAllRanges()
+ return
+ }
let view = this.cm.display.view
let old = sel.rangeCount && sel.getRangeAt(0)
From facc9c869f1799d8c25ec0fd76995c5ed8f9d675 Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Thu, 9 Mar 2017 15:01:38 +0100
Subject: [PATCH 0547/2085] [linter] Allow tabs in lib/codemirror.css
Since rollup is now adding those
---
test/lint.js | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/test/lint.js b/test/lint.js
index 502706de8e..e7c114cd91 100644
--- a/test/lint.js
+++ b/test/lint.js
@@ -4,7 +4,8 @@ var blint = require("blint");
blint.checkDir(dir, {
browser: true,
allowedGlobals: ["CodeMirror", "define", "test", "requirejs"],
- ecmaVersion: 5
+ ecmaVersion: 5,
+ tabs: dir == "lib"
});
});
From c88b02c5badc415abb25ef83ed28e22a8f94f940 Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Thu, 9 Mar 2017 15:29:16 +0100
Subject: [PATCH 0548/2085] Always check whether to scroll window on
.scrollIntoView
Issue #4491
---
src/display/operations.js | 6 ++--
src/display/scrolling.js | 73 ++++++++++++++++++++-------------------
src/edit/methods.js | 12 ++++---
3 files changed, 48 insertions(+), 43 deletions(-)
diff --git a/src/display/operations.js b/src/display/operations.js
index 30a63a613a..6c4a0e1392 100644
--- a/src/display/operations.js
+++ b/src/display/operations.js
@@ -155,9 +155,9 @@ function endOperation_finish(op) {
}
// If we need to scroll a specific position into view, do so.
if (op.scrollToPos) {
- 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)
+ let rect = scrollPosIntoView(cm, clipPos(doc, op.scrollToPos.from),
+ clipPos(doc, op.scrollToPos.to), op.scrollToPos.margin)
+ maybeScrollWindow(cm, rect)
}
// Fire events for markers that are hidden/unidden by editing or
diff --git a/src/display/scrolling.js b/src/display/scrolling.js
index db9463cd04..b2deb2ed64 100644
--- a/src/display/scrolling.js
+++ b/src/display/scrolling.js
@@ -10,17 +10,17 @@ import { setScrollLeft, setScrollTop } from "./scroll_events"
// 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) {
+export function maybeScrollWindow(cm, rect) {
if (signalDOMEvent(cm, "scrollCursorIntoView")) return
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 (rect.top + box.top < 0) doScroll = true
+ else if (rect.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;`)
+ top: ${rect.top - display.viewOffset - paddingTop(cm.display)}px;
+ height: ${rect.bottom - rect.top + scrollGap(cm) + display.barHeight}px;
+ left: ${rect.left}px; width: ${Math.max(2, rect.right - rect.left)}px;`)
cm.display.lineSpace.appendChild(scrollNode)
scrollNode.scrollIntoView(doScroll)
cm.display.lineSpace.removeChild(scrollNode)
@@ -32,15 +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
- let coords
+ let rect
for (let limit = 0; limit < 5; limit++) {
let changed = false
- coords = cursorCoords(cm, pos)
+ let 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)
+ rect = {left: Math.min(coords.left, endCoords.left),
+ top: Math.min(coords.top, endCoords.top) - margin,
+ right: Math.max(coords.left, endCoords.left),
+ bottom: Math.max(coords.bottom, endCoords.bottom) + margin}
+ let scrollPos = calculateScrollPos(cm, rect)
let startTop = cm.doc.scrollTop, startLeft = cm.doc.scrollLeft
if (scrollPos.scrollTop != null) {
setScrollTop(cm, scrollPos.scrollTop)
@@ -52,12 +53,12 @@ export function scrollPosIntoView(cm, pos, end, margin) {
}
if (!changed) break
}
- return coords
+ return rect
}
// Scroll a given set of coordinates into view (immediately).
-export function scrollIntoView(cm, x1, y1, x2, y2) {
- let scrollPos = calculateScrollPos(cm, x1, y1, x2, y2)
+export function scrollIntoView(cm, rect) {
+ let scrollPos = calculateScrollPos(cm, rect)
if (scrollPos.scrollTop != null) setScrollTop(cm, scrollPos.scrollTop)
if (scrollPos.scrollLeft != null) setScrollLeft(cm, scrollPos.scrollLeft)
}
@@ -66,31 +67,31 @@ export function scrollIntoView(cm, x1, y1, x2, y2) {
// rectangle into view. Returns an object with scrollTop and
// scrollLeft properties. When these are undefined, the
// vertical/horizontal position does not need to be adjusted.
-export function calculateScrollPos(cm, x1, y1, x2, y2) {
+export function calculateScrollPos(cm, rect) {
let display = cm.display, snapMargin = textHeight(cm.display)
- if (y1 < 0) y1 = 0
+ if (rect.top < 0) rect.top = 0
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
+ if (rect.bottom - rect.top > screen) rect.bottom = rect.top + screen
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) {
- let newTop = Math.min(y1, (atBottom ? docBottom : y2) - screen)
+ let atTop = rect.top < snapMargin, atBottom = rect.bottom > docBottom - snapMargin
+ if (rect.top < screentop) {
+ result.scrollTop = atTop ? 0 : rect.top
+ } else if (rect.bottom > screentop + screen) {
+ let newTop = Math.min(rect.top, (atBottom ? docBottom : rect.bottom) - screen)
if (newTop != screentop) result.scrollTop = newTop
}
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)
+ let tooWide = rect.right - rect.left > screenw
+ if (tooWide) rect.right = rect.left + screenw
+ if (rect.left < 10)
result.scrollLeft = 0
- else if (x1 < screenleft)
- result.scrollLeft = Math.max(0, x1 - (tooWide ? 0 : 10))
- else if (x2 > screenw + screenleft - 3)
- result.scrollLeft = x2 + (tooWide ? 0 : 10) - screenw
+ else if (rect.left < screenleft)
+ result.scrollLeft = Math.max(0, rect.left - (tooWide ? 0 : 10))
+ else if (rect.right > screenw + screenleft - 3)
+ result.scrollLeft = rect.right + (tooWide ? 0 : 10) - screenw
return result
}
@@ -113,7 +114,7 @@ export function ensureCursorVisible(cm) {
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}
}
// When an operation has its scrollToPos property set, and another
@@ -125,10 +126,12 @@ export function resolveScrollToPos(cm) {
if (range) {
cm.curOp.scrollToPos = null
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)
+ let sPos = calculateScrollPos(cm, {
+ left: Math.min(from.left, to.left),
+ top: Math.min(from.top, to.top) - range.margin,
+ right: Math.max(from.right, to.right),
+ bottom: Math.max(from.bottom, to.bottom) + range.margin
+ })
cm.scrollTo(sPos.scrollLeft, sPos.scrollTop)
}
}
diff --git a/src/edit/methods.js b/src/edit/methods.js
index d4efa31559..b8dd343d09 100644
--- a/src/edit/methods.js
+++ b/src/edit/methods.js
@@ -252,7 +252,7 @@ export default function(CodeMirror) {
node.style.left = left + "px"
}
if (scroll)
- scrollIntoView(this, left, top, left + node.offsetWidth, top + node.offsetHeight)
+ scrollIntoView(this, {left, top, right: left + node.offsetWidth, bottom: top + node.offsetHeight})
},
triggerOnKeyDown: methodOp(onKeyDown),
@@ -388,10 +388,12 @@ export default function(CodeMirror) {
resolveScrollToPos(this)
this.curOp.scrollToPos = range
} else {
- 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)
+ let sPos = calculateScrollPos(this, {
+ left: Math.min(range.from.left, range.to.left),
+ top: Math.min(range.from.top, range.to.top) - range.margin,
+ right: Math.max(range.from.right, range.to.right),
+ bottom: Math.max(range.from.bottom, range.to.bottom) + range.margin
+ })
this.scrollTo(sPos.scrollLeft, sPos.scrollTop)
}
}),
From 7580a1dc1d7ce1ab929a0be7cd3875d976fcf056 Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Thu, 9 Mar 2017 15:46:32 +0100
Subject: [PATCH 0549/2085] [markdown mode] Simplify leaving fenced block
Closes #4628
---
mode/markdown/markdown.js | 20 ++++++--------------
1 file changed, 6 insertions(+), 14 deletions(-)
diff --git a/mode/markdown/markdown.js b/mode/markdown/markdown.js
index 21120ab482..e649b09179 100644
--- a/mode/markdown/markdown.js
+++ b/mode/markdown/markdown.js
@@ -241,9 +241,13 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
function local(stream, state) {
if (state.fencedChars && stream.match(state.fencedChars)) {
if (modeCfg.highlightFormatting) state.formatting = "code-block";
+ var returnType = getType(state)
state.localMode = state.localState = null;
- state.f = state.block = leavingLocal;
- return getType(state)
+ state.block = blockNormal;
+ state.f = inlineNormal;
+ state.fencedChars = null;
+ state.code = 0
+ return returnType;
} else if (state.fencedChars && stream.skipTo(state.fencedChars)) {
return "comment"
} else if (state.localMode) {
@@ -254,18 +258,6 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
}
}
- function leavingLocal(stream, state) {
- stream.match(state.fencedChars);
- state.block = blockNormal;
- state.f = inlineNormal;
- state.fencedChars = null;
- if (modeCfg.highlightFormatting) state.formatting = "code-block";
- state.code = 1
- var returnType = getType(state);
- state.code = 0
- return returnType;
- }
-
// Inline
function getType(state) {
var styles = [];
From aab23579713141613399605e3bb6a4a25a1c13dd Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Thu, 9 Mar 2017 15:51:38 +0100
Subject: [PATCH 0550/2085] [css mode] Add system-ui as value keyword
Closes #4609
---
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 23d0b4db0c..02cc93d9cf 100644
--- a/mode/css/css.js
+++ b/mode/css/css.js
@@ -667,7 +667,7 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
"small", "small-caps", "small-caption", "smaller", "soft-light", "solid", "somali",
"source-atop", "source-in", "source-out", "source-over", "space", "space-around", "space-between", "spell-out", "square",
"square-button", "start", "static", "status-bar", "stretch", "stroke", "sub",
- "subpixel-antialiased", "super", "sw-resize", "symbolic", "symbols", "table",
+ "subpixel-antialiased", "super", "sw-resize", "symbolic", "symbols", "system-ui", "table",
"table-caption", "table-cell", "table-column", "table-column-group",
"table-footer-group", "table-header-group", "table-row", "table-row-group",
"tamil",
From cfcad600a6b2713347c64d2f434a6e78ffeb4e8f Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Thu, 9 Mar 2017 16:47:52 +0100
Subject: [PATCH 0551/2085] Fix bug in reading contenteditable text from DOM
Which often caused strangeness around empty lines
Issue #4307
---
src/input/ContentEditableInput.js | 30 ++++++++++++++++++------------
test/contenteditable_test.js | 21 +++++++++++++++++++++
2 files changed, 39 insertions(+), 12 deletions(-)
diff --git a/src/input/ContentEditableInput.js b/src/input/ContentEditableInput.js
index 7d73602a97..a0452d4646 100644
--- a/src/input/ContentEditableInput.js
+++ b/src/input/ContentEditableInput.js
@@ -375,34 +375,40 @@ 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 marker => marker.id == id }
+ function close() {
+ if (closing) {
+ text += lineSep
+ closing = false
+ }
+ }
+ function addText(str) {
+ if (str) {
+ close()
+ text += str
+ }
+ }
function walk(node) {
if (node.nodeType == 1) {
let cmText = node.getAttribute("cm-text")
if (cmText != null) {
- if (cmText == "") text += node.textContent.replace(/\u200b/g, "")
- else text += cmText
+ addText(cmText || node.textContent.replace(/\u200b/g, ""))
return
}
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()))
- text += getBetween(cm.doc, range.from, range.to).join(lineSep)
+ addText(getBetween(cm.doc, range.from, range.to).join(lineSep))
return
}
if (node.getAttribute("contenteditable") == "false") return
+ let isBlock = /^(pre|div|p)$/i.test(node.nodeName)
+ if (isBlock) close()
for (let i = 0; i < node.childNodes.length; i++)
walk(node.childNodes[i])
- if (/^(pre|div|p)$/i.test(node.nodeName))
- closing = true
+ if (isBlock) closing = true
} else if (node.nodeType == 3) {
- let val = node.nodeValue
- if (!val) return
- if (closing) {
- text += lineSep
- closing = false
- }
- text += val
+ addText(node.nodeValue)
}
}
for (;;) {
diff --git a/test/contenteditable_test.js b/test/contenteditable_test.js
index bc41c5a065..9130fa4965 100644
--- a/test/contenteditable_test.js
+++ b/test/contenteditable_test.js
@@ -86,4 +86,25 @@
cm.display.input.updateFromDOM()
eq(byClassName(cm.getInputField(), "inserted").length, 0)
}, {inputStyle: "contenteditable", value: "foo"})
+
+ testCM("type_on_empty_line", function(cm) {
+ cm.setSelection(Pos(1, 0))
+ findTextNode(cm, "\u200b").nodeValue += "hello"
+ cm.display.input.updateFromDOM()
+ eq(cm.getValue(), "foo\nhello\nbar")
+ }, {inputStyle: "contenteditable", value: "foo\n\nbar"})
+
+ testCM("type_after_empty_line", function(cm) {
+ cm.setSelection(Pos(2, 0))
+ findTextNode(cm, "bar").nodeValue = "hellobar"
+ cm.display.input.updateFromDOM()
+ eq(cm.getValue(), "foo\n\nhellobar")
+ }, {inputStyle: "contenteditable", value: "foo\n\nbar"})
+
+ testCM("type_before_empty_line", function(cm) {
+ cm.setSelection(Pos(0, 3))
+ findTextNode(cm, "foo").nodeValue = "foohello"
+ cm.display.input.updateFromDOM()
+ eq(cm.getValue(), "foohello\n\nbar")
+ }, {inputStyle: "contenteditable", value: "foo\n\nbar"})
})();
From 1d00df9e9e6ced8516a3400e3f4bdb8147ab514d Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Fri, 10 Mar 2017 13:40:29 +0100
Subject: [PATCH 0552/2085] Make backspace across lines work in Chrome Android
(when there is a gutter)
Issue #4637
---
lib/codemirror.css | 6 +----
src/input/ContentEditableInput.js | 37 +++++++++++++++++++++++--------
src/util/browser.js | 3 ++-
3 files changed, 31 insertions(+), 15 deletions(-)
diff --git a/lib/codemirror.css b/lib/codemirror.css
index 75cc4c42f1..62b907dfa0 100644
--- a/lib/codemirror.css
+++ b/lib/codemirror.css
@@ -223,11 +223,7 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
cursor: default;
z-index: 4;
}
-.CodeMirror-gutter-wrapper {
- -webkit-user-select: none;
- -moz-user-select: none;
- user-select: none;
-}
+.CodeMirror-gutter-wrapper ::selection { background-color: transparent }
.CodeMirror-lines {
cursor: text;
diff --git a/src/input/ContentEditableInput.js b/src/input/ContentEditableInput.js
index a0452d4646..7e7fca9a8b 100644
--- a/src/input/ContentEditableInput.js
+++ b/src/input/ContentEditableInput.js
@@ -13,6 +13,7 @@ import { 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 { chrome, android } from "../util/browser"
// CONTENTEDITABLE INPUT STYLE
@@ -219,16 +220,28 @@ export default class ContentEditableInput {
}
pollSelection() {
- 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)
- let head = domToPos(cm, sel.focusNode, sel.focusOffset)
- if (anchor && head) runInOp(cm, () => {
- setSelection(cm.doc, simpleSelection(anchor, head), sel_dontScroll)
- if (anchor.bad || head.bad) cm.curOp.selectionChanged = true
- })
+ if (this.readDOMTimeout != null || this.gracePeriod || !this.selectionChanged()) return
+ let sel = window.getSelection(), cm = this.cm
+ // On Android Chrome (version 56, at least), backspacing into an
+ // uneditable block element will put the cursor in that element,
+ // and then, because it's not editable, hide the virtual keyboard.
+ // 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)) {
+ this.cm.triggerOnKeyDown({type: "keydown", keyCode: 8, preventDefault: Math.abs})
+ this.blur()
+ this.focus()
+ return
}
+ if (this.composing) return
+ this.rememberSelection()
+ let anchor = domToPos(cm, sel.anchorNode, sel.anchorOffset)
+ let head = domToPos(cm, sel.focusNode, sel.focusOffset)
+ if (anchor && head) runInOp(cm, () => {
+ setSelection(cm.doc, simpleSelection(anchor, head), sel_dontScroll)
+ if (anchor.bad || head.bad) cm.curOp.selectionChanged = true
+ })
}
pollContent() {
@@ -370,6 +383,12 @@ function posToDOM(cm, pos) {
return result
}
+function isInGutter(node) {
+ for (let scan = node; scan; scan = scan.parentNode)
+ if (/CodeMirror-gutter-wrapper/.test(scan.className)) return true
+ return false
+}
+
function badPos(pos, bad) { if (bad) pos.bad = true; return pos }
function domTextBetween(cm, from, to, fromLine, toLine) {
diff --git a/src/util/browser.js b/src/util/browser.js
index 3891d13e9c..9fc4602c68 100644
--- a/src/util/browser.js
+++ b/src/util/browser.js
@@ -18,8 +18,9 @@ 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 = !edge && /AppleWebKit/.test(userAgent) && /Mobile\/\w+/.test(userAgent)
+export let android = /Android/.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 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)
From 27bfabac6e7f39088092b9ce9a692bdb382d3b1b Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Fri, 10 Mar 2017 13:41:04 +0100
Subject: [PATCH 0553/2085] Also hide selection in gutter on Firefox
---
lib/codemirror.css | 1 +
1 file changed, 1 insertion(+)
diff --git a/lib/codemirror.css b/lib/codemirror.css
index 62b907dfa0..b962b38374 100644
--- a/lib/codemirror.css
+++ b/lib/codemirror.css
@@ -224,6 +224,7 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
z-index: 4;
}
.CodeMirror-gutter-wrapper ::selection { background-color: transparent }
+.CodeMirror-gutter-wrapper ::-moz-selection { background-color: transparent }
.CodeMirror-lines {
cursor: text;
From 66c68db0c4fcbc6bb6ce735c2def8f03c3f43478 Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Wed, 15 Mar 2017 10:39:11 +0100
Subject: [PATCH 0554/2085] [comment addon] When uncommenting a block comment,
make sure its a single one
Closes #4641
---
addon/comment/comment.js | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/addon/comment/comment.js b/addon/comment/comment.js
index 1c9878716f..568e639dcd 100644
--- a/addon/comment/comment.js
+++ b/addon/comment/comment.js
@@ -46,7 +46,7 @@
// Rough heuristic to try and detect lines that are part of multi-line string
function probablyInsideString(cm, pos, line) {
- return /\bstring\b/.test(cm.getTokenTypeAt(Pos(pos.line, 0))) && !/^[\'\"`]/.test(line)
+ return /\bstring\b/.test(cm.getTokenTypeAt(Pos(pos.line, 0))) && !/^[\'\"\`]/.test(line)
}
function getMode(cm, pos) {
@@ -176,9 +176,11 @@
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(Pos(start, open + 1))) ||
- !/comment/.test(self.getTokenTypeAt(Pos(end, close + 1))))
+ !/comment/.test(self.getTokenTypeAt(insideStart)) ||
+ !/comment/.test(self.getTokenTypeAt(insideEnd)) ||
+ self.getRange(insideStart, insideEnd, "\n").indexOf(endString) > -1)
return false;
// Avoid killing block comments completely outside the selection.
From 2f43bccc955c06016aca79e7523a3f84e8f5d9f8 Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Wed, 15 Mar 2017 20:36:44 +0100
Subject: [PATCH 0555/2085] [javascript mode] Recognize TypeScript class fields
without type
Closes #4643
---
mode/javascript/javascript.js | 1 +
1 file changed, 1 insertion(+)
diff --git a/mode/javascript/javascript.js b/mode/javascript/javascript.js
index 316fe4a27a..3f614cc2c5 100644
--- a/mode/javascript/javascript.js
+++ b/mode/javascript/javascript.js
@@ -662,6 +662,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
function classfield(type, value) {
if (value == "?") return cont(classfield)
if (type == ":") return cont(typeexpr, maybeAssign)
+ if (value == "=") return cont(expressionNoComma)
return pass(functiondef)
}
function afterExport(type, value) {
From c96aae3d946b3f8f78167e3ec8f22976f426a9ce Mon Sep 17 00:00:00 2001
From: Axel Lewenhaupt
Date: Sat, 25 Feb 2017 12:13:34 +0100
Subject: [PATCH 0556/2085] [soy mode] Fix indentation and restore previous
local states
---
mode/soy/soy.js | 47 ++++++++++++++++++++++++++++++++++-------------
mode/soy/test.js | 12 ++++++++++++
2 files changed, 46 insertions(+), 13 deletions(-)
diff --git a/mode/soy/soy.js b/mode/soy/soy.js
index 21f9f28303..cff7719b87 100644
--- a/mode/soy/soy.js
+++ b/mode/soy/soy.js
@@ -31,6 +31,12 @@
}
function tokenUntil(stream, state, untilRegExp) {
+ if (stream.sol()) {
+ for (var indent = 0; indent < state.indent; indent++) {
+ if (!stream.eat(/\s/)) break;
+ }
+ if (indent) return null;
+ }
var oldString = stream.string;
var match = untilRegExp.exec(oldString.substr(stream.pos));
if (match) {
@@ -39,7 +45,8 @@
stream.string = oldString.substr(0, stream.pos + match.index);
}
var result = stream.hideFirstChars(state.indent, function() {
- return state.localMode.token(stream, state.localState);
+ var localState = last(state.localStates);
+ return localState.mode.token(stream, localState.state);
});
stream.string = oldString;
return result;
@@ -83,8 +90,10 @@
variables: null,
scopes: null,
indent: 0,
- localMode: modes.html,
- localState: CodeMirror.startState(modes.html)
+ localStates: [{
+ mode: modes.html,
+ state: CodeMirror.startState(modes.html)
+ }]
};
},
@@ -98,8 +107,12 @@
variables: state.variables,
scopes: state.scopes,
indent: state.indent, // Indentation of the following line.
- localMode: state.localMode,
- localState: CodeMirror.copyState(state.localMode, state.localState)
+ localStates: state.localStates.map(function(localState) {
+ return {
+ mode: localState.mode,
+ state: CodeMirror.copyState(localState.mode, localState.state)
+ };
+ })
};
},
@@ -187,8 +200,13 @@
var kind = match[1];
state.kind.push(kind);
state.kindTag.push(state.tag);
- state.localMode = modes[kind] || modes.html;
- state.localState = CodeMirror.startState(state.localMode);
+ var mode = modes[kind] || modes.html;
+ var localState = last(state.localStates);
+ state.indent += localState.mode.indent(localState.state, "");
+ state.localStates.push({
+ mode: mode,
+ state: CodeMirror.startState(mode)
+ });
}
return "attribute";
} else if (stream.match(/^"/)) {
@@ -233,14 +251,15 @@
return "keyword";
} else if (match = stream.match(/^\{([\/@\\]?[\w?]*)/)) {
if (match[1] != "/switch")
- state.indent += (/^(\/|(else|elseif|ifempty|case|default)$)/.test(match[1]) && state.tag != "switch" ? 1 : 2) * config.indentUnit;
+ state.indent += (/^(\/|(else|elseif|ifempty|case|fallbackmsg|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="".
state.kind.pop();
state.kindTag.pop();
- state.localMode = modes[last(state.kind)] || modes.html;
- state.localState = CodeMirror.startState(state.localMode);
+ state.localStates.pop();
+ var localState = last(state.localStates);
+ state.indent -= localState.mode.indent(localState.state, "");
}
state.soyState.push("tag");
if (state.tag == "template" || state.tag == "deltemplate") {
@@ -277,14 +296,16 @@
if (state.tag != "switch" && /^\{(case|default)\b/.test(textAfter)) indent -= config.indentUnit;
if (/^\{\/switch\b/.test(textAfter)) indent -= config.indentUnit;
}
- if (indent && state.localMode.indent)
- indent += state.localMode.indent(state.localState, textAfter);
+ var localState = last(state.localStates);
+ if (indent && localState.mode.indent) {
+ indent += localState.mode.indent(localState.state, textAfter);
+ }
return indent;
},
innerMode: function(state) {
if (state.soyState.length && last(state.soyState) != "literal") return null;
- else return {state: state.localState, mode: state.localMode};
+ else return last(state.localStates);
},
electricInput: /^\s*\{(\/|\/template|\/deltemplate|\/switch|fallbackmsg|elseif|else|case|default|ifempty|\/literal\})$/,
diff --git a/mode/soy/test.js b/mode/soy/test.js
index 9e265c1b0c..38ad7cbeb4 100644
--- a/mode/soy/test.js
+++ b/mode/soy/test.js
@@ -80,4 +80,16 @@
' nothing',
'[keyword {/foreach}]',
'');
+
+ 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 {param] [attribute kind]=[string "js"][keyword }]',
+ ' [keyword var] [def bar] [operator =] [number 5];',
+ ' [keyword {/param}]',
+ ' [keyword {/call}]',
+ ' [tag&bracket ][tag div][tag&bracket >]',
+ '[keyword {/template}]',
+ '');
})();
From 6b59a2c832fc3aec5e730da973081c822d1f3dfa Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Mon, 20 Mar 2017 11:20:26 +0100
Subject: [PATCH 0557/2085] [real-world uses] Add some links
---
doc/realworld.html | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/doc/realworld.html b/doc/realworld.html
index 2f36caf4a9..c60597e076 100644
--- a/doc/realworld.html
+++ b/doc/realworld.html
@@ -82,8 +82,10 @@ CodeMirror real-world uses
Firepad (collaborative text editor)
Gerrit 's diff view and inline editor
Git Crx (Chrome App for browsing local git repos)
- Go language tour
GitHub's Android app
+ Github 's in-browser edit feature
+ Glitch (community-driven app building)
+ Go language tour
Google Apps Script
Graphit (function graphing)
HackMD (Realtime collaborative markdown notes on all platforms)
@@ -135,6 +137,7 @@ CodeMirror real-world uses
Prose.io (github content editor)
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)
@@ -145,6 +148,7 @@ CodeMirror real-world uses
SageMathCell (interactive mathematical software)
SageMathCloud (interactive mathematical software environment)
ServePHP (PHP code testing in Chrome dev tools)
+ Scastie (Scala playground)
Shadertoy (shader sharing)
sketchPatch Livecodelab
Skulpt (in-browser Python environment)
From f583ab93d0e6f5f55eedbb9bd3c99e26a0270887 Mon Sep 17 00:00:00 2001
From: brrd
Date: Thu, 16 Mar 2017 13:17:39 +0100
Subject: [PATCH 0558/2085] [continuelist addon] Continue task lists
---
addon/edit/continuelist.js | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/addon/edit/continuelist.js b/addon/edit/continuelist.js
index 5a845907d7..f13e521993 100644
--- a/addon/edit/continuelist.js
+++ b/addon/edit/continuelist.js
@@ -11,8 +11,8 @@
})(function(CodeMirror) {
"use strict";
- var listRE = /^(\s*)(>[> ]*|- \[[x ]\]\s|[*+-]\s|(\d+)([.)]))(\s*)/,
- emptyListRE = /^(\s*)(>[> ]*|- \[[x ]\]|[*+-]|(\d+)[.)])(\s*)$/,
+ var listRE = /^(\s*)(>[> ]*|[*+-] \[[x ]\]\s|[*+-]\s|(\d+)([.)]))(\s*)/,
+ emptyListRE = /^(\s*)(>[> ]*|[*+-] \[[x ]\]|[*+-]|(\d+)[.)])(\s*)$/,
unorderedListRE = /[*+-]\s/;
CodeMirror.commands.newlineAndIndentContinueMarkdownList = function(cm) {
From 0b00c0e12fe919865929053e03b2c9dccda0779c Mon Sep 17 00:00:00 2001
From: Emmanuel Schanzer
Date: Thu, 16 Mar 2017 18:19:39 -0400
Subject: [PATCH 0559/2085] Add role=presentation to more elements to help
screenreaders
---
src/display/Display.js | 9 +++++----
src/line/line_data.js | 9 +++------
src/model/mark_text.js | 5 ++---
src/util/dom.js | 6 ++++++
4 files changed, 16 insertions(+), 13 deletions(-)
diff --git a/src/display/Display.js b/src/display/Display.js
index c1879e7b16..ad0256bdfd 100644
--- a/src/display/Display.js
+++ b/src/display/Display.js
@@ -1,5 +1,5 @@
import { gecko, ie, ie_version, mobile, webkit } from "../util/browser"
-import { elt } from "../util/dom"
+import { elt, eltP } from "../util/dom"
import { scrollerGap } from "../util/misc"
// The display handles the DOM integration, both for input reading
@@ -18,7 +18,7 @@ export function Display(place, doc, input) {
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 = eltP("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")
@@ -27,10 +27,11 @@ export function Display(place, doc, input) {
// When lines outside of the viewport are measured, they are drawn in this.
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],
+ d.lineSpace = eltP("div", [d.measure, d.lineMeasure, d.selectionDiv, d.cursorDiv, d.lineDiv],
null, "position: relative; outline: none")
+ let lines = eltP("div", [d.lineSpace], "CodeMirror-lines")
// 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", [lines], null, "position: relative")
// Set to the height of the document, allowing scrolling.
d.sizer = elt("div", [d.mover], "CodeMirror-sizer")
d.sizerWidth = null
diff --git a/src/line/line_data.js b/src/line/line_data.js
index 9c3b55d13a..e444184bb0 100644
--- a/src/line/line_data.js
+++ b/src/line/line_data.js
@@ -1,6 +1,6 @@
import { getOrder } from "../util/bidi"
import { ie, ie_version, webkit } from "../util/browser"
-import { elt, joinClasses } from "../util/dom"
+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"
@@ -64,14 +64,11 @@ 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).
- let content = elt("span", null, null, webkit ? "padding-right: .1px" : null)
- let builder = {pre: elt("pre", [content], "CodeMirror-line"), content: content,
+ let content = eltP("span", null, null, webkit ? "padding-right: .1px" : null)
+ let builder = {pre: eltP("pre", [content], "CodeMirror-line"), content: content,
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.
diff --git a/src/model/mark_text.js b/src/model/mark_text.js
index af1b724b27..ccdcc9d3b8 100644
--- a/src/model/mark_text.js
+++ b/src/model/mark_text.js
@@ -1,4 +1,4 @@
-import { elt } from "../util/dom"
+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"
@@ -166,8 +166,7 @@ export function markText(doc, from, to, options, type) {
if (marker.replacedWith) {
// 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
+ marker.widgetNode = eltP("span", [marker.replacedWith], "CodeMirror-widget")
if (!options.handleMouseEvents) marker.widgetNode.setAttribute("cm-ignore-events", "true")
if (options.insertLeft) marker.widgetNode.insertLeft = true
}
diff --git a/src/util/dom.js b/src/util/dom.js
index 9b5d1e85ab..94823c21b9 100644
--- a/src/util/dom.js
+++ b/src/util/dom.js
@@ -29,6 +29,12 @@ export function elt(tag, content, className, style) {
else if (content) for (let i = 0; i < content.length; ++i) e.appendChild(content[i])
return e
}
+// wrapper for elt, which removes the elt from the accessibility tree
+export function eltP(tag, content, className, style) {
+ let e = elt(tag, content, className, style)
+ e.setAttribute("role", "presentation")
+ return e
+}
export let range
if (document.createRange) range = function(node, start, end, endNode) {
From f2d6eeeb94734429d0d82b05e7ffdece49736bd6 Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Mon, 20 Mar 2017 11:40:04 +0100
Subject: [PATCH 0560/2085] [fold demo] Use htmlmixed mode in HTML folding demo
---
demo/folding.html | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)
diff --git a/demo/folding.html b/demo/folding.html
index 06d5830208..1882b7c810 100644
--- a/demo/folding.html
+++ b/demo/folding.html
@@ -5,6 +5,13 @@
+
+
@@ -17,6 +24,8 @@
+
+
+ The N-Triples mode also works well with on
+ N-Quad documents.
+
.
@@ -42,4 +46,25 @@
var editor = CodeMirror.fromTextArea(document.getElementById("ntriples"), {});
MIME types defined: application/n-triples.
+
+
+ N-Quads add a fourth
+ element to the statement to track which graph the statement is from.
+ Otherwise, it's identical to N-Triples.
+
+
+
+ .
+ "literal 1" .
+ _:bnode3 .
+ _:bnode4 "literal 2"@lang .
+ # if a graph labe
+ _:bnode5 "literal 3"^^ .
+
+
+
+
+ MIME types defined: application/n-quads.
diff --git a/mode/ntriples/ntriples.js b/mode/ntriples/ntriples.js
index 765be0cfb3..148272e1fd 100644
--- a/mode/ntriples/ntriples.js
+++ b/mode/ntriples/ntriples.js
@@ -185,6 +185,10 @@ CodeMirror.defineMode("ntriples", function() {
// https://www.w3.org/TR/n-triples/#n-triples-mediatype
CodeMirror.defineMIME("application/n-triples", "ntriples");
+// N-Quads is based on the N-Triples format (so same highlighting works)
+// https://www.w3.org/TR/n-quads/
+CodeMirror.defineMIME("application/n-quads", "ntriples");
+
// previously used, though technically incorrect media type for n-triples
CodeMirror.defineMIME("text/n-triples", "ntriples");
From d9e926358854f997b8cc900b9ec2eb777f7ebaab Mon Sep 17 00:00:00 2001
From: Benjamin Young
Date: Tue, 11 Jul 2017 10:24:44 -0400
Subject: [PATCH 0736/2085] Add application/n-triples, n-quads, .nq to meta
---
mode/meta.js | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/mode/meta.js b/mode/meta.js
index c4aa32b4ca..edaae033eb 100644
--- a/mode/meta.js
+++ b/mode/meta.js
@@ -91,7 +91,8 @@
{name: "MySQL", mime: "text/x-mysql", mode: "sql"},
{name: "Nginx", mime: "text/x-nginx-conf", mode: "nginx", file: /nginx.*\.conf$/i},
{name: "NSIS", mime: "text/x-nsis", mode: "nsis", ext: ["nsh", "nsi"]},
- {name: "NTriples", mime: "text/n-triples", mode: "ntriples", ext: ["nt"]},
+ {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: "OCaml", mime: "text/x-ocaml", mode: "mllike", ext: ["ml", "mli", "mll", "mly"]},
{name: "Octave", mime: "text/x-octave", mode: "octave", ext: ["m"]},
From 2f695c1eb2705c419f449d72925a2340d72f6a69 Mon Sep 17 00:00:00 2001
From: dwelle
Date: Thu, 13 Jul 2017 11:00:43 +0200
Subject: [PATCH 0737/2085] add .editorconfig
---
.editorconfig | 7 +++++++
1 file changed, 7 insertions(+)
create mode 100644 .editorconfig
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000000..7ed020678d
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,7 @@
+root = true
+
+[*]
+indent_style = space
+indent_size = 2
+end_of_line = lf
+charset = utf-8
From 8005b65181328c1fa94efc08148bb8ca37f085cd Mon Sep 17 00:00:00 2001
From: dwelle
Date: Thu, 13 Jul 2017 11:01:01 +0200
Subject: [PATCH 0738/2085] ensure lf eol on checkout
---
.gitattributes | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/.gitattributes b/.gitattributes
index f8bdd60f49..1c8c82d8f1 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -1,8 +1,8 @@
-*.txt text
-*.js text
-*.html text
-*.md text
-*.json text
-*.yml text
-*.css text
-*.svg text
+*.txt text eol=lf
+*.js text eol=lf
+*.html text eol=lf
+*.md text eol=lf
+*.json text eol=lf
+*.yml text eol=lf
+*.css text eol=lf
+*.svg text eol=lf
From 3073bdd97a4e55c3fa8c69ecbabdc18c4dd0b3a7 Mon Sep 17 00:00:00 2001
From: Nicolas Kick
Date: Thu, 13 Jul 2017 20:14:35 +0200
Subject: [PATCH 0739/2085] [sublime keymap] "Add to selection" bindings
(Ctrl-Alt-Up/Down on Windows, Cmd-Shift-Up/Down on Mac)
---
keymap/sublime.js | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
diff --git a/keymap/sublime.js b/keymap/sublime.js
index 0ce8955875..98266e44f0 100644
--- a/keymap/sublime.js
+++ b/keymap/sublime.js
@@ -165,6 +165,23 @@
cm.state.sublimeFindFullWord = cm.doc.sel;
};
+ function addCursorToSelection(cm, dir) {
+ var ranges = cm.listSelections(), newRanges = [];
+ for (var i = 0; i < ranges.length; i++) {
+ var range = ranges[i];
+ var newAnchor = cm.findPosV(range.anchor, dir, "line");
+ var newHead = cm.findPosV(range.head, dir, "line");
+ var newRange = {anchor: newAnchor, head: newHead};
+ newRanges.push(range);
+ newRanges.push(newRange);
+ }
+ 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); };
+
function isSelectedRange(ranges, from, to) {
for (var i = 0; i < ranges.length; i++)
if (ranges[i].from() == from && ranges[i].to() == to) return true
From 9a223d81a6233e916f730463a842ffd58cb960ab Mon Sep 17 00:00:00 2001
From: Tyler Long
Date: Fri, 14 Jul 2017 10:39:05 +0800
Subject: [PATCH 0740/2085] [midnight theme] Remove top border and bottom
border
Because the borders don't make much visual sense. And they cause dual
scroll bar issue when the editor takes 100% of page height.
You ca reproduce the issue here: http://mdp.tylingsoft.com/ Press
CMD-, to bring up the preferences panel and change the
theme to midnight.
---
theme/midnight.css | 2 --
1 file changed, 2 deletions(-)
diff --git a/theme/midnight.css b/theme/midnight.css
index e41f10560f..17ed39c8bc 100644
--- a/theme/midnight.css
+++ b/theme/midnight.css
@@ -12,8 +12,6 @@
color: #D1EDFF;
}
-.cm-s-midnight.CodeMirror { border-top: 1px solid black; border-bottom: 1px solid black; }
-
.cm-s-midnight div.CodeMirror-selected { background: #314D67; }
.cm-s-midnight .CodeMirror-line::selection, .cm-s-midnight .CodeMirror-line > span::selection, .cm-s-midnight .CodeMirror-line > span > span::selection { background: rgba(49, 77, 103, .99); }
.cm-s-midnight .CodeMirror-line::-moz-selection, .cm-s-midnight .CodeMirror-line > span::-moz-selection, .cm-s-midnight .CodeMirror-line > span > span::-moz-selection { background: rgba(49, 77, 103, .99); }
From e371c094ccf1ba1de429b3ac099bb0053558d0ca Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Fri, 14 Jul 2017 10:56:17 +0200
Subject: [PATCH 0741/2085] [sql mode] Make multiple dots their own token
Closes #4855
---
mode/sql/sql.js | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/mode/sql/sql.js b/mode/sql/sql.js
index 62775128d4..9f5c7987e5 100644
--- a/mode/sql/sql.js
+++ b/mode/sql/sql.js
@@ -47,8 +47,8 @@ CodeMirror.defineMode("sql", function(config, parserConfig) {
} else if (ch.charCodeAt(0) > 47 && ch.charCodeAt(0) < 58) {
// 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 && stream.eat('.');
+ stream.match(/^[0-9]*(\.[0-9]+)?([eE][-+]?[0-9]+)?/);
+ support.decimallessFloat && stream.match(/^\.(?!\.)/);
return "number";
} else if (ch == "?" && (stream.eatSpace() || stream.eol() || stream.eat(";"))) {
// placeholders
@@ -84,14 +84,14 @@ CodeMirror.defineMode("sql", function(config, parserConfig) {
return state.tokenize(stream, state);
} else if (ch == ".") {
// .1 for 0.1
- if (support.zerolessFloat && stream.match(/^(?:\d+(?:e[+-]?\d+)?)/i)) {
+ if (support.zerolessFloat && stream.match(/^(?:\d+(?:e[+-]?\d+)?)/i))
return "number";
- }
+ if (stream.match(/^\.+/))
+ return null
// .table_name (ODBC)
// // ref: http://dev.mysql.com/doc/refman/5.6/en/identifier-qualifiers.html
- if (support.ODBCdotTable && stream.match(/^[\w\d_]+/)) {
+ if (support.ODBCdotTable && stream.match(/^[\w\d_]+/))
return "variable-2";
- }
} else if (operatorChars.test(ch)) {
// operators
stream.eatWhile(operatorChars);
From 0f8d75934cc2d058670aa9976f132049d1311a34 Mon Sep 17 00:00:00 2001
From: dwelle
Date: Fri, 14 Jul 2017 22:01:46 +0200
Subject: [PATCH 0742/2085] [markdown mode] Further align list tokenizer with
CommonMark
---
mode/markdown/markdown.js | 55 +++++++++++++++++++++------------------
mode/markdown/test.js | 47 +++++++++++++++++++++++++++++++++
2 files changed, 76 insertions(+), 26 deletions(-)
diff --git a/mode/markdown/markdown.js b/mode/markdown/markdown.js
index 638d9aad72..78a9c100d9 100644
--- a/mode/markdown/markdown.js
+++ b/mode/markdown/markdown.js
@@ -87,6 +87,7 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
, fencedCodeRE = new RegExp("^(" + (modeCfg.fencedCodeBlocks === true ? "~~~+|```+" : modeCfg.fencedCodeBlocks) +
")[ \\t]*([\\w+#\-]*)")
, punctuation = /[!\"#$%&\'()*+,\-\.\/:;<=>?@\[\\\]^_`{|}~—]/
+ , expandedTab = " " // CommonMark specifies tab as 4 spaces
function switchInline(stream, state, f) {
state.f = state.inline = f;
@@ -138,23 +139,34 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
state.indentedCode = false;
- if (prevLineIsList) {
- if (state.indentationDiff >= 0) { // Continued list
- if (state.indentationDiff < 4) { // Only adjust indentation if *not* a code block
- state.indentation -= state.indentationDiff;
- }
- state.list = null;
- } else if (state.indentation > 0) {
+ var lineIndentation;
+ // compute once per line (on first token)
+ if (state.indentationDiff === null) {
+ lineIndentation = state.indentation;
+ state.indentationDiff = state.indentation;
+ if (prevLineIsList) {
state.list = null;
- } else { // No longer a list
- state.list = false;
+ // While this list item's marker's indentation is less than the deepest
+ // list item's content's indentation,pop the deepest list item
+ // indentation off the stack, and update block indentation state
+ while (lineIndentation < state.listStack[state.listStack.length - 1]) {
+ state.listStack.pop();
+ if (state.listStack.length) {
+ state.indentation = state.listStack[state.listStack.length - 1];
+ // less than the first list's indent -> the line is no longer a list
+ } else {
+ state.list = false;
+ }
+ }
+ if (state.list !== false) {
+ state.indentationDiff = lineIndentation - state.listStack[state.listStack.length - 1]
+ }
}
}
var match = null;
if (state.indentationDiff >= 4 && (prevLineIsIndentedCode || lineIsEmpty(state.prevLine))) {
stream.skipToEnd();
- state.indentation -= 4;
state.indentedCode = true;
return tokenTypes.code;
} else if (stream.eatSpace()) {
@@ -182,15 +194,9 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
return tokenTypes.hr;
} else if (match = stream.match(listRE)) {
var listType = match[1] ? "ol" : "ul";
- state.indentation = stream.column() + stream.current().length;
- state.list = true;
- // While this list item's marker's indentation
- // is less than the deepest list item's content's indentation,
- // pop the deepest list item indentation off the stack.
- while (state.listStack && stream.column() < state.listStack[state.listStack.length - 1]) {
- state.listStack.pop();
- }
+ state.indentation = lineIndentation + stream.current().length;
+ state.list = true;
// Add this list item's content's indentation to the stack
state.listStack.push(state.indentation);
@@ -739,16 +745,13 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
state.formatting = false;
if (stream != state.thisLine) {
- var forceBlankLine = state.header || state.hr;
-
// Reset state.header and state.hr
state.header = 0;
state.hr = false;
- if (stream.match(/^\s*$/, true) || forceBlankLine) {
+ if (stream.match(/^\s*$/, true)) {
blankLine(state);
- if (!forceBlankLine) return null
- state.prevLine = null
+ return null;
}
state.prevLine = state.thisLine
@@ -763,9 +766,9 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
state.f = state.block;
if (state.f != htmlBlock) {
- var indentation = stream.match(/^\s*/, true)[0].replace(/\t/g, ' ').length;
- state.indentationDiff = Math.min(indentation - state.indentation, 4);
- state.indentation = state.indentation + state.indentationDiff;
+ var indentation = stream.match(/^\s*/, true)[0].replace(/\t/g, expandedTab).length;
+ state.indentation = indentation;
+ state.indentationDiff = null;
if (indentation > 0) return null;
}
}
diff --git a/mode/markdown/test.js b/mode/markdown/test.js
index 04c72fdaf3..ef59890607 100644
--- a/mode/markdown/test.js
+++ b/mode/markdown/test.js
@@ -469,6 +469,53 @@
" [variable-3 * Otherwise, it will be part of the level where it does meet this.]",
" [variable-2 * World]");
+ // should handle nested and un-nested lists
+ MT("listCommonMark_MixedIndents",
+ "[variable-2 * list1]",
+ " [variable-2 list1]",
+ " [variable-2&header&header-1 # heading still part of list1]",
+ " [variable-2 text after heading still part of list1]",
+ "",
+ " [comment indented codeblock]",
+ " [variable-2 list1 after code block]",
+ " [variable-3 * list2]",
+ // amount of spaces on empty lines between lists doesn't matter
+ " ",
+ // extra empty lines irrelevant
+ "",
+ "",
+ " [variable-3 indented text part of list2]",
+ " [keyword * list3]",
+ "",
+ " [variable-3 text at level of list2]",
+ "",
+ " [variable-2 de-indented text part of list1 again]",
+ "",
+ " [variable-2&comment ```]",
+ " [variable-2&comment code]",
+ " [variable-2&comment ```]",
+ "",
+ " [variable-2 text after fenced code]");
+
+ // should correctly parse numbered list content indentation
+ MT("listCommonMark_NumeberedListIndent",
+ "[variable-2 1000. list with base indent of 6]",
+ "",
+ " [variable-2 text must be indented 6 spaces at minimum]",
+ "",
+ " [variable-2 9-spaces indented text still part of list]",
+ "",
+ " [comment indented codeblock starts at 10 spaces]",
+ "",
+ " [comment text indented by 5 spaces no longer belong to list]");
+
+ // should consider tab as 4 spaces
+ MT("listCommonMark_TabIndented",
+ "[variable-2 * list]",
+ "\t[variable-3 * list2]",
+ "",
+ "\t\t[variable-3 part of list2]");
+
// Blockquote
MT("blockquote",
"[variable-2 * foo]",
From 7a2fa50d94c9a00771c33d28dfd8c58600aacd41 Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Mon, 17 Jul 2017 11:59:03 +0200
Subject: [PATCH 0743/2085] [tern addon] Make argument hint tooltip vanish on
blur/scroll/etc
Issue #3059
---
addon/tern/tern.js | 36 ++++++++++++++++++++++++++----------
1 file changed, 26 insertions(+), 10 deletions(-)
diff --git a/addon/tern/tern.js b/addon/tern/tern.js
index efdf2ed628..644e495f65 100644
--- a/addon/tern/tern.js
+++ b/addon/tern/tern.js
@@ -334,7 +334,11 @@
tip.appendChild(document.createTextNode(tp.rettype ? ") ->\u00a0" : ")"));
if (tp.rettype) tip.appendChild(elt("span", cls + "type", tp.rettype));
var place = cm.cursorCoords(null, "page");
- ts.activeArgHints = makeTooltip(place.right + 1, place.bottom, tip);
+ var tooltip = ts.activeArgHints = makeTooltip(place.right + 1, place.bottom, tip)
+ setTimeout(function() {
+ tooltip.clear = onEditorActivity(cm, function() {
+ if (ts.activeArgHints == tooltip) closeArgHints(ts) })
+ }, 20)
}
function parseFnType(text) {
@@ -604,11 +608,8 @@
}
function clear() {
cm.state.ternTooltip = null;
- if (!tip.parentNode) return;
- cm.off("cursorActivity", clear);
- cm.off('blur', clear);
- cm.off('scroll', clear);
- fadeOut(tip);
+ if (tip.parentNode) fadeOut(tip)
+ clearActivity()
}
var mouseOnTip = false, old = false;
CodeMirror.on(tip, "mousemove", function() { mouseOnTip = true; });
@@ -619,9 +620,20 @@
}
});
setTimeout(maybeClear, ts.options.hintDelay ? ts.options.hintDelay : 1700);
- cm.on("cursorActivity", clear);
- cm.on('blur', clear);
- cm.on('scroll', clear);
+ var clearActivity = onEditorActivity(cm, clear)
+ }
+
+ function onEditorActivity(cm, f) {
+ cm.on("cursorActivity", f)
+ cm.on("blur", f)
+ cm.on("scroll", f)
+ cm.on("setDoc", f)
+ return function() {
+ cm.off("cursorActivity", f)
+ cm.off("blur", f)
+ cm.off("scroll", f)
+ cm.off("setDoc", f)
+ }
}
function makeTooltip(x, y, content) {
@@ -650,7 +662,11 @@
}
function closeArgHints(ts) {
- if (ts.activeArgHints) { remove(ts.activeArgHints); ts.activeArgHints = null; }
+ if (ts.activeArgHints) {
+ if (ts.activeArgHints.clear) ts.activeArgHints.clear()
+ remove(ts.activeArgHints)
+ ts.activeArgHints = null
+ }
}
function docValue(ts, doc) {
From 03d99e588aee36e0f1bb81a5ff2cc2dc343d5380 Mon Sep 17 00:00:00 2001
From: George Stephanis
Date: Thu, 29 Jun 2017 14:07:31 -0400
Subject: [PATCH 0744/2085] [lint plugins] Provide better error messages if the
lint library is missing
This should help developers building this functionality out that may not
realize they need to source the linting library seperately.
---
addon/lint/coffeescript-lint.js | 6 ++++++
addon/lint/css-lint.js | 7 ++++++-
addon/lint/html-lint.js | 7 ++++++-
addon/lint/javascript-lint.js | 7 ++++++-
addon/lint/json-lint.js | 6 ++++++
addon/lint/yaml-lint.js | 6 ++++++
6 files changed, 36 insertions(+), 3 deletions(-)
diff --git a/addon/lint/coffeescript-lint.js b/addon/lint/coffeescript-lint.js
index 7e39428f7a..70621a1bdd 100644
--- a/addon/lint/coffeescript-lint.js
+++ b/addon/lint/coffeescript-lint.js
@@ -17,6 +17,12 @@
CodeMirror.registerHelper("lint", "coffeescript", function(text) {
var found = [];
+ if (!window.coffeelint) {
+ if (window.console) {
+ window.console.error("Error: window.coffeelint not defined, CodeMirror CoffeeScript linting cannot run.");
+ }
+ return found;
+ }
var parseError = function(err) {
var loc = err.lineNumber;
found.push({from: CodeMirror.Pos(loc-1, 0),
diff --git a/addon/lint/css-lint.js b/addon/lint/css-lint.js
index 1f61b479b2..b7e1a4fe5d 100644
--- a/addon/lint/css-lint.js
+++ b/addon/lint/css-lint.js
@@ -17,7 +17,12 @@
CodeMirror.registerHelper("lint", "css", function(text) {
var found = [];
- if (!window.CSSLint) return found;
+ if (!window.CSSLint) {
+ if (window.console) {
+ window.console.error("Error: window.CSSLint not defined, CodeMirror CSS linting cannot run.");
+ }
+ return found;
+ }
var results = CSSLint.verify(text), messages = results.messages, message = null;
for ( var i = 0; i < messages.length; i++) {
message = messages[i];
diff --git a/addon/lint/html-lint.js b/addon/lint/html-lint.js
index 1e8417098d..98c36b0b64 100644
--- a/addon/lint/html-lint.js
+++ b/addon/lint/html-lint.js
@@ -29,7 +29,12 @@
CodeMirror.registerHelper("lint", "html", function(text, options) {
var found = [];
- if (!window.HTMLHint) return found;
+ if (!window.HTMLHint) {
+ if (window.console) {
+ window.console.error("Error: window.HTMLHint not defined, CodeMirror HTML linting cannot run.");
+ }
+ return found;
+ }
var messages = HTMLHint.verify(text, options && options.rules || defaultRules);
for (var i = 0; i < messages.length; i++) {
var message = messages[i];
diff --git a/addon/lint/javascript-lint.js b/addon/lint/javascript-lint.js
index d4f2ae9a1f..c58f785025 100644
--- a/addon/lint/javascript-lint.js
+++ b/addon/lint/javascript-lint.js
@@ -22,7 +22,12 @@
"Unclosed string", "Stopping, unable to continue" ];
function validator(text, options) {
- if (!window.JSHINT) return [];
+ if (!window.JSHINT) {
+ if (window.console) {
+ window.console.error("Error: window.JSHINT not defined, CodeMirror JavaScript linting cannot run.");
+ }
+ return [];
+ }
JSHINT(text, options, options.globals);
var errors = JSHINT.data().errors, result = [];
if (errors) parseErrors(errors, result);
diff --git a/addon/lint/json-lint.js b/addon/lint/json-lint.js
index 9dbb616b3b..849641ee5e 100644
--- a/addon/lint/json-lint.js
+++ b/addon/lint/json-lint.js
@@ -17,6 +17,12 @@
CodeMirror.registerHelper("lint", "json", function(text) {
var found = [];
+ if (!window.jsonlint) {
+ if (window.console) {
+ window.console.error("Error: window.jsonlint not defined, CodeMirror JSON linting cannot run.");
+ }
+ return found;
+ }
jsonlint.parseError = function(str, hash) {
var loc = hash.loc;
found.push({from: CodeMirror.Pos(loc.first_line - 1, loc.first_column),
diff --git a/addon/lint/yaml-lint.js b/addon/lint/yaml-lint.js
index 90b0eaab57..3954ed38eb 100644
--- a/addon/lint/yaml-lint.js
+++ b/addon/lint/yaml-lint.js
@@ -17,6 +17,12 @@
CodeMirror.registerHelper("lint", "yaml", function(text) {
var found = [];
+ if (!window.jsyaml) {
+ if (window.console) {
+ window.console.error("Error: window.jsyaml not defined, CodeMirror YAML linting cannot run.");
+ }
+ return found;
+ }
try { jsyaml.load(text); }
catch(e) {
var loc = e.mark,
From 99c8d8b9029d7a32884f9a21ead90039144c53da Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Michal=20=C4=8Ciha=C5=99?=
Date: Tue, 18 Jul 2017 11:02:03 +0200
Subject: [PATCH 0745/2085] [lint plugins] Allow lint plugins to output HTML
formatted messages
This is opt-in, the plugin needs to produce message_html instead of
message to be rendered as HTML.
---
addon/lint/lint.js | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/addon/lint/lint.js b/addon/lint/lint.js
index 4e0c71fb65..825065ed2a 100644
--- a/addon/lint/lint.js
+++ b/addon/lint/lint.js
@@ -112,7 +112,11 @@
if (!severity) severity = "error";
var tip = document.createElement("div");
tip.className = "CodeMirror-lint-message-" + severity;
- tip.appendChild(document.createTextNode(ann.message));
+ if (typeof ann.messageHTML != 'undefined') {
+ tip.innerHTML = ann.messageHTML;
+ } else {
+ tip.appendChild(document.createTextNode(ann.message));
+ }
return tip;
}
From b11179d1f6e1a82bb43d9cdecb03052119b89e09 Mon Sep 17 00:00:00 2001
From: dwelle
Date: Thu, 20 Jul 2017 22:59:13 +0200
Subject: [PATCH 0746/2085] [gfm mode] Add emoji rule
---
mode/gfm/gfm.js | 3 ++-
mode/gfm/index.html | 6 +++++-
mode/gfm/test.js | 7 +++++++
mode/markdown/markdown.js | 19 +++++++++++++++++--
mode/markdown/test.js | 7 ++++++-
5 files changed, 37 insertions(+), 5 deletions(-)
diff --git a/mode/gfm/gfm.js b/mode/gfm/gfm.js
index 23126ff465..aac04812d8 100644
--- a/mode/gfm/gfm.js
+++ b/mode/gfm/gfm.js
@@ -115,7 +115,8 @@ CodeMirror.defineMode("gfm", function(config, modeConfig) {
var markdownConfig = {
taskLists: true,
fencedCodeBlocks: '```',
- strikethrough: true
+ strikethrough: true,
+ emoji: true
};
for (var attr in modeConfig) {
markdownConfig[attr] = modeConfig[attr];
diff --git a/mode/gfm/index.html b/mode/gfm/index.html
index 24c90c068e..642c8ce71d 100644
--- a/mode/gfm/index.html
+++ b/mode/gfm/index.html
@@ -15,7 +15,10 @@
-
+
CodeMirror
@@ -73,6 +76,7 @@
GFM mode
* \#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/.
diff --git a/mode/gfm/test.js b/mode/gfm/test.js
index 3d7d7e0132..fa6db91a20 100644
--- a/mode/gfm/test.js
+++ b/mode/gfm/test.js
@@ -29,6 +29,9 @@
FT("formatting_strikethrough",
"foo [strikethrough&formatting&formatting-strikethrough ~~][strikethrough bar][strikethrough&formatting&formatting-strikethrough ~~]");
+ FT("formatting_emoji",
+ "foo [emoji&formatting&formatting-emoji :smile:] foo");
+
MT("emInWordAsterisk",
"foo[em *bar*]hello");
@@ -231,4 +234,8 @@
MT("strikethroughStrong",
"[strong **][strong&strikethrough ~~foo~~][strong **]");
+ MT("emoji",
+ "text [emoji :blush:] text [emoji :v:] text [emoji :+1:] text",
+ ":text text: [emoji :smiley_cat:]");
+
})();
diff --git a/mode/markdown/markdown.js b/mode/markdown/markdown.js
index 78a9c100d9..f67b4244d0 100644
--- a/mode/markdown/markdown.js
+++ b/mode/markdown/markdown.js
@@ -47,6 +47,9 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
if (modeCfg.strikethrough === undefined)
modeCfg.strikethrough = false;
+ if (modeCfg.emoji === undefined)
+ modeCfg.emoji = false;
+
// Allow token types to be overridden by user-provided token types.
if (modeCfg.tokenTypeOverrides === undefined)
modeCfg.tokenTypeOverrides = {};
@@ -69,7 +72,8 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
linkHref: "string",
em: "em",
strong: "strong",
- strikethrough: "strikethrough"
+ strikethrough: "strikethrough",
+ emoji: "emoji"
};
for (var tokenType in tokenTypes) {
@@ -83,7 +87,7 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
, taskListRE = /^\[(x| )\](?=\s)/ // Must follow listRE
, atxHeaderRE = modeCfg.allowAtxHeaderWithoutSpace ? /^(#+)/ : /^(#+)(?: |$)/
, setextHeaderRE = /^ *(?:\={1,}|-{1,})\s*$/
- , textRE = /^[^#!\[\]*_\\<>` "'(~]+/
+ , textRE = /^[^#!\[\]*_\\<>` "'(~:]+/
, fencedCodeRE = new RegExp("^(" + (modeCfg.fencedCodeBlocks === true ? "~~~+|```+" : modeCfg.fencedCodeBlocks) +
")[ \\t]*([\\w+#\-]*)")
, punctuation = /[!\"#$%&\'()*+,\-\.\/:;<=>?@\[\\\]^_`{|}~—]/
@@ -299,6 +303,7 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
if (state.strong) { styles.push(tokenTypes.strong); }
if (state.em) { styles.push(tokenTypes.em); }
if (state.strikethrough) { styles.push(tokenTypes.strikethrough); }
+ if (state.emoji) { styles.push(tokenTypes.emoji); }
if (state.linkText) { styles.push(tokenTypes.linkText); }
if (state.code) { styles.push(tokenTypes.code); }
if (state.image) { styles.push(tokenTypes.image); }
@@ -556,6 +561,14 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
}
}
+ if (modeCfg.emoji && ch === ":" && stream.match(/^[a-z_\d+-]+:/)) {
+ state.emoji = true;
+ if (modeCfg.highlightFormatting) state.formatting = "emoji";
+ var retType = getType(state);
+ state.emoji = false;
+ return retType;
+ }
+
if (ch === ' ') {
if (stream.match(/ +$/, false)) {
state.trailingSpace++;
@@ -698,6 +711,7 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
trailingSpace: 0,
trailingSpaceNewLine: false,
strikethrough: false,
+ emoji: false,
fencedChars: null
};
},
@@ -725,6 +739,7 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
em: s.em,
strong: s.strong,
strikethrough: s.strikethrough,
+ emoji: s.emoji,
header: s.header,
hr: s.hr,
taskList: s.taskList,
diff --git a/mode/markdown/test.js b/mode/markdown/test.js
index ef59890607..8dac53aade 100644
--- a/mode/markdown/test.js
+++ b/mode/markdown/test.js
@@ -14,6 +14,7 @@
var modeOverrideClasses = CodeMirror.getMode(config, {
name: "markdown",
strikethrough: true,
+ emoji: true,
tokenTypeOverrides: {
"header" : "override-header",
"code" : "override-code",
@@ -31,7 +32,8 @@
"linkHref" : "override-link-href",
"em" : "override-em",
"strong" : "override-strong",
- "strikethrough" : "override-strikethrough"
+ "strikethrough" : "override-strikethrough",
+ "emoji" : "override-emoji"
}});
function TokenTypeOverrideTest(name) { test.mode(name, modeOverrideClasses, Array.prototype.slice.call(arguments, 1)); }
var modeFormattingOverride = CodeMirror.getMode(config, {
@@ -966,6 +968,9 @@
TokenTypeOverrideTest("overrideStrikethrough",
"[override-strikethrough ~~foo~~]");
+ TokenTypeOverrideTest("overrideEmoji",
+ "[override-emoji :foo:]");
+
FormatTokenTypeOverrideTest("overrideFormatting",
"[override-formatting-escape \\*]");
From 4292c2ff1bad00278c87e0d3bb0e7fd8d32a4ab8 Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Fri, 21 Jul 2017 11:51:38 +0200
Subject: [PATCH 0747/2085] [markdown mode] Change default emoji style to
'builtin'
Issue #4864
---
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 f67b4244d0..db8359e9b9 100644
--- a/mode/markdown/markdown.js
+++ b/mode/markdown/markdown.js
@@ -73,7 +73,7 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
em: "em",
strong: "strong",
strikethrough: "strikethrough",
- emoji: "emoji"
+ emoji: "builtin"
};
for (var tokenType in tokenTypes) {
From cb01fa18c8d42605489deda0d16881b42246c6c4 Mon Sep 17 00:00:00 2001
From: Kevin Kwok
Date: Wed, 19 Jul 2017 12:00:04 -0700
Subject: [PATCH 0748/2085] [sql mode] Set lineComment to "--" when no other
style is used
---
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 9f5c7987e5..c98d9f686b 100644
--- a/mode/sql/sql.js
+++ b/mode/sql/sql.js
@@ -193,7 +193,7 @@ CodeMirror.defineMode("sql", function(config, parserConfig) {
blockCommentStart: "/*",
blockCommentEnd: "*/",
- lineComment: support.commentSlashSlash ? "//" : support.commentHash ? "#" : null
+ lineComment: support.commentSlashSlash ? "//" : support.commentHash ? "#" : "--"
};
});
From 6cad72fc3b484d3ea50c8cbb47ec98b8b795dd65 Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Fri, 21 Jul 2017 11:59:27 +0200
Subject: [PATCH 0749/2085] [gfm mode] Adjust tests to to change in emoji token
type
---
mode/gfm/test.js | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/mode/gfm/test.js b/mode/gfm/test.js
index fa6db91a20..5c8b0332ac 100644
--- a/mode/gfm/test.js
+++ b/mode/gfm/test.js
@@ -30,7 +30,7 @@
"foo [strikethrough&formatting&formatting-strikethrough ~~][strikethrough bar][strikethrough&formatting&formatting-strikethrough ~~]");
FT("formatting_emoji",
- "foo [emoji&formatting&formatting-emoji :smile:] foo");
+ "foo [builtin&formatting&formatting-emoji :smile:] foo");
MT("emInWordAsterisk",
"foo[em *bar*]hello");
@@ -235,7 +235,7 @@
"[strong **][strong&strikethrough ~~foo~~][strong **]");
MT("emoji",
- "text [emoji :blush:] text [emoji :v:] text [emoji :+1:] text",
- ":text text: [emoji :smiley_cat:]");
+ "text [builtin :blush:] text [builtin :v:] text [builtin :+1:] text",
+ ":text text: [builtin :smiley_cat:]");
})();
From cde3e9902d56c6eda26669d0076b88f8a8a0833b Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Fri, 21 Jul 2017 12:08:50 +0200
Subject: [PATCH 0750/2085] Add anchor ids to startOperation/endOperation docs
---
doc/manual.html | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/doc/manual.html b/doc/manual.html
index 829cf7476a..8fcd5b45c0 100644
--- a/doc/manual.html
+++ b/doc/manual.html
@@ -2048,8 +2048,8 @@ Miscellaneous methods
lot faster. The return value from this method will be the return
value of your function.
- 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 0751/2085] 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 88e625bec0..07b8fbd09e 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 62b337e7c9..2c0887ed5c 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 8fcd5b45c0..b4500c375b 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 15bd24e163..86cadb1512 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 :
-
- 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 ef824b0b0a..e413d2c57b 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 54a7a6e3cc..5a20c6473e 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 0752/2085] 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 b4500c375b..da4423f960 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 e413d2c57b..ff6971cd8e 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 5a20c6473e..e7a2122d6e 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 0753/2085] [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 642c8ce71d..bec130ca51 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 0758/2085] 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 b008351a62..f4d3c5f40b 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 0759/2085] [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 2d2b08c810..c318793207 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 0760/2085] [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 db8359e9b9..d646d884b4 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 8dac53aade..c2ac548305 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 0761/2085] [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 dc8c74cdf4..d6c61facf3 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 0762/2085] [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 b11d51e6ca..d7385ef223 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 0763/2085] 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 edaae033eb..20973ace3d 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 0b56300b12..e42f4b5f3b 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 6af814c43e..c5619afe7c 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 0764/2085] 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 d830903767..fa1b0f8c61 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 8ba1b5c76c..4d584efbcd 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 20973ace3d..b08ff933f4 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 0765/2085] 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 adf2184fd7..ae955db344 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 93a5f4f309..92d161e9dd 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 b08ff933f4..d1c42a03a7 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 0766/2085] [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 c1a9c6a990..950eed51e6 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 0767/2085] Fix broken links
---
CHANGELOG.md | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2c0887ed5c..49290390fd 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 0768/2085] 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 86cadb1512..1c882dc310 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 0769/2085] [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 d646d884b4..59a17012da 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 c2ac548305..c2c9fb1120 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 0770/2085] [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 59a17012da..d7f225f82f 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 c2c9fb1120..d01b07300a 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 0771/2085] [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 d7f225f82f..6082eab8b2 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 d01b07300a..e9025882f9 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 0772/2085] [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 6082eab8b2..0692f7eac3 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 e9025882f9..21829afbd0 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 0773/2085] [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 aac04812d8..689cd6e2ec 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 5c8b0332ac..9cda5c45a3 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 15660c2618..77f56ab200 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><blink></code> tags.</p>
-
+
<p>I wish SmartyPants used named entities like
<code>&mdash;</code> instead of decimal-encoded
entites like <code>&#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><blockquote>
<p>For example.</p>
</blockquote>
</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 0692f7eac3..beb29dd1e6 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 21829afbd0..eb9d856abe 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 0774/2085] [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 beb29dd1e6..58f1dff8aa 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 eb9d856abe..86935c9587 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 ][tag div][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 0775/2085] [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 bec130ca51..ea4bac15fe 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 77f56ab200..abb379f61a 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 0776/2085] 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 d103d2d08f..67de3b1836 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 0777/2085] 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 214e0231ab..308dc6b399 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 0778/2085] [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 056c48e680..bfe11d3b05 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 7a496fb091..6fc6e33ca5 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 0779/2085] [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 d1c42a03a7..c49bd6c737 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 0780/2085] 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 ceaffd32e6..645e648c86 100644
--- a/demo/bidi.html
+++ b/demo/bidi.html
@@ -60,6 +60,11 @@ Bi-directional Text Demo
LTR
RTL
+
+ HTML document direction:
+ LTR
+ RTL
+
Use visual order for arrow key movement.
@@ -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 f4d3c5f40b..9d8ff0ce66 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 0781/2085] [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 c5619afe7c..9b8b90b305 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 0782/2085] [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 d77862b1d5..50d33dde31 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 8fd13aee49..2632fd1df5 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 0783/2085] [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 98c36b0b64..23de9bb204 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 0784/2085] [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 3995f93e9d..7c5231ba81 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 0785/2085] [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 dba069dc81..cd95872820 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 32a6c28080..e61365d690 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 0826/2085] 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 7c5231ba81..f0a75abf72 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 0827/2085] 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 349a3dba3e..645e648c86 100644
--- a/demo/bidi.html
+++ b/demo/bidi.html
@@ -26,9 +26,8 @@
Bi-directional Text Demo
-
+
- هل يمكنك اختيار مستوى قسط التأمين الذي ترغب بدفعه؟
value (string or Doc)
@@ -75,8 +74,8 @@ Bi-directional Text Demo
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
mode: "text/html",
lineNumbers: true,
- lineWrapping: false,
- direction: "ltr"
+ lineWrapping: true,
+ direction: "rtl"
});
var dirRadios = {ltr: document.getElementById("ltr"),
@@ -98,9 +97,6 @@ Bi-directional Text Demo
moveCheckbox.onchange = function() {
editor.setOption("rtlMoveVisually", moveCheckbox.checked);
};
-editor.on("cursorActivity", () => console.log(editor.getCursor("anchor"), editor.getCursor("head")))
-console.log(editor.coordsChar({left: 914, top: 122}, "window"))
-
Demonstration of bi-directional text support. See
From 5a5c75fa68da86f5e0b85551d2282301b2c7c856 Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Thu, 14 Sep 2017 11:11:51 +0200
Subject: [PATCH 0828/2085] Make binary search utility only call predicate once
per position
---
src/util/misc.js | 13 +++++++++----
1 file changed, 9 insertions(+), 4 deletions(-)
diff --git a/src/util/misc.js b/src/util/misc.js
index 2ad1d0ed73..39661eb448 100644
--- a/src/util/misc.js
+++ b/src/util/misc.js
@@ -134,12 +134,17 @@ export function skipExtendingChars(str, pos, dir) {
}
// Returns the value from the range [`from`; `to`] that satisfies
-// `pred` and is closest to `from`. Assumes that at least `to` satisfies `pred`.
+// `pred` and is closest to `from`. Assumes that at least `to`
+// satisfies `pred`. Supports `from` being greater than `to`.
export function findFirst(pred, from, to) {
+ // At any point we are certain `to` satisfies `pred`, don't know
+ // whether `from` does.
+ let dir = from > to ? -1 : 1
for (;;) {
- if (Math.abs(from - to) <= 1) return pred(from) ? from : to
- let mid = Math.floor((from + to) / 2)
+ if (from == to) return from
+ let midF = (from + to) / 2, mid = dir < 0 ? Math.ceil(midF) : Math.floor(midF)
+ if (mid == from) return pred(mid) ? from : to
if (pred(mid)) to = mid
- else from = mid
+ else from = mid + dir
}
}
From 834199b2219284e6bfda083b653f5a83f2a01e6e Mon Sep 17 00:00:00 2001
From: Adrian Heine
Date: Thu, 14 Sep 2017 21:56:41 +0200
Subject: [PATCH 0829/2085] [simplemode demo] Fix type of property `token`
---
demo/simplemode.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/demo/simplemode.html b/demo/simplemode.html
index d719b63d18..3774138049 100644
--- a/demo/simplemode.html
+++ b/demo/simplemode.html
@@ -67,7 +67,7 @@ Simple Mode Demo
will be taken into account when matching the token. This regex
should only capture groups when the token property is
an array.
- token : string | null
+ token : string | array<string> | null
An optional token style. Multiple styles can be specified by
separating them with dots or spaces. When the regex for
this rule captures groups, it must capture all of the
From 57c1e62f44aee54967727af407595fc4f0d5a952 Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Thu, 14 Sep 2017 22:37:41 +0200
Subject: [PATCH 0830/2085] [mode/simple addon] Don't try to style match groups
when token is a string
Closes #4970
---
addon/mode/simple.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/addon/mode/simple.js b/addon/mode/simple.js
index 15cc215825..744a66c034 100644
--- a/addon/mode/simple.js
+++ b/addon/mode/simple.js
@@ -136,7 +136,7 @@
state.indent.pop();
var token = rule.token
if (token && token.apply) token = token(matches)
- if (matches.length > 2) {
+ if (matches.length > 2 && typeof rule.token != "string") {
state.pending = [];
for (var j = 2; j < matches.length; j++)
if (matches[j])
From 15b2903cc3ac98099ba13e18c61b34c8c92ec95c Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Fri, 15 Sep 2017 13:05:12 +0200
Subject: [PATCH 0831/2085] Fix drawing of wrapped bidi selections
---
src/display/selection.js | 21 +++++++++++++++++----
src/measurement/position_measurement.js | 1 +
src/util/bidi.js | 4 ++--
test/test.js | 12 +++++++++++-
4 files changed, 31 insertions(+), 7 deletions(-)
diff --git a/src/display/selection.js b/src/display/selection.js
index 32c9a611fe..2030b073d9 100644
--- a/src/display/selection.js
+++ b/src/display/selection.js
@@ -1,7 +1,7 @@
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 { charCoords, cursorCoords, displayWidth, paddingH, wrappedLineExtentChar } from "../measurement/position_measurement"
import { getOrder, iterateBidiSections } from "../util/bidi"
import { elt } from "../util/dom"
@@ -72,7 +72,8 @@ function drawSelectionRange(cm, range, output) {
return charCoords(cm, Pos(line, ch), "div", lineObj, bias)
}
- iterateBidiSections(getOrder(lineObj, doc.direction), fromArg || 0, toArg == null ? lineLen : toArg, (from, to, dir) => {
+ let order = getOrder(lineObj, doc.direction)
+ iterateBidiSections(order, fromArg || 0, toArg == null ? lineLen : toArg, (from, to, dir, i) => {
let fromPos, toPos
if (dir == "ltr") {
fromPos = coords(from, "left")
@@ -94,9 +95,21 @@ function drawSelectionRange(cm, range, output) {
if (toPos.top - fromPos.top <= 3) { // Single line
add(toLeft, toPos.top, fromRight - toLeft, toPos.bottom)
} else { // Multiple lines
- add(leftSide, fromPos.top, fromRight - leftSide, fromPos.bottom)
+ let topLeft = leftSide
+ if (i) {
+ let topEnd = wrappedLineExtentChar(cm, lineObj, null, from).end
+ // The coordinates returned for an RTL wrapped space tend to
+ // be complete bogus, so try to skip that here.
+ topLeft = coords(topEnd - (/\s/.test(lineObj.text.charAt(topEnd - 1)) ? 2 : 1), "left").left
+ }
+ add(topLeft, fromPos.top, fromRight - topLeft, fromPos.bottom)
if (fromPos.bottom < toPos.top) add(leftSide, fromPos.bottom, null, toPos.top)
- add(toLeft, toPos.top, null, toPos.bottom)
+ let botWidth = null
+ if (i < order.length - 1 || true) {
+ let botStart = wrappedLineExtentChar(cm, lineObj, null, to).begin
+ botWidth = coords(botStart, "right").right - toLeft
+ }
+ add(toLeft, toPos.top, botWidth, toPos.bottom)
}
}
diff --git a/src/measurement/position_measurement.js b/src/measurement/position_measurement.js
index 72601559c9..5995e91761 100644
--- a/src/measurement/position_measurement.js
+++ b/src/measurement/position_measurement.js
@@ -442,6 +442,7 @@ function wrappedLineExtent(cm, lineObj, preparedMeasure, y) {
}
export function wrappedLineExtentChar(cm, lineObj, preparedMeasure, target) {
+ if (!preparedMeasure) preparedMeasure = prepareMeasureForLine(cm, lineObj)
let targetTop = intoCoordSystem(cm, lineObj, measureCharPrepared(cm, preparedMeasure, target), "line").top
return wrappedLineExtent(cm, lineObj, preparedMeasure, targetTop)
}
diff --git a/src/util/bidi.js b/src/util/bidi.js
index e7429fce06..c3e63f7a3e 100644
--- a/src/util/bidi.js
+++ b/src/util/bidi.js
@@ -3,12 +3,12 @@ import { lst } from "./misc"
// BIDI HELPERS
export function iterateBidiSections(order, from, to, f) {
- if (!order) return f(from, to, "ltr")
+ if (!order) return f(from, to, "ltr", 0)
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")
+ f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "rtl" : "ltr", i)
found = true
}
}
diff --git a/test/test.js b/test/test.js
index e61365d690..15183511ec 100644
--- a/test/test.js
+++ b/test/test.js
@@ -2473,11 +2473,21 @@ for (var i = 0; i < 5; ++i) {
}
*/
-testCM("bidi_wrapped_selection", function(cm) {
+testCM("rtl_wrapped_selection", function(cm) {
cm.setSelection(Pos(0, 10), Pos(0, 190))
is(byClassName(cm.getWrapperElement(), "CodeMirror-selected").length >= 3)
}, {value: new Array(10).join(" فتي تم تضمينها فتي تم"), lineWrapping: true})
+testCM("bidi_wrapped_selection", function(cm) {
+ cm.setSize(cm.charCoords(Pos(0, 10), "editor").left)
+ cm.setSelection(Pos(0, 37), Pos(0, 80))
+ var blocks = byClassName(cm.getWrapperElement(), "CodeMirror-selected")
+ eq(blocks.length, 2)
+ let boxTop = blocks[0].getBoundingClientRect(), boxBot = blocks[1].getBoundingClientRect()
+ is(boxTop.left > cm.charCoords(Pos(0, 1)).right)
+ is(boxBot.right < cm.charCoords(Pos(0, cm.getLine(0).length - 2)).left)
+}, {value: "مفتي11 تم تضمينهفتي تم تضمينها فتي تفتي تم تضمينها فتي تفتي تم تضمينها فتي تفتي تم تضمينها فتي تا فت10ي ت
", lineWrapping: true})
+
testCM("delete_wrapped", function(cm) {
makeItWrapAfter(cm, Pos(0, 2));
cm.doc.setCursor(Pos(0, 3, "after"));
From eb18ac4e7ca0153f52503ae3dc939308090545ab Mon Sep 17 00:00:00 2001
From: Louis Mauchet
Date: Fri, 15 Sep 2017 11:02:36 +0200
Subject: [PATCH 0832/2085] #4969 : Fix gutter click behaviour on touch devices
---
src/edit/CodeMirror.js | 2 ++
src/edit/mouse_events.js | 9 +++++++--
2 files changed, 9 insertions(+), 2 deletions(-)
diff --git a/src/edit/CodeMirror.js b/src/edit/CodeMirror.js
index 1b2758ec01..4cd2e39227 100644
--- a/src/edit/CodeMirror.js
+++ b/src/edit/CodeMirror.js
@@ -143,6 +143,8 @@ function registerEventHandlers(cm) {
return dx * dx + dy * dy > 20 * 20
}
on(d.scroller, "touchstart", e => {
+ if (clickInGutter(cm, e)) return
+
if (!signalDOMEvent(cm, e) && !isMouseLikeTouchEvent(e)) {
d.input.ensurePolled()
clearTimeout(touchFinished)
diff --git a/src/edit/mouse_events.js b/src/edit/mouse_events.js
index f816a4ad41..25b7d4995c 100644
--- a/src/edit/mouse_events.js
+++ b/src/edit/mouse_events.js
@@ -326,8 +326,13 @@ function leftButtonSelect(cm, event, start, behavior) {
// handlers for the corresponding event.
function gutterEvent(cm, e, type, prevent) {
let mX, mY
- try { mX = e.clientX; mY = e.clientY }
- catch(e) { return false }
+ if (e.type == "touchstart"){
+ try { mX = e.touches[0].clientX; mY = e.touches[0].clientY }
+ catch(e) { return false }
+ } else {
+ 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)
From dcbfd6e7053d1efd46f25f6a631b945ec04b25fd Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Fri, 15 Sep 2017 13:10:06 +0200
Subject: [PATCH 0833/2085] Clean up previous patch
---
src/edit/CodeMirror.js | 4 +---
src/edit/mouse_events.js | 10 +++++-----
2 files changed, 6 insertions(+), 8 deletions(-)
diff --git a/src/edit/CodeMirror.js b/src/edit/CodeMirror.js
index 4cd2e39227..0f0e58900c 100644
--- a/src/edit/CodeMirror.js
+++ b/src/edit/CodeMirror.js
@@ -143,9 +143,7 @@ function registerEventHandlers(cm) {
return dx * dx + dy * dy > 20 * 20
}
on(d.scroller, "touchstart", e => {
- if (clickInGutter(cm, e)) return
-
- if (!signalDOMEvent(cm, e) && !isMouseLikeTouchEvent(e)) {
+ if (!signalDOMEvent(cm, e) && !isMouseLikeTouchEvent(e) && !clickInGutter(cm, e)) {
d.input.ensurePolled()
clearTimeout(touchFinished)
let now = +new Date
diff --git a/src/edit/mouse_events.js b/src/edit/mouse_events.js
index 25b7d4995c..e9b61ecdc7 100644
--- a/src/edit/mouse_events.js
+++ b/src/edit/mouse_events.js
@@ -326,12 +326,12 @@ function leftButtonSelect(cm, event, start, behavior) {
// handlers for the corresponding event.
function gutterEvent(cm, e, type, prevent) {
let mX, mY
- if (e.type == "touchstart"){
- try { mX = e.touches[0].clientX; mY = e.touches[0].clientY }
- catch(e) { return false }
+ if (e.touches) {
+ mX = e.touches[0].clientX
+ mY = e.touches[0].clientY
} else {
- try { mX = e.clientX; mY = e.clientY }
- catch(e) { return false }
+ 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)
From 6bb82e89a20b9cdf0384ff05567e856d7e1b138b Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Fri, 15 Sep 2017 13:15:30 +0200
Subject: [PATCH 0834/2085] Fix tests again...
---
test/test.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/test/test.js b/test/test.js
index 15183511ec..2421c78a18 100644
--- a/test/test.js
+++ b/test/test.js
@@ -2483,7 +2483,7 @@ testCM("bidi_wrapped_selection", function(cm) {
cm.setSelection(Pos(0, 37), Pos(0, 80))
var blocks = byClassName(cm.getWrapperElement(), "CodeMirror-selected")
eq(blocks.length, 2)
- let boxTop = blocks[0].getBoundingClientRect(), boxBot = blocks[1].getBoundingClientRect()
+ var boxTop = blocks[0].getBoundingClientRect(), boxBot = blocks[1].getBoundingClientRect()
is(boxTop.left > cm.charCoords(Pos(0, 1)).right)
is(boxBot.right < cm.charCoords(Pos(0, cm.getLine(0).length - 2)).left)
}, {value: "مفتي11 تم تضمينهفتي تم تضمينها فتي تفتي تم تضمينها فتي تفتي تم تضمينها فتي تفتي تم تضمينها فتي تا فت10ي ت
", lineWrapping: true})
From faadade2151e7a0801242c5302e92d4bd2156164 Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Fri, 15 Sep 2017 13:18:57 +0200
Subject: [PATCH 0835/2085] Fix tests better
We really need to get rid of PhantomJS
---
test/test.js | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/test/test.js b/test/test.js
index 2421c78a18..6faea6acac 100644
--- a/test/test.js
+++ b/test/test.js
@@ -2482,8 +2482,9 @@ testCM("bidi_wrapped_selection", function(cm) {
cm.setSize(cm.charCoords(Pos(0, 10), "editor").left)
cm.setSelection(Pos(0, 37), Pos(0, 80))
var blocks = byClassName(cm.getWrapperElement(), "CodeMirror-selected")
- eq(blocks.length, 2)
- var boxTop = blocks[0].getBoundingClientRect(), boxBot = blocks[1].getBoundingClientRect()
+ is(blocks.length >= 2)
+ is(blocks.length <= 3)
+ var boxTop = blocks[0].getBoundingClientRect(), boxBot = blocks[blocks.length - 1].getBoundingClientRect()
is(boxTop.left > cm.charCoords(Pos(0, 1)).right)
is(boxBot.right < cm.charCoords(Pos(0, cm.getLine(0).length - 2)).left)
}, {value: "مفتي11 تم تضمينهفتي تم تضمينها فتي تفتي تم تضمينها فتي تفتي تم تضمينها فتي تفتي تم تضمينها فتي تا فت10ي ت
", lineWrapping: true})
From 15912c3f7117b881e7d0df29a7ada5b29d269f39 Mon Sep 17 00:00:00 2001
From: Adrian Heine
Date: Thu, 14 Sep 2017 22:44:54 +0200
Subject: [PATCH 0836/2085] [mode/simple addon] Improve fix for #4970, adapt
docs.
---
addon/mode/simple.js | 2 +-
demo/simplemode.html | 13 ++++++-------
2 files changed, 7 insertions(+), 8 deletions(-)
diff --git a/addon/mode/simple.js b/addon/mode/simple.js
index 744a66c034..c0f801088c 100644
--- a/addon/mode/simple.js
+++ b/addon/mode/simple.js
@@ -136,7 +136,7 @@
state.indent.pop();
var token = rule.token
if (token && token.apply) token = token(matches)
- if (matches.length > 2 && typeof rule.token != "string") {
+ if (matches.length > 2 && rule.token && typeof rule.token != "string") {
state.pending = [];
for (var j = 2; j < matches.length; j++)
if (matches[j])
diff --git a/demo/simplemode.html b/demo/simplemode.html
index 3774138049..04c194a4ba 100644
--- a/demo/simplemode.html
+++ b/demo/simplemode.html
@@ -65,15 +65,14 @@ Simple Mode Demo
The regular expression that matches the token. May be a string
or a regex object. When a regex, the ignoreCase flag
will be taken into account when matching the token. This regex
- should only capture groups when the token property is
- an array.
+ has to capture groups when the token property is
+ an array. If it captures groups, it must capture all of the string
+ (since JS provides no way to find out where a group matched).
token : string | array<string> | null
An optional token style. Multiple styles can be specified by
- separating them with dots or spaces. When the regex for
- this rule captures groups, it must capture all of the
- string (since JS provides no way to find out where a group matched),
- and this property must hold an array of token styles that has one
- style for each matched group.
+ separating them with dots or spaces. When this property holds an array of token styles,
+ the regex for this rule must capture a group for each array item.
+
sol : boolean
When true, this token will only match at the start of the line.
(The ^ regexp marker doesn't work as you'd expect in
From 5c087ac4e4fab2b3d9510caa006616535a207ff6 Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Fri, 15 Sep 2017 13:24:39 +0200
Subject: [PATCH 0837/2085] Give up and disable test on PhantomJS
---
test/test.js | 1 +
1 file changed, 1 insertion(+)
diff --git a/test/test.js b/test/test.js
index 6faea6acac..6c7cf167ab 100644
--- a/test/test.js
+++ b/test/test.js
@@ -2479,6 +2479,7 @@ testCM("rtl_wrapped_selection", function(cm) {
}, {value: new Array(10).join(" فتي تم تضمينها فتي تم"), lineWrapping: true})
testCM("bidi_wrapped_selection", function(cm) {
+ if (phantom) return
cm.setSize(cm.charCoords(Pos(0, 10), "editor").left)
cm.setSelection(Pos(0, 37), Pos(0, 80))
var blocks = byClassName(cm.getWrapperElement(), "CodeMirror-selected")
From 68b4f44e3c6ea789abd454e1beac829c3afad26a Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Thu, 14 Sep 2017 15:15:24 +0200
Subject: [PATCH 0838/2085] Rewrite coordsCharInner
Issue #4926
---
src/measurement/position_measurement.js | 191 ++++++++++++++++--------
test/test.js | 10 ++
2 files changed, 141 insertions(+), 60 deletions(-)
diff --git a/src/measurement/position_measurement.js b/src/measurement/position_measurement.js
index 5995e91761..36e60601ae 100644
--- a/src/measurement/position_measurement.js
+++ b/src/measurement/position_measurement.js
@@ -1,4 +1,3 @@
-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"
@@ -293,14 +292,21 @@ function pageScrollY() {
return window.pageYOffset || (document.documentElement || document.body).scrollTop
}
+function widgetTopHeight(lineObj) {
+ let height = 0
+ if (lineObj.widgets) for (let i = 0; i < lineObj.widgets.length; ++i) if (lineObj.widgets[i].above)
+ height += widgetHeight(lineObj.widgets[i])
+ return height
+}
+
// Converts a {top, bottom, left, right} box from line-local
// 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, 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
+ if (!includeWidgets) {
+ let height = widgetTopHeight(lineObj)
+ rect.top += height; rect.bottom += height
}
if (context == "line") return rect
if (!context) context = "local"
@@ -376,7 +382,7 @@ export function cursorCoords(cm, pos, context, lineObj, preparedMeasure, varHeig
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
+ let part = order[partPos], right = part.level == 1
return get(invert ? ch - 1 : ch, right != invert)
}
let partPos = getBidiPartAt(order, ch, sticky)
@@ -434,10 +440,10 @@ export function coordsChar(cm, x, y) {
}
function wrappedLineExtent(cm, lineObj, preparedMeasure, y) {
- let measure = ch => intoCoordSystem(cm, lineObj, measureCharPrepared(cm, preparedMeasure, ch), "line")
+ y -= widgetTopHeight(lineObj)
let end = lineObj.text.length
- let begin = findFirst(ch => measure(ch - 1).bottom <= y, end, 0)
- end = findFirst(ch => measure(ch).top > y, begin, end)
+ let begin = findFirst(ch => measureCharPrepared(cm, preparedMeasure, ch - 1).bottom <= y, end, 0)
+ end = findFirst(ch => measureCharPrepared(cm, preparedMeasure, ch).top > y, begin, end)
return {begin, end}
}
@@ -447,66 +453,131 @@ export function wrappedLineExtentChar(cm, lineObj, preparedMeasure, target) {
return wrappedLineExtent(cm, lineObj, preparedMeasure, targetTop)
}
+// Returns true if the given side of a box is after the given
+// coordinates, in top-to-bottom, left-to-right order.
+function boxIsAfter(box, x, y, left) {
+ return box.bottom <= y ? false : box.top > y ? true : (left ? box.left : box.right) > x
+}
+
function coordsCharInner(cm, lineObj, lineNo, x, y) {
+ // Move y into line-local coordinate space
y -= heightAtLine(lineObj)
- let begin = 0, end = lineObj.text.length
let preparedMeasure = prepareMeasureForLine(cm, lineObj)
- let pos
+ // When directly calling `measureCharPrepared`, we have to adjust
+ // for the widgets at this line.
+ let widgetHeight = widgetTopHeight(lineObj)
+ let begin = 0, end = lineObj.text.length, ltr = true
+
let order = getOrder(lineObj, cm.doc.direction)
+ // If the line isn't plain left-to-right text, first figure out
+ // which bidi section the coordinates fall into.
if (order) {
- if (cm.options.lineWrapping) {
- ;({begin, end} = wrappedLineExtent(cm, lineObj, preparedMeasure, y))
- }
- pos = new Pos(lineNo, Math.floor(begin + (end - begin) / 2))
- let beginLeft = cursorCoords(cm, pos, "line", lineObj, preparedMeasure).left
- let dir = beginLeft < x ? 1 : -1
- let prevDiff, diff = beginLeft - x, prevPos
- let steps = Math.ceil((end - begin) / 4)
- outer: do {
- prevDiff = diff
- prevPos = pos
- // Make these steps don't take us outside of the current bidi part
- let bidiPart = order[getBidiPartAt(order, pos.ch, dir < 0 ? "before" : "after")]
- let maxSteps = Math.max(1, Math.min(steps, (dir < 0) != (bidiPart.level % 2 > 0) ? pos.ch - bidiPart.from : bidiPart.to - pos.ch))
- for (let i = 0; i < maxSteps; ++i) {
- let 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 outer
- }
- }
- diff = cursorCoords(cm, pos, "line", lineObj, preparedMeasure).left - x
- if (steps > 1) {
- let diffChangePerStep = Math.abs(diff - prevDiff) / steps
- steps = Math.min(steps, Math.ceil(Math.abs(diff) / diffChangePerStep))
- dir = diff < 0 ? 1 : -1
- }
- } while (diff != 0 && (steps > 1 || ((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
+ let part = (cm.options.lineWrapping ? coordsBidiPartWrapped : coordsBidiPart)
+ (cm, lineObj, lineNo, preparedMeasure, order, x, y)
+ ltr = part.level != 1
+ // The awkward -1 offsets are needed because findFirst (called
+ // on these below) will treat its first bound as inclusive,
+ // second as exclusive, but we want to actually address the
+ // characters in the part's range
+ begin = ltr ? part.from : part.to - 1
+ end = ltr ? part.to : part.from - 1
+ }
+
+ // A binary search to find the first character whose bounding box
+ // starts after the coordinates. If we run across any whose box wrap
+ // the coordinates, store that.
+ let chAround = null, boxAround = null
+ let ch = findFirst(ch => {
+ let box = measureCharPrepared(cm, preparedMeasure, ch)
+ box.top += widgetHeight; box.bottom += widgetHeight
+ if (!boxIsAfter(box, x, y, false)) return false
+ if (box.top <= y && box.left <= x) {
+ chAround = ch
+ boxAround = box
}
+ return true
+ }, begin, end)
+
+ let baseX, sticky, outside = false
+ // If a box around the coordinates was found, use that
+ if (boxAround) {
+ // Distinguish coordinates nearer to the left or right side of the box
+ let atLeft = x - boxAround.left < boxAround.right - x, atStart = atLeft == ltr
+ ch = chAround + (atStart ? 0 : 1)
+ sticky = atStart ? "after" : "before"
+ baseX = atLeft ? boxAround.left : boxAround.right
} else {
- let ch = findFirst(ch => {
- let box = intoCoordSystem(cm, lineObj, measureCharPrepared(cm, preparedMeasure, ch), "line")
- if (box.top > y) {
- // For the cursor stickiness
- 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)
- ch = skipExtendingChars(lineObj.text, ch, 1)
- pos = new Pos(lineNo, ch, ch == end ? "before" : "after")
+ // (Adjust for extended bound, if necessary.)
+ if (!ltr && (ch == end || ch == begin)) ch++
+ // To determine which side to associate with, get the box to the
+ // left of the character and compare it's vertical position to the
+ // coordinates
+ sticky = ch == 0 ? "after" : ch == lineObj.text.length ? "before" :
+ (measureCharPrepared(cm, preparedMeasure, ch - (ltr ? 1 : 0)).bottom + widgetHeight <= y) == ltr ?
+ "after" : "before"
+ // Now get accurate coordinates for this place, in order to get a
+ // base X position
+ let coords = cursorCoords(cm, Pos(lineNo, ch, sticky), "line", lineObj, preparedMeasure)
+ baseX = coords.left
+ outside = y < coords.top || y > coords.bottom
}
- 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
+
+ ch = skipExtendingChars(lineObj.text, ch, 1)
+ return PosWithInfo(lineNo, ch, sticky, outside, x - baseX)
+}
+
+function coordsBidiPart(cm, lineObj, lineNo, preparedMeasure, order, x, y) {
+ // Bidi parts are sorted left-to-right, and in a non-line-wrapping
+ // situation, we can take this ordering to correspond to the visual
+ // ordering. This finds the first part whose end is after the given
+ // coordinates.
+ let index = findFirst(i => {
+ let part = order[i], ltr = part.level != 1
+ return boxIsAfter(cursorCoords(cm, Pos(lineNo, ltr ? part.to : part.from, ltr ? "before" : "after"),
+ "line", lineObj, preparedMeasure), x, y, true)
+ }, 0, order.length - 1)
+ let part = order[index]
+ // If this isn't the first part, the part's start is also after
+ // the coordinates, and the coordinates aren't on the same line as
+ // that start, move one part back.
+ if (index > 0) {
+ let ltr = part.level != 1
+ let start = cursorCoords(cm, Pos(lineNo, ltr ? part.from : part.to, ltr ? "after" : "before"),
+ "line", lineObj, preparedMeasure)
+ if (boxIsAfter(start, x, y, true) && start.top > y)
+ part = order[index - 1]
+ }
+ return part
+}
+
+function coordsBidiPartWrapped(cm, lineObj, _lineNo, preparedMeasure, order, x, y) {
+ // In a wrapped line, rtl text on wrapping boundaries can do things
+ // that don't correspond to the ordering in our `order` array at
+ // all, so a binary search doesn't work, and we want to return a
+ // part that only spans one line so that the binary search in
+ // coordsCharInner is safe. As such, we first find the extent of the
+ // wrapped line, and then do a flat search in which we discard any
+ // spans that aren't on the line.
+ let {begin, end} = wrappedLineExtent(cm, lineObj, preparedMeasure, y)
+ let part = null, closestDist = null
+ for (let i = 0; i < order.length; i++) {
+ let p = order[i]
+ if (p.from >= end || p.to <= begin) continue
+ let ltr = p.level != 1
+ let endX = measureCharPrepared(cm, preparedMeasure, ltr ? Math.min(end, p.to) - 1 : Math.max(begin, p.from)).right
+ // Weigh against spans ending before this, so that they are only
+ // picked if nothing ends after
+ let dist = endX < x ? x - endX + 1e9 : endX - x
+ if (!part || closestDist > dist) {
+ part = p
+ closestDist = dist
+ }
+ }
+ if (!part) part = order[order.length - 1]
+ // Clip the part to the wrapped line.
+ if (part.from < begin) part = {from: begin, to: part.to, level: part.level}
+ if (part.to > end) part = {from: part.from, to: end, level: part.level}
+ return part
}
let measureText
diff --git a/test/test.js b/test/test.js
index 6c7cf167ab..96e989d12c 100644
--- a/test/test.js
+++ b/test/test.js
@@ -1161,6 +1161,16 @@ testCM("measureWrappedEndOfLine", function(cm) {
}
}, {mode: "text/html", value: "0123456789abcde0123456789", lineWrapping: true}, ie_lt8 || opera_lt10);
+testCM("measureEndOfLineBidi", function(cm) {
+ eqCursorPos(cm.coordsChar({left: 5000, top: cm.charCoords(Pos(0, 0)).top}), Pos(0, 8, "after"))
+}, {value: "إإإإuuuuإإإإ"})
+
+testCM("measureWrappedBidiLevel2", function(cm) {
+ cm.setSize(cm.charCoords(Pos(0, 6), "editor").right + 60)
+ var c9 = cm.charCoords(Pos(0, 9))
+ eqCharPos(cm.coordsChar({left: c9.right - 1, top: c9.top + 1}), Pos(0, 9))
+}, {value: "foobar إإ إإ إإ إإ 555 بببببب", lineWrapping: true})
+
testCM("measureWrappedBeginOfLine", function(cm) {
if (phantom) return;
cm.setSize(null, "auto");
From 1f06484ce98b0010e5a2531b2d1a7ae3f286a8a0 Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Mon, 18 Sep 2017 11:19:51 +0200
Subject: [PATCH 0839/2085] Fix issue when drawing selection starting at the
end of an rtl line
---
src/display/selection.js | 9 +++------
1 file changed, 3 insertions(+), 6 deletions(-)
diff --git a/src/display/selection.js b/src/display/selection.js
index 2030b073d9..252f1474fc 100644
--- a/src/display/selection.js
+++ b/src/display/selection.js
@@ -74,10 +74,9 @@ function drawSelectionRange(cm, range, output) {
let order = getOrder(lineObj, doc.direction)
iterateBidiSections(order, fromArg || 0, toArg == null ? lineLen : toArg, (from, to, dir, i) => {
- let fromPos, toPos
+ let fromPos = coords(from, dir == "ltr" ? "left" : "right")
+ let toPos = coords(to - 1, dir == "ltr" ? "right" : "left")
if (dir == "ltr") {
- fromPos = coords(from, "left")
- toPos = coords(to - 1, "right")
let fromLeft = fromArg == null && from == 0 ? leftSide : fromPos.left
let toRight = toArg == null && to == lineLen ? rightSide : toPos.right
if (toPos.top - fromPos.top <= 3) { // Single line
@@ -87,9 +86,7 @@ function drawSelectionRange(cm, range, output) {
if (fromPos.bottom < toPos.top) add(leftSide, fromPos.bottom, null, toPos.top)
add(leftSide, toPos.top, toPos.right, toPos.bottom)
}
- } else { // RTL
- fromPos = coords(from, "right")
- toPos = coords(to - 1, "left")
+ } else if (from < to) { // RTL
let fromRight = fromArg == null && from == 0 ? rightSide : fromPos.right
let toLeft = toArg == null && to == lineLen ? leftSide : toPos.left
if (toPos.top - fromPos.top <= 3) { // Single line
From febbe7cb4ae2d6785d7aaac6cbd14d6f93a6e179 Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Mon, 18 Sep 2017 11:22:18 +0200
Subject: [PATCH 0840/2085] Normalize anchors on bidi jumps when
mouse-selecting
To avoid unpredictable selection behavior depending on whether the
mouse drag started a pixel to the left or right of the boundary.
Issue #4926
---
src/edit/mouse_events.js | 29 ++++++++++++++++++++++++++++-
1 file changed, 28 insertions(+), 1 deletion(-)
diff --git a/src/edit/mouse_events.js b/src/edit/mouse_events.js
index e9b61ecdc7..688907669f 100644
--- a/src/edit/mouse_events.js
+++ b/src/edit/mouse_events.js
@@ -8,6 +8,7 @@ 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"
@@ -269,7 +270,7 @@ function leftButtonSelect(cm, event, start, behavior) {
anchor = maxPos(oldRange.to(), range.head)
}
let ranges = startSel.ranges.slice(0)
- ranges[ourIndex] = new Range(clipPos(doc, anchor), head)
+ ranges[ourIndex] = bidiSimplify(cm, new Range(clipPos(doc, anchor), head))
setSelection(doc, normalizeSelection(ranges, ourIndex), sel_mouse)
}
}
@@ -321,6 +322,32 @@ function leftButtonSelect(cm, event, start, behavior) {
on(document, "mouseup", up)
}
+// Used when mouse-selecting to adjust the anchor to the proper side
+// of a bidi jump depending on the visual position of the head.
+function bidiSimplify(cm, range) {
+ let {anchor, head} = range, anchorLine = getLine(cm.doc, anchor.line)
+ if (cmp(anchor, head) == 0 && anchor.sticky == head.sticky) return range
+ let order = getOrder(anchorLine)
+ if (!order) return range
+ let index = getBidiPartAt(order, anchor.ch, anchor.sticky), part = order[index]
+ if (part.from != anchor.ch && part.to != anchor.ch) return range
+ let boundary = index + ((part.from == anchor.ch) == (part.level != 1) ? 0 : 1)
+ if (boundary == 0 || boundary == order.length) return range
+
+ // Compute the relative visual position of the head compared to the
+ // anchor (<0 is to the left, >0 to the right)
+ let dir = head.line - anchor.line
+ if (dir == 0) {
+ let headIndex = getBidiPartAt(order, head.ch, head.sticky)
+ dir = headIndex - index || (head.ch - anchor.ch) * (part.level == 1 ? -1 : 1)
+ }
+
+ let biasTo = boundary + (dir < 0 ? -1 : 0)
+ if (biasTo == index) return range
+ let targetPart = order[biasTo], from = (dir > 0) == (targetPart.level != 1)
+ return new Range(new Pos(anchor.line, from ? targetPart.from : targetPart.to, from ? "after" : "before"), head)
+}
+
// Determines whether an event happened in the gutter, and fires the
// handlers for the corresponding event.
From e5a13e622afb152e6411a8e8ce632a2cbe56b40a Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Mon, 18 Sep 2017 11:38:26 +0200
Subject: [PATCH 0841/2085] [gfm mode] Require at least one number in the first
6 digits of a SHA
To avoid matching words that happen to consist of a-f.
Closes #4966
---
mode/gfm/gfm.js | 2 +-
mode/gfm/test.js | 3 +++
2 files changed, 4 insertions(+), 1 deletion(-)
diff --git a/mode/gfm/gfm.js b/mode/gfm/gfm.js
index 689cd6e2ec..471ae906b9 100644
--- a/mode/gfm/gfm.js
+++ b/mode/gfm/gfm.js
@@ -81,7 +81,7 @@ CodeMirror.defineMode("gfm", function(config, modeConfig) {
if (stream.sol() || state.ateSpace) {
state.ateSpace = false;
if (modeConfig.gitHubSpice !== false) {
- if(stream.match(/^(?:[a-zA-Z0-9\-_]+\/)?(?:[a-zA-Z0-9\-_]+@)?(?:[a-f0-9]{7,40}\b)/)) {
+ if(stream.match(/^(?:[a-zA-Z0-9\-_]+\/)?(?:[a-zA-Z0-9\-_]+@)?(?=.{0,6}\d)(?:[a-f0-9]{7,40}\b)/)) {
// User/Project@SHA
// User@SHA
// SHA
diff --git a/mode/gfm/test.js b/mode/gfm/test.js
index 9cda5c45a3..e7135264c3 100644
--- a/mode/gfm/test.js
+++ b/mode/gfm/test.js
@@ -91,6 +91,9 @@
MT("userProjectSHAEmphasis",
"[em *foo ][em&link bar/hello@be6a8cc1c1ecfe9489fb51e4869af15a13fc2cd2][em *]");
+ MT("wordSHA",
+ "ask for feedbac")
+
MT("num",
"foo [link #1] bar");
From e4b1293d1246885025add6459c95e7188b66288b Mon Sep 17 00:00:00 2001
From: Adrian Heine
Date: Mon, 18 Sep 2017 12:17:37 +0200
Subject: [PATCH 0842/2085] Don't check whether document has focus for
takeFocus
Closes #4975. Checking `hasFocus` is a reasonable idea, but in Firefox
`hasFocus` returns false on `mousedown` if the window, but not the document
was focussed before. This leads to a wrong cursor in `contenteditable`
input style.
---
src/display/operations.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/display/operations.js b/src/display/operations.js
index 6748af43e3..46e77cae3c 100644
--- a/src/display/operations.js
+++ b/src/display/operations.js
@@ -115,7 +115,7 @@ function endOperation_W2(op) {
cm.display.maxLineChanged = false
}
- let takeFocus = op.focus && op.focus == activeElt() && (!document.hasFocus || document.hasFocus())
+ let takeFocus = op.focus && op.focus == activeElt()
if (op.preparedSelection)
cm.display.input.showSelection(op.preparedSelection, takeFocus)
if (op.updatedDisplay || op.startHeight != cm.doc.height)
From 954c51e9c1b6db24ff566cc2fcb1c9c23f3a9da2 Mon Sep 17 00:00:00 2001
From: Adrian Heine
Date: Mon, 18 Sep 2017 12:26:09 +0200
Subject: [PATCH 0843/2085] Don't pass argument nobody expects
---
src/display/operations.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/display/operations.js b/src/display/operations.js
index 46e77cae3c..c3004508e7 100644
--- a/src/display/operations.js
+++ b/src/display/operations.js
@@ -102,7 +102,7 @@ function endOperation_R2(op) {
}
if (op.updatedDisplay || op.selectionChanged)
- op.preparedSelection = display.input.prepareSelection(op.focus)
+ op.preparedSelection = display.input.prepareSelection()
}
function endOperation_W2(op) {
From 635683b394cc486d869065858a5f3ef69bf26168 Mon Sep 17 00:00:00 2001
From: Adrian Heine
Date: Mon, 18 Sep 2017 12:27:22 +0200
Subject: [PATCH 0844/2085] Use more nice ES6 things
---
src/display/selection.js | 4 ++--
src/model/changes.js | 4 ++--
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/display/selection.js b/src/display/selection.js
index 252f1474fc..ba6feb8f14 100644
--- a/src/display/selection.js
+++ b/src/display/selection.js
@@ -9,13 +9,13 @@ export function updateSelection(cm) {
cm.display.input.showSelection(cm.display.input.prepareSelection())
}
-export function prepareSelection(cm, primary) {
+export function prepareSelection(cm, primary = true) {
let doc = cm.doc, result = {}
let curFragment = result.cursors = document.createDocumentFragment()
let selFragment = result.selection = document.createDocumentFragment()
for (let i = 0; i < doc.sel.ranges.length; i++) {
- if (primary === false && i == doc.sel.primIndex) continue
+ if (!primary && i == doc.sel.primIndex) continue
let range = doc.sel.ranges[i]
if (range.from().line >= cm.display.viewTo || range.to().line < cm.display.viewFrom) continue
let collapsed = range.empty()
diff --git a/src/model/changes.js b/src/model/changes.js
index 308dc6b399..fd5ddd4c25 100644
--- a/src/model/changes.js
+++ b/src/model/changes.js
@@ -260,9 +260,9 @@ function makeChangeSingleDocInEditor(cm, change, spans) {
export function replaceRange(doc, code, from, to, origin) {
if (!to) to = from
- if (cmp(to, from) < 0) { let tmp = to; to = from; from = tmp }
+ if (cmp(to, from) < 0) { [from, to] = [to, from] }
if (typeof code == "string") code = doc.splitLines(code)
- makeChange(doc, {from: from, to: to, text: code, origin: origin})
+ makeChange(doc, {from, to, text: code, origin})
}
// Rebasing/resetting history to deal with externally-sourced changes
From 8f35a1aa387c34677338d66086b15f0ed68f661a Mon Sep 17 00:00:00 2001
From: Adrian Heine
Date: Mon, 18 Sep 2017 13:07:13 +0200
Subject: [PATCH 0845/2085] Remove superflous brackets and make blint happy
---
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 fd5ddd4c25..cfad529c68 100644
--- a/src/model/changes.js
+++ b/src/model/changes.js
@@ -260,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) { [from, to] = [to, from] }
+ if (cmp(to, from) < 0) [from, to] = [to, from]
if (typeof code == "string") code = doc.splitLines(code)
makeChange(doc, {from, to, text: code, origin})
}
From ed6454389872e29af75adef63647621bee64c332 Mon Sep 17 00:00:00 2001
From: Jakub Vrana
Date: Mon, 18 Sep 2017 16:19:54 +0200
Subject: [PATCH 0846/2085] [soy mode] Support @param declarations in comments
@param specified in a template comment is older but still supported way of
declaring a parameter. The comment must directly precede {template}.
Reference: https://developers.google.com/closure/templates/docs/concepts#namespace-decl
---
mode/soy/soy.js | 30 ++++++++++++++++++++++--------
1 file changed, 22 insertions(+), 8 deletions(-)
diff --git a/mode/soy/soy.js b/mode/soy/soy.js
index f986b8a064..0e24457042 100644
--- a/mode/soy/soy.js
+++ b/mode/soy/soy.js
@@ -128,6 +128,13 @@
} else {
stream.skipToEnd();
}
+ if (!state.scopes) {
+ var paramRe = /@param\??\s+(\S+)/g;
+ var current = stream.current();
+ for (var match; (match = paramRe.exec(current)); ) {
+ state.variables = prepend(state.variables, match[1]);
+ }
+ }
return "comment";
case "templ-def":
@@ -187,6 +194,7 @@
if (stream.match(/^\/?}/)) {
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") {
@@ -248,8 +256,14 @@
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}/)) {
state.indent += config.indentUnit;
@@ -274,18 +288,18 @@
state.soyState.push("tag");
if (state.tag == "template" || state.tag == "deltemplate") {
state.soyState.push("templ-def");
- }
- if (state.tag == "call" || state.tag == "delcall") {
+ } else if (state.tag == "call" || state.tag == "delcall") {
state.soyState.push("templ-ref");
- }
- if (state.tag == "let") {
+ } else if (state.tag == "let") {
state.soyState.push("var-def");
- }
- if (state.tag == "for" || state.tag == "foreach") {
+ } else if (state.tag == "for" || state.tag == "foreach") {
state.scopes = prepend(state.scopes, state.variables);
state.soyState.push("var-def");
- }
- if (state.tag.match(/^@(?:param\??|inject)/)) {
+ } else if (state.tag == "namespace") {
+ if (!state.scopes) {
+ state.variables = prepend(null, 'ij');
+ }
+ } else if (state.tag.match(/^@(?:param\??|inject)/)) {
state.soyState.push("param-def");
}
return "keyword";
From 6fccbea411dca29e8f985ca06f42d65acd71383b Mon Sep 17 00:00:00 2001
From: Daniel Thwaites
Date: Mon, 18 Sep 2017 19:09:40 +0100
Subject: [PATCH 0847/2085] Emphasised on highlighting
Mentioned CodeMirror's great ability to highlight code.
---
README.md | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 3328e3bdfb..a3a351be4d 100644
--- a/README.md
+++ b/README.md
@@ -7,7 +7,8 @@
CodeMirror is a versatile text editor implemented in JavaScript for
the browser. It is specialized for editing code, and comes with over
100 language modes and various addons that implement more advanced
-editing functionality.
+editing functionality. Every language comes with fully-featured code
+and syntax highlighting to help with reading and editing complex code.
A rich programming API and a CSS theming system are available for
customizing CodeMirror to fit your application, and extending it with
From 697556c910521b916c6d53071820259ea2f7f7a2 Mon Sep 17 00:00:00 2001
From: Daniel Thwaites
Date: Mon, 18 Sep 2017 19:21:11 +0100
Subject: [PATCH 0848/2085] Improved description
Improved the description of "make the problem occur", explaining that maintainers want to reproduce the error.
---
doc/reporting.html | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/doc/reporting.html b/doc/reporting.html
index 3c582d9618..1657400ce9 100644
--- a/doc/reporting.html
+++ b/doc/reporting.html
@@ -44,9 +44,8 @@ Reporting bugs effectively
Mention very precisely what went wrong. "X is broken" is not a
good bug report. What did you expect to happen? What happened
- instead? Describe the exact steps a maintainer has to take to make
- the problem occur. We can not fix something that we can not
- observe.
+ instead? Describe the exact steps a maintainer has to take to reproduce
+ the error. We can not fix something that we can not observe.
If the problem can not be reproduced in any of the demos
included in the CodeMirror distribution, please provide an HTML
From 036beb5408be701e160597460d5a3a9e8cd96146 Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Tue, 19 Sep 2017 10:21:17 +0200
Subject: [PATCH 0849/2085] [javascript mode] Handle some corner cases around
tokenizing regexps
Issue #4981
---
mode/javascript/javascript.js | 9 +++++++--
mode/javascript/test.js | 9 +++++++++
2 files changed, 16 insertions(+), 2 deletions(-)
diff --git a/mode/javascript/javascript.js b/mode/javascript/javascript.js
index 77bd59c372..a011eb8d70 100644
--- a/mode/javascript/javascript.js
+++ b/mode/javascript/javascript.js
@@ -28,7 +28,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
var jsKeywords = {
"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,
+ "return": C, "break": C, "continue": C, "new": kw("new"), "delete": C, "void": C, "throw": C, "debugger": C,
"var": kw("var"), "const": kw("var"), "let": kw("var"),
"function": kw("function"), "catch": kw("catch"),
"for": kw("for"), "switch": kw("switch"), "case": kw("case"), "default": kw("default"),
@@ -443,6 +443,11 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
if (type == ".") return cont(property, me);
if (type == "[") return cont(pushlex("]"), maybeexpression, expect("]"), poplex, me);
if (isTS && value == "as") { cx.marked = "keyword"; return cont(typeexpr, me) }
+ if (type == "regexp") {
+ cx.state.lastType = cx.marked = "operator"
+ cx.stream.backUp(cx.stream.pos - cx.stream.start - 1)
+ return cont(expr)
+ }
}
function quasi(type, value) {
if (type != "quasi") return pass();
@@ -739,7 +744,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
function expressionAllowed(stream, state, backUp) {
return state.tokenize == tokenBase &&
- /^(?:operator|sof|keyword c|case|new|export|default|[\[{}\(,;:]|=>)$/.test(state.lastType) ||
+ /^(?:operator|sof|keyword [bc]|case|new|export|default|spread|[\[{}\(,;:]|=>)$/.test(state.lastType) ||
(state.lastType == "quasi" && /\{\s*$/.test(stream.string.slice(0, stream.pos - (backUp || 0))))
}
diff --git a/mode/javascript/test.js b/mode/javascript/test.js
index 8b6ca41128..7b0892cfab 100644
--- a/mode/javascript/test.js
+++ b/mode/javascript/test.js
@@ -236,6 +236,15 @@
" [keyword return] [number 2]",
"}")
+ MT("regexp_corner_case",
+ "[operator +]{} [operator /] [atom undefined];",
+ "[[[meta ...][string-2 /\\//] ]];",
+ "[keyword void] [string-2 /\\//];",
+ "[keyword do] [string-2 /\\//]; [keyword while] ([number 0]);",
+ "[keyword if] ([number 0]) {} [keyword else] [string-2 /\\//];",
+ "[string-2 `${][variable async][operator ++][string-2 }//`];",
+ "[string-2 `${]{} [operator /] [string-2 /\\//}`];")
+
var ts_mode = CodeMirror.getMode({indentUnit: 2}, "application/typescript")
function TS(name) {
test.mode(name, ts_mode, Array.prototype.slice.call(arguments, 1))
From c700778446124170e605dd5b96ffacd453934be2 Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Tue, 19 Sep 2017 15:25:06 +0200
Subject: [PATCH 0850/2085] Further refine mouse-selection bidi adjustment
Issue #4926
---
src/edit/mouse_events.js | 20 +++++++++++++-------
1 file changed, 13 insertions(+), 7 deletions(-)
diff --git a/src/edit/mouse_events.js b/src/edit/mouse_events.js
index 688907669f..57159e3a14 100644
--- a/src/edit/mouse_events.js
+++ b/src/edit/mouse_events.js
@@ -336,16 +336,22 @@ function bidiSimplify(cm, range) {
// Compute the relative visual position of the head compared to the
// anchor (<0 is to the left, >0 to the right)
- let dir = head.line - anchor.line
- if (dir == 0) {
+ let leftSide
+ if (head.line != anchor.line) {
+ leftSide = (head.line - anchor.line) * (cm.doc.direction == "ltr" ? 1 : -1) > 0
+ } else {
let headIndex = getBidiPartAt(order, head.ch, head.sticky)
- dir = headIndex - index || (head.ch - anchor.ch) * (part.level == 1 ? -1 : 1)
+ let dir = headIndex - index || (head.ch - anchor.ch) * (part.level == 1 ? -1 : 1)
+ if (headIndex == boundary - 1 || headIndex == boundary)
+ leftSide = dir < 0
+ else
+ leftSide = dir > 0
}
- let biasTo = boundary + (dir < 0 ? -1 : 0)
- if (biasTo == index) return range
- let targetPart = order[biasTo], from = (dir > 0) == (targetPart.level != 1)
- return new Range(new Pos(anchor.line, from ? targetPart.from : targetPart.to, from ? "after" : "before"), head)
+ let usePart = order[boundary + (leftSide ? -1 : 0)]
+ let from = leftSide == (usePart.level == 1)
+ let ch = from ? usePart.from : usePart.to, sticky = from ? "after" : "before"
+ return anchor.ch == ch && anchor.sticky == sticky ? range : new Range(new Pos(anchor.line, ch, sticky), head)
}
From d9be8fc44ff0b7751cc784285df07fc628b522de Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Tue, 19 Sep 2017 15:51:10 +0200
Subject: [PATCH 0851/2085] [javascript mode] Allow decorators on parameters
Though the decorator proposal doesn't support this, it is
apparently something TypeScript adds.
Closes #4959
---
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 a011eb8d70..0a46dc7768 100644
--- a/mode/javascript/javascript.js
+++ b/mode/javascript/javascript.js
@@ -656,7 +656,8 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, maybetype, statement, popcontext);
if (isTS && value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, functiondef)
}
- function funarg(type) {
+ function funarg(type, value) {
+ if (value == "@") cont(expression, funarg)
if (type == "spread" || type == "modifier") return cont(funarg);
return pass(pattern, maybetype, maybeAssign);
}
From 3fef35109e688160412cf7715aafc09b7b05cc13 Mon Sep 17 00:00:00 2001
From: Marijn Haverbeke
Date: Tue, 19 Sep 2017 16:03:03 +0200
Subject: [PATCH 0852/2085] [tern addon] Fix corner case bug when editing near
end of document
Closes #4954
---
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 644e495f65..a80dc7e4b8 100644
--- a/addon/tern/tern.js
+++ b/addon/tern/tern.js
@@ -571,7 +571,7 @@
return {type: "part",
name: data.name,
offsetLines: from.line,
- text: doc.getRange(from, Pos(endLine, 0))};
+ text: doc.getRange(from, Pos(endLine, end.line == endLine ? null : 0))};
}
// Generic utilities
From 282cf36664d05f776eba88a1f8ace03c1f085cd0 Mon Sep 17 00:00:00 2001
From: Fredrik Borg
Date: Tue, 19 Sep 2017 20:11:42 +0200
Subject: [PATCH 0853/2085] [sql mode] Support esper SQL dialect
---
mode/meta.js | 1 +
mode/sql/index.html | 3 ++-
mode/sql/sql.js | 13 +++++++++++++
3 files changed, 16 insertions(+), 1 deletion(-)
diff --git a/mode/meta.js b/mode/meta.js
index c49bd6c737..d5426a79b7 100644
--- a/mode/meta.js
+++ b/mode/meta.js
@@ -47,6 +47,7 @@
{name: "Embedded Javascript", mime: "application/x-ejs", mode: "htmlembedded", ext: ["ejs"]},
{name: "Embedded Ruby", mime: "application/x-erb", mode: "htmlembedded", ext: ["erb"]},
{name: "Erlang", mime: "text/x-erlang", mode: "erlang", ext: ["erl"]},
+ {name: "Esper", mime: "text/x-esper", mode: "sql"},
{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"]},
diff --git a/mode/sql/index.html b/mode/sql/index.html
index cd95872820..e12b289bfa 100644
--- a/mode/sql/index.html
+++ b/mode/sql/index.html
@@ -49,7 +49,7 @@ SQL Mode for CodeMirror
LIMIT 1 OFFSET 0;
- 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 bcae276e8d..93cb3b0e05 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 0885/2085] 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 d7385ef223..6552b09bbf 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 0993ca4c4e..d6d12c7117 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 bfe11d3b05..00e9b3df13 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 d92f8622d4..d6cc4771bd 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 0886/2085] [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 f27b8b8e2b..bd704f7583 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 13c39a1ea0..7cf5a956e0 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 9d8ff0ce66..255de98606 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 0887/2085] [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 6552b09bbf..d92318b345 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 d6cc4771bd..61a6de4be7 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 0888/2085] [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 98266e44f0..eeccab1721 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 0889/2085] [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 eeccab1721..08c9ebfb3f 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 0890/2085] [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 36aec0d4b5..7b07f7fd8a 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 0891/2085] [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 093cb30876..21c72696c8 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 0892/2085] 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 67d5e4258a..0ab9cf0d79 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 430d86a224..82e2aeee29 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 ac9555f1ce..a14b1b6430 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 9c768e3f6b..415dd9f0bc 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 0893/2085] [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 e12b289bfa..b434f0f405 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 0894/2085] [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 61a6de4be7..ca9fe8ba86 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 0895/2085] (Update my name)
---
AUTHORS | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/AUTHORS b/AUTHORS
index 1264357043..e4206048bf 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 0896/2085] [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 b3ee99c6dd..3f228c1fb0 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 0897/2085] 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 82e2aeee29..13921585a7 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 0898/2085] 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 13921585a7..c5e6b8aacc 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 0899/2085] 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 e4206048bf..f800b86b7d 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 ff7d6e2311..409c7234bb 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 0ab9cf0d79..f2f04459b4 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 e16ab6dd43..23de4c8023 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 d161e77c6f..555e4804aa 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 f251b238fd..2241ad9317 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 4298c61ff1..b0fd5de810 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 0900/2085] 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 f2f04459b4..7666e0df63 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 2241ad9317..6eda061f17 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 b0fd5de810..6500e08d05 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 0901/2085] [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 d6d12c7117..02a85319ff 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 0902/2085] 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 f0a75abf72..2049bf26fd 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 0903/2085] [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 7b07f7fd8a..7592ef0071 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 0904/2085] [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 ca9fe8ba86..c838e31628 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 0905/2085] [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 c94b27a7b8..dc2e77c60e 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 7583f3c159..bc8a9c60e4 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 0906/2085] 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 bc8a9c60e4..6c59188957 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 0907/2085] [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 02c8eff9fa..30893965fe 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 0908/2085] [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 568e639dcd..84c67edf78 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 0909/2085] [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 eccd81aab6..58bc47c2c3 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 0910/2085] 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 2955e4ae0c..06f37be078 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 0911/2085] [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 c838e31628..5c772526f2 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 0912/2085] 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 2049bf26fd..da0f4e4e61 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 0913/2085] 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 255de98606..8f4f22f5d6 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 0914/2085] [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 7592ef0071..460f662f80 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 0915/2085] [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 d95f6c1b27..fcd1d70de6 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 0916/2085] 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 3c16cc875e..2a2f54e4c9 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 ad0256bdfd..54b228a9b3 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 ee52daffac..aa731b4353 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 7ccf119e80..37405b6d8c 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 e868c42f3c..6069815719 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 c48f2204d5..3ab957509c 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 ca0a534ca4..5d8ebf250e 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 c3004508e7..5cc26d265d 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 d3902809e7..fbed426637 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 27060d18ef..7308c5e272 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 e16cf9ecac..26ec993b0f 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 dca96a442b..c658c0a272 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 e58db48a51..86c7132131 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 15a2394257..db9df26d92 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 6c59188957..7f06018d04 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 b9abd2fc40..41464f2350 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 0f0e58900c..4759209cf2 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 e1a4327c8f..3916b129fb 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 5a9bd2cfd5..82e331a5ff 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 43e996fb68..12c760f0d6 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 5d920830b3..92498c1045 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 b2ab7d57c1..269e870ef1 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 06f37be078..f0521d070a 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 bc3df6c8f1..889badbe59 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 6500e08d05..6d9eb8790b 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 8dc692529e..5cefed7c18 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 57159e3a14..696a4bbfa2 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 0601a83c3c..28f8bb60c6 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 61f795572d..fda0be7412 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 67de3b1836..e3af520a04 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 3262ea1ba6..c0f04aaaf2 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 024f5f9254..c88772cb6b 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 fa85209ee8..ff86c39d31 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 36ac3e61b9..1dfcf8aff6 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 927ed6e14d..8d50fd2a04 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 c5e6b8aacc..79f0884511 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 e444184bb0..74acdaffc0 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 4f5e4c5594..2a498f8f3c 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 6c413d2fc2..f7e5f4b6e9 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 e4e6943f55..c886294353 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 78986e03db..aeff0e5b8d 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 554cf80977..39d7553d1f 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 c3da76d74e..b64ac84373 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 881f39eb46..4d45313dee 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 cfad529c68..b00e29b13d 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 056ef91bb9..d82716ded4 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 7f6e3367d1..d946e7af10 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 83938cf4c1..753a89da92 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 a11f9c2742..4a82d5389e 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 ccdcc9d3b8..955c72c4a7 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 97084fbc15..2e374aa822 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 bf5ad8c76f..77986a9e9a 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 065a463b5b..8384517027 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 a14b1b6430..022c4bc209 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 3d13dd86d2..33ab854d88 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 94823c21b9..04d2569d28 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 29fd4c5981..4b6c770578 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 e65881d4ca..c33734ebb9 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 b8fa78ac48..f6815949d8 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 0917/2085] [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 5c772526f2..139e53dfe4 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 0918/2085] [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 213bab06a8..d560fdbacb 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 0919/2085] [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 0e24457042..98f308658e 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 0920/2085] [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 33db0c15a2..3160453283 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 b73eedaa6a..412dba4b42 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 0921/2085] [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 139e53dfe4..e43543dd3c 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 d560fdbacb..972a345651 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 0922/2085] [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 e43543dd3c..0dcd8af391 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 972a345651..2437edcca5 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 0923/2085] [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 f72a0a9c69..62c683cb8c 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 0924/2085] [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 0dcd8af391..4978d8d1e7 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 2437edcca5..167e6d0165 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 0925/2085] 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 409c7234bb..1fa5781617 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 4978d8d1e7..514de1c8da 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 0926/2085] 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 f800b86b7d..65d8480893 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 1fa5781617..f81fcdd07b 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 7666e0df63..f46e6bd5ab 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 23de4c8023..7fb8eebef1 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 555e4804aa..d62ab84a39 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 6eda061f17..3e9f20c650 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 6d9eb8790b..c89e5d8792 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 0927/2085] 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 f46e6bd5ab..ea5642cdf9 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 3e9f20c650..3ffad1d5d1 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 c89e5d8792..260f7d0af5 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 0928/2085] 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 02a85319ff..ff00cf5002 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 0929/2085] [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 a80dc7e4b8..70202c6fc5 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 0930/2085] 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 70202c6fc5..6276b53893 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 0931/2085] [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 a9eb8fa66b..e00e77a20c 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 0932/2085] [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 00e9b3df13..f5f3a41ba8 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 0933/2085] [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 6fc6e33ca5..e5c55d3999 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 0934/2085] [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 30893965fe..b83dc505ff 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 0935/2085] =?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 34da269f33..91a925268e 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 0936/2085] [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 464dc57f83..8099d370be 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 0937/2085] [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 7cf5a956e0..b082268183 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 18268ee789..5a42b90fa8 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 0938/2085] 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 9773f80126..f4c4dbfe0c 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 0939/2085] [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 45c3024aba..039e37bb5e 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 61f84ebe82..891f98830d 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 ][tag foo][bracket&tag >][operator ++])")
+ MT("openclosefragment",
+ "([bracket&tag <><][tag foo][bracket&tag >]hello [atom &][bracket&tag ][tag foo][bracket&tag >>][operator ++])")
+
MT("attr",
"([bracket&tag <][tag foo] [attribute abc]=[string 'value'][bracket&tag >]hello [atom &][bracket&tag ][tag foo][bracket&tag >][operator ++])")
diff --git a/mode/xml/xml.js b/mode/xml/xml.js
index f987a3a3ce..0f1c9b175e 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 0940/2085] [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 5923af8f87..b1ed6c7d78 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 4d0be609c4..90e5b41a63 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 0941/2085] 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 ea5642cdf9..9d95e39c75 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 0942/2085] 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 9d95e39c75..5500b9904b 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 0943/2085] [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 43ab7c8fb4..1795d86e94 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 786b89e299..4091ac6f4c 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 0944/2085] [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 ff00cf5002..7706429063 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 dad2e246ae..e3bde772a5 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 0945/2085] [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 08c9ebfb3f..37ae6fec27 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 e9cd342ff4..27132d16a7 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 0946/2085] 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 d358f9c3a0..0000000000
--- 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 0947/2085] [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 a518da3ec1..83f133a5e7 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 ? "/" : "";
+ var opt = cm.getOption("autoCloseTags");
+ var dontIndentOnAutoClose = (typeof opt == "object" && opt.dontIndentOnSlash);
for (var i = 0; i < ranges.length; i++) {
if (!ranges[i].empty()) return CodeMirror.Pass;
var pos = ranges[i].head, tok = cm.getTokenAt(pos);
@@ -127,9 +131,11 @@
}
cm.replaceSelections(replacements);
ranges = cm.listSelections();
- for (var i = 0; i < ranges.length; i++)
- if (i == ranges.length - 1 || ranges[i].head.line < ranges[i + 1].head.line)
- cm.indentLine(ranges[i].head.line);
+ if (!dontIndentOnAutoClose) {
+ for (var i = 0; i < ranges.length; i++)
+ if (i == ranges.length - 1 || ranges[i].head.line < ranges[i + 1].head.line)
+ cm.indentLine(ranges[i].head.line);
+ }
}
function autoCloseSlash(cm) {
diff --git a/src/edit/options.js b/src/edit/options.js
index 28f8bb60c6..49cf10112b 100644
--- a/src/edit/options.js
+++ b/src/edit/options.js
@@ -52,6 +52,7 @@ export function defineOptions(CodeMirror) {
clearCaches(cm)
regChange(cm)
}, true)
+
option("lineSeparator", null, (cm, val) => {
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 0948/2085] [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 514de1c8da..edab99f3d7 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 0949/2085] 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 8f4f22f5d6..c7a8ae7047 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 0950/2085] [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 37ae6fec27..5925b7c51f 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 27132d16a7..09bb951247 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 0951/2085] 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 65d8480893..47c79d1577 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 f81fcdd07b..855e6e4d9a 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 5500b9904b..37275fd9d7 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 7fb8eebef1..4051a32c55 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 d62ab84a39..1e4df9328a 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 3ffad1d5d1..9894b4f0d3 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 260f7d0af5..a49a7c2f39 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 0952/2085] 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 37275fd9d7..01721009d7 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 9894b4f0d3..28a2925884 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 a49a7c2f39..a12e4e3bb7 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 0953/2085] [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 b83be16f42..a9f50c05d1 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 0954/2085] 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 1dfcf8aff6..63f18b58a9 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 66bc80010c..9a61d152b8 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 0955/2085] [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 b2f79fc302..60f1b30026 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 0956/2085] [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 03cf671498..dde54574d5 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 0957/2085] [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 91a925268e..89b2ced87c 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 0958/2085] [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 89b2ced87c..4ab3e08e3e 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 0959/2085] [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 60f1b30026..7dfddccd39 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 0960/2085] [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 da416f2048..63e87733bf 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 0961/2085] [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 7dfddccd39..24e2468026 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 0962/2085] [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 90e5b41a63..e25e6627af 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 0963/2085] [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 5925b7c51f..7a9aadd330 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 0964/2085] [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 4ab3e08e3e..298074db21 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 e25e6627af..7038a33992 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 0965/2085] [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 2f8b1f84ae..65753ebf3f 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 0966/2085] [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 f5ec2cac1f..5600c8390d 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 0967/2085] [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 5600c8390d..5d20eea625 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 0968/2085] [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 9194dcea8b..0c52d256d9 100644
--- a/demo/theme.html
+++ b/demo/theme.html
@@ -34,6 +34,7 @@
+
@@ -119,6 +120,7 @@ Theme Demo
neat
neo
night
+ oceanic-next
panda-syntax
paraiso-dark
paraiso-light
diff --git a/theme/oceanic-next.css b/theme/oceanic-next.css
new file mode 100644
index 0000000000..296277ba04
--- /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 0969/2085] [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 0c52d256d9..e01f79a70f 100644
--- a/demo/theme.html
+++ b/demo/theme.html
@@ -42,6 +42,7 @@
+
@@ -128,6 +129,7 @@ Theme Demo
railscasts
rubyblue
seti
+ shadowfox
solarized dark
solarized light
the-matrix
diff --git a/theme/shadowfox.css b/theme/shadowfox.css
new file mode 100644
index 0000000000..32d59b139a
--- /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 0970/2085] [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 460f662f80..86b2fe1c95 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 0971/2085] [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 edab99f3d7..29085a21c4 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 0972/2085] [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 29085a21c4..64c910d849 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 0973/2085] [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 08e2149553..3acf952d9d 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 0974/2085] [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 64c910d849..9eb50ba974 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 167e6d0165..14a5183cab 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 0975/2085] [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 9b8b90b305..9fcd671cf5 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 86e344c572..05f07d22b1 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 0976/2085] [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 c58f785025..f73aaa51a0 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 0977/2085] [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 f73aaa51a0..a6d31210a2 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 0978/2085] [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 96009b4e1f..2a1c30b47d 100644
--- a/demo/lint.html
+++ b/demo/lint.html
@@ -9,7 +9,7 @@
-
+
diff --git a/demo/widget.html b/demo/widget.html
index da39a9297a..58ebb4d97a 100644
--- a/demo/widget.html
+++ b/demo/widget.html
@@ -7,7 +7,7 @@
-
+
-
CodeMirror
+
CodeMirror
Home
@@ -52,9 +52,9 @@ Active Line Demo
-
codemirror: Posted a description of the CodeMirror 2 internals at
- http://codemirror.net/2/internals.html
+ https://codemirror.net/2/internals.html
codemirror: Posted a description of the CodeMirror 2 internals at
- http://codemirror.net/2/internals.html
+ https://codemirror.net/2/internals.html
Wed, 02 Mar 2011 12:15:09 +0000
http://twitter.com/codemirror/statuses/42920879788789760
http://twitter.com/codemirror/statuses/42920879788789760
diff --git a/demo/anywordhint.html b/demo/anywordhint.html
index 0a7caece24..471b5bde7b 100644
--- a/demo/anywordhint.html
+++ b/demo/anywordhint.html
@@ -11,7 +11,7 @@
-
CodeMirror
+
CodeMirror
Home
diff --git a/demo/bidi.html b/demo/bidi.html
index 645e648c86..742cf95f7b 100644
--- a/demo/bidi.html
+++ b/demo/bidi.html
@@ -12,7 +12,7 @@
fieldset {border: none}
-
CodeMirror
+
CodeMirror
Home
diff --git a/demo/btree.html b/demo/btree.html
index ba07bc74f4..985309d11b 100644
--- a/demo/btree.html
+++ b/demo/btree.html
@@ -11,7 +11,7 @@
.CodeMirror {border: 1px solid #aaa; height: 400px}
-
CodeMirror
+
CodeMirror
Home
diff --git a/demo/buffers.html b/demo/buffers.html
index 16ffc7dfe9..619c522524 100644
--- a/demo/buffers.html
+++ b/demo/buffers.html
@@ -12,7 +12,7 @@
.CodeMirror {border: 1px solid black; height: 250px;}
-
CodeMirror
+
CodeMirror
Home
diff --git a/demo/changemode.html b/demo/changemode.html
index 9405932abe..fb189fb965 100644
--- a/demo/changemode.html
+++ b/demo/changemode.html
@@ -12,7 +12,7 @@
.CodeMirror {border: 1px solid black;}
-
CodeMirror
+
CodeMirror
Home
diff --git a/demo/closebrackets.html b/demo/closebrackets.html
index d702f52696..6fe44c74d0 100644
--- a/demo/closebrackets.html
+++ b/demo/closebrackets.html
@@ -12,7 +12,7 @@
.CodeMirror {border-top: 1px solid #888; border-bottom: 1px solid #888;}
-
CodeMirror
+
CodeMirror
Home
diff --git a/demo/closetag.html b/demo/closetag.html
index 79959d2c4d..b0f3980611 100644
--- a/demo/closetag.html
+++ b/demo/closetag.html
@@ -16,7 +16,7 @@
.CodeMirror {border-top: 1px solid #888; border-bottom: 1px solid #888;}
-
CodeMirror
+
CodeMirror
Home
diff --git a/demo/complete.html b/demo/complete.html
index 0464389ebf..2fef796401 100644
--- a/demo/complete.html
+++ b/demo/complete.html
@@ -13,7 +13,7 @@
-
CodeMirror
+
CodeMirror
Home
diff --git a/demo/emacs.html b/demo/emacs.html
index c626b8d408..69d5e600eb 100644
--- a/demo/emacs.html
+++ b/demo/emacs.html
@@ -18,7 +18,7 @@
.CodeMirror {border-top: 1px solid #eee; border-bottom: 1px solid #eee;}
-
CodeMirror
+
CodeMirror
Home
diff --git a/demo/folding.html b/demo/folding.html
index 1882b7c810..745939b191 100644
--- a/demo/folding.html
+++ b/demo/folding.html
@@ -35,7 +35,7 @@
-
CodeMirror
+
CodeMirror
Home
diff --git a/demo/fullscreen.html b/demo/fullscreen.html
index 1fbdc488e1..06bf03e107 100644
--- a/demo/fullscreen.html
+++ b/demo/fullscreen.html
@@ -12,7 +12,7 @@
-
CodeMirror
+
CodeMirror
Home
diff --git a/demo/hardwrap.html b/demo/hardwrap.html
index 84ba0cc0c2..a4e4cf7dae 100644
--- a/demo/hardwrap.html
+++ b/demo/hardwrap.html
@@ -12,7 +12,7 @@
.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}
-
CodeMirror
+
CodeMirror
Home
diff --git a/demo/html5complete.html b/demo/html5complete.html
index 411baae3ed..8433a92119 100644
--- a/demo/html5complete.html
+++ b/demo/html5complete.html
@@ -22,7 +22,7 @@
-
CodeMirror
+
CodeMirror
Home
diff --git a/demo/indentwrap.html b/demo/indentwrap.html
index 3d3d0af6ab..660d44eb2b 100644
--- a/demo/indentwrap.html
+++ b/demo/indentwrap.html
@@ -12,7 +12,7 @@
.CodeMirror pre > * { text-indent: 0px; }
-
CodeMirror
+
CodeMirror
Home
diff --git a/demo/lint.html b/demo/lint.html
index 3abb9224fb..8d9d8f6dd4 100644
--- a/demo/lint.html
+++ b/demo/lint.html
@@ -20,7 +20,7 @@
.CodeMirror {border: 1px solid black;}
-
CodeMirror
+
CodeMirror
Home
diff --git a/demo/loadmode.html b/demo/loadmode.html
index 809cd90225..93b0ed5fd0 100644
--- a/demo/loadmode.html
+++ b/demo/loadmode.html
@@ -12,7 +12,7 @@
.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}
-
CodeMirror
+
CodeMirror
Home
diff --git a/demo/marker.html b/demo/marker.html
index 3a8b850009..5b500b7b24 100644
--- a/demo/marker.html
+++ b/demo/marker.html
@@ -13,7 +13,7 @@
.CodeMirror {border: 1px solid #aaa;}
-
CodeMirror
+
CodeMirror
Home
diff --git a/demo/markselection.html b/demo/markselection.html
index d4c8a7a0d1..2e0a89be02 100644
--- a/demo/markselection.html
+++ b/demo/markselection.html
@@ -15,7 +15,7 @@
.styled-background { background-color: #ff7; }
-
CodeMirror
+
CodeMirror
Home
diff --git a/demo/matchhighlighter.html b/demo/matchhighlighter.html
index 893d72134b..2b2f5cb5b8 100644
--- a/demo/matchhighlighter.html
+++ b/demo/matchhighlighter.html
@@ -21,7 +21,7 @@
.CodeMirror-selection-highlight-scrollbar {background-color: green}
-
CodeMirror
+
CodeMirror
Home
diff --git a/demo/matchtags.html b/demo/matchtags.html
index 175639a396..e90707d176 100644
--- a/demo/matchtags.html
+++ b/demo/matchtags.html
@@ -13,7 +13,7 @@
.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}
-
CodeMirror
+
CodeMirror
Home
diff --git a/demo/merge.html b/demo/merge.html
index 9e2d14ea37..6eff813452 100644
--- a/demo/merge.html
+++ b/demo/merge.html
@@ -28,7 +28,7 @@
}
-
CodeMirror
+
CodeMirror
Home
diff --git a/demo/multiplex.html b/demo/multiplex.html
index ca8c80aedc..ec3870baa5 100644
--- a/demo/multiplex.html
+++ b/demo/multiplex.html
@@ -13,7 +13,7 @@
.cm-delimit {color: #fa4;}
-
CodeMirror
+
CodeMirror
Home
diff --git a/demo/mustache.html b/demo/mustache.html
index ae4e6a891b..ca1d7db90e 100644
--- a/demo/mustache.html
+++ b/demo/mustache.html
@@ -13,7 +13,7 @@
.cm-mustache {color: #0ca;}
-
CodeMirror
+
CodeMirror
Home
diff --git a/demo/panel.html b/demo/panel.html
index 1ce3d87c79..34d0017e2b 100644
--- a/demo/panel.html
+++ b/demo/panel.html
@@ -40,7 +40,7 @@
-
CodeMirror
+
CodeMirror
Home
diff --git a/demo/placeholder.html b/demo/placeholder.html
index 432331a486..99f709a312 100644
--- a/demo/placeholder.html
+++ b/demo/placeholder.html
@@ -14,7 +14,7 @@
.CodeMirror pre.CodeMirror-placeholder { color: #999; }
-
CodeMirror
+
CodeMirror
Home
diff --git a/demo/preview.html b/demo/preview.html
index 19e1530b80..660c7921c6 100644
--- a/demo/preview.html
+++ b/demo/preview.html
@@ -25,7 +25,7 @@
}
-
CodeMirror
+
CodeMirror
Home
diff --git a/demo/requirejs.html b/demo/requirejs.html
index 438ef9fbf6..796031736a 100644
--- a/demo/requirejs.html
+++ b/demo/requirejs.html
@@ -15,7 +15,7 @@
-
CodeMirror
+
CodeMirror
Home
Manual
diff --git a/demo/resize.html b/demo/resize.html
index 1c1ef390ab..6556f50f30 100644
--- a/demo/resize.html
+++ b/demo/resize.html
@@ -14,7 +14,7 @@
}
-
CodeMirror
+
CodeMirror
Home
diff --git a/demo/rulers.html b/demo/rulers.html
index 2ac4111582..7c3530bd0c 100644
--- a/demo/rulers.html
+++ b/demo/rulers.html
@@ -11,7 +11,7 @@
.CodeMirror {border-top: 1px solid #888; border-bottom: 1px solid #888;}
-
CodeMirror
+
CodeMirror
Home
diff --git a/demo/runmode.html b/demo/runmode.html
index ab8938d8d3..0284c40567 100644
--- a/demo/runmode.html
+++ b/demo/runmode.html
@@ -9,7 +9,7 @@
-
CodeMirror
+
CodeMirror
Home
diff --git a/demo/search.html b/demo/search.html
index 4ca81e20f5..502af0c15c 100644
--- a/demo/search.html
+++ b/demo/search.html
@@ -20,7 +20,7 @@
dt {font-family: monospace; color: #666;}
-
CodeMirror
+
CodeMirror
Home
diff --git a/demo/simplemode.html b/demo/simplemode.html
index 04c194a4ba..d547e6f6fb 100644
--- a/demo/simplemode.html
+++ b/demo/simplemode.html
@@ -16,7 +16,7 @@
-
CodeMirror
+
CodeMirror
Home
diff --git a/demo/simplescrollbars.html b/demo/simplescrollbars.html
index 9d40932649..1604297894 100644
--- a/demo/simplescrollbars.html
+++ b/demo/simplescrollbars.html
@@ -12,7 +12,7 @@
-
CodeMirror
+
CodeMirror
Home
diff --git a/demo/spanaffectswrapping_shim.html b/demo/spanaffectswrapping_shim.html
index 879d99b606..46667ddc69 100644
--- a/demo/spanaffectswrapping_shim.html
+++ b/demo/spanaffectswrapping_shim.html
@@ -5,7 +5,7 @@
-
CodeMirror
+
CodeMirror
Home
diff --git a/demo/sublime.html b/demo/sublime.html
index 233dd83a4e..645f926816 100644
--- a/demo/sublime.html
+++ b/demo/sublime.html
@@ -25,7 +25,7 @@
.CodeMirror-linenumbers { padding: 0 8px; }
-
CodeMirror
+
CodeMirror
Home
diff --git a/demo/tern.html b/demo/tern.html
index d0dee8a70e..a7ffe35cda 100644
--- a/demo/tern.html
+++ b/demo/tern.html
@@ -27,7 +27,7 @@
.CodeMirror {border: 1px solid #ddd;}
-
CodeMirror
+
CodeMirror
Home
diff --git a/demo/theme.html b/demo/theme.html
index c4e7899f09..c81768c46b 100644
--- a/demo/theme.html
+++ b/demo/theme.html
@@ -66,7 +66,7 @@
.CodeMirror {border: 1px solid black; font-size:13px}
-
CodeMirror
+
CodeMirror
Home
diff --git a/demo/trailingspace.html b/demo/trailingspace.html
index 1992ba3ff9..fa8afba9e0 100644
--- a/demo/trailingspace.html
+++ b/demo/trailingspace.html
@@ -16,7 +16,7 @@
}
-
CodeMirror
+
CodeMirror
Home
diff --git a/demo/variableheight.html b/demo/variableheight.html
index d49942864b..d5495255e2 100644
--- a/demo/variableheight.html
+++ b/demo/variableheight.html
@@ -20,7 +20,7 @@
.cm-strong { font-size: 140%; }
-
CodeMirror
+
CodeMirror
Home
diff --git a/demo/vim.html b/demo/vim.html
index bd704f7583..d51b9a0ed9 100644
--- a/demo/vim.html
+++ b/demo/vim.html
@@ -18,7 +18,7 @@
.CodeMirror {border-top: 1px solid #eee; border-bottom: 1px solid #eee;}
-
CodeMirror
+
CodeMirror
Home
diff --git a/demo/visibletabs.html b/demo/visibletabs.html
index 2eec337ed3..d00ba16f10 100644
--- a/demo/visibletabs.html
+++ b/demo/visibletabs.html
@@ -16,7 +16,7 @@
}
-
CodeMirror
+
CodeMirror
Home
diff --git a/demo/widget.html b/demo/widget.html
index 58ebb4d97a..c77b35a0b7 100644
--- a/demo/widget.html
+++ b/demo/widget.html
@@ -14,7 +14,7 @@
.lint-error-icon {color: white; background-color: red; font-weight: bold; border-radius: 50%; padding: 0 3px; margin-right: 7px;}
-
CodeMirror
+
CodeMirror
Home
diff --git a/demo/xmlcomplete.html b/demo/xmlcomplete.html
index 043f0c429c..613ca2ec61 100644
--- a/demo/xmlcomplete.html
+++ b/demo/xmlcomplete.html
@@ -14,7 +14,7 @@
.CodeMirror { border: 1px solid #eee; }
-
CodeMirror
+
CodeMirror
Home
diff --git a/doc/internals.html b/doc/internals.html
index 079466f630..2137c937f2 100644
--- a/doc/internals.html
+++ b/doc/internals.html
@@ -7,7 +7,7 @@
-
CodeMirror
+
CodeMirror
Home
@@ -46,7 +46,7 @@ (Re-) Implementing A Syntax-Highlighting Editor in JavaScript
on my blog.
This is a followup to
-my Brutal Odyssey to the
+my Brutal Odyssey to the
Dark Side of the DOM Tree story. That one describes the
mind-bending process of implementing (what would become) CodeMirror 1.
This one describes the internals of CodeMirror 2, a complete rewrite
@@ -108,7 +108,7 @@
(Re-) Implementing A Syntax-Highlighting Editor in JavaScript
incompatibility and bugginess in these features, I had to paper over
with my own code. Not only did I have to do some serious stunt-work to
get it to work on older browsers (as detailed in the
-previous story ), things
+previous story ), things
also kept breaking in newly released versions, requiring me to come up
with new scary hacks in order to keep up. This was starting
to lose its appeal.
@@ -293,7 +293,7 @@ Intelligent Updating
Parsers can be Simple
When I wrote CodeMirror 1, I
-thought interruptable
+thought interruptable
parsers were a hugely scary and complicated thing, and I used a
bunch of heavyweight abstractions to keep this supposed complexity
under control: parsers
diff --git a/doc/manual.html b/doc/manual.html
index d389f1da39..7bd16a44d7 100644
--- a/doc/manual.html
+++ b/doc/manual.html
@@ -24,7 +24,7 @@
-
CodeMirror
+
CodeMirror
Home
Manual
diff --git a/doc/realworld.html b/doc/realworld.html
index df698cc1ea..b226f0204e 100644
--- a/doc/realworld.html
+++ b/doc/realworld.html
@@ -5,7 +5,7 @@
-
CodeMirror
+
CodeMirror
Home
diff --git a/doc/releases.html b/doc/releases.html
index 4ad74880cf..1eff6792a0 100644
--- a/doc/releases.html
+++ b/doc/releases.html
@@ -6,7 +6,7 @@
-
CodeMirror
+
CodeMirror
Home
@@ -30,24 +30,24 @@ Release notes and version history
Version 5.x
- 20-07-2018: Version 5.39.2 :
+ 20-07-2018: Version 5.39.2 :
Fix issue where when you pass the document as a Doc instance to the CodeMirror constructor, the mode option was ignored.
Fix bug where line height could be computed wrong with a line widget below a collapsed line.
Fix overeager .npmignore dropping the bin/source-highlight utility from the distribution.
- show-hint addon : Fix behavior when backspacing to the start of the line with completions open.
+ show-hint addon : Fix behavior when backspacing to the start of the line with completions open.
- 20-06-2018: Version 5.39.0 :
+ 20-06-2018: Version 5.39.0 :
Fix issue that in some circumstances caused content to be clipped off at the bottom after a resize.
- markdown mode : Improve handling of blank lines in HTML tags.
- stex mode : Add an inMathMode option to start the mode in math mode.
+ markdown mode : Improve handling of blank lines in HTML tags.
+ stex mode : Add an inMathMode option to start the mode in math mode.
- 21-05-2018: Version 5.38.0 :
+ 21-05-2018: Version 5.38.0 :
Improve reliability of noticing a missing mouseup event during dragging.
@@ -55,189 +55,189 @@ Version 5.x
Fix interpretation of line breaks and non-breaking spaces inserted by renderer in contentEditable mode.
Work around some browsers inexplicably making the fake scrollbars focusable.
Make sure coordsChar doesn't return positions inside collapsed ranges.
- javascript mode : Support block scopes, bindingless catch, bignum suffix, s regexp flag.
- markdown mode : Adjust a wasteful regexp.
- show-hint addon : Allow opening the control without any item selected.
- New theme: darcula .
- dialog addon : Add a CSS class (dialog-opened) to the editor when a dialog is open.
+ javascript mode : Support block scopes, bindingless catch, bignum suffix, s regexp flag.
+ markdown mode : Adjust a wasteful regexp.
+ show-hint addon : Allow opening the control without any item selected.
+ New theme: darcula .
+ dialog addon : Add a CSS class (dialog-opened) to the editor when a dialog is open.
- 20-04-2018: Version 5.37.0 :
+ 20-04-2018: Version 5.37.0 :
- 20-03-2018: Version 5.36.0 :
+ 20-03-2018: Version 5.36.0 :
Make sure all document-level event handlers are registered on the document that the editor is part of.
Fix issue that prevented edits whose origin starts with + from being combined in history events for an editor-less document.
- multiplex addon : Improve handling of indentation.
- merge addon : Use CSS :after element to style the scroll-lock icon.
- javascript-hint addon : Don't provide completions in JSON mode.
- continuelist addon : Fix numbering error.
- show-hint addon : Make fromList completion strategy act on the current token up to the cursor, rather than the entire token.
- markdown mode : Fix a regexp with potentially exponental complexity.
- New theme: lucario .
+ multiplex addon : Improve handling of indentation.
+ merge addon : Use CSS :after element to style the scroll-lock icon.
+ javascript-hint addon : Don't provide completions in JSON mode.
+ continuelist addon : Fix numbering error.
+ show-hint addon : Make fromList completion strategy act on the current token up to the cursor, rather than the entire token.
+ markdown mode : Fix a regexp with potentially exponental complexity.
+ New theme: lucario .
- 20-02-2018: Version 5.35.0 :
+ 20-02-2018: Version 5.35.0 :
Fix problem where selection undo might change read-only documents.
Fix crash when calling addLineWidget on a document that has no attached editor.
- searchcursor addon : Fix behavior of ^ in multiline regexp mode.
- match-highlighter addon : Fix problem with matching words that have regexp special syntax in them.
- sublime bindings : Fix addCursorToSelection for short lines.
- vim bindings : Support alternative delimiters in replace command.
- javascript mode : Support TypeScript intersection types, dynamic import.
- stex mode : Fix parsing of \( \) delimiters, recognize more atom arguments.
- haskell mode : Highlight more builtins, support <* and *>.
- sql mode : Make it possible to disable backslash escapes in strings for dialects that don't have them, do this for MS SQL.
- dockerfile mode : Highlight strings and ports, recognize more instructions.
+ searchcursor addon : Fix behavior of ^ in multiline regexp mode.
+ match-highlighter addon : Fix problem with matching words that have regexp special syntax in them.
+ sublime bindings : Fix addCursorToSelection for short lines.
+ vim bindings : Support alternative delimiters in replace command.
+ javascript mode : Support TypeScript intersection types, dynamic import.
+ stex mode : Fix parsing of \( \) delimiters, recognize more atom arguments.
+ haskell mode : Highlight more builtins, support <* and *>.
+ sql mode : Make it possible to disable backslash escapes in strings for dialects that don't have them, do this for MS SQL.
+ dockerfile mode : Highlight strings and ports, recognize more instructions.
- 29-01-2018: Version 5.34.0 :
+ 29-01-2018: Version 5.34.0 :
- 21-12-2017: Version 5.33.0 :
+ 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.
+ 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 :
+ 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.
+ 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 :
+ 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.
+ 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.
+ 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 :
+ 20-09-2017: Version 5.30.0 :
Fixed a number of issues with drawing right-to-left selections and mouse selection in bidirectional text.
- search addon : Fix crash when restarting search after doing empty search.
+ search addon : Fix crash when restarting search after doing empty search.
mark-selection addon : Fix off-by-one bug.
- tern addon : Fix bad request made when editing at the bottom of a large document.
- javascript mode : Improve parsing in a number of corner cases.
- markdown mode : Fix crash when a sub-mode doesn't support indentation, allow uppercase X in task lists.
- gfm mode : Don't highlight SHA1 'hashes' without numbers to avoid false positives.
- soy mode : Support injected data and @param in comments.
- simple mode addon : Allow groups in regexps when token isn't an array.
+ tern addon : Fix bad request made when editing at the bottom of a large document.
+ javascript mode : Improve parsing in a number of corner cases.
+ markdown mode : Fix crash when a sub-mode doesn't support indentation, allow uppercase X in task lists.
+ gfm mode : Don't highlight SHA1 'hashes' without numbers to avoid false positives.
+ soy mode : Support injected data and @param in comments.
+ simple mode addon : Allow groups in regexps when token isn't an array.
- 24-08-2017: Version 5.29.0 :
+ 24-08-2017: Version 5.29.0 :
Fix crash in contentEditable input style when editing near a bookmark.
- Make sure change origins are preserved when splitting changes on read-only marks .
- javascript mode : More support for TypeScript syntax.
- d mode : Support nested comments.
- python mode : Improve tokenizing of operators.
- markdown mode : Further improve CommonMark conformance.
- css mode : Don't run comment tokens through the mode's state machine.
- shell mode : Allow strings to span lines.
- search addon : Fix crash in persistent search when extraKeys is null.
+ Make sure change origins are preserved when splitting changes on read-only marks .
+ javascript mode : More support for TypeScript syntax.
+ d mode : Support nested comments.
+ python mode : Improve tokenizing of operators.
+ markdown mode : Further improve CommonMark conformance.
+ css mode : Don't run comment tokens through the mode's state machine.
+ shell mode : Allow strings to span lines.
+ search addon : Fix crash in persistent search when extraKeys is null.
- 21-07-2017: Version 5.28.0 :
+ 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.
+ 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 :
+ 29-06-2017: Version 5.27.4 :
Fix crash when using mode lookahead.
- markdown mode : Don't block inner mode's indentation support.
+ markdown mode : Don't block inner mode's indentation support.
- 22-06-2017: Version 5.27.2 :
+ 22-06-2017: Version 5.27.2 :
- 22-06-2017: Version 5.27.0 :
+ 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.
- 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.
- rust mode : Add the correct MIME type.
- matchbrackets addon : Document options.
+ 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.
+ 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.
- The behavior of mouse selection and dragging can now be customized with the configureMouse option.
- Modes can now look ahead across line boundaries with the StringStream.lookahead method.
+ The behavior of mouse selection and dragging can now be customized with the configureMouse option.
+ Modes can now look ahead across line boundaries with the StringStream.lookahead method.
Introduces a "type" token type, makes modes that recognize types output it, and add styling for it to the themes.
- New pasteLinesPerSelection option to control the behavior of pasting multiple lines into multiple selections.
- searchcursor addon : Support multi-line regular expression matches, and normalize strings when matching.
+ New pasteLinesPerSelection option to control the behavior of pasting multiple lines into multiple selections.
+ searchcursor addon : Support multi-line regular expression matches, and normalize strings when matching.
- 22-05-2017: Version 5.26.0 :
+ 22-05-2017: Version 5.26.0 :
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.
- 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.
- soy mode : Support single-quoted strings.
- go mode : Don't try to indent inside strings or comments.
+ 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.
+ soy mode : Support single-quoted strings.
+ go mode : Don't try to indent inside strings or comments.
- 20-04-2017: Version 5.25.2 :
+ 20-04-2017: Version 5.25.2 :
Better handling of selections that cover the whole viewport in contentEditable-mode.
@@ -245,12 +245,12 @@ Version 5.x
Work around Chrome Android bug when converting screen coordinates to editor positions.
Make sure long-clicking a selection sets a cursor and doesn't show the editor losing focus.
Fix issue where pointer events were incorrectly disabled on Chrome's overlay scrollbars.
- javascript mode : Recognize annotations and TypeScript-style type parameters.
- shell mode : Handle nested braces.
- markdown mode : Make parsing of strong/em delimiters CommonMark-compliant.
+ javascript mode : Recognize annotations and TypeScript-style type parameters.
+ shell mode : Handle nested braces.
+ markdown mode : Make parsing of strong/em delimiters CommonMark-compliant.
- 20-03-2017: Version 5.25.0 :
+ 20-03-2017: Version 5.25.0 :
In contentEditable-mode, properly locate changes that repeat a character when inserted with IME.
@@ -259,163 +259,163 @@ Version 5.x
Count Unicode control characters 0x80 to 0x9F as special (non-printing) chars.
Fix handling of shadow DOM roots when finding the active element.
Add role=presentation to more DOM elements to improve screen reader support.
- merge addon : Make aligning of unchanged chunks more robust.
- comment addon : Fix comment-toggling on a block of text that starts and ends in a (differnet) block comment.
- javascript mode : Improve support for TypeScript syntax.
- r mode : Fix indentation after semicolon-less statements.
- shell mode : Properly handle escaped parentheses in parenthesized expressions.
- markdown mode : Fix a few bugs around leaving fenced code blocks.
- 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.
- sql mode : Support sqlite dialect.
+ merge addon : Make aligning of unchanged chunks more robust.
+ comment addon : Fix comment-toggling on a block of text that starts and ends in a (differnet) block comment.
+ javascript mode : Improve support for TypeScript syntax.
+ r mode : Fix indentation after semicolon-less statements.
+ shell mode : Properly handle escaped parentheses in parenthesized expressions.
+ markdown mode : Fix a few bugs around leaving fenced code blocks.
+ 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.
+ sql mode : Support sqlite dialect.
- 22-02-2017: Version 5.24.2 :
+ 22-02-2017: Version 5.24.2 :
- javascript mode : Support computed class method names.
- merge addon : Improve aligning of unchanged code in the presence of marks and line widgets.
+ javascript mode : Support computed class method names.
+ merge addon : Improve aligning of unchanged code in the presence of marks and line widgets.
- 20-02-2017: Version 5.24.0 :
+ 20-02-2017: Version 5.24.0 :
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.
- comment addon : Support a per-mode useInnerComments option to optionally suppress descending to the inner modes to get comment strings.
+ 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.
Fix bug in handling of read-only marked text.
- shell mode : Properly tokenize nested parentheses.
- python mode : Support underscores in number literals.
- sass mode : Uses the full list of CSS properties and keywords from the CSS mode, rather than defining its own incomplete subset. Now depends on the css mode.
- css mode : Expose lineComment property for LESS and SCSS dialects. Recognize vendor prefixes on pseudo-elements.
- julia mode : Properly indent elseif lines.
- markdown mode : Properly recognize the end of fenced code blocks when inside other markup.
- scala mode : Improve handling of operators containing #, @, and : chars.
- xml mode : Allow dashes in HTML tag names.
- javascript mode : Improve parsing of async methods, TypeScript-style comma-separated superclass lists.
- indent-fold addon : Ignore comment lines.
+ shell mode : Properly tokenize nested parentheses.
+ python mode : Support underscores in number literals.
+ sass mode : Uses the full list of CSS properties and keywords from the CSS mode, rather than defining its own incomplete subset. Now depends on the css mode.
+ css mode : Expose lineComment property for LESS and SCSS dialects. Recognize vendor prefixes on pseudo-elements.
+ julia mode : Properly indent elseif lines.
+ markdown mode : Properly recognize the end of fenced code blocks when inside other markup.
+ scala mode : Improve handling of operators containing #, @, and : chars.
+ xml mode : Allow dashes in HTML tag names.
+ javascript mode : Improve parsing of async methods, TypeScript-style comma-separated superclass lists.
+ indent-fold addon : Ignore comment lines.
- 19-01-2017: Version 5.23.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.
+ 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.
+ 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 :
+ 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.
+ 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 .
+ 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 :
+ 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 .
+ 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.
+ 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 :
+ 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.
+ 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.
+ The refresh event is now documented and stable.
- 20-09-2016: Version 5.19.0 :
+ 20-09-2016: Version 5.19.0 :
- erlang mode : Fix mode crash when trying to read an empty context.
- comment addon : Fix broken behavior when toggling comments inside a comment.
+ erlang mode : Fix mode crash when trying to read an empty context.
+ comment addon : Fix broken behavior when toggling comments inside a comment.
xml-fold addon: Fix a null-dereference bug.
Page up and page down now do something even in single-line documents.
Fix an issue where the cursor position could be off in really long (~8000 character) tokens.
- javascript mode : Better indentation when semicolons are missing. Better support for TypeScript classes, optional parameters, and the type keyword.
- The blur and focus events now pass the DOM event to their handlers.
+ javascript mode : Better indentation when semicolons are missing. Better support for TypeScript classes, optional parameters, and the type keyword.
+ The blur and focus events now pass the DOM event to their handlers.
- 23-08-2016: Version 5.18.2 :
+ 23-08-2016: Version 5.18.2 :
- vue mode : Fix outdated references to renamed Pug mode dependency.
+ vue mode : Fix outdated references to renamed Pug mode dependency.
- 22-08-2016: Version 5.18.0 :
+ 22-08-2016: Version 5.18.0 :
- Make sure gutter backgrounds stick to the rest of the gutter during horizontal scrolling.
- The contenteditable inputStyle now properly supports pasting on pre-Edge IE versions.
- javascript mode : Fix some small parsing bugs and improve TypeScript support.
- matchbrackets addon : Fix bug where active highlighting was left in editor when the addon was disabled.
- match-highlighter addon : Only start highlighting things when the editor gains focus.
- javascript-hint addon : Also complete non-enumerable properties.
- The addOverlay method now supports a priority option to control the order in which overlays are applied.
+ Make sure gutter backgrounds stick to the rest of the gutter during horizontal scrolling.
+ The contenteditable inputStyle now properly supports pasting on pre-Edge IE versions.
+ javascript mode : Fix some small parsing bugs and improve TypeScript support.
+ matchbrackets addon : Fix bug where active highlighting was left in editor when the addon was disabled.
+ match-highlighter addon : Only start highlighting things when the editor gains focus.
+ javascript-hint addon : Also complete non-enumerable properties.
+ The 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.
- The mode formerly known as Jade was renamed to Pug .
- The Python mode now defaults to Python 3 (rather than 2) syntax.
+ The mode formerly known as Jade was renamed to Pug .
+ The Python mode now defaults to Python 3 (rather than 2) syntax.
- 19-07-2016: Version 5.17.0 :
+ 19-07-2016: Version 5.17.0 :
Fix problem with wrapped trailing whitespace displaying incorrectly.
Prevent IME dialog from overlapping typed content in Chrome.
Improve measuring of characters near a line wrap.
- javascript mode : Improve support for async, allow trailing commas in import lists.
- vim bindings : Fix backspace in replace mode.
- sublime bindings : Fix some key bindings on OS X to match Sublime Text.
- markdown mode : Add more classes to image links in highlight-formatting mode.
+ javascript mode : Improve support for async, allow trailing commas in import lists.
+ vim bindings : Fix backspace in replace mode.
+ sublime bindings : Fix some key bindings on OS X to match Sublime Text.
+ markdown mode : Add more classes to image links in highlight-formatting mode.
- 20-06-2016: Version 5.16.0 :
+ 20-06-2016: Version 5.16.0 :
Fix glitches when dragging content caused by the drop indicator receiving mouse events.
Make Control-drag work on Firefox.
Make clicking or selection-dragging at the end of a wrapped line select the right position.
- show-hint addon : Prevent widget scrollbar from hiding part of the hint text.
- rulers addon : Prevent rulers from forcing a horizontal editor scrollbar.
- search addon : Automatically bind search-related keys in persistent dialog.
- sublime keymap : Add a multi-cursor aware smart backspace binding.
+ show-hint addon : Prevent widget scrollbar from hiding part of the hint text.
+ rulers addon : Prevent rulers from forcing a horizontal editor scrollbar.
+ search addon : Automatically bind search-related keys in persistent dialog.
+ sublime keymap : Add a multi-cursor aware smart backspace binding.
- 20-05-2016: Version 5.15.2 :
+ 20-05-2016: Version 5.15.2 :
Fix a critical document corruption bug that occurs when a document is gradually grown.
- 20-05-2016: Version 5.15.0 :
+ 20-05-2016: Version 5.15.0 :
Fix bug that caused the selection to reset when focusing the editor in contentEditable input mode.
@@ -424,77 +424,77 @@ Version 5.x
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.
Explicitly bind Ctrl-O on OS X to make that binding (“open line”) act as expected.
- Pasting linewise-copied content when there is no selection now inserts the lines above the current line.
- markdown mode : Fix several issues in matching link targets.
- clike mode : Improve indentation of C++ template declarations.
- javascript mode : Support async/await and improve support for TypeScript type syntax.
-
-
- 20-04-2016: Version 5.14.0 :
-
-
+
+ 20-04-2016: Version 5.14.0 :
+
+
- 21-03-2016: Version 5.13.2 :
+ 21-03-2016: Version 5.13.2 :
Solves a problem where the gutter would sometimes not extend all the way to the end of the document.
- 21-03-2016: Version 5.13 :
+ 21-03-2016: Version 5.13 :
- 19-02-2016: Version 5.12 :
+ 19-02-2016: Version 5.12 :
- Vim bindings : Ctrl-Q is now an alias for Ctrl-V.
- Vim bindings : The Vim API now exposes an unmap method to unmap bindings.
- active-line addon : This addon can now style the active line's gutter.
- FCL mode : Newly added.
- SQL mode : Now has a Postgresql dialect.
+ Vim bindings : Ctrl-Q is now an alias for Ctrl-V.
+ Vim bindings : The Vim API now exposes an unmap method to unmap bindings.
+ active-line addon : This addon can now style the active line's gutter.
+ FCL mode : Newly added.
+ SQL mode : Now has a Postgresql dialect.
Fix issue where trying to scroll to a horizontal position outside of the document's width could cause the gutter to be positioned incorrectly.
Use absolute, rather than fixed positioning in the context-menu intercept hack, to work around a problem when the editor is inside a transformed parent container.
Solve a problem where the horizontal scrollbar could hide text in Firefox.
Fix a bug that caused phantom scroll space under the text in some situations.
- Sublime Text bindings : Bind delete-line to Shift-Ctrl-K on OS X.
- Markdown mode : Fix issue where the mode would keep state related to fenced code blocks in an unsafe way, leading to occasional corrupted parses.
- Markdown mode : Ignore backslashes in code fragments.
- Markdown mode : Use whichever mode is registered as text/html to parse HTML.
- Clike mode : Improve indentation of Scala => functions.
- Python mode : Improve indentation of bracketed code.
- HTMLMixed mode : Support multi-line opening tags for sub-languages (<script>, <style>, etc).
- Spreadsheet mode : Fix bug where the mode did not advance the stream when finding a backslash.
- XML mode : The mode now takes a matchClosing option to configure whether mismatched closing tags should be highlighted as errors.
+ Sublime Text bindings : Bind delete-line to Shift-Ctrl-K on OS X.
+ Markdown mode : Fix issue where the mode would keep state related to fenced code blocks in an unsafe way, leading to occasional corrupted parses.
+ Markdown mode : Ignore backslashes in code fragments.
+ Markdown mode : Use whichever mode is registered as text/html to parse HTML.
+ Clike mode : Improve indentation of Scala => functions.
+ Python mode : Improve indentation of bracketed code.
+ HTMLMixed mode : Support multi-line opening tags for sub-languages (<script>, <style>, etc).
+ Spreadsheet mode : Fix bug where the mode did not advance the stream when finding a backslash.
+ XML mode : The mode now takes a matchClosing option to configure whether mismatched closing tags should be highlighted as errors.
- 20-01-2016: Version 5.11 :
+ 20-01-2016: Version 5.11 :
- 21-12-2015: Version 5.10 :
+ 21-12-2015: Version 5.10 :
Modify the way atomic ranges are skipped by selection to try and make it less surprising.
@@ -518,7 +518,7 @@ Version 5.x
Full list of patches
- 23-11-2015: Version 5.9 :
+ 23-11-2015: Version 5.9 :
Improve the way overlay (OS X-style) scrollbars are handled
@@ -529,7 +529,7 @@ Version 5.x
Full list of patches
- 20-10-2015: Version 5.8 :
+ 20-10-2015: Version 5.8 :
Fixes an infinite loop in
@@ -542,7 +542,7 @@ Version 5.x
Full list of patches
- 20-09-2015: Version 5.7 :
+ 20-09-2015: Version 5.7 :
- 20-08-2015: Version 5.6 :
+ 20-08-2015: Version 5.6 :
Fix bug where you could paste into a readOnly editor
@@ -570,7 +570,7 @@ Version 5.x
Full list of patches
- 20-07-2015: Version 5.5 :
+ 20-07-2015: Version 5.5 :
- 25-06-2015: Version 5.4 :
+ 25-06-2015: Version 5.4 :
- 20-05-2015: Version 5.3 :
+ 20-05-2015: Version 5.3 :
Fix several regressions in the show-hint addon (completeSingle option, "shown" and "close" events)
@@ -602,7 +602,7 @@ Version 5.x
Full list of patches
- 20-04-2015: Version 5.2 :
+ 20-04-2015: Version 5.2 :
Fix several race conditions
@@ -619,7 +619,7 @@ Version 5.x
Full list of patches
- 23-03-2015: Version 5.1 :
+ 23-03-2015: Version 5.1 :
- 20-02-2015: Version 5.0 :
+ 20-02-2015: Version 5.0 :
Experimental mobile support (tested on iOS, Android Chrome, stock Android browser)
@@ -646,7 +646,7 @@ Version 5.x
Version 4.x
- 20-02-2015: Version 4.13 :
+ 20-02-2015: Version 4.13 :
- 22-01-2015: Version 4.12 :
+ 22-01-2015: Version 4.12 :
- 9-01-2015: Version 4.11 :
+ 9-01-2015: Version 4.11 :
Unfortunately, 4.10 did not take care of the
Firefox scrolling issue entirely. This release adds two more patches
to address that.
- 29-12-2014: Version 4.10 :
+ 29-12-2014: Version 4.10 :
Emergency single-patch update to 4.9. Fixes
Firefox-specific problem where the cursor could end up behind the
horizontal scrollbar.
- 23-12-2014: Version 4.9 :
+ 23-12-2014: Version 4.9 :
Overhauled scroll bar handling.
@@ -711,7 +711,7 @@ Version 4.x
Full list of patches .
- 22-11-2014: Version 4.8 :
+ 22-11-2014: Version 4.8 :
- 20-10-2014: Version 4.7 :
+ 20-10-2014: Version 4.7 :
- 19-09-2014: Version 4.6 :
+ 19-09-2014: Version 4.6 :
- 21-08-2014: Version 4.5 :
+ 21-08-2014: Version 4.5 :
Fix several serious bugs with horizontal scrolling
@@ -773,7 +773,7 @@ Version 4.x
Full list of patches .
- 21-07-2014: Version 4.4 :
+ 21-07-2014: Version 4.4 :
Note: Some events might now fire in slightly
@@ -787,7 +787,7 @@ Version 4.x
Full list of patches .
- 23-06-2014: Version 4.3 :
+ 23-06-2014: Version 4.3 :
- 19-05-2014: Version 4.2 :
+ 19-05-2014: Version 4.2 :
Fix problem where some modes were broken by the fact that empty tokens were forbidden.
@@ -817,7 +817,7 @@ Version 4.x
Full list of patches .
- 22-04-2014: Version 4.1 :
+ 22-04-2014: Version 4.1 :
Slightly incompatible :
@@ -832,7 +832,7 @@ Version 4.x
Full list of patches .
- 20-03-2014: Version 4.0 :
+ 20-03-2014: Version 4.0 :
This is a new major version of CodeMirror. There
are a few incompatible changes in the API. Upgrade
@@ -854,13 +854,13 @@
Version 4.x
Version 3.x
- 22-04-2014: Version 3.24 :
+ 22-04-2014: Version 3.24 :
Merges the improvements from 4.1 that could
easily be applied to the 3.x code. Also improves the way the editor
size is updated when line widgets change.
- 20-03-2014: Version 3.23 :
+ 20-03-2014: Version 3.23 :
- 21-02-2014: Version 3.22 :
+ 21-02-2014: Version 3.22 :
- 16-01-2014: Version 3.21 :
+ 16-01-2014: Version 3.21 :
Auto-indenting a block will no longer add trailing whitespace to blank lines.
@@ -894,7 +894,7 @@ Version 3.x
Full list of patches .
- 21-11-2013: Version 3.20 :
+ 21-11-2013: Version 3.20 :
- 21-10-2013: Version 3.19 :
+ 21-10-2013: Version 3.19 :
- 23-09-2013: Version 3.18 :
+ 23-09-2013: Version 3.18 :
Emergency release to fix a problem in 3.17
where .setOption("lineNumbers", false) would raise an
error.
- 23-09-2013: Version 3.17 :
+ 23-09-2013: Version 3.17 :
- 21-08-2013: Version 3.16 :
+ 21-08-2013: Version 3.16 :
- 29-07-2013: Version 3.15 :
+ 29-07-2013: Version 3.15 :
- 20-06-2013: Version 3.14 :
+ 20-06-2013: Version 3.14 :
- 20-05-2013: Version 3.13 :
+ 20-05-2013: Version 3.13 :
- 19-04-2013: Version 3.12 :
+ 19-04-2013: Version 3.12 :
- 20-03-2013: Version 3.11 :
+ 20-03-2013: Version 3.11 :
Removed code: collapserange,
@@ -1029,7 +1029,7 @@ Version 3.x
Full list of patches .
- 21-02-2013: Version 3.1 :
+ 21-02-2013: Version 3.1 :
Incompatible: key handlers may
@@ -1066,13 +1066,13 @@ Version 3.x
- 25-01-2013: Version 3.02 :
+ 25-01-2013: Version 3.02 :
Single-bugfix release. Fixes a problem that
prevents CodeMirror instances from being garbage-collected after
they become unused.
- 21-01-2013: Version 3.01 :
+ 21-01-2013: Version 3.01 :
Move all add-ons into an organized directory structure
@@ -1091,7 +1091,7 @@ Version 3.x
Full list of patches .
- 10-12-2012: Version 3.0 :
+ 10-12-2012: Version 3.0 :
New major version . Only
partially backwards-compatible. See
@@ -1105,7 +1105,7 @@
Version 3.x
Full list of patches .
- 20-11-2012: Version 3.0, release candidate 2 :
+ 20-11-2012: Version 3.0, release candidate 2 :
- 20-11-2012: Version 3.0, release candidate 1 :
+ 20-11-2012: Version 3.0, release candidate 1 :
- 22-10-2012: Version 3.0, beta 2 :
+ 22-10-2012: Version 3.0, beta 2 :
Fix page-based coordinate computation.
@@ -1150,7 +1150,7 @@ Version 3.x
Full list of patches .
- 19-09-2012: Version 3.0, beta 1 :
+ 19-09-2012: Version 3.0, beta 1 :
Bi-directional text support.
@@ -1166,14 +1166,14 @@ Version 3.x
Version 2.x
- 21-01-2013: Version 2.38 :
+ 21-01-2013: Version 2.38 :
Integrate some bugfixes, enhancements to the vim keymap, and new
modes
(D , Sass , APL )
from the v3 branch.
- 20-12-2012: Version 2.37 :
+ 20-12-2012: Version 2.37 :
- 20-11-2012: Version 2.36 :
+ 20-11-2012: Version 2.36 :
- 22-10-2012: Version 2.35 :
+ 22-10-2012: Version 2.35 :
- 19-09-2012: Version 2.34 :
+ 19-09-2012: Version 2.34 :
- 23-08-2012: Version 2.33 :
+ 23-08-2012: Version 2.33 :
New mode: Sieve .
@@ -1234,13 +1234,13 @@ Version 2.x
Full list of patches.
- 23-07-2012: Version 2.32 :
+ 23-07-2012: Version 2.32 :
Emergency fix for a bug where an editor with
line wrapping on IE will break when there is no
scrollbar.
- 20-07-2012: Version 2.31 :
+ 20-07-2012: Version 2.31 :
- 22-06-2012: Version 2.3 :
+ 22-06-2012: Version 2.3 :
New scrollbar implementation . Should flicker less. Changes DOM structure of the editor.
@@ -1264,7 +1264,7 @@ Version 2.x
Lots of other fixes .
- 23-05-2012: Version 2.25 :
+ 23-05-2012: Version 2.25 :
New mode: Erlang .
@@ -1275,7 +1275,7 @@ Version 2.x
Fix backspace and tab key repeat in Opera.
- 23-04-2012: Version 2.24 :
+ 23-04-2012: Version 2.24 :
Drop support for Internet Explorer 6 .
@@ -1294,7 +1294,7 @@ Version 2.x
add nofallthrough boolean field instead.
-
26-03-2012: Version 2.23 :
+
26-03-2012: Version 2.23 :
Change default binding for tab [more]
@@ -1317,7 +1317,7 @@ Version 2.x
Add findMarksAt method.
-
27-02-2012: Version 2.22 :
+
27-02-2012: Version 2.22 :
Allow key handlers to pass up events, allow binding characters.
@@ -1330,7 +1330,7 @@ Version 2.x
Fix many bugs.
-
27-01-2012: Version 2.21 :
+
27-01-2012: Version 2.21 :
Added LESS , MySQL ,
@@ -1345,7 +1345,7 @@ Version 2.x
Lots and lots of bugfixes.
-
20-12-2011: Version 2.2 :
+
20-12-2011: Version 2.2 :
Slightly incompatible API changes. Read this .
@@ -1368,10 +1368,10 @@ Version 2.x
method.
-
21-11-2011: Version 2.18 :
+
21-11-2011: Version 2.18 :
Fixes TextMarker.clear, which is broken in 2.17.
-
21-11-2011: Version 2.17 :
+
21-11-2011: Version 2.17 :
-
27-10-2011: Version 2.16 :
+
27-10-2011: Version 2.16 :
Add Perl , Rust , TiddlyWiki , and Groovy modes.
Dragging text inside the editor now moves, rather than copies.
@@ -1403,12 +1403,12 @@ Version 2.x
Fix editing code with tabs in Internet Explorer.
-
26-09-2011: Version 2.15 :
+
26-09-2011: Version 2.15 :
Fix bug that snuck into 2.14: Clicking the
character that currently has the cursor didn't re-focus the
editor.
-
26-09-2011: Version 2.14 :
+
26-09-2011: Version 2.14 :
-
23-08-2011: Version 2.13 :
+
23-08-2011: Version 2.13 :
-
25-07-2011: Version 2.12 :
+
25-07-2011: Version 2.12 :
Add a SPARQL mode.
Fix bug with cursor jumping around in an unfocused editor in IE.
@@ -1442,7 +1442,7 @@ Version 2.x
Fix width feedback loop bug that caused the width of an inner DIV to shrink.
-
04-07-2011: Version 2.11 :
+
04-07-2011: Version 2.11 :
-
07-06-2011: Version 2.1 :
+
07-06-2011: Version 2.1 :
Add
a theme system
(demo ). Note that this is not
backwards-compatible—you'll have to update your styles and
modes!
-
07-06-2011: Version 2.02 :
+
07-06-2011: Version 2.02 :
Add a Lua mode .
Fix reverse-searching for a regexp.
@@ -1474,7 +1474,7 @@ Version 2.x
Fix problem with 'sticking' horizontal scrollbar.
-
26-05-2011: Version 2.01 :
+
26-05-2011: Version 2.01 :
-
28-03-2011: Version 2.0 :
+
28-03-2011: Version 2.0 :
CodeMirror 2 is a complete rewrite that's
faster, smaller, simpler to use, and less dependent on browser
quirks. See this
@@ -1502,18 +1502,18 @@
Version 2.x
22-02-2011: Version 2.0 beta 2 :
Somewhat more mature API, lots of bugs shaken out.
-
17-02-2011: Version 0.94 :
+
17-02-2011: Version 0.94 :
tabMode: "spaces" was modified slightly (now indents when something is selected).
Fixes a bug that would cause the selection code to break on some IE versions.
Disabling spell-check on WebKit browsers now works.
-
08-02-2011: Version 2.0 beta 1 :
+
08-02-2011: Version 2.0 beta 1 :
CodeMirror 2 is a complete rewrite of
CodeMirror, no longer depending on an editable frame.
-
19-01-2011: Version 0.93 :
+
19-01-2011: Version 0.93 :
Added a Regular Expression parser.
Fixes to the PHP parser.
@@ -1529,14 +1529,14 @@ Version 2.x
Version 0.x
- 28-03-2011: Version 1.0 :
+ 28-03-2011: Version 1.0 :
Fix error when debug history overflows.
Refine handling of C# verbatim strings.
Fix some issues with JavaScript indentation.
- 17-12-2010: Version 0.92 :
+ 17-12-2010: Version 0.92 :
Make CodeMirror work in XHTML documents.
Fix bug in handling of backslashes in Python strings.
@@ -1550,7 +1550,7 @@ Version 0.x
11-11-2010: Version 0.91 :
+ href="https://codemirror.net/codemirror-0.91.zip">Version 0.91:
Adds support for Java .
Small additions to the PHP and SQL parsers.
@@ -1561,7 +1561,7 @@ Version 0.x
02-10-2010: Version 0.9 :
+ href="https://codemirror.net/codemirror-0.9.zip">Version 0.9:
Add support for searching backwards.
There are now parsers for Scheme , XQuery , and OmetaJS .
@@ -1574,7 +1574,7 @@ Version 0.x
22-07-2010: Version 0.8 :
+ href="https://codemirror.net/codemirror-0.8.zip">Version 0.8:
Add a cursorCoords method to find the screen
coordinates of the cursor.
@@ -1597,7 +1597,7 @@ Version 0.x
27-04-2010: Version
+ href="https://codemirror.net/codemirror-0.67.zip">Version
0.67 :
More consistent page-up/page-down behaviour
across browsers. Fix some issues with hidden editors looping forever
@@ -1609,7 +1609,7 @@
Version 0.x
running editor.
01-03-2010: Version
+ href="https://codemirror.net/codemirror-0.66.zip">Version
0.66 :
Adds removeLine method to API.
Introduces the PLSQL parser .
@@ -1618,7 +1618,7 @@
Version 0.x
selection bugs, and a number of small glitches.
12-11-2009: Version
+ href="https://codemirror.net/codemirror-0.65.zip">Version
0.65 :
Add support for having both line-wrapping and
line-numbers turned on, make paren-highlighting style customisable
@@ -1627,7 +1627,7 @@
Version 0.x
re introduced in version 10.
23-10-2009: Version
+ href="https://codemirror.net/codemirror-0.64.zip">Version
0.64 :
Solves some issues introduced by the
paste-handling changes from the previous release. Adds
@@ -1640,12 +1640,12 @@
Version 0.x
parser. And, as usual, add workarounds for various newly discovered
browser incompatibilities.
- 31-08-2009: Version 0.63 :
+ 31-08-2009: Version 0.63 :
Overhaul of paste-handling (less fragile), fixes for several
serious IE8 issues (cursor jumping, end-of-document bugs) and a number
of small problems.
- 30-05-2009: Version 0.62 :
+ 30-05-2009: Version 0.62 :
Introduces Python
and Lua parsers. Add
setParser (on-the-fly mode changing) and
diff --git a/doc/reporting.html b/doc/reporting.html
index 1657400ce9..32d7986d79 100644
--- a/doc/reporting.html
+++ b/doc/reporting.html
@@ -5,7 +5,7 @@
-
CodeMirror
+
CodeMirror
Home
diff --git a/doc/upgrade_v2.2.html b/doc/upgrade_v2.2.html
index 3948ce6e56..5709e652bf 100644
--- a/doc/upgrade_v2.2.html
+++ b/doc/upgrade_v2.2.html
@@ -5,7 +5,7 @@
-
CodeMirror
+
CodeMirror
Home
diff --git a/doc/upgrade_v3.html b/doc/upgrade_v3.html
index 5f94067aa0..2fec440fe9 100644
--- a/doc/upgrade_v3.html
+++ b/doc/upgrade_v3.html
@@ -14,7 +14,7 @@
-
CodeMirror
+
CodeMirror
Home
diff --git a/doc/upgrade_v4.html b/doc/upgrade_v4.html
index 09df00ca1b..dc79654f01 100644
--- a/doc/upgrade_v4.html
+++ b/doc/upgrade_v4.html
@@ -6,7 +6,7 @@
-
CodeMirror
+
CodeMirror
Home
diff --git a/index.html b/index.html
index 926d636a3c..2b3ccceab8 100644
--- a/index.html
+++ b/index.html
@@ -21,7 +21,7 @@
-
CodeMirror
+
CodeMirror
Home
@@ -89,14 +89,14 @@ This is CodeMirror
- Get the current version:
5.39.2 .
+ Get the current version:
5.39.2 .
You can see the
code ,
read the
release notes ,
or study the
user manual .
@@ -153,7 +153,7 @@
Community
on
github
(
alternate git
repository ).
- Please
read these
+ Please read these
pointers before submitting a bug. Use pull requests to submit
patches. All contributions must be released under the same MIT
license that CodeMirror uses.
diff --git a/keymap/emacs.js b/keymap/emacs.js
index f7c51b8131..d96a6fbec9 100644
--- a/keymap/emacs.js
+++ b/keymap/emacs.js
@@ -1,5 +1,5 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
diff --git a/keymap/sublime.js b/keymap/sublime.js
index aa9b65e3b1..b4799fd62a 100644
--- a/keymap/sublime.js
+++ b/keymap/sublime.js
@@ -1,5 +1,5 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
// A rough approximation of Sublime Text's keybindings
// Depends on addon/search/searchcursor.js and optionally addon/dialog/dialogs.js
diff --git a/keymap/vim.js b/keymap/vim.js
index ef79585f7d..b03c1393cb 100644
--- a/keymap/vim.js
+++ b/keymap/vim.js
@@ -1,5 +1,5 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
/**
* Supported keybindings:
diff --git a/mode/apl/apl.js b/mode/apl/apl.js
index caafe4e913..b1955f6c94 100644
--- a/mode/apl/apl.js
+++ b/mode/apl/apl.js
@@ -1,5 +1,5 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
diff --git a/mode/apl/index.html b/mode/apl/index.html
index 53dda6b586..56ab02ffba 100644
--- a/mode/apl/index.html
+++ b/mode/apl/index.html
@@ -12,7 +12,7 @@
.CodeMirror { border: 2px inset #dee; }
-
CodeMirror
+
CodeMirror
Home
diff --git a/mode/asciiarmor/asciiarmor.js b/mode/asciiarmor/asciiarmor.js
index fa1b0f8c61..f560f42423 100644
--- a/mode/asciiarmor/asciiarmor.js
+++ b/mode/asciiarmor/asciiarmor.js
@@ -1,5 +1,5 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
diff --git a/mode/asciiarmor/index.html b/mode/asciiarmor/index.html
index 4d584efbcd..de39a18573 100644
--- a/mode/asciiarmor/index.html
+++ b/mode/asciiarmor/index.html
@@ -9,7 +9,7 @@
-
CodeMirror
+
CodeMirror
Home
diff --git a/mode/asn.1/asn.1.js b/mode/asn.1/asn.1.js
index 9600247ea6..d3ecb08781 100644
--- a/mode/asn.1/asn.1.js
+++ b/mode/asn.1/asn.1.js
@@ -1,5 +1,5 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
diff --git a/mode/asn.1/index.html b/mode/asn.1/index.html
index 699fd4473d..5d80523e0a 100644
--- a/mode/asn.1/index.html
+++ b/mode/asn.1/index.html
@@ -14,7 +14,7 @@
}
-
CodeMirror
+ CodeMirror
diff --git a/mode/asterisk/asterisk.js b/mode/asterisk/asterisk.js
index b7ebfc5ad7..025a8b3ef9 100644
--- a/mode/asterisk/asterisk.js
+++ b/mode/asterisk/asterisk.js
@@ -1,5 +1,5 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
/*
* =====================================================================================
diff --git a/mode/asterisk/index.html b/mode/asterisk/index.html
index 257bd39875..7ede9948b1 100644
--- a/mode/asterisk/index.html
+++ b/mode/asterisk/index.html
@@ -12,7 +12,7 @@
.cm-s-default span.cm-arrow { color: red; }
-
CodeMirror
+
CodeMirror
Home
diff --git a/mode/brainfuck/brainfuck.js b/mode/brainfuck/brainfuck.js
index 3becf2a5a3..af6d889aee 100644
--- a/mode/brainfuck/brainfuck.js
+++ b/mode/brainfuck/brainfuck.js
@@ -1,5 +1,5 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
// Brainfuck mode created by Michael Kaminsky https://github.com/mkaminsky11
diff --git a/mode/brainfuck/index.html b/mode/brainfuck/index.html
index 6048fc2412..a04bddb14e 100644
--- a/mode/brainfuck/index.html
+++ b/mode/brainfuck/index.html
@@ -12,7 +12,7 @@
.CodeMirror { border: 2px inset #dee; }
-
CodeMirror
+
CodeMirror
Home
diff --git a/mode/clike/clike.js b/mode/clike/clike.js
index dec87a474a..42033bd0a0 100644
--- a/mode/clike/clike.js
+++ b/mode/clike/clike.js
@@ -1,5 +1,5 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
diff --git a/mode/clike/index.html b/mode/clike/index.html
index 45c670ae58..b1cef526b1 100644
--- a/mode/clike/index.html
+++ b/mode/clike/index.html
@@ -12,7 +12,7 @@
-
CodeMirror
+
CodeMirror
Home
diff --git a/mode/clike/scala.html b/mode/clike/scala.html
index aa04cf0f04..ddbd9a0cae 100644
--- a/mode/clike/scala.html
+++ b/mode/clike/scala.html
@@ -10,7 +10,7 @@
-
CodeMirror
+
CodeMirror
Home
diff --git a/mode/clike/test.js b/mode/clike/test.js
index e3bde772a5..fc1f5d6cf1 100644
--- a/mode/clike/test.js
+++ b/mode/clike/test.js
@@ -1,5 +1,5 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
(function() {
var mode = CodeMirror.getMode({indentUnit: 2}, "text/x-c");
diff --git a/mode/clojure/clojure.js b/mode/clojure/clojure.js
index ed6af2c83c..2015edff14 100644
--- a/mode/clojure/clojure.js
+++ b/mode/clojure/clojure.js
@@ -1,5 +1,5 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
/**
* Author: Hans Engel
diff --git a/mode/clojure/index.html b/mode/clojure/index.html
index 81294bc14a..5399ce876e 100644
--- a/mode/clojure/index.html
+++ b/mode/clojure/index.html
@@ -9,7 +9,7 @@
-
CodeMirror
+
CodeMirror
Home
diff --git a/mode/cmake/cmake.js b/mode/cmake/cmake.js
index 9f9eda5417..496c71d1fd 100644
--- a/mode/cmake/cmake.js
+++ b/mode/cmake/cmake.js
@@ -1,5 +1,5 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
(function(mod) {
if (typeof exports == "object" && typeof module == "object")
diff --git a/mode/cmake/index.html b/mode/cmake/index.html
index ed114fece5..6dcfd7b4a8 100644
--- a/mode/cmake/index.html
+++ b/mode/cmake/index.html
@@ -13,7 +13,7 @@
.cm-s-default span.cm-arrow { color: red; }
-
CodeMirror
+
CodeMirror
Home
diff --git a/mode/cobol/cobol.js b/mode/cobol/cobol.js
index 897022b18c..275857b4bd 100644
--- a/mode/cobol/cobol.js
+++ b/mode/cobol/cobol.js
@@ -1,5 +1,5 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
/**
* Author: Gautam Mehta
diff --git a/mode/cobol/index.html b/mode/cobol/index.html
index 4352419a0c..1e08740529 100644
--- a/mode/cobol/index.html
+++ b/mode/cobol/index.html
@@ -39,7 +39,7 @@
.CodeMirror-activeline-background {background: #555555 !important;}
-
CodeMirror
+
CodeMirror
Home
diff --git a/mode/coffeescript/coffeescript.js b/mode/coffeescript/coffeescript.js
index ae955db344..a54e9d5ed0 100644
--- a/mode/coffeescript/coffeescript.js
+++ b/mode/coffeescript/coffeescript.js
@@ -1,5 +1,5 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
/**
* Link to the project's GitHub page:
diff --git a/mode/coffeescript/index.html b/mode/coffeescript/index.html
index 92d161e9dd..650ccf5db8 100644
--- a/mode/coffeescript/index.html
+++ b/mode/coffeescript/index.html
@@ -9,7 +9,7 @@
-
CodeMirror
+
CodeMirror
Home
diff --git a/mode/commonlisp/commonlisp.js b/mode/commonlisp/commonlisp.js
index 8cd212d3a8..52abbb25c6 100644
--- a/mode/commonlisp/commonlisp.js
+++ b/mode/commonlisp/commonlisp.js
@@ -1,5 +1,5 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
diff --git a/mode/commonlisp/index.html b/mode/commonlisp/index.html
index f2bf4522d6..a5b907e831 100644
--- a/mode/commonlisp/index.html
+++ b/mode/commonlisp/index.html
@@ -9,7 +9,7 @@
-
CodeMirror
+
CodeMirror
Home
diff --git a/mode/crystal/crystal.js b/mode/crystal/crystal.js
index dada112da8..5c601c6ab3 100644
--- a/mode/crystal/crystal.js
+++ b/mode/crystal/crystal.js
@@ -1,5 +1,5 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
diff --git a/mode/crystal/index.html b/mode/crystal/index.html
index 95412579f2..ae4218eec5 100644
--- a/mode/crystal/index.html
+++ b/mode/crystal/index.html
@@ -14,7 +14,7 @@
-
CodeMirror
+
CodeMirror
Home
diff --git a/mode/css/css.js b/mode/css/css.js
index f5f3a41ba8..8b5722905d 100644
--- a/mode/css/css.js
+++ b/mode/css/css.js
@@ -1,5 +1,5 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
diff --git a/mode/css/gss.html b/mode/css/gss.html
index 232fe8c12b..3854747c42 100644
--- a/mode/css/gss.html
+++ b/mode/css/gss.html
@@ -12,7 +12,7 @@