puffer_pages 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +1 -2
- data/.rvmrc +1 -0
- data/.travis.yml +6 -0
- data/CHANGELOG.md +12 -0
- data/Gemfile.lock +83 -78
- data/app/assets/javascripts/puffer/codemirror.js +1382 -818
- data/app/assets/javascripts/puffer/codemirror/htmlmixed.js +4 -5
- data/app/assets/javascripts/puffer/codemirror/javascript.js +12 -4
- data/app/assets/javascripts/puffer/codemirror/xml.js +35 -14
- data/app/assets/javascripts/puffer/liquid.js +15 -0
- data/app/assets/javascripts/puffer/puffer_pages.js +56 -6
- data/app/assets/stylesheets/puffer/codemirror.css +50 -14
- data/app/assets/stylesheets/puffer/codemirror/cobalt.css +1 -0
- data/app/assets/stylesheets/puffer/codemirror/eclipse.css +3 -2
- data/app/assets/stylesheets/puffer/codemirror/elegant.css +1 -0
- data/app/assets/stylesheets/puffer/codemirror/monokai.css +28 -0
- data/app/assets/stylesheets/puffer/codemirror/neat.css +1 -0
- data/app/assets/stylesheets/puffer/codemirror/night.css +1 -0
- data/app/assets/stylesheets/puffer/codemirror/rubyblue.css +21 -0
- data/app/assets/stylesheets/puffer/puffer_pages.css +59 -3
- data/app/components/codemirror/form.html.erb +16 -0
- data/app/components/codemirror_component.rb +9 -0
- data/app/components/page_parts/form.html.erb +13 -4
- data/app/controllers/pages_controller.rb +1 -10
- data/app/controllers/puffer_pages/layouts_base.rb +1 -1
- data/app/controllers/puffer_pages/pages_base.rb +1 -1
- data/app/controllers/puffer_pages/snippets_base.rb +1 -1
- data/app/models/puffer_pages/page.rb +4 -3
- data/app/views/layouts/puffer_pages_layout.html.erb +2 -1
- data/gemfiles/Gemfile-rails-3.1 +5 -0
- data/gemfiles/Gemfile-rails-3.1.lock +173 -0
- data/gemfiles/Gemfile-rails-3.2 +5 -0
- data/gemfiles/Gemfile-rails-3.2.lock +171 -0
- data/lib/puffer_pages.rb +5 -0
- data/lib/puffer_pages/extensions/controller.rb +12 -16
- data/lib/puffer_pages/extensions/mapper.rb +7 -13
- data/lib/puffer_pages/liquid/tags/page_attribute.rb +39 -0
- data/lib/puffer_pages/version.rb +1 -1
- data/puffer_pages.gemspec +7 -4
- data/spec/dummy/app/controllers/application_controller.rb +4 -0
- data/spec/lib/tags_spec.rb +25 -3
- data/spec/models/page_spec.rb +2 -3
- metadata +75 -43
- data/app/assets/stylesheets/puffer/codemirror/default.css +0 -19
- data/app/components/page_part_body/form.html.erb +0 -3
- data/app/components/page_part_body_component.rb +0 -3
data/.gitignore
CHANGED
data/.rvmrc
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
rvm use 1.9.2@puffer_pages --create
|
data/.travis.yml
ADDED
data/CHANGELOG.md
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
## 0.1.2 \[ In Development \] \[ Branch: master \]
|
2
|
+
|
3
|
+
## 0.1.1
|
4
|
+
|
5
|
+
### New features
|
6
|
+
|
7
|
+
* Updated codemirror to 2.2 version.
|
8
|
+
* Custom component renamed to Codemirror component.
|
9
|
+
* Added buttons panel to Codemirror editor.
|
10
|
+
* Added fullscreen feature to Codemirror.
|
11
|
+
* Added Liquid overlay to Codemirror.
|
12
|
+
* Rails 3.2 compatible.
|
data/Gemfile.lock
CHANGED
@@ -1,146 +1,149 @@
|
|
1
1
|
GIT
|
2
2
|
remote: git://github.com/puffer/puffer.git
|
3
|
-
revision:
|
3
|
+
revision: 719b80730fd723c7f665c95315a19edbb8148593
|
4
4
|
specs:
|
5
|
-
puffer (0.1.
|
5
|
+
puffer (0.1.1)
|
6
6
|
kaminari
|
7
7
|
orm_adapter
|
8
|
-
rails (~> 3.1
|
8
|
+
rails (~> 3.1)
|
9
9
|
|
10
10
|
PATH
|
11
11
|
remote: .
|
12
12
|
specs:
|
13
|
-
puffer_pages (0.1.
|
13
|
+
puffer_pages (0.1.1)
|
14
14
|
liquid
|
15
15
|
nested_set
|
16
16
|
puffer
|
17
|
-
rails (~> 3.1
|
17
|
+
rails (~> 3.1)
|
18
18
|
|
19
19
|
GEM
|
20
20
|
remote: http://rubygems.org/
|
21
21
|
specs:
|
22
|
-
actionmailer (3.
|
23
|
-
actionpack (= 3.
|
24
|
-
mail (~> 2.
|
25
|
-
actionpack (3.
|
26
|
-
activemodel (= 3.
|
27
|
-
activesupport (= 3.
|
22
|
+
actionmailer (3.2.1)
|
23
|
+
actionpack (= 3.2.1)
|
24
|
+
mail (~> 2.4.0)
|
25
|
+
actionpack (3.2.1)
|
26
|
+
activemodel (= 3.2.1)
|
27
|
+
activesupport (= 3.2.1)
|
28
28
|
builder (~> 3.0.0)
|
29
29
|
erubis (~> 2.7.0)
|
30
|
-
|
31
|
-
rack (~> 1.
|
30
|
+
journey (~> 1.0.1)
|
31
|
+
rack (~> 1.4.0)
|
32
32
|
rack-cache (~> 1.1)
|
33
|
-
rack-mount (~> 0.8.2)
|
34
33
|
rack-test (~> 0.6.1)
|
35
|
-
sprockets (~> 2.
|
36
|
-
activemodel (3.
|
37
|
-
activesupport (= 3.
|
34
|
+
sprockets (~> 2.1.2)
|
35
|
+
activemodel (3.2.1)
|
36
|
+
activesupport (= 3.2.1)
|
38
37
|
builder (~> 3.0.0)
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
arel (~> 2.2.1)
|
38
|
+
activerecord (3.2.1)
|
39
|
+
activemodel (= 3.2.1)
|
40
|
+
activesupport (= 3.2.1)
|
41
|
+
arel (~> 3.0.0)
|
44
42
|
tzinfo (~> 0.3.29)
|
45
|
-
activeresource (3.
|
46
|
-
activemodel (= 3.
|
47
|
-
activesupport (= 3.
|
48
|
-
activesupport (3.
|
43
|
+
activeresource (3.2.1)
|
44
|
+
activemodel (= 3.2.1)
|
45
|
+
activesupport (= 3.2.1)
|
46
|
+
activesupport (3.2.1)
|
47
|
+
i18n (~> 0.6)
|
49
48
|
multi_json (~> 1.0)
|
50
|
-
arel (
|
49
|
+
arel (3.0.0)
|
50
|
+
bcrypt-ruby (3.0.1)
|
51
51
|
builder (3.0.0)
|
52
|
-
capybara (1.1.
|
52
|
+
capybara (1.1.2)
|
53
53
|
mime-types (>= 1.16)
|
54
54
|
nokogiri (>= 1.3.3)
|
55
55
|
rack (>= 1.0.0)
|
56
56
|
rack-test (>= 0.5.4)
|
57
57
|
selenium-webdriver (~> 2.0)
|
58
58
|
xpath (~> 0.1.4)
|
59
|
-
childprocess (0.
|
59
|
+
childprocess (0.3.1)
|
60
60
|
ffi (~> 1.0.6)
|
61
|
-
database_cleaner (0.
|
61
|
+
database_cleaner (0.7.1)
|
62
62
|
diff-lcs (1.1.3)
|
63
63
|
erubis (2.7.0)
|
64
|
-
fabrication (1.
|
65
|
-
ffi (1.0.
|
64
|
+
fabrication (1.2.0)
|
65
|
+
ffi (1.0.11)
|
66
66
|
forgery (0.5.0)
|
67
|
-
guard (0.
|
67
|
+
guard (1.0.0)
|
68
|
+
ffi (>= 0.5.0)
|
68
69
|
thor (~> 0.14.6)
|
69
|
-
guard-rspec (0.
|
70
|
-
guard (>= 0.
|
70
|
+
guard-rspec (0.6.0)
|
71
|
+
guard (>= 0.10.0)
|
71
72
|
hike (1.2.1)
|
72
73
|
i18n (0.6.0)
|
73
|
-
|
74
|
-
|
75
|
-
kaminari (0.
|
76
|
-
|
77
|
-
|
74
|
+
journey (1.0.1)
|
75
|
+
json (1.6.5)
|
76
|
+
kaminari (0.13.0)
|
77
|
+
actionpack (>= 3.0.0)
|
78
|
+
activesupport (>= 3.0.0)
|
79
|
+
railties (>= 3.0.0)
|
80
|
+
libnotify (0.7.2)
|
78
81
|
liquid (2.3.0)
|
79
|
-
mail (2.
|
82
|
+
mail (2.4.1)
|
80
83
|
i18n (>= 0.4.0)
|
81
84
|
mime-types (~> 1.16)
|
82
85
|
treetop (~> 1.4.8)
|
83
86
|
mime-types (1.17.2)
|
84
|
-
multi_json (1.0.
|
87
|
+
multi_json (1.0.4)
|
85
88
|
mysql (2.8.1)
|
86
89
|
nested_set (1.6.8)
|
87
90
|
activerecord (>= 3.0.0)
|
88
91
|
railties (>= 3.0.0)
|
89
92
|
nokogiri (1.5.0)
|
90
|
-
orm_adapter (0.0.
|
91
|
-
pg (0.
|
93
|
+
orm_adapter (0.0.6)
|
94
|
+
pg (0.12.2)
|
92
95
|
polyglot (0.3.3)
|
93
|
-
rack (1.
|
96
|
+
rack (1.4.1)
|
94
97
|
rack-cache (1.1)
|
95
98
|
rack (>= 0.4)
|
96
|
-
rack-mount (0.8.3)
|
97
|
-
rack (>= 1.0.0)
|
98
99
|
rack-ssl (1.3.2)
|
99
100
|
rack
|
100
101
|
rack-test (0.6.1)
|
101
102
|
rack (>= 1.0)
|
102
|
-
rails (3.
|
103
|
-
actionmailer (= 3.
|
104
|
-
actionpack (= 3.
|
105
|
-
activerecord (= 3.
|
106
|
-
activeresource (= 3.
|
107
|
-
activesupport (= 3.
|
103
|
+
rails (3.2.1)
|
104
|
+
actionmailer (= 3.2.1)
|
105
|
+
actionpack (= 3.2.1)
|
106
|
+
activerecord (= 3.2.1)
|
107
|
+
activeresource (= 3.2.1)
|
108
|
+
activesupport (= 3.2.1)
|
108
109
|
bundler (~> 1.0)
|
109
|
-
railties (= 3.
|
110
|
-
railties (3.
|
111
|
-
actionpack (= 3.
|
112
|
-
activesupport (= 3.
|
110
|
+
railties (= 3.2.1)
|
111
|
+
railties (3.2.1)
|
112
|
+
actionpack (= 3.2.1)
|
113
|
+
activesupport (= 3.2.1)
|
113
114
|
rack-ssl (~> 1.3.2)
|
114
115
|
rake (>= 0.8.7)
|
115
116
|
rdoc (~> 3.4)
|
116
117
|
thor (~> 0.14.6)
|
117
118
|
rake (0.9.2.2)
|
118
|
-
|
119
|
+
rb-inotify (0.8.8)
|
120
|
+
ffi (>= 0.5.0)
|
121
|
+
rdoc (3.12)
|
119
122
|
json (~> 1.4)
|
120
|
-
rspec (2.
|
121
|
-
rspec-core (~> 2.
|
122
|
-
rspec-expectations (~> 2.
|
123
|
-
rspec-mocks (~> 2.
|
124
|
-
rspec-core (2.
|
125
|
-
rspec-expectations (2.
|
123
|
+
rspec (2.8.0)
|
124
|
+
rspec-core (~> 2.8.0)
|
125
|
+
rspec-expectations (~> 2.8.0)
|
126
|
+
rspec-mocks (~> 2.8.0)
|
127
|
+
rspec-core (2.8.0)
|
128
|
+
rspec-expectations (2.8.0)
|
126
129
|
diff-lcs (~> 1.1.2)
|
127
|
-
rspec-mocks (2.
|
128
|
-
rspec-rails (2.
|
129
|
-
actionpack (
|
130
|
-
activesupport (
|
131
|
-
railties (
|
132
|
-
rspec (~> 2.
|
133
|
-
rubyzip (0.9.
|
134
|
-
selenium-webdriver (2.
|
135
|
-
childprocess (>= 0.2.
|
136
|
-
ffi (
|
137
|
-
|
130
|
+
rspec-mocks (2.8.0)
|
131
|
+
rspec-rails (2.8.1)
|
132
|
+
actionpack (>= 3.0)
|
133
|
+
activesupport (>= 3.0)
|
134
|
+
railties (>= 3.0)
|
135
|
+
rspec (~> 2.8.0)
|
136
|
+
rubyzip (0.9.5)
|
137
|
+
selenium-webdriver (2.18.0)
|
138
|
+
childprocess (>= 0.2.5)
|
139
|
+
ffi (~> 1.0.9)
|
140
|
+
multi_json (~> 1.0.4)
|
138
141
|
rubyzip
|
139
|
-
sprockets (2.
|
142
|
+
sprockets (2.1.2)
|
140
143
|
hike (~> 1.2)
|
141
144
|
rack (~> 1.0)
|
142
|
-
tilt (
|
143
|
-
sqlite3 (1.3.
|
145
|
+
tilt (~> 1.1, != 1.3.0)
|
146
|
+
sqlite3 (1.3.5)
|
144
147
|
sqlite3-ruby (1.3.3)
|
145
148
|
sqlite3 (>= 1.3.3)
|
146
149
|
thor (0.14.6)
|
@@ -156,6 +159,7 @@ PLATFORMS
|
|
156
159
|
ruby
|
157
160
|
|
158
161
|
DEPENDENCIES
|
162
|
+
bcrypt-ruby
|
159
163
|
capybara (>= 0.4.0)
|
160
164
|
database_cleaner
|
161
165
|
fabrication
|
@@ -167,5 +171,6 @@ DEPENDENCIES
|
|
167
171
|
pg
|
168
172
|
puffer!
|
169
173
|
puffer_pages!
|
174
|
+
rb-inotify
|
170
175
|
rspec-rails
|
171
176
|
sqlite3-ruby
|
@@ -1,3 +1,5 @@
|
|
1
|
+
// CodeMirror version 2.2
|
2
|
+
//
|
1
3
|
// All functions that need access to the editor's state live inside
|
2
4
|
// the CodeMirror function. Below that, at the bottom of the file,
|
3
5
|
// some utilities are defined.
|
@@ -16,19 +18,19 @@ var CodeMirror = (function() {
|
|
16
18
|
var targetDocument = options["document"];
|
17
19
|
// The element in which the editor lives.
|
18
20
|
var wrapper = targetDocument.createElement("div");
|
19
|
-
wrapper.className = "CodeMirror";
|
21
|
+
wrapper.className = "CodeMirror" + (options.lineWrapping ? " CodeMirror-wrap" : "");
|
20
22
|
// This mess creates the base DOM structure for the editor.
|
21
23
|
wrapper.innerHTML =
|
22
|
-
'<div style="overflow: hidden; position: relative; width:
|
23
|
-
'<textarea style="position: absolute; width:
|
24
|
+
'<div style="overflow: hidden; position: relative; width: 3px; height: 0px;">' + // Wraps and hides input textarea
|
25
|
+
'<textarea style="position: absolute; padding: 0; width: 1px;" wrap="off" ' +
|
24
26
|
'autocorrect="off" autocapitalize="off"></textarea></div>' +
|
25
|
-
'<div class="CodeMirror-scroll
|
27
|
+
'<div class="CodeMirror-scroll" tabindex="-1">' +
|
26
28
|
'<div style="position: relative">' + // Set to the height of the text, causes scrolling
|
27
|
-
'<div style="position: absolute; height: 0; width: 0; overflow: hidden;"></div>' +
|
28
29
|
'<div style="position: relative">' + // Moved around its parent to cover visible view
|
29
30
|
'<div class="CodeMirror-gutter"><div class="CodeMirror-gutter-text"></div></div>' +
|
30
31
|
// Provides positioning relative to (visible) text origin
|
31
|
-
'<div class="CodeMirror-lines"><div style="position: relative"
|
32
|
+
'<div class="CodeMirror-lines"><div style="position: relative">' +
|
33
|
+
'<div style="position: absolute; width: 100%; height: 0; overflow: hidden; visibility: hidden"></div>' +
|
32
34
|
'<pre class="CodeMirror-cursor"> </pre>' + // Absolutely positioned blinky cursor
|
33
35
|
'<div></div>' + // This DIV contains the actual code
|
34
36
|
'</div></div></div></div></div>';
|
@@ -36,51 +38,62 @@ var CodeMirror = (function() {
|
|
36
38
|
// I've never seen more elegant code in my life.
|
37
39
|
var inputDiv = wrapper.firstChild, input = inputDiv.firstChild,
|
38
40
|
scroller = wrapper.lastChild, code = scroller.firstChild,
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
41
|
+
mover = code.firstChild, gutter = mover.firstChild, gutterText = gutter.firstChild,
|
42
|
+
lineSpace = gutter.nextSibling.firstChild, measure = lineSpace.firstChild,
|
43
|
+
cursor = measure.nextSibling, lineDiv = cursor.nextSibling;
|
44
|
+
themeChanged();
|
45
|
+
// Needed to hide big blue blinking cursor on Mobile Safari
|
46
|
+
if (/AppleWebKit/.test(navigator.userAgent) && /Mobile\/\w+/.test(navigator.userAgent)) input.style.width = "0px";
|
47
|
+
if (!webkit) lineSpace.draggable = true;
|
48
|
+
if (options.tabindex != null) input.tabIndex = options.tabindex;
|
44
49
|
if (!options.gutter && !options.lineNumbers) gutter.style.display = "none";
|
45
50
|
|
51
|
+
// Check for problem with IE innerHTML not working when we have a
|
52
|
+
// P (or similar) parent node.
|
53
|
+
try { stringWidth("x"); }
|
54
|
+
catch (e) {
|
55
|
+
if (e.message.match(/runtime/i))
|
56
|
+
e = new Error("A CodeMirror inside a P-style element does not work in Internet Explorer. (innerHTML bug)");
|
57
|
+
throw e;
|
58
|
+
}
|
59
|
+
|
46
60
|
// Delayed object wrap timeouts, making sure only one is active. blinker holds an interval.
|
47
61
|
var poll = new Delayed(), highlight = new Delayed(), blinker;
|
48
62
|
|
49
|
-
// mode holds a mode API object.
|
50
|
-
//
|
51
|
-
//
|
52
|
-
|
53
|
-
var mode, lines = [new Line("")], work, history = new History(), focused;
|
63
|
+
// mode holds a mode API object. doc is the tree of Line objects,
|
64
|
+
// work an array of lines that should be parsed, and history the
|
65
|
+
// undo history (instance of History constructor).
|
66
|
+
var mode, doc = new BranchChunk([new LeafChunk([new Line("")])]), work, focused;
|
54
67
|
loadMode();
|
55
68
|
// The selection. These are always maintained to point at valid
|
56
69
|
// positions. Inverted is used to remember that the user is
|
57
70
|
// selecting bottom-to-top.
|
58
71
|
var sel = {from: {line: 0, ch: 0}, to: {line: 0, ch: 0}, inverted: false};
|
59
72
|
// Selection-related flags. shiftSelecting obviously tracks
|
60
|
-
// whether the user is holding shift.
|
61
|
-
|
62
|
-
// selections. See below.
|
63
|
-
var shiftSelecting, reducedSelection, lastClick, lastDoubleClick;
|
73
|
+
// whether the user is holding shift.
|
74
|
+
var shiftSelecting, lastClick, lastDoubleClick, draggingText, overwrite = false;
|
64
75
|
// Variables used by startOperation/endOperation to track what
|
65
76
|
// happened during the operation.
|
66
|
-
var updateInput, changes, textChanged, selectionChanged, leaveInputAlone,
|
77
|
+
var updateInput, userSelChange, changes, textChanged, selectionChanged, leaveInputAlone,
|
78
|
+
gutterDirty, callbacks;
|
67
79
|
// Current visible range (may be bigger than the view window).
|
68
|
-
var
|
69
|
-
// editing will hold an object describing the things we put in the
|
70
|
-
// textarea, to help figure out whether something changed.
|
80
|
+
var displayOffset = 0, showingFrom = 0, showingTo = 0, lastSizeC = 0;
|
71
81
|
// bracketHighlighted is used to remember that a backet has been
|
72
82
|
// marked.
|
73
|
-
var
|
83
|
+
var bracketHighlighted;
|
74
84
|
// Tracks the maximum line length so that the horizontal scrollbar
|
75
85
|
// can be kept static when scrolling.
|
76
|
-
var maxLine = "", maxWidth;
|
86
|
+
var maxLine = "", maxWidth, tabText = computeTabText();
|
77
87
|
|
78
88
|
// Initialize the content.
|
79
89
|
operation(function(){setValue(options.value || ""); updateInput = false;})();
|
90
|
+
var history = new History();
|
80
91
|
|
81
92
|
// Register our event handlers.
|
82
93
|
connect(scroller, "mousedown", operation(onMouseDown));
|
94
|
+
connect(scroller, "dblclick", operation(onDoubleClick));
|
83
95
|
connect(lineSpace, "dragstart", onDragStart);
|
96
|
+
connect(lineSpace, "selectstart", e_preventDefault);
|
84
97
|
// Gecko browsers fire contextmenu *after* opening the menu, at
|
85
98
|
// which point we can't mess with it anymore. Context menu is
|
86
99
|
// handled in onMouseDown for Gecko.
|
@@ -92,6 +105,7 @@ var CodeMirror = (function() {
|
|
92
105
|
});
|
93
106
|
connect(window, "resize", function() {updateDisplay(true);});
|
94
107
|
connect(input, "keyup", operation(onKeyUp));
|
108
|
+
connect(input, "input", fastPoll);
|
95
109
|
connect(input, "keydown", operation(onKeyDown));
|
96
110
|
connect(input, "keypress", operation(onKeyPress));
|
97
111
|
connect(input, "focus", onFocus);
|
@@ -101,16 +115,16 @@ var CodeMirror = (function() {
|
|
101
115
|
connect(scroller, "dragover", e_stop);
|
102
116
|
connect(scroller, "drop", operation(onDrop));
|
103
117
|
connect(scroller, "paste", function(){focusInput(); fastPoll();});
|
104
|
-
connect(input, "paste",
|
105
|
-
connect(input, "cut", function(){
|
106
|
-
|
107
|
-
// IE throws unspecified error in certain cases, when
|
118
|
+
connect(input, "paste", fastPoll);
|
119
|
+
connect(input, "cut", operation(function(){replaceSelection("");}));
|
120
|
+
|
121
|
+
// IE throws unspecified error in certain cases, when
|
108
122
|
// trying to access activeElement before onload
|
109
123
|
var hasFocus; try { hasFocus = (targetDocument.activeElement == input); } catch(e) { }
|
110
124
|
if (hasFocus) setTimeout(onFocus, 20);
|
111
125
|
else onBlur();
|
112
126
|
|
113
|
-
function isLine(l) {return l >= 0 && l <
|
127
|
+
function isLine(l) {return l >= 0 && l < doc.size;}
|
114
128
|
// The instance object that we'll return. Mostly calls out to
|
115
129
|
// local functions in the CodeMirror function. Some do some extra
|
116
130
|
// range checking and/or clipping. operation is used to wrap the
|
@@ -123,12 +137,15 @@ var CodeMirror = (function() {
|
|
123
137
|
replaceSelection: operation(replaceSelection),
|
124
138
|
focus: function(){focusInput(); onFocus(); fastPoll();},
|
125
139
|
setOption: function(option, value) {
|
140
|
+
var oldVal = options[option];
|
126
141
|
options[option] = value;
|
127
|
-
if (option == "
|
142
|
+
if (option == "mode" || option == "indentUnit") loadMode();
|
143
|
+
else if (option == "readOnly" && value) {onBlur(); input.blur();}
|
144
|
+
else if (option == "theme") themeChanged();
|
145
|
+
else if (option == "lineWrapping" && oldVal != value) operation(wrappingChanged)();
|
146
|
+
else if (option == "tabSize") operation(tabsChanged)();
|
147
|
+
if (option == "lineNumbers" || option == "gutter" || option == "firstLineNumber" || option == "theme")
|
128
148
|
operation(gutterChanged)();
|
129
|
-
else if (option == "mode" || option == "indentUnit") loadMode();
|
130
|
-
else if (option == "readOnly" && value == "nocursor") input.blur();
|
131
|
-
else if (option == "theme") scroller.className = scroller.className.replace(/cm-s-\w+/, "cm-s-" + value);
|
132
149
|
},
|
133
150
|
getOption: function(option) {return options[option];},
|
134
151
|
undo: operation(undo),
|
@@ -136,14 +153,16 @@ var CodeMirror = (function() {
|
|
136
153
|
indentLine: operation(function(n, dir) {
|
137
154
|
if (isLine(n)) indentLine(n, dir == null ? "smart" : dir ? "add" : "subtract");
|
138
155
|
}),
|
156
|
+
indentSelection: operation(indentSelected),
|
139
157
|
historySize: function() {return {undo: history.done.length, redo: history.undone.length};},
|
158
|
+
clearHistory: function() {history = new History();},
|
140
159
|
matchBrackets: operation(function(){matchBrackets(true);}),
|
141
|
-
getTokenAt: function(pos) {
|
160
|
+
getTokenAt: operation(function(pos) {
|
142
161
|
pos = clipPos(pos);
|
143
|
-
return
|
144
|
-
},
|
162
|
+
return getLine(pos.line).getTokenAt(mode, getStateBefore(pos.line), pos.ch);
|
163
|
+
}),
|
145
164
|
getStateAfter: function(line) {
|
146
|
-
line = clipLine(line == null ?
|
165
|
+
line = clipLine(line == null ? doc.size - 1: line);
|
147
166
|
return getStateBefore(line + 1);
|
148
167
|
},
|
149
168
|
cursorCoords: function(start){
|
@@ -153,14 +172,23 @@ var CodeMirror = (function() {
|
|
153
172
|
charCoords: function(pos){return pageCoords(clipPos(pos));},
|
154
173
|
coordsChar: function(coords) {
|
155
174
|
var off = eltOffset(lineSpace);
|
156
|
-
|
157
|
-
return clipPos({line: line, ch: charFromX(clipLine(line), coords.x - off.left)});
|
175
|
+
return coordsChar(coords.x - off.left, coords.y - off.top);
|
158
176
|
},
|
159
|
-
|
160
|
-
|
177
|
+
markText: operation(markText),
|
178
|
+
setBookmark: setBookmark,
|
161
179
|
setMarker: operation(addGutterMarker),
|
162
180
|
clearMarker: operation(removeGutterMarker),
|
163
181
|
setLineClass: operation(setLineClass),
|
182
|
+
hideLine: operation(function(h) {return setLineHidden(h, true);}),
|
183
|
+
showLine: operation(function(h) {return setLineHidden(h, false);}),
|
184
|
+
onDeleteLine: function(line, f) {
|
185
|
+
if (typeof line == "number") {
|
186
|
+
if (!isLine(line)) return null;
|
187
|
+
line = getLine(line);
|
188
|
+
}
|
189
|
+
(line.handlers || (line.handlers = [])).push(f);
|
190
|
+
return line;
|
191
|
+
},
|
164
192
|
lineInfo: lineInfo,
|
165
193
|
addWidget: function(pos, node, scroll, vert, horiz) {
|
166
194
|
pos = localCoords(clipPos(pos));
|
@@ -169,7 +197,7 @@ var CodeMirror = (function() {
|
|
169
197
|
code.appendChild(node);
|
170
198
|
if (vert == "over") top = pos.y;
|
171
199
|
else if (vert == "near") {
|
172
|
-
var vspace = Math.max(scroller.offsetHeight,
|
200
|
+
var vspace = Math.max(scroller.offsetHeight, doc.height * textHeight()),
|
173
201
|
hspace = Math.max(code.clientWidth, lineSpace.clientWidth) - paddingLeft();
|
174
202
|
if (pos.yBot + node.offsetHeight > vspace && pos.y > node.offsetHeight)
|
175
203
|
top = pos.y - node.offsetHeight;
|
@@ -190,20 +218,24 @@ var CodeMirror = (function() {
|
|
190
218
|
scrollIntoView(left, top, left + node.offsetWidth, top + node.offsetHeight);
|
191
219
|
},
|
192
220
|
|
193
|
-
lineCount: function() {return
|
221
|
+
lineCount: function() {return doc.size;},
|
222
|
+
clipPos: clipPos,
|
194
223
|
getCursor: function(start) {
|
195
224
|
if (start == null) start = sel.inverted;
|
196
225
|
return copyPos(start ? sel.from : sel.to);
|
197
226
|
},
|
198
227
|
somethingSelected: function() {return !posEq(sel.from, sel.to);},
|
199
|
-
setCursor: operation(function(line, ch) {
|
200
|
-
if (ch == null && typeof line.line == "number") setCursor(line.line, line.ch);
|
201
|
-
else setCursor(line, ch);
|
228
|
+
setCursor: operation(function(line, ch, user) {
|
229
|
+
if (ch == null && typeof line.line == "number") setCursor(line.line, line.ch, user);
|
230
|
+
else setCursor(line, ch, user);
|
231
|
+
}),
|
232
|
+
setSelection: operation(function(from, to, user) {
|
233
|
+
(user ? setSelectionUser : setSelection)(clipPos(from), clipPos(to || from));
|
202
234
|
}),
|
203
|
-
|
204
|
-
|
235
|
+
getLine: function(line) {if (isLine(line)) return getLine(line).text;},
|
236
|
+
getLineHandle: function(line) {if (isLine(line)) return getLine(line);},
|
205
237
|
setLine: operation(function(line, text) {
|
206
|
-
if (isLine(line)) replaceRange(text, {line: line, ch: 0}, {line: line, ch:
|
238
|
+
if (isLine(line)) replaceRange(text, {line: line, ch: 0}, {line: line, ch: getLine(line).text.length});
|
207
239
|
}),
|
208
240
|
removeLine: operation(function(line) {
|
209
241
|
if (isLine(line)) replaceRange("", {line: line, ch: 0}, clipPos({line: line+1, ch: 0}));
|
@@ -211,6 +243,32 @@ var CodeMirror = (function() {
|
|
211
243
|
replaceRange: operation(replaceRange),
|
212
244
|
getRange: function(from, to) {return getRange(clipPos(from), clipPos(to));},
|
213
245
|
|
246
|
+
execCommand: function(cmd) {return commands[cmd](instance);},
|
247
|
+
// Stuff used by commands, probably not much use to outside code.
|
248
|
+
moveH: operation(moveH),
|
249
|
+
deleteH: operation(deleteH),
|
250
|
+
moveV: operation(moveV),
|
251
|
+
toggleOverwrite: function() {overwrite = !overwrite;},
|
252
|
+
|
253
|
+
posFromIndex: function(off) {
|
254
|
+
var lineNo = 0, ch;
|
255
|
+
doc.iter(0, doc.size, function(line) {
|
256
|
+
var sz = line.text.length + 1;
|
257
|
+
if (sz > off) { ch = off; return true; }
|
258
|
+
off -= sz;
|
259
|
+
++lineNo;
|
260
|
+
});
|
261
|
+
return clipPos({line: lineNo, ch: ch});
|
262
|
+
},
|
263
|
+
indexFromPos: function (coords) {
|
264
|
+
if (coords.line < 0 || coords.ch < 0) return 0;
|
265
|
+
var index = coords.ch;
|
266
|
+
doc.iter(0, coords.line, function (line) {
|
267
|
+
index += line.text.length + 1;
|
268
|
+
});
|
269
|
+
return index;
|
270
|
+
},
|
271
|
+
|
214
272
|
operation: function(f){return operation(f)();},
|
215
273
|
refresh: function(){updateDisplay(true);},
|
216
274
|
getInputField: function(){return input;},
|
@@ -219,27 +277,32 @@ var CodeMirror = (function() {
|
|
219
277
|
getGutterElement: function(){return gutter;}
|
220
278
|
};
|
221
279
|
|
280
|
+
function getLine(n) { return getLineAt(doc, n); }
|
281
|
+
function updateLineHeight(line, height) {
|
282
|
+
gutterDirty = true;
|
283
|
+
var diff = height - line.height;
|
284
|
+
for (var n = line; n; n = n.parent) n.height += diff;
|
285
|
+
}
|
286
|
+
|
222
287
|
function setValue(code) {
|
223
|
-
history = null;
|
224
288
|
var top = {line: 0, ch: 0};
|
225
|
-
updateLines(top, {line:
|
289
|
+
updateLines(top, {line: doc.size - 1, ch: getLine(doc.size-1).text.length},
|
226
290
|
splitLines(code), top, top);
|
227
|
-
history = new History();
|
228
291
|
updateInput = true;
|
229
292
|
}
|
230
293
|
function getValue(code) {
|
231
294
|
var text = [];
|
232
|
-
|
233
|
-
text.push(lines[i].text);
|
295
|
+
doc.iter(0, doc.size, function(line) { text.push(line.text); });
|
234
296
|
return text.join("\n");
|
235
297
|
}
|
236
298
|
|
237
299
|
function onMouseDown(e) {
|
300
|
+
setShift(e.shiftKey);
|
238
301
|
// Check whether this is a click in a widget
|
239
302
|
for (var n = e_target(e); n != wrapper; n = n.parentNode)
|
240
303
|
if (n.parentNode == code && n != mover) return;
|
241
304
|
|
242
|
-
//
|
305
|
+
// See if this is a click in the gutter
|
243
306
|
for (var n = e_target(e); n != wrapper; n = n.parentNode)
|
244
307
|
if (n.parentNode == gutterText) {
|
245
308
|
if (options.onGutterClick)
|
@@ -248,7 +311,7 @@ var CodeMirror = (function() {
|
|
248
311
|
}
|
249
312
|
|
250
313
|
var start = posFromMouse(e);
|
251
|
-
|
314
|
+
|
252
315
|
switch (e_button(e)) {
|
253
316
|
case 3:
|
254
317
|
if (gecko && !mac) onContextMenu(e);
|
@@ -263,21 +326,34 @@ var CodeMirror = (function() {
|
|
263
326
|
if (!start) {if (e_target(e) == scroller) e_preventDefault(e); return;}
|
264
327
|
|
265
328
|
if (!focused) onFocus();
|
266
|
-
|
329
|
+
|
267
330
|
var now = +new Date;
|
268
|
-
if (lastDoubleClick > now - 400) {
|
331
|
+
if (lastDoubleClick && lastDoubleClick.time > now - 400 && posEq(lastDoubleClick.pos, start)) {
|
269
332
|
e_preventDefault(e);
|
333
|
+
setTimeout(focusInput, 20);
|
270
334
|
return selectLine(start.line);
|
271
|
-
} else if (lastClick > now - 400) {
|
272
|
-
lastDoubleClick = now;
|
335
|
+
} else if (lastClick && lastClick.time > now - 400 && posEq(lastClick.pos, start)) {
|
336
|
+
lastDoubleClick = {time: now, pos: start};
|
273
337
|
e_preventDefault(e);
|
274
338
|
return selectWordAt(start);
|
275
|
-
} else { lastClick = now; }
|
339
|
+
} else { lastClick = {time: now, pos: start}; }
|
276
340
|
|
277
341
|
var last = start, going;
|
278
342
|
if (dragAndDrop && !posEq(sel.from, sel.to) &&
|
279
343
|
!posLess(start, sel.from) && !posLess(sel.to, start)) {
|
280
344
|
// Let the drag handler handle this.
|
345
|
+
if (webkit) lineSpace.draggable = true;
|
346
|
+
var up = connect(targetDocument, "mouseup", operation(function(e2) {
|
347
|
+
if (webkit) lineSpace.draggable = false;
|
348
|
+
draggingText = false;
|
349
|
+
up();
|
350
|
+
if (Math.abs(e.clientX - e2.clientX) + Math.abs(e.clientY - e2.clientY) < 10) {
|
351
|
+
e_preventDefault(e2);
|
352
|
+
setCursor(start.line, start.ch, true);
|
353
|
+
focusInput();
|
354
|
+
}
|
355
|
+
}), true);
|
356
|
+
draggingText = true;
|
281
357
|
return;
|
282
358
|
}
|
283
359
|
e_preventDefault(e);
|
@@ -311,6 +387,15 @@ var CodeMirror = (function() {
|
|
311
387
|
move(); up();
|
312
388
|
}), true);
|
313
389
|
}
|
390
|
+
function onDoubleClick(e) {
|
391
|
+
for (var n = e_target(e); n != wrapper; n = n.parentNode)
|
392
|
+
if (n.parentNode == gutterText) return e_preventDefault(e);
|
393
|
+
var start = posFromMouse(e);
|
394
|
+
if (!start) return;
|
395
|
+
lastDoubleClick = {time: +new Date, pos: start};
|
396
|
+
e_preventDefault(e);
|
397
|
+
selectWordAt(start);
|
398
|
+
}
|
314
399
|
function onDrop(e) {
|
315
400
|
e.preventDefault();
|
316
401
|
var pos = posFromMouse(e, true), files = e.dataTransfer.files;
|
@@ -320,7 +405,13 @@ var CodeMirror = (function() {
|
|
320
405
|
var reader = new FileReader;
|
321
406
|
reader.onload = function() {
|
322
407
|
text[i] = reader.result;
|
323
|
-
if (++read == n)
|
408
|
+
if (++read == n) {
|
409
|
+
pos = clipPos(pos);
|
410
|
+
operation(function() {
|
411
|
+
var end = replaceRange(text.join(""), pos, pos);
|
412
|
+
setSelectionUser(pos, end);
|
413
|
+
})();
|
414
|
+
}
|
324
415
|
};
|
325
416
|
reader.readAsText(file);
|
326
417
|
}
|
@@ -330,7 +421,13 @@ var CodeMirror = (function() {
|
|
330
421
|
else {
|
331
422
|
try {
|
332
423
|
var text = e.dataTransfer.getData("Text");
|
333
|
-
if (text)
|
424
|
+
if (text) {
|
425
|
+
var end = replaceRange(text, pos, pos);
|
426
|
+
var curFrom = sel.from, curTo = sel.to;
|
427
|
+
setSelectionUser(pos, end);
|
428
|
+
if (draggingText) replaceRange("", curFrom, curTo);
|
429
|
+
focusInput();
|
430
|
+
}
|
334
431
|
}
|
335
432
|
catch(e){}
|
336
433
|
}
|
@@ -342,81 +439,76 @@ var CodeMirror = (function() {
|
|
342
439
|
e.dataTransfer.setDragImage(escapeElement, 0, 0);
|
343
440
|
e.dataTransfer.setData("Text", txt);
|
344
441
|
}
|
442
|
+
function handleKeyBinding(e) {
|
443
|
+
var name = keyNames[e.keyCode], next = keyMap[options.keyMap].auto, bound, dropShift;
|
444
|
+
if (name == null || e.altGraphKey) {
|
445
|
+
if (next) options.keyMap = next;
|
446
|
+
return null;
|
447
|
+
}
|
448
|
+
if (e.altKey) name = "Alt-" + name;
|
449
|
+
if (e.ctrlKey) name = "Ctrl-" + name;
|
450
|
+
if (e.metaKey) name = "Cmd-" + name;
|
451
|
+
if (e.shiftKey && (bound = lookupKey("Shift-" + name, options.extraKeys, options.keyMap))) {
|
452
|
+
dropShift = true;
|
453
|
+
} else {
|
454
|
+
bound = lookupKey(name, options.extraKeys, options.keyMap);
|
455
|
+
}
|
456
|
+
if (typeof bound == "string") {
|
457
|
+
if (commands.propertyIsEnumerable(bound)) bound = commands[bound];
|
458
|
+
else bound = null;
|
459
|
+
}
|
460
|
+
if (next && (bound || !isModifierKey(e))) options.keyMap = next;
|
461
|
+
if (!bound) return false;
|
462
|
+
if (dropShift) {
|
463
|
+
var prevShift = shiftSelecting;
|
464
|
+
shiftSelecting = null;
|
465
|
+
bound(instance);
|
466
|
+
shiftSelecting = prevShift;
|
467
|
+
} else bound(instance);
|
468
|
+
e_preventDefault(e);
|
469
|
+
return true;
|
470
|
+
}
|
471
|
+
var lastStoppedKey = null;
|
345
472
|
function onKeyDown(e) {
|
346
473
|
if (!focused) onFocus();
|
347
|
-
|
348
474
|
var code = e.keyCode;
|
349
475
|
// IE does strange things with escape.
|
350
476
|
if (ie && code == 27) { e.returnValue = false; }
|
351
|
-
|
352
|
-
var mod = (mac ? e.metaKey : e.ctrlKey) && !e.altKey, anyMod = e.ctrlKey || e.altKey || e.metaKey;
|
353
|
-
if (code == 16 || e.shiftKey) shiftSelecting = shiftSelecting || (sel.inverted ? sel.to : sel.from);
|
354
|
-
else shiftSelecting = null;
|
477
|
+
setShift(code == 16 || e.shiftKey);
|
355
478
|
// First give onKeyEvent option a chance to handle this.
|
356
479
|
if (options.onKeyEvent && options.onKeyEvent(instance, addStop(e))) return;
|
357
|
-
|
358
|
-
if (
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
if (mod && code == 65) {selectAll(); return e_preventDefault(e);} // ctrl-a
|
364
|
-
if (!options.readOnly) {
|
365
|
-
if (!anyMod && code == 13) {return;} // enter
|
366
|
-
if (!anyMod && code == 9 && handleTab(e.shiftKey)) return e_preventDefault(e); // tab
|
367
|
-
if (mod && code == 90) {undo(); return e_preventDefault(e);} // ctrl-z
|
368
|
-
if (mod && ((e.shiftKey && code == 90) || code == 89)) {redo(); return e_preventDefault(e);} // ctrl-shift-z, ctrl-y
|
369
|
-
}
|
370
|
-
if (code == 36) { if (options.smartHome) { smartHome(); return e_preventDefault(e); } }
|
371
|
-
|
372
|
-
// Key id to use in the movementKeys map. We also pass it to
|
373
|
-
// fastPoll in order to 'self learn'. We need this because
|
374
|
-
// reducedSelection, the hack where we collapse the selection to
|
375
|
-
// its start when it is inverted and a movement key is pressed
|
376
|
-
// (and later restore it again), shouldn't be used for
|
377
|
-
// non-movement keys.
|
378
|
-
curKeyId = (mod ? "c" : "") + (e.altKey ? "a" : "") + code;
|
379
|
-
if (sel.inverted && movementKeys[curKeyId] === true) {
|
380
|
-
var range = selRange(input);
|
381
|
-
if (range) {
|
382
|
-
reducedSelection = {anchor: range.start};
|
383
|
-
setSelRange(input, range.start, range.start);
|
384
|
-
}
|
480
|
+
var handled = handleKeyBinding(e);
|
481
|
+
if (window.opera) {
|
482
|
+
lastStoppedKey = handled ? e.keyCode : null;
|
483
|
+
// Opera has no cut event... we try to at least catch the key combo
|
484
|
+
if (!handled && (mac ? e.metaKey : e.ctrlKey) && e.keyCode == 88)
|
485
|
+
replaceSelection("");
|
385
486
|
}
|
386
|
-
// Don't save the key as a movementkey unless it had a modifier
|
387
|
-
if (!mod && !e.altKey) curKeyId = null;
|
388
|
-
fastPoll(curKeyId);
|
389
|
-
}
|
390
|
-
function onKeyUp(e) {
|
391
|
-
if (options.onKeyEvent && options.onKeyEvent(instance, addStop(e))) return;
|
392
|
-
if (reducedSelection) {
|
393
|
-
reducedSelection = null;
|
394
|
-
updateInput = true;
|
395
|
-
}
|
396
|
-
if (e.keyCode == 16) shiftSelecting = null;
|
397
487
|
}
|
398
488
|
function onKeyPress(e) {
|
489
|
+
if (window.opera && e.keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return;}
|
399
490
|
if (options.onKeyEvent && options.onKeyEvent(instance, addStop(e))) return;
|
491
|
+
if (window.opera && !e.which && handleKeyBinding(e)) return;
|
400
492
|
if (options.electricChars && mode.electricChars) {
|
401
493
|
var ch = String.fromCharCode(e.charCode == null ? e.keyCode : e.charCode);
|
402
494
|
if (mode.electricChars.indexOf(ch) > -1)
|
403
|
-
setTimeout(operation(function() {indentLine(sel.to.line, "smart");}),
|
495
|
+
setTimeout(operation(function() {indentLine(sel.to.line, "smart");}), 75);
|
404
496
|
}
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
497
|
+
fastPoll();
|
498
|
+
}
|
499
|
+
function onKeyUp(e) {
|
500
|
+
if (options.onKeyEvent && options.onKeyEvent(instance, addStop(e))) return;
|
501
|
+
if (e.keyCode == 16) shiftSelecting = null;
|
410
502
|
}
|
411
503
|
|
412
504
|
function onFocus() {
|
413
|
-
if (options.readOnly
|
505
|
+
if (options.readOnly) return;
|
414
506
|
if (!focused) {
|
415
507
|
if (options.onFocus) options.onFocus(instance);
|
416
508
|
focused = true;
|
417
509
|
if (wrapper.className.search(/\bCodeMirror-focused\b/) == -1)
|
418
510
|
wrapper.className += " CodeMirror-focused";
|
419
|
-
if (!leaveInputAlone)
|
511
|
+
if (!leaveInputAlone) resetInput(true);
|
420
512
|
}
|
421
513
|
slowPoll();
|
422
514
|
restartBlink();
|
@@ -436,7 +528,7 @@ var CodeMirror = (function() {
|
|
436
528
|
function updateLines(from, to, newText, selFrom, selTo) {
|
437
529
|
if (history) {
|
438
530
|
var old = [];
|
439
|
-
|
531
|
+
doc.iter(from.line, to.line + 1, function(line) { old.push(line.text); });
|
440
532
|
history.addChange(from.line, newText.length, old);
|
441
533
|
while (history.done.length > options.undoDepth) history.done.shift();
|
442
534
|
}
|
@@ -446,11 +538,11 @@ var CodeMirror = (function() {
|
|
446
538
|
var change = from.pop();
|
447
539
|
if (change) {
|
448
540
|
var replaced = [], end = change.start + change.added;
|
449
|
-
|
541
|
+
doc.iter(change.start, end, function(line) { replaced.push(line.text); });
|
450
542
|
to.push({start: change.start, added: change.old.length, old: replaced});
|
451
543
|
var pos = clipPos({line: change.start + change.old.length - 1,
|
452
544
|
ch: editEnd(replaced[replaced.length-1], change.old[change.old.length-1])});
|
453
|
-
updateLinesNoUndo({line: change.start, ch: 0}, {line: end - 1, ch:
|
545
|
+
updateLinesNoUndo({line: change.start, ch: 0}, {line: end - 1, ch: getLine(end-1).text.length}, change.old, pos, pos);
|
454
546
|
updateInput = true;
|
455
547
|
}
|
456
548
|
}
|
@@ -459,51 +551,77 @@ var CodeMirror = (function() {
|
|
459
551
|
|
460
552
|
function updateLinesNoUndo(from, to, newText, selFrom, selTo) {
|
461
553
|
var recomputeMaxLength = false, maxLineLength = maxLine.length;
|
462
|
-
|
463
|
-
|
464
|
-
|
554
|
+
if (!options.lineWrapping)
|
555
|
+
doc.iter(from.line, to.line, function(line) {
|
556
|
+
if (line.text.length == maxLineLength) {recomputeMaxLength = true; return true;}
|
557
|
+
});
|
558
|
+
if (from.line != to.line || newText.length > 1) gutterDirty = true;
|
465
559
|
|
466
|
-
var nlines = to.line - from.line, firstLine =
|
560
|
+
var nlines = to.line - from.line, firstLine = getLine(from.line), lastLine = getLine(to.line);
|
467
561
|
// First adjust the line structure, taking some care to leave highlighting intact.
|
468
|
-
if (
|
562
|
+
if (from.ch == 0 && to.ch == 0 && newText[newText.length - 1] == "") {
|
563
|
+
// This is a whole-line replace. Treated specially to make
|
564
|
+
// sure line objects move the way they are supposed to.
|
565
|
+
var added = [], prevLine = null;
|
566
|
+
if (from.line) {
|
567
|
+
prevLine = getLine(from.line - 1);
|
568
|
+
prevLine.fixMarkEnds(lastLine);
|
569
|
+
} else lastLine.fixMarkStarts();
|
570
|
+
for (var i = 0, e = newText.length - 1; i < e; ++i)
|
571
|
+
added.push(Line.inheritMarks(newText[i], prevLine));
|
572
|
+
if (nlines) doc.remove(from.line, nlines, callbacks);
|
573
|
+
if (added.length) doc.insert(from.line, added);
|
574
|
+
} else if (firstLine == lastLine) {
|
469
575
|
if (newText.length == 1)
|
470
576
|
firstLine.replace(from.ch, to.ch, newText[0]);
|
471
577
|
else {
|
472
578
|
lastLine = firstLine.split(to.ch, newText[newText.length-1]);
|
473
|
-
|
474
|
-
firstLine.
|
475
|
-
|
476
|
-
|
477
|
-
|
579
|
+
firstLine.replace(from.ch, null, newText[0]);
|
580
|
+
firstLine.fixMarkEnds(lastLine);
|
581
|
+
var added = [];
|
582
|
+
for (var i = 1, e = newText.length - 1; i < e; ++i)
|
583
|
+
added.push(Line.inheritMarks(newText[i], firstLine));
|
584
|
+
added.push(lastLine);
|
585
|
+
doc.insert(from.line + 1, added);
|
478
586
|
}
|
479
|
-
}
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
else {
|
485
|
-
var
|
486
|
-
firstLine.replace(from.ch,
|
487
|
-
lastLine.replace(
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
var l =
|
587
|
+
} else if (newText.length == 1) {
|
588
|
+
firstLine.replace(from.ch, null, newText[0]);
|
589
|
+
lastLine.replace(null, to.ch, "");
|
590
|
+
firstLine.append(lastLine);
|
591
|
+
doc.remove(from.line + 1, nlines, callbacks);
|
592
|
+
} else {
|
593
|
+
var added = [];
|
594
|
+
firstLine.replace(from.ch, null, newText[0]);
|
595
|
+
lastLine.replace(null, to.ch, newText[newText.length-1]);
|
596
|
+
firstLine.fixMarkEnds(lastLine);
|
597
|
+
for (var i = 1, e = newText.length - 1; i < e; ++i)
|
598
|
+
added.push(Line.inheritMarks(newText[i], firstLine));
|
599
|
+
if (nlines > 1) doc.remove(from.line + 1, nlines - 1, callbacks);
|
600
|
+
doc.insert(from.line + 1, added);
|
601
|
+
}
|
602
|
+
if (options.lineWrapping) {
|
603
|
+
var perLine = scroller.clientWidth / charWidth() - 3;
|
604
|
+
doc.iter(from.line, from.line + newText.length, function(line) {
|
605
|
+
if (line.hidden) return;
|
606
|
+
var guess = Math.ceil(line.text.length / perLine) || 1;
|
607
|
+
if (guess != line.height) updateLineHeight(line, guess);
|
608
|
+
});
|
609
|
+
} else {
|
610
|
+
doc.iter(from.line, i + newText.length, function(line) {
|
611
|
+
var l = line.text;
|
504
612
|
if (l.length > maxLineLength) {
|
505
|
-
maxLineLength = l.length;
|
613
|
+
maxLine = l; maxLineLength = l.length; maxWidth = null;
|
614
|
+
recomputeMaxLength = false;
|
506
615
|
}
|
616
|
+
});
|
617
|
+
if (recomputeMaxLength) {
|
618
|
+
maxLineLength = 0; maxLine = ""; maxWidth = null;
|
619
|
+
doc.iter(0, doc.size, function(line) {
|
620
|
+
var l = line.text;
|
621
|
+
if (l.length > maxLineLength) {
|
622
|
+
maxLineLength = l.length; maxLine = l;
|
623
|
+
}
|
624
|
+
});
|
507
625
|
}
|
508
626
|
}
|
509
627
|
|
@@ -515,24 +633,25 @@ var CodeMirror = (function() {
|
|
515
633
|
if (task < from.line) newWork.push(task);
|
516
634
|
else if (task > to.line) newWork.push(task + lendiff);
|
517
635
|
}
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
} else {
|
522
|
-
newWork.push(from.line);
|
523
|
-
}
|
636
|
+
var hlEnd = from.line + Math.min(newText.length, 500);
|
637
|
+
highlightLines(from.line, hlEnd);
|
638
|
+
newWork.push(hlEnd);
|
524
639
|
work = newWork;
|
525
640
|
startWorker(100);
|
526
641
|
// Remember that these lines changed, for updating the display
|
527
642
|
changes.push({from: from.line, to: to.line + 1, diff: lendiff});
|
528
|
-
|
643
|
+
var changeObj = {from: from, to: to, text: newText};
|
644
|
+
if (textChanged) {
|
645
|
+
for (var cur = textChanged; cur.next; cur = cur.next) {}
|
646
|
+
cur.next = changeObj;
|
647
|
+
} else textChanged = changeObj;
|
529
648
|
|
530
649
|
// Update the selection
|
531
650
|
function updateLine(n) {return n <= Math.min(to.line, to.line + lendiff) ? n : n + lendiff;}
|
532
651
|
setSelection(selFrom, selTo, updateLine(sel.from.line), updateLine(sel.to.line));
|
533
652
|
|
534
653
|
// Make sure the scroll-size div has the correct height.
|
535
|
-
code.style.height = (
|
654
|
+
code.style.height = (doc.height * textHeight() + 2 * paddingTop()) + "px";
|
536
655
|
}
|
537
656
|
|
538
657
|
function replaceRange(code, from, to) {
|
@@ -570,10 +689,10 @@ var CodeMirror = (function() {
|
|
570
689
|
|
571
690
|
function getRange(from, to) {
|
572
691
|
var l1 = from.line, l2 = to.line;
|
573
|
-
if (l1 == l2) return
|
574
|
-
var code = [
|
575
|
-
|
576
|
-
code.push(
|
692
|
+
if (l1 == l2) return getLine(l1).text.slice(from.ch, to.ch);
|
693
|
+
var code = [getLine(l1).text.slice(from.ch)];
|
694
|
+
doc.iter(l1 + 1, l2, function(line) { code.push(line.text); });
|
695
|
+
code.push(getLine(l2).text.slice(0, to.ch));
|
577
696
|
return code.join("\n");
|
578
697
|
}
|
579
698
|
function getSelection() {
|
@@ -583,131 +702,74 @@ var CodeMirror = (function() {
|
|
583
702
|
var pollingFast = false; // Ensures slowPoll doesn't cancel fastPoll
|
584
703
|
function slowPoll() {
|
585
704
|
if (pollingFast) return;
|
586
|
-
poll.set(
|
705
|
+
poll.set(options.pollInterval, function() {
|
587
706
|
startOperation();
|
588
707
|
readInput();
|
589
708
|
if (focused) slowPoll();
|
590
709
|
endOperation();
|
591
710
|
});
|
592
711
|
}
|
593
|
-
function fastPoll(
|
712
|
+
function fastPoll() {
|
594
713
|
var missed = false;
|
595
714
|
pollingFast = true;
|
596
715
|
function p() {
|
597
716
|
startOperation();
|
598
717
|
var changed = readInput();
|
599
|
-
if (changed &&
|
600
|
-
if (changed == "moved" && movementKeys[keyId] == null) movementKeys[keyId] = true;
|
601
|
-
if (changed == "changed") movementKeys[keyId] = false;
|
602
|
-
}
|
603
|
-
if (!changed && !missed) {missed = true; poll.set(80, p);}
|
718
|
+
if (!changed && !missed) {missed = true; poll.set(60, p);}
|
604
719
|
else {pollingFast = false; slowPoll();}
|
605
720
|
endOperation();
|
606
721
|
}
|
607
722
|
poll.set(20, p);
|
608
723
|
}
|
609
724
|
|
610
|
-
//
|
611
|
-
//
|
612
|
-
//
|
725
|
+
// Previnput is a hack to work with IME. If we reset the textarea
|
726
|
+
// on every change, that breaks IME. So we look for changes
|
727
|
+
// compared to the previous content instead. (Modern browsers have
|
728
|
+
// events that indicate IME taking place, but these are not widely
|
729
|
+
// supported or compatible enough yet to rely on.)
|
730
|
+
var prevInput = "";
|
613
731
|
function readInput() {
|
614
|
-
if (leaveInputAlone || !focused) return;
|
615
|
-
var
|
616
|
-
if (
|
617
|
-
|
618
|
-
var
|
619
|
-
|
620
|
-
if (
|
621
|
-
|
622
|
-
|
623
|
-
|
624
|
-
|
625
|
-
|
626
|
-
|
627
|
-
var pos = 0;
|
628
|
-
for (;;) {
|
629
|
-
var found = text.indexOf("\n", pos);
|
630
|
-
if (found == -1 || (text.charAt(found-1) == "\r" ? found - 1 : found) >= n)
|
631
|
-
return {line: startLine, ch: n - pos};
|
632
|
-
++startLine;
|
633
|
-
pos = found + 1;
|
634
|
-
}
|
635
|
-
}
|
636
|
-
var from = computeOffset(sr.start, editing.from),
|
637
|
-
to = computeOffset(sr.end, editing.from);
|
638
|
-
// Here we have to take the reducedSelection hack into account,
|
639
|
-
// so that you can, for example, press shift-up at the start of
|
640
|
-
// your selection and have the right thing happen.
|
641
|
-
if (rs) {
|
642
|
-
var head = sr.start == rs.anchor ? to : from;
|
643
|
-
var tail = shiftSelecting ? sel.to : sr.start == rs.anchor ? from : to;
|
644
|
-
if (sel.inverted = posLess(head, tail)) { from = head; to = tail; }
|
645
|
-
else { reducedSelection = null; from = tail; to = head; }
|
646
|
-
}
|
647
|
-
|
648
|
-
// In some cases (cursor on same line as before), we don't have
|
649
|
-
// to update the textarea content at all.
|
650
|
-
if (from.line == to.line && from.line == sel.from.line && from.line == sel.to.line && !shiftSelecting)
|
651
|
-
updateInput = false;
|
652
|
-
|
653
|
-
// Magic mess to extract precise edited range from the changed
|
654
|
-
// string.
|
655
|
-
if (changed) {
|
656
|
-
var start = 0, end = text.length, len = Math.min(end, editing.text.length);
|
657
|
-
var c, line = editing.from, nl = -1;
|
658
|
-
while (start < len && (c = text.charAt(start)) == editing.text.charAt(start)) {
|
659
|
-
++start;
|
660
|
-
if (c == "\n") {line++; nl = start;}
|
661
|
-
}
|
662
|
-
var ch = nl > -1 ? start - nl : start, endline = editing.to - 1, edend = editing.text.length;
|
663
|
-
for (;;) {
|
664
|
-
c = editing.text.charAt(edend);
|
665
|
-
if (text.charAt(end) != c) {++end; ++edend; break;}
|
666
|
-
if (c == "\n") endline--;
|
667
|
-
if (edend <= start || end <= start) break;
|
668
|
-
--end; --edend;
|
669
|
-
}
|
670
|
-
var nl = editing.text.lastIndexOf("\n", edend - 1), endch = nl == -1 ? edend : edend - nl - 1;
|
671
|
-
updateLines({line: line, ch: ch}, {line: endline, ch: endch}, splitLines(text.slice(start, end)), from, to);
|
672
|
-
if (line != endline || from.line != line) updateInput = true;
|
673
|
-
}
|
674
|
-
else setSelection(from, to);
|
675
|
-
|
676
|
-
editing.text = text; editing.start = sr.start; editing.end = sr.end;
|
677
|
-
return changed ? "changed" : moved ? "moved" : false;
|
732
|
+
if (leaveInputAlone || !focused || hasSelection(input)) return false;
|
733
|
+
var text = input.value;
|
734
|
+
if (text == prevInput) return false;
|
735
|
+
shiftSelecting = null;
|
736
|
+
var same = 0, l = Math.min(prevInput.length, text.length);
|
737
|
+
while (same < l && prevInput[same] == text[same]) ++same;
|
738
|
+
if (same < prevInput.length)
|
739
|
+
sel.from = {line: sel.from.line, ch: sel.from.ch - (prevInput.length - same)};
|
740
|
+
else if (overwrite && posEq(sel.from, sel.to))
|
741
|
+
sel.to = {line: sel.to.line, ch: Math.min(getLine(sel.to.line).text.length, sel.to.ch + (text.length - same))};
|
742
|
+
replaceSelection(text.slice(same), "end");
|
743
|
+
prevInput = text;
|
744
|
+
return true;
|
678
745
|
}
|
679
|
-
|
680
|
-
|
681
|
-
|
682
|
-
|
683
|
-
|
684
|
-
|
685
|
-
for (var i = from; i < to; ++i) text.push(lines[i].text);
|
686
|
-
text = input.value = text.join(lineSep);
|
687
|
-
var startch = sel.from.ch, endch = sel.to.ch;
|
688
|
-
for (var i = from; i < sel.from.line; ++i)
|
689
|
-
startch += lineSep.length + lines[i].text.length;
|
690
|
-
for (var i = from; i < sel.to.line; ++i)
|
691
|
-
endch += lineSep.length + lines[i].text.length;
|
692
|
-
editing = {text: text, from: from, to: to, start: startch, end: endch};
|
693
|
-
setSelRange(input, startch, reducedSelection ? startch : endch);
|
746
|
+
function resetInput(user) {
|
747
|
+
if (!posEq(sel.from, sel.to)) {
|
748
|
+
prevInput = "";
|
749
|
+
input.value = getSelection();
|
750
|
+
input.select();
|
751
|
+
} else if (user) prevInput = input.value = "";
|
694
752
|
}
|
753
|
+
|
695
754
|
function focusInput() {
|
696
|
-
if (options.readOnly
|
755
|
+
if (!options.readOnly) input.focus();
|
697
756
|
}
|
698
757
|
|
699
758
|
function scrollEditorIntoView() {
|
700
759
|
if (!cursor.getBoundingClientRect) return;
|
701
760
|
var rect = cursor.getBoundingClientRect();
|
702
|
-
|
761
|
+
// IE returns bogus coordinates when the instance sits inside of an iframe and the cursor is hidden
|
762
|
+
if (ie && rect.top == rect.bottom) return;
|
763
|
+
var winH = window.innerHeight || Math.max(document.body.offsetHeight, document.documentElement.offsetHeight);
|
703
764
|
if (rect.top < 0 || rect.bottom > winH) cursor.scrollIntoView();
|
704
765
|
}
|
705
766
|
function scrollCursorIntoView() {
|
706
767
|
var cursor = localCoords(sel.inverted ? sel.from : sel.to);
|
707
|
-
|
768
|
+
var x = options.lineWrapping ? Math.min(cursor.x, lineSpace.offsetWidth) : cursor.x;
|
769
|
+
return scrollIntoView(x, cursor.y, x, cursor.yBot);
|
708
770
|
}
|
709
771
|
function scrollIntoView(x1, y1, x2, y2) {
|
710
|
-
var pl = paddingLeft(), pt = paddingTop(), lh =
|
772
|
+
var pl = paddingLeft(), pt = paddingTop(), lh = textHeight();
|
711
773
|
y1 += pt; y2 += pt; x1 += pl; x2 += pl;
|
712
774
|
var screen = scroller.clientHeight, screentop = scroller.scrollTop, scrolled = false, result = true;
|
713
775
|
if (y1 < screentop) {scroller.scrollTop = Math.max(0, y1 - 2*lh); scrolled = true;}
|
@@ -720,7 +782,7 @@ var CodeMirror = (function() {
|
|
720
782
|
scroller.scrollLeft = Math.max(0, x1 - 10 - gutterw);
|
721
783
|
scrolled = true;
|
722
784
|
}
|
723
|
-
else if (x2 > screenw + screenleft) {
|
785
|
+
else if (x2 > screenw + screenleft - 3) {
|
724
786
|
scroller.scrollLeft = x2 + 10 - screenw;
|
725
787
|
scrolled = true;
|
726
788
|
if (x2 > code.clientWidth) result = false;
|
@@ -730,32 +792,107 @@ var CodeMirror = (function() {
|
|
730
792
|
}
|
731
793
|
|
732
794
|
function visibleLines() {
|
733
|
-
var lh =
|
734
|
-
|
735
|
-
|
795
|
+
var lh = textHeight(), top = scroller.scrollTop - paddingTop();
|
796
|
+
var from_height = Math.max(0, Math.floor(top / lh));
|
797
|
+
var to_height = Math.ceil((top + scroller.clientHeight) / lh);
|
798
|
+
return {from: lineAtHeight(doc, from_height),
|
799
|
+
to: lineAtHeight(doc, to_height)};
|
736
800
|
}
|
737
801
|
// Uses a set of changes plus the current scroll position to
|
738
802
|
// determine which DOM updates have to be made, and makes the
|
739
803
|
// updates.
|
740
|
-
function updateDisplay(changes) {
|
804
|
+
function updateDisplay(changes, suppressCallback) {
|
741
805
|
if (!scroller.clientWidth) {
|
742
|
-
showingFrom = showingTo = 0;
|
806
|
+
showingFrom = showingTo = displayOffset = 0;
|
743
807
|
return;
|
744
808
|
}
|
745
|
-
//
|
746
|
-
|
747
|
-
|
809
|
+
// Compute the new visible window
|
810
|
+
var visible = visibleLines();
|
811
|
+
// Bail out if the visible area is already rendered and nothing changed.
|
812
|
+
if (changes !== true && changes.length == 0 && visible.from >= showingFrom && visible.to <= showingTo) return;
|
813
|
+
var from = Math.max(visible.from - 100, 0), to = Math.min(doc.size, visible.to + 100);
|
814
|
+
if (showingFrom < from && from - showingFrom < 20) from = showingFrom;
|
815
|
+
if (showingTo > to && showingTo - to < 20) to = Math.min(doc.size, showingTo);
|
816
|
+
|
817
|
+
// Create a range of theoretically intact lines, and punch holes
|
818
|
+
// in that using the change info.
|
819
|
+
var intact = changes === true ? [] :
|
820
|
+
computeIntact([{from: showingFrom, to: showingTo, domStart: 0}], changes);
|
821
|
+
// Clip off the parts that won't be visible
|
822
|
+
var intactLines = 0;
|
823
|
+
for (var i = 0; i < intact.length; ++i) {
|
824
|
+
var range = intact[i];
|
825
|
+
if (range.from < from) {range.domStart += (from - range.from); range.from = from;}
|
826
|
+
if (range.to > to) range.to = to;
|
827
|
+
if (range.from >= range.to) intact.splice(i--, 1);
|
828
|
+
else intactLines += range.to - range.from;
|
829
|
+
}
|
830
|
+
if (intactLines == to - from) return;
|
831
|
+
intact.sort(function(a, b) {return a.domStart - b.domStart;});
|
832
|
+
|
833
|
+
var th = textHeight(), gutterDisplay = gutter.style.display;
|
834
|
+
lineDiv.style.display = gutter.style.display = "none";
|
835
|
+
patchDisplay(from, to, intact);
|
836
|
+
lineDiv.style.display = "";
|
837
|
+
|
838
|
+
// Position the mover div to align with the lines it's supposed
|
839
|
+
// to be showing (which will cover the visible display)
|
840
|
+
var different = from != showingFrom || to != showingTo || lastSizeC != scroller.clientHeight + th;
|
841
|
+
// This is just a bogus formula that detects when the editor is
|
842
|
+
// resized or the font size changes.
|
843
|
+
if (different) lastSizeC = scroller.clientHeight + th;
|
844
|
+
showingFrom = from; showingTo = to;
|
845
|
+
displayOffset = heightAtLine(doc, from);
|
846
|
+
mover.style.top = (displayOffset * th) + "px";
|
847
|
+
code.style.height = (doc.height * th + 2 * paddingTop()) + "px";
|
848
|
+
|
849
|
+
// Since this is all rather error prone, it is honoured with the
|
850
|
+
// only assertion in the whole file.
|
851
|
+
if (lineDiv.childNodes.length != showingTo - showingFrom)
|
852
|
+
throw new Error("BAD PATCH! " + JSON.stringify(intact) + " size=" + (showingTo - showingFrom) +
|
853
|
+
" nodes=" + lineDiv.childNodes.length);
|
854
|
+
|
855
|
+
if (options.lineWrapping) {
|
856
|
+
maxWidth = scroller.clientWidth;
|
857
|
+
var curNode = lineDiv.firstChild;
|
858
|
+
doc.iter(showingFrom, showingTo, function(line) {
|
859
|
+
if (!line.hidden) {
|
860
|
+
var height = Math.round(curNode.offsetHeight / th) || 1;
|
861
|
+
if (line.height != height) {updateLineHeight(line, height); gutterDirty = true;}
|
862
|
+
}
|
863
|
+
curNode = curNode.nextSibling;
|
864
|
+
});
|
865
|
+
} else {
|
866
|
+
if (maxWidth == null) maxWidth = stringWidth(maxLine);
|
867
|
+
if (maxWidth > scroller.clientWidth) {
|
868
|
+
lineSpace.style.width = maxWidth + "px";
|
869
|
+
// Needed to prevent odd wrapping/hiding of widgets placed in here.
|
870
|
+
code.style.width = "";
|
871
|
+
code.style.width = scroller.scrollWidth + "px";
|
872
|
+
} else {
|
873
|
+
lineSpace.style.width = code.style.width = "";
|
874
|
+
}
|
875
|
+
}
|
876
|
+
gutter.style.display = gutterDisplay;
|
877
|
+
if (different || gutterDirty) updateGutter();
|
878
|
+
updateCursor();
|
879
|
+
if (!suppressCallback && options.onUpdate) options.onUpdate(instance);
|
880
|
+
return true;
|
881
|
+
}
|
882
|
+
|
883
|
+
function computeIntact(intact, changes) {
|
748
884
|
for (var i = 0, l = changes.length || 0; i < l; ++i) {
|
749
885
|
var change = changes[i], intact2 = [], diff = change.diff || 0;
|
750
886
|
for (var j = 0, l2 = intact.length; j < l2; ++j) {
|
751
887
|
var range = intact[j];
|
752
|
-
if (change.to <= range.from)
|
753
|
-
intact2.push({from: range.from + diff, to: range.to + diff,
|
754
|
-
|
888
|
+
if (change.to <= range.from && change.diff)
|
889
|
+
intact2.push({from: range.from + diff, to: range.to + diff,
|
890
|
+
domStart: range.domStart});
|
891
|
+
else if (change.to <= range.from || change.from >= range.to)
|
755
892
|
intact2.push(range);
|
756
893
|
else {
|
757
894
|
if (change.from > range.from)
|
758
|
-
intact2.push({from: range.from, to: change.from, domStart: range.domStart})
|
895
|
+
intact2.push({from: range.from, to: change.from, domStart: range.domStart});
|
759
896
|
if (change.to < range.to)
|
760
897
|
intact2.push({from: change.to + diff, to: range.to + diff,
|
761
898
|
domStart: range.domStart + (change.to - range.from)});
|
@@ -763,148 +900,75 @@ var CodeMirror = (function() {
|
|
763
900
|
}
|
764
901
|
intact = intact2;
|
765
902
|
}
|
903
|
+
return intact;
|
904
|
+
}
|
766
905
|
|
767
|
-
|
768
|
-
//
|
769
|
-
|
770
|
-
|
771
|
-
|
772
|
-
|
773
|
-
|
774
|
-
|
775
|
-
var range = intact[i];
|
776
|
-
if (range.to <= from) continue;
|
777
|
-
if (range.from >= to) break;
|
778
|
-
if (range.domStart > domPos || range.from > pos) {
|
779
|
-
updates.push({from: pos, to: range.from, domSize: range.domStart - domPos, domStart: domPos});
|
780
|
-
changedLines += range.from - pos;
|
906
|
+
function patchDisplay(from, to, intact) {
|
907
|
+
// The first pass removes the DOM nodes that aren't intact.
|
908
|
+
if (!intact.length) lineDiv.innerHTML = "";
|
909
|
+
else {
|
910
|
+
function killNode(node) {
|
911
|
+
var tmp = node.nextSibling;
|
912
|
+
node.parentNode.removeChild(node);
|
913
|
+
return tmp;
|
781
914
|
}
|
782
|
-
|
783
|
-
|
784
|
-
|
785
|
-
|
786
|
-
|
787
|
-
|
788
|
-
|
789
|
-
}
|
790
|
-
|
791
|
-
if (!updates.length) return;
|
792
|
-
lineDiv.style.display = "none";
|
793
|
-
// If more than 30% of the screen needs update, just do a full
|
794
|
-
// redraw (which is quicker than patching)
|
795
|
-
if (changedLines > (visible.to - visible.from) * .3)
|
796
|
-
refreshDisplay(from = Math.max(visible.from - 10, 0), to = Math.min(visible.to + 7, lines.length));
|
797
|
-
// Otherwise, only update the stuff that needs updating.
|
798
|
-
else
|
799
|
-
patchDisplay(updates);
|
800
|
-
lineDiv.style.display = "";
|
801
|
-
|
802
|
-
// Position the mover div to align with the lines it's supposed
|
803
|
-
// to be showing (which will cover the visible display)
|
804
|
-
var different = from != showingFrom || to != showingTo || lastHeight != scroller.clientHeight;
|
805
|
-
showingFrom = from; showingTo = to;
|
806
|
-
mover.style.top = (from * lineHeight()) + "px";
|
807
|
-
if (different) {
|
808
|
-
lastHeight = scroller.clientHeight;
|
809
|
-
code.style.height = (lines.length * lineHeight() + 2 * paddingTop()) + "px";
|
810
|
-
}
|
811
|
-
if (different || gutterDirty) updateGutter();
|
812
|
-
|
813
|
-
if (maxWidth == null) maxWidth = stringWidth(maxLine);
|
814
|
-
if (maxWidth > scroller.clientWidth) {
|
815
|
-
lineSpace.style.width = maxWidth + "px";
|
816
|
-
// Needed to prevent odd wrapping/hiding of widgets placed in here.
|
817
|
-
code.style.width = "";
|
818
|
-
code.style.width = scroller.scrollWidth + "px";
|
819
|
-
} else {
|
820
|
-
lineSpace.style.width = code.style.width = "";
|
915
|
+
var domPos = 0, curNode = lineDiv.firstChild, n;
|
916
|
+
for (var i = 0; i < intact.length; ++i) {
|
917
|
+
var cur = intact[i];
|
918
|
+
while (cur.domStart > domPos) {curNode = killNode(curNode); domPos++;}
|
919
|
+
for (var j = 0, e = cur.to - cur.from; j < e; ++j) {curNode = curNode.nextSibling; domPos++;}
|
920
|
+
}
|
921
|
+
while (curNode) curNode = killNode(curNode);
|
821
922
|
}
|
822
|
-
|
823
|
-
|
824
|
-
|
825
|
-
|
826
|
-
|
827
|
-
" nodes=" + lineDiv.childNodes.length);
|
828
|
-
updateCursor();
|
829
|
-
}
|
830
|
-
|
831
|
-
function refreshDisplay(from, to) {
|
832
|
-
var html = [], start = {line: from, ch: 0}, inSel = posLess(sel.from, start) && !posLess(sel.to, start);
|
833
|
-
for (var i = from; i < to; ++i) {
|
923
|
+
// This pass fills in the lines that actually changed.
|
924
|
+
var nextIntact = intact.shift(), curNode = lineDiv.firstChild, j = from;
|
925
|
+
var sfrom = sel.from.line, sto = sel.to.line, inSel = sfrom < from && sto >= from;
|
926
|
+
var scratch = targetDocument.createElement("div"), newElt;
|
927
|
+
doc.iter(from, to, function(line) {
|
834
928
|
var ch1 = null, ch2 = null;
|
835
929
|
if (inSel) {
|
836
930
|
ch1 = 0;
|
837
|
-
if (
|
838
|
-
}
|
839
|
-
|
840
|
-
if (sel.to.line == i) {ch1 = sel.from.ch; ch2 = sel.to.ch;}
|
931
|
+
if (sto == j) {inSel = false; ch2 = sel.to.ch;}
|
932
|
+
} else if (sfrom == j) {
|
933
|
+
if (sto == j) {ch1 = sel.from.ch; ch2 = sel.to.ch;}
|
841
934
|
else {inSel = true; ch1 = sel.from.ch;}
|
842
935
|
}
|
843
|
-
|
844
|
-
|
845
|
-
|
846
|
-
|
847
|
-
|
848
|
-
|
849
|
-
|
850
|
-
// whitespace.
|
851
|
-
var sfrom = sel.from.line, sto = sel.to.line, off = 0,
|
852
|
-
scratch = badInnerHTML && targetDocument.createElement("div");
|
853
|
-
for (var i = 0, e = updates.length; i < e; ++i) {
|
854
|
-
var rec = updates[i];
|
855
|
-
var extra = (rec.to - rec.from) - rec.domSize;
|
856
|
-
var nodeAfter = lineDiv.childNodes[rec.domStart + rec.domSize + off] || null;
|
857
|
-
if (badInnerHTML)
|
858
|
-
for (var j = Math.max(-extra, rec.domSize); j > 0; --j)
|
859
|
-
lineDiv.removeChild(nodeAfter ? nodeAfter.previousSibling : lineDiv.lastChild);
|
860
|
-
else if (extra) {
|
861
|
-
for (var j = Math.max(0, extra); j > 0; --j)
|
862
|
-
lineDiv.insertBefore(targetDocument.createElement("pre"), nodeAfter);
|
863
|
-
for (var j = Math.max(0, -extra); j > 0; --j)
|
864
|
-
lineDiv.removeChild(nodeAfter ? nodeAfter.previousSibling : lineDiv.lastChild);
|
865
|
-
}
|
866
|
-
var node = lineDiv.childNodes[rec.domStart + off], inSel = sfrom < rec.from && sto >= rec.from;
|
867
|
-
for (var j = rec.from; j < rec.to; ++j) {
|
868
|
-
var ch1 = null, ch2 = null;
|
869
|
-
if (inSel) {
|
870
|
-
ch1 = 0;
|
871
|
-
if (sto == j) {inSel = false; ch2 = sel.to.ch;}
|
872
|
-
}
|
873
|
-
else if (sfrom == j) {
|
874
|
-
if (sto == j) {ch1 = sel.from.ch; ch2 = sel.to.ch;}
|
875
|
-
else {inSel = true; ch1 = sel.from.ch;}
|
876
|
-
}
|
877
|
-
if (badInnerHTML) {
|
878
|
-
scratch.innerHTML = lines[j].getHTML(ch1, ch2, true);
|
879
|
-
lineDiv.insertBefore(scratch.firstChild, nodeAfter);
|
880
|
-
}
|
881
|
-
else {
|
882
|
-
node.innerHTML = lines[j].getHTML(ch1, ch2, false);
|
883
|
-
node.className = lines[j].className || "";
|
884
|
-
node = node.nextSibling;
|
885
|
-
}
|
936
|
+
if (nextIntact && nextIntact.to == j) nextIntact = intact.shift();
|
937
|
+
if (!nextIntact || nextIntact.from > j) {
|
938
|
+
if (line.hidden) scratch.innerHTML = "<pre></pre>";
|
939
|
+
else scratch.innerHTML = line.getHTML(ch1, ch2, true, tabText);
|
940
|
+
lineDiv.insertBefore(scratch.firstChild, curNode);
|
941
|
+
} else {
|
942
|
+
curNode = curNode.nextSibling;
|
886
943
|
}
|
887
|
-
|
888
|
-
}
|
944
|
+
++j;
|
945
|
+
});
|
889
946
|
}
|
890
947
|
|
891
948
|
function updateGutter() {
|
892
949
|
if (!options.gutter && !options.lineNumbers) return;
|
893
950
|
var hText = mover.offsetHeight, hEditor = scroller.clientHeight;
|
894
951
|
gutter.style.height = (hText - hEditor < 2 ? hEditor : hText) + "px";
|
895
|
-
var html = [];
|
896
|
-
|
897
|
-
|
898
|
-
|
899
|
-
|
900
|
-
|
901
|
-
|
902
|
-
|
903
|
-
|
904
|
-
|
952
|
+
var html = [], i = showingFrom;
|
953
|
+
doc.iter(showingFrom, Math.max(showingTo, showingFrom + 1), function(line) {
|
954
|
+
if (line.hidden) {
|
955
|
+
html.push("<pre></pre>");
|
956
|
+
} else {
|
957
|
+
var marker = line.gutterMarker;
|
958
|
+
var text = options.lineNumbers ? i + options.firstLineNumber : null;
|
959
|
+
if (marker && marker.text)
|
960
|
+
text = marker.text.replace("%N%", text != null ? text : "");
|
961
|
+
else if (text == null)
|
962
|
+
text = "\u00a0";
|
963
|
+
html.push((marker && marker.style ? '<pre class="' + marker.style + '">' : "<pre>"), text);
|
964
|
+
for (var j = 1; j < line.height; ++j) html.push("<br/> ");
|
965
|
+
html.push("</pre>");
|
966
|
+
}
|
967
|
+
++i;
|
968
|
+
});
|
905
969
|
gutter.style.display = "none";
|
906
970
|
gutterText.innerHTML = html.join("");
|
907
|
-
var minwidth = String(
|
971
|
+
var minwidth = String(doc.size).length, firstNode = gutterText.firstChild, val = eltText(firstNode), pad = "";
|
908
972
|
while (val.length + pad.length < minwidth) pad += "\u00a0";
|
909
973
|
if (pad) firstNode.insertBefore(targetDocument.createTextNode(pad), firstNode.firstChild);
|
910
974
|
gutter.style.display = "";
|
@@ -912,19 +976,23 @@ var CodeMirror = (function() {
|
|
912
976
|
gutterDirty = false;
|
913
977
|
}
|
914
978
|
function updateCursor() {
|
915
|
-
var head = sel.inverted ? sel.from : sel.to, lh =
|
916
|
-
var
|
917
|
-
var
|
918
|
-
inputDiv.style.top =
|
919
|
-
inputDiv.style.left = (x -
|
979
|
+
var head = sel.inverted ? sel.from : sel.to, lh = textHeight();
|
980
|
+
var pos = localCoords(head, true);
|
981
|
+
var wrapOff = eltOffset(wrapper), lineOff = eltOffset(lineDiv);
|
982
|
+
inputDiv.style.top = (pos.y + lineOff.top - wrapOff.top) + "px";
|
983
|
+
inputDiv.style.left = (pos.x + lineOff.left - wrapOff.left) + "px";
|
920
984
|
if (posEq(sel.from, sel.to)) {
|
921
|
-
cursor.style.top =
|
922
|
-
cursor.style.left = x + "px";
|
985
|
+
cursor.style.top = pos.y + "px";
|
986
|
+
cursor.style.left = (options.lineWrapping ? Math.min(pos.x, lineSpace.offsetWidth) : pos.x) + "px";
|
923
987
|
cursor.style.display = "";
|
924
988
|
}
|
925
989
|
else cursor.style.display = "none";
|
926
990
|
}
|
927
991
|
|
992
|
+
function setShift(val) {
|
993
|
+
if (val) shiftSelecting = shiftSelecting || (sel.inverted ? sel.to : sel.from);
|
994
|
+
else shiftSelecting = null;
|
995
|
+
}
|
928
996
|
function setSelectionUser(from, to) {
|
929
997
|
var sh = shiftSelecting && clipPos(shiftSelecting);
|
930
998
|
if (sh) {
|
@@ -932,14 +1000,21 @@ var CodeMirror = (function() {
|
|
932
1000
|
else if (posLess(to, sh)) to = sh;
|
933
1001
|
}
|
934
1002
|
setSelection(from, to);
|
1003
|
+
userSelChange = true;
|
935
1004
|
}
|
936
1005
|
// Update the selection. Last two args are only used by
|
937
1006
|
// updateLines, since they have to be expressed in the line
|
938
1007
|
// numbers before the update.
|
939
1008
|
function setSelection(from, to, oldFrom, oldTo) {
|
1009
|
+
goalColumn = null;
|
1010
|
+
if (oldFrom == null) {oldFrom = sel.from.line; oldTo = sel.to.line;}
|
940
1011
|
if (posEq(sel.from, from) && posEq(sel.to, to)) return;
|
941
1012
|
if (posLess(to, from)) {var tmp = to; to = from; from = tmp;}
|
942
1013
|
|
1014
|
+
// Skip over hidden lines.
|
1015
|
+
if (from.line != oldFrom) from = skipHidden(from, oldFrom, sel.from.ch);
|
1016
|
+
if (to.line != oldTo) to = skipHidden(to, oldTo, sel.to.ch);
|
1017
|
+
|
943
1018
|
if (posEq(from, to)) sel.inverted = false;
|
944
1019
|
else if (posEq(from, sel.to)) sel.inverted = false;
|
945
1020
|
else if (posEq(to, sel.from)) sel.inverted = true;
|
@@ -947,7 +1022,6 @@ var CodeMirror = (function() {
|
|
947
1022
|
// Some ugly logic used to only mark the lines that actually did
|
948
1023
|
// see a change in selection as changed, rather than the whole
|
949
1024
|
// selected range.
|
950
|
-
if (oldFrom == null) {oldFrom = sel.from.line; oldTo = sel.to.line;}
|
951
1025
|
if (posEq(from, to)) {
|
952
1026
|
if (!posEq(sel.from, sel.to))
|
953
1027
|
changes.push({from: oldFrom, to: oldTo + 1});
|
@@ -972,90 +1046,120 @@ var CodeMirror = (function() {
|
|
972
1046
|
sel.from = from; sel.to = to;
|
973
1047
|
selectionChanged = true;
|
974
1048
|
}
|
1049
|
+
function skipHidden(pos, oldLine, oldCh) {
|
1050
|
+
function getNonHidden(dir) {
|
1051
|
+
var lNo = pos.line + dir, end = dir == 1 ? doc.size : -1;
|
1052
|
+
while (lNo != end) {
|
1053
|
+
var line = getLine(lNo);
|
1054
|
+
if (!line.hidden) {
|
1055
|
+
var ch = pos.ch;
|
1056
|
+
if (ch > oldCh || ch > line.text.length) ch = line.text.length;
|
1057
|
+
return {line: lNo, ch: ch};
|
1058
|
+
}
|
1059
|
+
lNo += dir;
|
1060
|
+
}
|
1061
|
+
}
|
1062
|
+
var line = getLine(pos.line);
|
1063
|
+
if (!line.hidden) return pos;
|
1064
|
+
if (pos.line >= oldLine) return getNonHidden(1) || getNonHidden(-1);
|
1065
|
+
else return getNonHidden(-1) || getNonHidden(1);
|
1066
|
+
}
|
975
1067
|
function setCursor(line, ch, user) {
|
976
1068
|
var pos = clipPos({line: line, ch: ch || 0});
|
977
1069
|
(user ? setSelectionUser : setSelection)(pos, pos);
|
978
1070
|
}
|
979
1071
|
|
980
|
-
function clipLine(n) {return Math.max(0, Math.min(n,
|
1072
|
+
function clipLine(n) {return Math.max(0, Math.min(n, doc.size-1));}
|
981
1073
|
function clipPos(pos) {
|
982
1074
|
if (pos.line < 0) return {line: 0, ch: 0};
|
983
|
-
if (pos.line >=
|
984
|
-
var ch = pos.ch, linelen =
|
1075
|
+
if (pos.line >= doc.size) return {line: doc.size-1, ch: getLine(doc.size-1).text.length};
|
1076
|
+
var ch = pos.ch, linelen = getLine(pos.line).text.length;
|
985
1077
|
if (ch == null || ch > linelen) return {line: pos.line, ch: linelen};
|
986
1078
|
else if (ch < 0) return {line: pos.line, ch: 0};
|
987
1079
|
else return pos;
|
988
1080
|
}
|
989
1081
|
|
990
|
-
function
|
991
|
-
var
|
992
|
-
|
1082
|
+
function findPosH(dir, unit) {
|
1083
|
+
var end = sel.inverted ? sel.from : sel.to, line = end.line, ch = end.ch;
|
1084
|
+
var lineObj = getLine(line);
|
1085
|
+
function findNextLine() {
|
1086
|
+
for (var l = line + dir, e = dir < 0 ? -1 : doc.size; l != e; l += dir) {
|
1087
|
+
var lo = getLine(l);
|
1088
|
+
if (!lo.hidden) { line = l; lineObj = lo; return true; }
|
1089
|
+
}
|
1090
|
+
}
|
1091
|
+
function moveOnce(boundToLine) {
|
1092
|
+
if (ch == (dir < 0 ? 0 : lineObj.text.length)) {
|
1093
|
+
if (!boundToLine && findNextLine()) ch = dir < 0 ? lineObj.text.length : 0;
|
1094
|
+
else return false;
|
1095
|
+
} else ch += dir;
|
1096
|
+
return true;
|
1097
|
+
}
|
1098
|
+
if (unit == "char") moveOnce();
|
1099
|
+
else if (unit == "column") moveOnce(true);
|
1100
|
+
else if (unit == "word") {
|
1101
|
+
var sawWord = false;
|
1102
|
+
for (;;) {
|
1103
|
+
if (dir < 0) if (!moveOnce()) break;
|
1104
|
+
if (isWordChar(lineObj.text.charAt(ch))) sawWord = true;
|
1105
|
+
else if (sawWord) {if (dir < 0) {dir = 1; moveOnce();} break;}
|
1106
|
+
if (dir > 0) if (!moveOnce()) break;
|
1107
|
+
}
|
1108
|
+
}
|
1109
|
+
return {line: line, ch: ch};
|
1110
|
+
}
|
1111
|
+
function moveH(dir, unit) {
|
1112
|
+
var pos = dir < 0 ? sel.from : sel.to;
|
1113
|
+
if (shiftSelecting || posEq(sel.from, sel.to)) pos = findPosH(dir, unit);
|
1114
|
+
setCursor(pos.line, pos.ch, true);
|
993
1115
|
}
|
994
|
-
function
|
995
|
-
|
996
|
-
|
1116
|
+
function deleteH(dir, unit) {
|
1117
|
+
if (!posEq(sel.from, sel.to)) replaceRange("", sel.from, sel.to);
|
1118
|
+
else if (dir < 0) replaceRange("", findPosH(dir, unit), sel.to);
|
1119
|
+
else replaceRange("", sel.from, findPosH(dir, unit));
|
1120
|
+
userSelChange = true;
|
997
1121
|
}
|
998
|
-
|
999
|
-
|
1000
|
-
|
1122
|
+
var goalColumn = null;
|
1123
|
+
function moveV(dir, unit) {
|
1124
|
+
var dist = 0, pos = localCoords(sel.inverted ? sel.from : sel.to, true);
|
1125
|
+
if (goalColumn != null) pos.x = goalColumn;
|
1126
|
+
if (unit == "page") dist = scroller.clientHeight;
|
1127
|
+
else if (unit == "line") dist = textHeight();
|
1128
|
+
var target = coordsChar(pos.x, pos.y + dist * dir + 2);
|
1129
|
+
setCursor(target.line, target.ch, true);
|
1130
|
+
goalColumn = pos.x;
|
1001
1131
|
}
|
1132
|
+
|
1002
1133
|
function selectWordAt(pos) {
|
1003
|
-
var line =
|
1134
|
+
var line = getLine(pos.line).text;
|
1004
1135
|
var start = pos.ch, end = pos.ch;
|
1005
|
-
while (start > 0 &&
|
1006
|
-
while (end < line.length &&
|
1136
|
+
while (start > 0 && isWordChar(line.charAt(start - 1))) --start;
|
1137
|
+
while (end < line.length && isWordChar(line.charAt(end))) ++end;
|
1007
1138
|
setSelectionUser({line: pos.line, ch: start}, {line: pos.line, ch: end});
|
1008
1139
|
}
|
1009
1140
|
function selectLine(line) {
|
1010
|
-
setSelectionUser({line: line, ch: 0}, {line: line, ch:
|
1141
|
+
setSelectionUser({line: line, ch: 0}, {line: line, ch: getLine(line).text.length});
|
1011
1142
|
}
|
1012
|
-
function
|
1013
|
-
|
1014
|
-
|
1015
|
-
|
1016
|
-
}
|
1017
|
-
function handleTab(shift) {
|
1018
|
-
function indentSelected(mode) {
|
1019
|
-
if (posEq(sel.from, sel.to)) return indentLine(sel.from.line, mode);
|
1020
|
-
var e = sel.to.line - (sel.to.ch ? 0 : 1);
|
1021
|
-
for (var i = sel.from.line; i <= e; ++i) indentLine(i, mode);
|
1022
|
-
}
|
1023
|
-
shiftSelecting = null;
|
1024
|
-
switch (options.tabMode) {
|
1025
|
-
case "default":
|
1026
|
-
return false;
|
1027
|
-
case "indent":
|
1028
|
-
indentSelected("smart");
|
1029
|
-
break;
|
1030
|
-
case "classic":
|
1031
|
-
if (posEq(sel.from, sel.to)) {
|
1032
|
-
if (shift) indentLine(sel.from.line, "smart");
|
1033
|
-
else replaceSelection("\t", "end");
|
1034
|
-
break;
|
1035
|
-
}
|
1036
|
-
case "shift":
|
1037
|
-
indentSelected(shift ? "subtract" : "add");
|
1038
|
-
break;
|
1039
|
-
}
|
1040
|
-
return true;
|
1041
|
-
}
|
1042
|
-
function smartHome() {
|
1043
|
-
var firstNonWS = Math.max(0, lines[sel.from.line].text.search(/\S/));
|
1044
|
-
setCursor(sel.from.line, sel.from.ch <= firstNonWS && sel.from.ch ? 0 : firstNonWS, true);
|
1143
|
+
function indentSelected(mode) {
|
1144
|
+
if (posEq(sel.from, sel.to)) return indentLine(sel.from.line, mode);
|
1145
|
+
var e = sel.to.line - (sel.to.ch ? 0 : 1);
|
1146
|
+
for (var i = sel.from.line; i <= e; ++i) indentLine(i, mode);
|
1045
1147
|
}
|
1046
1148
|
|
1047
1149
|
function indentLine(n, how) {
|
1150
|
+
if (!how) how = "add";
|
1048
1151
|
if (how == "smart") {
|
1049
1152
|
if (!mode.indent) how = "prev";
|
1050
1153
|
else var state = getStateBefore(n);
|
1051
1154
|
}
|
1052
1155
|
|
1053
|
-
var line =
|
1156
|
+
var line = getLine(n), curSpace = line.indentation(options.tabSize),
|
1157
|
+
curSpaceString = line.text.match(/^\s*/)[0], indentation;
|
1054
1158
|
if (how == "prev") {
|
1055
|
-
if (n) indentation =
|
1159
|
+
if (n) indentation = getLine(n-1).indentation(options.tabSize);
|
1056
1160
|
else indentation = 0;
|
1057
1161
|
}
|
1058
|
-
else if (how == "smart") indentation = mode.indent(state, line.text.slice(curSpaceString.length));
|
1162
|
+
else if (how == "smart") indentation = mode.indent(state, line.text.slice(curSpaceString.length), line.text);
|
1059
1163
|
else if (how == "add") indentation = curSpace + options.indentUnit;
|
1060
1164
|
else if (how == "subtract") indentation = curSpace - options.indentUnit;
|
1061
1165
|
indentation = Math.max(0, indentation);
|
@@ -1068,7 +1172,7 @@ var CodeMirror = (function() {
|
|
1068
1172
|
else {
|
1069
1173
|
var indentString = "", pos = 0;
|
1070
1174
|
if (options.indentWithTabs)
|
1071
|
-
for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += "\t";}
|
1175
|
+
for (var i = Math.floor(indentation / options.tabSize); i; --i) {pos += options.tabSize; indentString += "\t";}
|
1072
1176
|
while (pos < indentation) {++pos; indentString += " ";}
|
1073
1177
|
}
|
1074
1178
|
|
@@ -1077,8 +1181,7 @@ var CodeMirror = (function() {
|
|
1077
1181
|
|
1078
1182
|
function loadMode() {
|
1079
1183
|
mode = CodeMirror.getMode(options, options.mode);
|
1080
|
-
|
1081
|
-
lines[i].stateAfter = null;
|
1184
|
+
doc.iter(0, doc.size, function(line) { line.stateAfter = null; });
|
1082
1185
|
work = [0];
|
1083
1186
|
startWorker();
|
1084
1187
|
}
|
@@ -1088,76 +1191,153 @@ var CodeMirror = (function() {
|
|
1088
1191
|
if (visible) gutterDirty = true;
|
1089
1192
|
else lineDiv.parentNode.style.marginLeft = 0;
|
1090
1193
|
}
|
1194
|
+
function wrappingChanged(from, to) {
|
1195
|
+
if (options.lineWrapping) {
|
1196
|
+
wrapper.className += " CodeMirror-wrap";
|
1197
|
+
var perLine = scroller.clientWidth / charWidth() - 3;
|
1198
|
+
doc.iter(0, doc.size, function(line) {
|
1199
|
+
if (line.hidden) return;
|
1200
|
+
var guess = Math.ceil(line.text.length / perLine) || 1;
|
1201
|
+
if (guess != 1) updateLineHeight(line, guess);
|
1202
|
+
});
|
1203
|
+
lineSpace.style.width = code.style.width = "";
|
1204
|
+
} else {
|
1205
|
+
wrapper.className = wrapper.className.replace(" CodeMirror-wrap", "");
|
1206
|
+
maxWidth = null; maxLine = "";
|
1207
|
+
doc.iter(0, doc.size, function(line) {
|
1208
|
+
if (line.height != 1 && !line.hidden) updateLineHeight(line, 1);
|
1209
|
+
if (line.text.length > maxLine.length) maxLine = line.text;
|
1210
|
+
});
|
1211
|
+
}
|
1212
|
+
changes.push({from: 0, to: doc.size});
|
1213
|
+
}
|
1214
|
+
function computeTabText() {
|
1215
|
+
for (var str = '<span class="cm-tab">', i = 0; i < options.tabSize; ++i) str += " ";
|
1216
|
+
return str + "</span>";
|
1217
|
+
}
|
1218
|
+
function tabsChanged() {
|
1219
|
+
tabText = computeTabText();
|
1220
|
+
updateDisplay(true);
|
1221
|
+
}
|
1222
|
+
function themeChanged() {
|
1223
|
+
scroller.className = scroller.className.replace(/\s*cm-s-\w+/g, "") +
|
1224
|
+
options.theme.replace(/(^|\s)\s*/g, " cm-s-");
|
1225
|
+
}
|
1226
|
+
|
1227
|
+
function TextMarker() { this.set = []; }
|
1228
|
+
TextMarker.prototype.clear = operation(function() {
|
1229
|
+
var min = Infinity, max = -Infinity;
|
1230
|
+
for (var i = 0, e = this.set.length; i < e; ++i) {
|
1231
|
+
var line = this.set[i], mk = line.marked;
|
1232
|
+
if (!mk || !line.parent) continue;
|
1233
|
+
var lineN = lineNo(line);
|
1234
|
+
min = Math.min(min, lineN); max = Math.max(max, lineN);
|
1235
|
+
for (var j = 0; j < mk.length; ++j)
|
1236
|
+
if (mk[j].set == this.set) mk.splice(j--, 1);
|
1237
|
+
}
|
1238
|
+
if (min != Infinity)
|
1239
|
+
changes.push({from: min, to: max + 1});
|
1240
|
+
});
|
1241
|
+
TextMarker.prototype.find = function() {
|
1242
|
+
var from, to;
|
1243
|
+
for (var i = 0, e = this.set.length; i < e; ++i) {
|
1244
|
+
var line = this.set[i], mk = line.marked;
|
1245
|
+
for (var j = 0; j < mk.length; ++j) {
|
1246
|
+
var mark = mk[j];
|
1247
|
+
if (mark.set == this.set) {
|
1248
|
+
if (mark.from != null || mark.to != null) {
|
1249
|
+
var found = lineNo(line);
|
1250
|
+
if (found != null) {
|
1251
|
+
if (mark.from != null) from = {line: found, ch: mark.from};
|
1252
|
+
if (mark.to != null) to = {line: found, ch: mark.to};
|
1253
|
+
}
|
1254
|
+
}
|
1255
|
+
}
|
1256
|
+
}
|
1257
|
+
}
|
1258
|
+
return {from: from, to: to};
|
1259
|
+
};
|
1091
1260
|
|
1092
1261
|
function markText(from, to, className) {
|
1093
1262
|
from = clipPos(from); to = clipPos(to);
|
1094
|
-
var
|
1263
|
+
var tm = new TextMarker();
|
1095
1264
|
function add(line, from, to, className) {
|
1096
|
-
|
1097
|
-
mark.line = line;
|
1098
|
-
accum.push(mark);
|
1265
|
+
getLine(line).addMark(new MarkedText(from, to, className, tm.set));
|
1099
1266
|
}
|
1100
1267
|
if (from.line == to.line) add(from.line, from.ch, to.ch, className);
|
1101
1268
|
else {
|
1102
1269
|
add(from.line, from.ch, null, className);
|
1103
1270
|
for (var i = from.line + 1, e = to.line; i < e; ++i)
|
1104
|
-
add(i,
|
1105
|
-
add(to.line,
|
1271
|
+
add(i, null, null, className);
|
1272
|
+
add(to.line, null, to.ch, className);
|
1106
1273
|
}
|
1107
1274
|
changes.push({from: from.line, to: to.line + 1});
|
1108
|
-
return
|
1109
|
-
|
1110
|
-
|
1111
|
-
|
1112
|
-
|
1113
|
-
|
1114
|
-
|
1115
|
-
|
1116
|
-
}
|
1117
|
-
}
|
1118
|
-
if (start != null) changes.push({from: start, to: end + 1});
|
1119
|
-
};
|
1275
|
+
return tm;
|
1276
|
+
}
|
1277
|
+
|
1278
|
+
function setBookmark(pos) {
|
1279
|
+
pos = clipPos(pos);
|
1280
|
+
var bm = new Bookmark(pos.ch);
|
1281
|
+
getLine(pos.line).addMark(bm);
|
1282
|
+
return bm;
|
1120
1283
|
}
|
1121
1284
|
|
1122
1285
|
function addGutterMarker(line, text, className) {
|
1123
|
-
if (typeof line == "number") line =
|
1286
|
+
if (typeof line == "number") line = getLine(clipLine(line));
|
1124
1287
|
line.gutterMarker = {text: text, style: className};
|
1125
1288
|
gutterDirty = true;
|
1126
1289
|
return line;
|
1127
1290
|
}
|
1128
1291
|
function removeGutterMarker(line) {
|
1129
|
-
if (typeof line == "number") line =
|
1292
|
+
if (typeof line == "number") line = getLine(clipLine(line));
|
1130
1293
|
line.gutterMarker = null;
|
1131
1294
|
gutterDirty = true;
|
1132
1295
|
}
|
1133
|
-
|
1134
|
-
|
1135
|
-
|
1136
|
-
|
1137
|
-
|
1138
|
-
|
1139
|
-
|
1140
|
-
|
1141
|
-
}
|
1142
|
-
if (line.className != className) {
|
1143
|
-
line.className = className;
|
1144
|
-
changes.push({from: no, to: no + 1});
|
1145
|
-
}
|
1296
|
+
|
1297
|
+
function changeLine(handle, op) {
|
1298
|
+
var no = handle, line = handle;
|
1299
|
+
if (typeof handle == "number") line = getLine(clipLine(handle));
|
1300
|
+
else no = lineNo(handle);
|
1301
|
+
if (no == null) return null;
|
1302
|
+
if (op(line, no)) changes.push({from: no, to: no + 1});
|
1303
|
+
else return null;
|
1146
1304
|
return line;
|
1147
1305
|
}
|
1306
|
+
function setLineClass(handle, className) {
|
1307
|
+
return changeLine(handle, function(line) {
|
1308
|
+
if (line.className != className) {
|
1309
|
+
line.className = className;
|
1310
|
+
return true;
|
1311
|
+
}
|
1312
|
+
});
|
1313
|
+
}
|
1314
|
+
function setLineHidden(handle, hidden) {
|
1315
|
+
return changeLine(handle, function(line, no) {
|
1316
|
+
if (line.hidden != hidden) {
|
1317
|
+
line.hidden = hidden;
|
1318
|
+
updateLineHeight(line, hidden ? 0 : 1);
|
1319
|
+
if (hidden && (sel.from.line == no || sel.to.line == no))
|
1320
|
+
setSelection(skipHidden(sel.from, sel.from.line, sel.from.ch),
|
1321
|
+
skipHidden(sel.to, sel.to.line, sel.to.ch));
|
1322
|
+
return (gutterDirty = true);
|
1323
|
+
}
|
1324
|
+
});
|
1325
|
+
}
|
1148
1326
|
|
1149
1327
|
function lineInfo(line) {
|
1150
1328
|
if (typeof line == "number") {
|
1329
|
+
if (!isLine(line)) return null;
|
1151
1330
|
var n = line;
|
1152
|
-
line =
|
1331
|
+
line = getLine(line);
|
1153
1332
|
if (!line) return null;
|
1154
1333
|
}
|
1155
1334
|
else {
|
1156
|
-
var n =
|
1157
|
-
if (n ==
|
1335
|
+
var n = lineNo(line);
|
1336
|
+
if (n == null) return null;
|
1158
1337
|
}
|
1159
1338
|
var marker = line.gutterMarker;
|
1160
|
-
return {line: n, text: line.text, markerText: marker && marker.text,
|
1339
|
+
return {line: n, handle: line, text: line.text, markerText: marker && marker.text,
|
1340
|
+
markerClass: marker && marker.style, lineClass: line.className};
|
1161
1341
|
}
|
1162
1342
|
|
1163
1343
|
function stringWidth(str) {
|
@@ -1167,21 +1347,16 @@ var CodeMirror = (function() {
|
|
1167
1347
|
}
|
1168
1348
|
// These are used to go from pixel positions to character
|
1169
1349
|
// positions, taking varying character widths into account.
|
1170
|
-
function charX(line, pos) {
|
1171
|
-
if (pos == 0) return 0;
|
1172
|
-
measure.innerHTML = "<pre><span>" + lines[line].getHTML(null, null, false, pos) + "</span></pre>";
|
1173
|
-
return measure.firstChild.firstChild.offsetWidth;
|
1174
|
-
}
|
1175
1350
|
function charFromX(line, x) {
|
1176
1351
|
if (x <= 0) return 0;
|
1177
|
-
var lineObj =
|
1352
|
+
var lineObj = getLine(line), text = lineObj.text;
|
1178
1353
|
function getX(len) {
|
1179
|
-
measure.innerHTML = "<pre><span>" + lineObj.getHTML(null, null, false, len) + "</span></pre>";
|
1354
|
+
measure.innerHTML = "<pre><span>" + lineObj.getHTML(null, null, false, tabText, len) + "</span></pre>";
|
1180
1355
|
return measure.firstChild.firstChild.offsetWidth;
|
1181
1356
|
}
|
1182
1357
|
var from = 0, fromX = 0, to = text.length, toX;
|
1183
1358
|
// Guess a suitable upper bound for our search.
|
1184
|
-
var estimated = Math.min(to, Math.ceil(x /
|
1359
|
+
var estimated = Math.min(to, Math.ceil(x / charWidth()));
|
1185
1360
|
for (;;) {
|
1186
1361
|
var estX = getX(estimated);
|
1187
1362
|
if (estX <= x && estimated < to) estimated = Math.min(to, Math.ceil(estimated * 1.2));
|
@@ -1200,20 +1375,100 @@ var CodeMirror = (function() {
|
|
1200
1375
|
}
|
1201
1376
|
}
|
1202
1377
|
|
1378
|
+
var tempId = Math.floor(Math.random() * 0xffffff).toString(16);
|
1379
|
+
function measureLine(line, ch) {
|
1380
|
+
var extra = "";
|
1381
|
+
// Include extra text at the end to make sure the measured line is wrapped in the right way.
|
1382
|
+
if (options.lineWrapping) {
|
1383
|
+
var end = line.text.indexOf(" ", ch + 2);
|
1384
|
+
extra = htmlEscape(line.text.slice(ch + 1, end < 0 ? line.text.length : end + (ie ? 5 : 0)));
|
1385
|
+
}
|
1386
|
+
measure.innerHTML = "<pre>" + line.getHTML(null, null, false, tabText, ch) +
|
1387
|
+
'<span id="CodeMirror-temp-' + tempId + '">' + htmlEscape(line.text.charAt(ch) || " ") + "</span>" +
|
1388
|
+
extra + "</pre>";
|
1389
|
+
var elt = document.getElementById("CodeMirror-temp-" + tempId);
|
1390
|
+
var top = elt.offsetTop, left = elt.offsetLeft;
|
1391
|
+
// Older IEs report zero offsets for spans directly after a wrap
|
1392
|
+
if (ie && ch && top == 0 && left == 0) {
|
1393
|
+
var backup = document.createElement("span");
|
1394
|
+
backup.innerHTML = "x";
|
1395
|
+
elt.parentNode.insertBefore(backup, elt.nextSibling);
|
1396
|
+
top = backup.offsetTop;
|
1397
|
+
}
|
1398
|
+
return {top: top, left: left};
|
1399
|
+
}
|
1203
1400
|
function localCoords(pos, inLineWrap) {
|
1204
|
-
var lh =
|
1205
|
-
|
1401
|
+
var x, lh = textHeight(), y = lh * (heightAtLine(doc, pos.line) - (inLineWrap ? displayOffset : 0));
|
1402
|
+
if (pos.ch == 0) x = 0;
|
1403
|
+
else {
|
1404
|
+
var sp = measureLine(getLine(pos.line), pos.ch);
|
1405
|
+
x = sp.left;
|
1406
|
+
if (options.lineWrapping) y += Math.max(0, sp.top);
|
1407
|
+
}
|
1408
|
+
return {x: x, y: y, yBot: y + lh};
|
1409
|
+
}
|
1410
|
+
// Coords must be lineSpace-local
|
1411
|
+
function coordsChar(x, y) {
|
1412
|
+
if (y < 0) y = 0;
|
1413
|
+
var th = textHeight(), cw = charWidth(), heightPos = displayOffset + Math.floor(y / th);
|
1414
|
+
var lineNo = lineAtHeight(doc, heightPos);
|
1415
|
+
if (lineNo >= doc.size) return {line: doc.size - 1, ch: getLine(doc.size - 1).text.length};
|
1416
|
+
var lineObj = getLine(lineNo), text = lineObj.text;
|
1417
|
+
var tw = options.lineWrapping, innerOff = tw ? heightPos - heightAtLine(doc, lineNo) : 0;
|
1418
|
+
if (x <= 0 && innerOff == 0) return {line: lineNo, ch: 0};
|
1419
|
+
function getX(len) {
|
1420
|
+
var sp = measureLine(lineObj, len);
|
1421
|
+
if (tw) {
|
1422
|
+
var off = Math.round(sp.top / th);
|
1423
|
+
return Math.max(0, sp.left + (off - innerOff) * scroller.clientWidth);
|
1424
|
+
}
|
1425
|
+
return sp.left;
|
1426
|
+
}
|
1427
|
+
var from = 0, fromX = 0, to = text.length, toX;
|
1428
|
+
// Guess a suitable upper bound for our search.
|
1429
|
+
var estimated = Math.min(to, Math.ceil((x + innerOff * scroller.clientWidth * .9) / cw));
|
1430
|
+
for (;;) {
|
1431
|
+
var estX = getX(estimated);
|
1432
|
+
if (estX <= x && estimated < to) estimated = Math.min(to, Math.ceil(estimated * 1.2));
|
1433
|
+
else {toX = estX; to = estimated; break;}
|
1434
|
+
}
|
1435
|
+
if (x > toX) return {line: lineNo, ch: to};
|
1436
|
+
// Try to guess a suitable lower bound as well.
|
1437
|
+
estimated = Math.floor(to * 0.8); estX = getX(estimated);
|
1438
|
+
if (estX < x) {from = estimated; fromX = estX;}
|
1439
|
+
// Do a binary search between these bounds.
|
1440
|
+
for (;;) {
|
1441
|
+
if (to - from <= 1) return {line: lineNo, ch: (toX - x > x - fromX) ? from : to};
|
1442
|
+
var middle = Math.ceil((from + to) / 2), middleX = getX(middle);
|
1443
|
+
if (middleX > x) {to = middle; toX = middleX;}
|
1444
|
+
else {from = middle; fromX = middleX;}
|
1445
|
+
}
|
1206
1446
|
}
|
1207
1447
|
function pageCoords(pos) {
|
1208
1448
|
var local = localCoords(pos, true), off = eltOffset(lineSpace);
|
1209
1449
|
return {x: off.left + local.x, y: off.top + local.y, yBot: off.top + local.yBot};
|
1210
1450
|
}
|
1211
1451
|
|
1212
|
-
|
1213
|
-
|
1214
|
-
if (
|
1215
|
-
|
1216
|
-
|
1452
|
+
var cachedHeight, cachedHeightFor, measureText;
|
1453
|
+
function textHeight() {
|
1454
|
+
if (measureText == null) {
|
1455
|
+
measureText = "<pre>";
|
1456
|
+
for (var i = 0; i < 49; ++i) measureText += "x<br/>";
|
1457
|
+
measureText += "x</pre>";
|
1458
|
+
}
|
1459
|
+
var offsetHeight = lineDiv.clientHeight;
|
1460
|
+
if (offsetHeight == cachedHeightFor) return cachedHeight;
|
1461
|
+
cachedHeightFor = offsetHeight;
|
1462
|
+
measure.innerHTML = measureText;
|
1463
|
+
cachedHeight = measure.firstChild.offsetHeight / 50 || 1;
|
1464
|
+
measure.innerHTML = "";
|
1465
|
+
return cachedHeight;
|
1466
|
+
}
|
1467
|
+
var cachedWidth, cachedWidthFor = 0;
|
1468
|
+
function charWidth() {
|
1469
|
+
if (scroller.clientWidth == cachedWidthFor) return cachedWidth;
|
1470
|
+
cachedWidthFor = scroller.clientWidth;
|
1471
|
+
return (cachedWidth = stringWidth("x"));
|
1217
1472
|
}
|
1218
1473
|
function paddingTop() {return lineSpace.offsetTop;}
|
1219
1474
|
function paddingLeft() {return lineSpace.offsetLeft;}
|
@@ -1228,8 +1483,7 @@ var CodeMirror = (function() {
|
|
1228
1483
|
if (!liberal && (x - offW.left > scroller.clientWidth || y - offW.top > scroller.clientHeight))
|
1229
1484
|
return null;
|
1230
1485
|
var offL = eltOffset(lineSpace, true);
|
1231
|
-
|
1232
|
-
return clipPos({line: line, ch: charFromX(clipLine(line), x - offL.left)});
|
1486
|
+
return coordsChar(x - offL.left, y - offL.top);
|
1233
1487
|
}
|
1234
1488
|
function onContextMenu(e) {
|
1235
1489
|
var pos = posFromMouse(e);
|
@@ -1245,17 +1499,17 @@ var CodeMirror = (function() {
|
|
1245
1499
|
leaveInputAlone = true;
|
1246
1500
|
var val = input.value = getSelection();
|
1247
1501
|
focusInput();
|
1248
|
-
|
1502
|
+
input.select();
|
1249
1503
|
function rehide() {
|
1250
1504
|
var newVal = splitLines(input.value).join("\n");
|
1251
1505
|
if (newVal != val) operation(replaceSelection)(newVal, "end");
|
1252
1506
|
inputDiv.style.position = "relative";
|
1253
1507
|
input.style.cssText = oldCSS;
|
1254
1508
|
leaveInputAlone = false;
|
1255
|
-
|
1509
|
+
resetInput(true);
|
1256
1510
|
slowPoll();
|
1257
1511
|
}
|
1258
|
-
|
1512
|
+
|
1259
1513
|
if (gecko) {
|
1260
1514
|
e_stop(e);
|
1261
1515
|
var mouseup = connect(window, "mouseup", function() {
|
@@ -1280,7 +1534,7 @@ var CodeMirror = (function() {
|
|
1280
1534
|
|
1281
1535
|
var matching = {"(": ")>", ")": "(<", "[": "]>", "]": "[<", "{": "}>", "}": "{<"};
|
1282
1536
|
function matchBrackets(autoclear) {
|
1283
|
-
var head = sel.inverted ? sel.from : sel.to, line =
|
1537
|
+
var head = sel.inverted ? sel.from : sel.to, line = getLine(head.line), pos = head.ch - 1;
|
1284
1538
|
var match = (pos >= 0 && matching[line.text.charAt(pos)]) || matching[line.text.charAt(++pos)];
|
1285
1539
|
if (!match) return;
|
1286
1540
|
var ch = match.charAt(0), forward = match.charAt(1) == ">", d = forward ? 1 : -1, st = line.styles;
|
@@ -1304,18 +1558,16 @@ var CodeMirror = (function() {
|
|
1304
1558
|
}
|
1305
1559
|
}
|
1306
1560
|
}
|
1307
|
-
for (var i = head.line, e = forward ? Math.min(i + 100,
|
1308
|
-
var line =
|
1561
|
+
for (var i = head.line, e = forward ? Math.min(i + 100, doc.size) : Math.max(-1, i - 100); i != e; i+=d) {
|
1562
|
+
var line = getLine(i), first = i == head.line;
|
1309
1563
|
var found = scan(line, first && forward ? pos + 1 : 0, first && !forward ? pos : line.text.length);
|
1310
1564
|
if (found) break;
|
1311
1565
|
}
|
1312
1566
|
if (!found) found = {pos: null, match: false};
|
1313
1567
|
var style = found.match ? "CodeMirror-matchingbracket" : "CodeMirror-nonmatchingbracket";
|
1314
1568
|
var one = markText({line: head.line, ch: pos}, {line: head.line, ch: pos+1}, style),
|
1315
|
-
two = found.pos != null
|
1316
|
-
|
1317
|
-
: function() {};
|
1318
|
-
var clear = operation(function(){one(); two();});
|
1569
|
+
two = found.pos != null && markText({line: i, ch: found.pos}, {line: i, ch: found.pos + 1}, style);
|
1570
|
+
var clear = operation(function(){one.clear(); two && two.clear();});
|
1319
1571
|
if (autoclear) setTimeout(clear, 800);
|
1320
1572
|
else bracketHighlighted = clear;
|
1321
1573
|
}
|
@@ -1329,9 +1581,9 @@ var CodeMirror = (function() {
|
|
1329
1581
|
var minindent, minline;
|
1330
1582
|
for (var search = n, lim = n - 40; search > lim; --search) {
|
1331
1583
|
if (search == 0) return 0;
|
1332
|
-
var line =
|
1584
|
+
var line = getLine(search-1);
|
1333
1585
|
if (line.stateAfter) return search;
|
1334
|
-
var indented = line.indentation();
|
1586
|
+
var indented = line.indentation(options.tabSize);
|
1335
1587
|
if (minline == null || minindent > indented) {
|
1336
1588
|
minline = search - 1;
|
1337
1589
|
minindent = indented;
|
@@ -1340,55 +1592,58 @@ var CodeMirror = (function() {
|
|
1340
1592
|
return minline;
|
1341
1593
|
}
|
1342
1594
|
function getStateBefore(n) {
|
1343
|
-
var start = findStartLine(n), state = start &&
|
1595
|
+
var start = findStartLine(n), state = start && getLine(start-1).stateAfter;
|
1344
1596
|
if (!state) state = startState(mode);
|
1345
1597
|
else state = copyState(mode, state);
|
1346
|
-
|
1347
|
-
|
1348
|
-
line.highlight(mode, state);
|
1598
|
+
doc.iter(start, n, function(line) {
|
1599
|
+
line.highlight(mode, state, options.tabSize);
|
1349
1600
|
line.stateAfter = copyState(mode, state);
|
1350
|
-
}
|
1351
|
-
if (
|
1601
|
+
});
|
1602
|
+
if (start < n) changes.push({from: start, to: n});
|
1603
|
+
if (n < doc.size && !getLine(n).stateAfter) work.push(n);
|
1352
1604
|
return state;
|
1353
1605
|
}
|
1354
1606
|
function highlightLines(start, end) {
|
1355
1607
|
var state = getStateBefore(start);
|
1356
|
-
|
1357
|
-
|
1358
|
-
line.highlight(mode, state);
|
1608
|
+
doc.iter(start, end, function(line) {
|
1609
|
+
line.highlight(mode, state, options.tabSize);
|
1359
1610
|
line.stateAfter = copyState(mode, state);
|
1360
|
-
}
|
1611
|
+
});
|
1361
1612
|
}
|
1362
1613
|
function highlightWorker() {
|
1363
1614
|
var end = +new Date + options.workTime;
|
1364
1615
|
var foundWork = work.length;
|
1365
1616
|
while (work.length) {
|
1366
|
-
if (!
|
1617
|
+
if (!getLine(showingFrom).stateAfter) var task = showingFrom;
|
1367
1618
|
else var task = work.pop();
|
1368
|
-
if (task >=
|
1369
|
-
var start = findStartLine(task), state = start &&
|
1619
|
+
if (task >= doc.size) continue;
|
1620
|
+
var start = findStartLine(task), state = start && getLine(start-1).stateAfter;
|
1370
1621
|
if (state) state = copyState(mode, state);
|
1371
1622
|
else state = startState(mode);
|
1372
1623
|
|
1373
|
-
var unchanged = 0, compare = mode.compareStates, realChange = false
|
1374
|
-
|
1375
|
-
|
1624
|
+
var unchanged = 0, compare = mode.compareStates, realChange = false,
|
1625
|
+
i = start, bail = false;
|
1626
|
+
doc.iter(i, doc.size, function(line) {
|
1627
|
+
var hadState = line.stateAfter;
|
1376
1628
|
if (+new Date > end) {
|
1377
1629
|
work.push(i);
|
1378
1630
|
startWorker(options.workDelay);
|
1379
1631
|
if (realChange) changes.push({from: task, to: i + 1});
|
1380
|
-
return;
|
1632
|
+
return (bail = true);
|
1381
1633
|
}
|
1382
|
-
var changed = line.highlight(mode, state);
|
1634
|
+
var changed = line.highlight(mode, state, options.tabSize);
|
1383
1635
|
if (changed) realChange = true;
|
1384
1636
|
line.stateAfter = copyState(mode, state);
|
1385
1637
|
if (compare) {
|
1386
|
-
if (hadState && compare(hadState, state))
|
1638
|
+
if (hadState && compare(hadState, state)) return true;
|
1387
1639
|
} else {
|
1388
1640
|
if (changed !== false || !hadState) unchanged = 0;
|
1389
|
-
else if (++unchanged > 3)
|
1641
|
+
else if (++unchanged > 3 && (!mode.indent || mode.indent(hadState, "") == mode.indent(state, "")))
|
1642
|
+
return true;
|
1390
1643
|
}
|
1391
|
-
|
1644
|
+
++i;
|
1645
|
+
});
|
1646
|
+
if (bail) return;
|
1392
1647
|
if (realChange) changes.push({from: task, to: i + 1});
|
1393
1648
|
}
|
1394
1649
|
if (foundWork && options.onHighlightComplete)
|
@@ -1404,12 +1659,13 @@ var CodeMirror = (function() {
|
|
1404
1659
|
// be awkward, slow, and error-prone), but instead updates are
|
1405
1660
|
// batched and then all combined and executed at once.
|
1406
1661
|
function startOperation() {
|
1407
|
-
updateInput =
|
1662
|
+
updateInput = userSelChange = textChanged = null;
|
1663
|
+
changes = []; selectionChanged = false; callbacks = [];
|
1408
1664
|
}
|
1409
1665
|
function endOperation() {
|
1410
|
-
var reScroll = false;
|
1666
|
+
var reScroll = false, updated;
|
1411
1667
|
if (selectionChanged) reScroll = !scrollCursorIntoView();
|
1412
|
-
if (changes.length) updateDisplay(changes);
|
1668
|
+
if (changes.length) updated = updateDisplay(changes, true);
|
1413
1669
|
else {
|
1414
1670
|
if (selectionChanged) updateCursor();
|
1415
1671
|
if (gutterDirty) updateGutter();
|
@@ -1417,22 +1673,22 @@ var CodeMirror = (function() {
|
|
1417
1673
|
if (reScroll) scrollCursorIntoView();
|
1418
1674
|
if (selectionChanged) {scrollEditorIntoView(); restartBlink();}
|
1419
1675
|
|
1420
|
-
// updateInput can be set to a boolean value to force/prevent an
|
1421
|
-
// update.
|
1422
1676
|
if (focused && !leaveInputAlone &&
|
1423
1677
|
(updateInput === true || (updateInput !== false && selectionChanged)))
|
1424
|
-
|
1678
|
+
resetInput(userSelChange);
|
1425
1679
|
|
1426
1680
|
if (selectionChanged && options.matchBrackets)
|
1427
1681
|
setTimeout(operation(function() {
|
1428
1682
|
if (bracketHighlighted) {bracketHighlighted(); bracketHighlighted = null;}
|
1429
|
-
matchBrackets(false);
|
1683
|
+
if (posEq(sel.from, sel.to)) matchBrackets(false);
|
1430
1684
|
}), 20);
|
1431
|
-
var tc = textChanged; //
|
1685
|
+
var tc = textChanged, cbs = callbacks; // these can be reset by callbacks
|
1432
1686
|
if (selectionChanged && options.onCursorActivity)
|
1433
1687
|
options.onCursorActivity(instance);
|
1434
1688
|
if (tc && options.onChange && instance)
|
1435
1689
|
options.onChange(instance, tc);
|
1690
|
+
for (var i = 0; i < cbs.length; ++i) cbs[i](instance);
|
1691
|
+
if (updated && options.onUpdate) options.onUpdate(instance);
|
1436
1692
|
}
|
1437
1693
|
var nestedOperation = 0;
|
1438
1694
|
function operation(f) {
|
@@ -1444,120 +1700,6 @@ var CodeMirror = (function() {
|
|
1444
1700
|
};
|
1445
1701
|
}
|
1446
1702
|
|
1447
|
-
function SearchCursor(query, pos, caseFold) {
|
1448
|
-
this.atOccurrence = false;
|
1449
|
-
if (caseFold == null) caseFold = typeof query == "string" && query == query.toLowerCase();
|
1450
|
-
|
1451
|
-
if (pos && typeof pos == "object") pos = clipPos(pos);
|
1452
|
-
else pos = {line: 0, ch: 0};
|
1453
|
-
this.pos = {from: pos, to: pos};
|
1454
|
-
|
1455
|
-
// The matches method is filled in based on the type of query.
|
1456
|
-
// It takes a position and a direction, and returns an object
|
1457
|
-
// describing the next occurrence of the query, or null if no
|
1458
|
-
// more matches were found.
|
1459
|
-
if (typeof query != "string") // Regexp match
|
1460
|
-
this.matches = function(reverse, pos) {
|
1461
|
-
if (reverse) {
|
1462
|
-
var line = lines[pos.line].text.slice(0, pos.ch), match = line.match(query), start = 0;
|
1463
|
-
while (match) {
|
1464
|
-
var ind = line.indexOf(match[0]);
|
1465
|
-
start += ind;
|
1466
|
-
line = line.slice(ind + 1);
|
1467
|
-
var newmatch = line.match(query);
|
1468
|
-
if (newmatch) match = newmatch;
|
1469
|
-
else break;
|
1470
|
-
start++;
|
1471
|
-
}
|
1472
|
-
}
|
1473
|
-
else {
|
1474
|
-
var line = lines[pos.line].text.slice(pos.ch), match = line.match(query),
|
1475
|
-
start = match && pos.ch + line.indexOf(match[0]);
|
1476
|
-
}
|
1477
|
-
if (match)
|
1478
|
-
return {from: {line: pos.line, ch: start},
|
1479
|
-
to: {line: pos.line, ch: start + match[0].length},
|
1480
|
-
match: match};
|
1481
|
-
};
|
1482
|
-
else { // String query
|
1483
|
-
if (caseFold) query = query.toLowerCase();
|
1484
|
-
var fold = caseFold ? function(str){return str.toLowerCase();} : function(str){return str;};
|
1485
|
-
var target = query.split("\n");
|
1486
|
-
// Different methods for single-line and multi-line queries
|
1487
|
-
if (target.length == 1)
|
1488
|
-
this.matches = function(reverse, pos) {
|
1489
|
-
var line = fold(lines[pos.line].text), len = query.length, match;
|
1490
|
-
if (reverse ? (pos.ch >= len && (match = line.lastIndexOf(query, pos.ch - len)) != -1)
|
1491
|
-
: (match = line.indexOf(query, pos.ch)) != -1)
|
1492
|
-
return {from: {line: pos.line, ch: match},
|
1493
|
-
to: {line: pos.line, ch: match + len}};
|
1494
|
-
};
|
1495
|
-
else
|
1496
|
-
this.matches = function(reverse, pos) {
|
1497
|
-
var ln = pos.line, idx = (reverse ? target.length - 1 : 0), match = target[idx], line = fold(lines[ln].text);
|
1498
|
-
var offsetA = (reverse ? line.indexOf(match) + match.length : line.lastIndexOf(match));
|
1499
|
-
if (reverse ? offsetA >= pos.ch || offsetA != match.length
|
1500
|
-
: offsetA <= pos.ch || offsetA != line.length - match.length)
|
1501
|
-
return;
|
1502
|
-
for (;;) {
|
1503
|
-
if (reverse ? !ln : ln == lines.length - 1) return;
|
1504
|
-
line = fold(lines[ln += reverse ? -1 : 1].text);
|
1505
|
-
match = target[reverse ? --idx : ++idx];
|
1506
|
-
if (idx > 0 && idx < target.length - 1) {
|
1507
|
-
if (line != match) return;
|
1508
|
-
else continue;
|
1509
|
-
}
|
1510
|
-
var offsetB = (reverse ? line.lastIndexOf(match) : line.indexOf(match) + match.length);
|
1511
|
-
if (reverse ? offsetB != line.length - match.length : offsetB != match.length)
|
1512
|
-
return;
|
1513
|
-
var start = {line: pos.line, ch: offsetA}, end = {line: ln, ch: offsetB};
|
1514
|
-
return {from: reverse ? end : start, to: reverse ? start : end};
|
1515
|
-
}
|
1516
|
-
};
|
1517
|
-
}
|
1518
|
-
}
|
1519
|
-
|
1520
|
-
SearchCursor.prototype = {
|
1521
|
-
findNext: function() {return this.find(false);},
|
1522
|
-
findPrevious: function() {return this.find(true);},
|
1523
|
-
|
1524
|
-
find: function(reverse) {
|
1525
|
-
var self = this, pos = clipPos(reverse ? this.pos.from : this.pos.to);
|
1526
|
-
function savePosAndFail(line) {
|
1527
|
-
var pos = {line: line, ch: 0};
|
1528
|
-
self.pos = {from: pos, to: pos};
|
1529
|
-
self.atOccurrence = false;
|
1530
|
-
return false;
|
1531
|
-
}
|
1532
|
-
|
1533
|
-
for (;;) {
|
1534
|
-
if (this.pos = this.matches(reverse, pos)) {
|
1535
|
-
this.atOccurrence = true;
|
1536
|
-
return this.pos.match || true;
|
1537
|
-
}
|
1538
|
-
if (reverse) {
|
1539
|
-
if (!pos.line) return savePosAndFail(0);
|
1540
|
-
pos = {line: pos.line-1, ch: lines[pos.line-1].text.length};
|
1541
|
-
}
|
1542
|
-
else {
|
1543
|
-
if (pos.line == lines.length - 1) return savePosAndFail(lines.length);
|
1544
|
-
pos = {line: pos.line+1, ch: 0};
|
1545
|
-
}
|
1546
|
-
}
|
1547
|
-
},
|
1548
|
-
|
1549
|
-
from: function() {if (this.atOccurrence) return copyPos(this.pos.from);},
|
1550
|
-
to: function() {if (this.atOccurrence) return copyPos(this.pos.to);},
|
1551
|
-
|
1552
|
-
replace: function(newText) {
|
1553
|
-
var self = this;
|
1554
|
-
if (this.atOccurrence)
|
1555
|
-
operation(function() {
|
1556
|
-
self.pos.to = replaceRange(newText, self.pos.from, self.pos.to);
|
1557
|
-
})();
|
1558
|
-
}
|
1559
|
-
};
|
1560
|
-
|
1561
1703
|
for (var ext in extensions)
|
1562
1704
|
if (extensions.propertyIsEnumerable(ext) &&
|
1563
1705
|
!instance.propertyIsEnumerable(ext))
|
@@ -1572,29 +1714,35 @@ var CodeMirror = (function() {
|
|
1572
1714
|
theme: "default",
|
1573
1715
|
indentUnit: 2,
|
1574
1716
|
indentWithTabs: false,
|
1575
|
-
|
1576
|
-
|
1717
|
+
tabSize: 4,
|
1718
|
+
keyMap: "default",
|
1719
|
+
extraKeys: null,
|
1577
1720
|
electricChars: true,
|
1578
1721
|
onKeyEvent: null,
|
1722
|
+
lineWrapping: false,
|
1579
1723
|
lineNumbers: false,
|
1580
1724
|
gutter: false,
|
1581
1725
|
fixedGutter: false,
|
1582
1726
|
firstLineNumber: 1,
|
1583
1727
|
readOnly: false,
|
1584
|
-
smartHome: true,
|
1585
1728
|
onChange: null,
|
1586
1729
|
onCursorActivity: null,
|
1587
1730
|
onGutterClick: null,
|
1588
1731
|
onHighlightComplete: null,
|
1732
|
+
onUpdate: null,
|
1589
1733
|
onFocus: null, onBlur: null, onScroll: null,
|
1590
1734
|
matchBrackets: false,
|
1591
1735
|
workTime: 100,
|
1592
1736
|
workDelay: 200,
|
1737
|
+
pollInterval: 100,
|
1593
1738
|
undoDepth: 40,
|
1594
1739
|
tabindex: null,
|
1595
1740
|
document: window.document
|
1596
1741
|
};
|
1597
1742
|
|
1743
|
+
var mac = /Mac/.test(navigator.platform);
|
1744
|
+
var win = /Win/.test(navigator.platform);
|
1745
|
+
|
1598
1746
|
// Known modes, by name and by MIME
|
1599
1747
|
var modes = {}, mimeModes = {};
|
1600
1748
|
CodeMirror.defineMode = function(name, mode) {
|
@@ -1631,11 +1779,114 @@ var CodeMirror = (function() {
|
|
1631
1779
|
return list;
|
1632
1780
|
};
|
1633
1781
|
|
1634
|
-
var extensions = {};
|
1782
|
+
var extensions = CodeMirror.extensions = {};
|
1635
1783
|
CodeMirror.defineExtension = function(name, func) {
|
1636
1784
|
extensions[name] = func;
|
1637
1785
|
};
|
1638
1786
|
|
1787
|
+
var commands = CodeMirror.commands = {
|
1788
|
+
selectAll: function(cm) {cm.setSelection({line: 0, ch: 0}, {line: cm.lineCount() - 1});},
|
1789
|
+
killLine: function(cm) {
|
1790
|
+
var from = cm.getCursor(true), to = cm.getCursor(false), sel = !posEq(from, to);
|
1791
|
+
if (!sel && cm.getLine(from.line).length == from.ch) cm.replaceRange("", from, {line: from.line + 1, ch: 0});
|
1792
|
+
else cm.replaceRange("", from, sel ? to : {line: from.line});
|
1793
|
+
},
|
1794
|
+
deleteLine: function(cm) {var l = cm.getCursor().line; cm.replaceRange("", {line: l, ch: 0}, {line: l});},
|
1795
|
+
undo: function(cm) {cm.undo();},
|
1796
|
+
redo: function(cm) {cm.redo();},
|
1797
|
+
goDocStart: function(cm) {cm.setCursor(0, 0, true);},
|
1798
|
+
goDocEnd: function(cm) {cm.setSelection({line: cm.lineCount() - 1}, null, true);},
|
1799
|
+
goLineStart: function(cm) {cm.setCursor(cm.getCursor().line, 0, true);},
|
1800
|
+
goLineStartSmart: function(cm) {
|
1801
|
+
var cur = cm.getCursor();
|
1802
|
+
var text = cm.getLine(cur.line), firstNonWS = Math.max(0, text.search(/\S/));
|
1803
|
+
cm.setCursor(cur.line, cur.ch <= firstNonWS && cur.ch ? 0 : firstNonWS, true);
|
1804
|
+
},
|
1805
|
+
goLineEnd: function(cm) {cm.setSelection({line: cm.getCursor().line}, null, true);},
|
1806
|
+
goLineUp: function(cm) {cm.moveV(-1, "line");},
|
1807
|
+
goLineDown: function(cm) {cm.moveV(1, "line");},
|
1808
|
+
goPageUp: function(cm) {cm.moveV(-1, "page");},
|
1809
|
+
goPageDown: function(cm) {cm.moveV(1, "page");},
|
1810
|
+
goCharLeft: function(cm) {cm.moveH(-1, "char");},
|
1811
|
+
goCharRight: function(cm) {cm.moveH(1, "char");},
|
1812
|
+
goColumnLeft: function(cm) {cm.moveH(-1, "column");},
|
1813
|
+
goColumnRight: function(cm) {cm.moveH(1, "column");},
|
1814
|
+
goWordLeft: function(cm) {cm.moveH(-1, "word");},
|
1815
|
+
goWordRight: function(cm) {cm.moveH(1, "word");},
|
1816
|
+
delCharLeft: function(cm) {cm.deleteH(-1, "char");},
|
1817
|
+
delCharRight: function(cm) {cm.deleteH(1, "char");},
|
1818
|
+
delWordLeft: function(cm) {cm.deleteH(-1, "word");},
|
1819
|
+
delWordRight: function(cm) {cm.deleteH(1, "word");},
|
1820
|
+
indentAuto: function(cm) {cm.indentSelection("smart");},
|
1821
|
+
indentMore: function(cm) {cm.indentSelection("add");},
|
1822
|
+
indentLess: function(cm) {cm.indentSelection("subtract");},
|
1823
|
+
insertTab: function(cm) {cm.replaceSelection("\t", "end");},
|
1824
|
+
transposeChars: function(cm) {
|
1825
|
+
var cur = cm.getCursor(), line = cm.getLine(cur.line);
|
1826
|
+
if (cur.ch > 0 && cur.ch < line.length - 1)
|
1827
|
+
cm.replaceRange(line.charAt(cur.ch) + line.charAt(cur.ch - 1),
|
1828
|
+
{line: cur.line, ch: cur.ch - 1}, {line: cur.line, ch: cur.ch + 1});
|
1829
|
+
},
|
1830
|
+
newlineAndIndent: function(cm) {
|
1831
|
+
cm.replaceSelection("\n", "end");
|
1832
|
+
cm.indentLine(cm.getCursor().line);
|
1833
|
+
},
|
1834
|
+
toggleOverwrite: function(cm) {cm.toggleOverwrite();}
|
1835
|
+
};
|
1836
|
+
|
1837
|
+
var keyMap = CodeMirror.keyMap = {};
|
1838
|
+
keyMap.basic = {
|
1839
|
+
"Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown",
|
1840
|
+
"End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown",
|
1841
|
+
"Delete": "delCharRight", "Backspace": "delCharLeft", "Tab": "indentMore", "Shift-Tab": "indentLess",
|
1842
|
+
"Enter": "newlineAndIndent", "Insert": "toggleOverwrite"
|
1843
|
+
};
|
1844
|
+
// Note that the save and find-related commands aren't defined by
|
1845
|
+
// default. Unknown commands are simply ignored.
|
1846
|
+
keyMap.pcDefault = {
|
1847
|
+
"Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo",
|
1848
|
+
"Ctrl-Home": "goDocStart", "Alt-Up": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Down": "goDocEnd",
|
1849
|
+
"Ctrl-Left": "goWordLeft", "Ctrl-Right": "goWordRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd",
|
1850
|
+
"Ctrl-Backspace": "delWordLeft", "Ctrl-Delete": "delWordRight", "Ctrl-S": "save", "Ctrl-F": "find",
|
1851
|
+
"Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll",
|
1852
|
+
fallthrough: "basic"
|
1853
|
+
};
|
1854
|
+
keyMap.macDefault = {
|
1855
|
+
"Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo",
|
1856
|
+
"Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goWordLeft",
|
1857
|
+
"Alt-Right": "goWordRight", "Cmd-Left": "goLineStart", "Cmd-Right": "goLineEnd", "Alt-Backspace": "delWordLeft",
|
1858
|
+
"Ctrl-Alt-Backspace": "delWordRight", "Alt-Delete": "delWordRight", "Cmd-S": "save", "Cmd-F": "find",
|
1859
|
+
"Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll",
|
1860
|
+
fallthrough: ["basic", "emacsy"]
|
1861
|
+
};
|
1862
|
+
keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault;
|
1863
|
+
keyMap.emacsy = {
|
1864
|
+
"Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown",
|
1865
|
+
"Alt-F": "goWordRight", "Alt-B": "goWordLeft", "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd",
|
1866
|
+
"Ctrl-V": "goPageUp", "Shift-Ctrl-V": "goPageDown", "Ctrl-D": "delCharRight", "Ctrl-H": "delCharLeft",
|
1867
|
+
"Alt-D": "delWordRight", "Alt-Backspace": "delWordLeft", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars"
|
1868
|
+
};
|
1869
|
+
|
1870
|
+
function lookupKey(name, extraMap, map) {
|
1871
|
+
function lookup(name, map, ft) {
|
1872
|
+
var found = map[name];
|
1873
|
+
if (found != null) return found;
|
1874
|
+
if (ft == null) ft = map.fallthrough;
|
1875
|
+
if (ft == null) return map.catchall;
|
1876
|
+
if (typeof ft == "string") return lookup(name, keyMap[ft]);
|
1877
|
+
for (var i = 0, e = ft.length; i < e; ++i) {
|
1878
|
+
found = lookup(name, keyMap[ft[i]]);
|
1879
|
+
if (found != null) return found;
|
1880
|
+
}
|
1881
|
+
return null;
|
1882
|
+
}
|
1883
|
+
return extraMap ? lookup(name, extraMap, map) : lookup(name, keyMap[map]);
|
1884
|
+
}
|
1885
|
+
function isModifierKey(event) {
|
1886
|
+
var name = keyNames[event.keyCode];
|
1887
|
+
return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod";
|
1888
|
+
}
|
1889
|
+
|
1639
1890
|
CodeMirror.fromTextArea = function(textarea, options) {
|
1640
1891
|
if (!options) options = {};
|
1641
1892
|
options.value = textarea.value;
|
@@ -1663,6 +1914,7 @@ var CodeMirror = (function() {
|
|
1663
1914
|
textarea.parentNode.insertBefore(node, textarea.nextSibling);
|
1664
1915
|
}, options);
|
1665
1916
|
instance.save = save;
|
1917
|
+
instance.getTextArea = function() { return textarea; };
|
1666
1918
|
instance.toTextArea = function() {
|
1667
1919
|
save();
|
1668
1920
|
textarea.parentNode.removeChild(instance.getWrapperElement());
|
@@ -1689,16 +1941,17 @@ var CodeMirror = (function() {
|
|
1689
1941
|
}
|
1690
1942
|
return nstate;
|
1691
1943
|
}
|
1692
|
-
CodeMirror.
|
1944
|
+
CodeMirror.copyState = copyState;
|
1693
1945
|
function startState(mode, a1, a2) {
|
1694
1946
|
return mode.startState ? mode.startState(a1, a2) : true;
|
1695
1947
|
}
|
1696
|
-
CodeMirror.
|
1948
|
+
CodeMirror.startState = startState;
|
1697
1949
|
|
1698
1950
|
// The character stream used by a mode's parser.
|
1699
|
-
function StringStream(string) {
|
1951
|
+
function StringStream(string, tabSize) {
|
1700
1952
|
this.pos = this.start = 0;
|
1701
1953
|
this.string = string;
|
1954
|
+
this.tabSize = tabSize || 8;
|
1702
1955
|
}
|
1703
1956
|
StringStream.prototype = {
|
1704
1957
|
eol: function() {return this.pos >= this.string.length;},
|
@@ -1730,8 +1983,8 @@ var CodeMirror = (function() {
|
|
1730
1983
|
if (found > -1) {this.pos = found; return true;}
|
1731
1984
|
},
|
1732
1985
|
backUp: function(n) {this.pos -= n;},
|
1733
|
-
column: function() {return countColumn(this.string, this.start);},
|
1734
|
-
indentation: function() {return countColumn(this.string);},
|
1986
|
+
column: function() {return countColumn(this.string, this.start, this.tabSize);},
|
1987
|
+
indentation: function() {return countColumn(this.string, null, this.tabSize);},
|
1735
1988
|
match: function(pattern, consume, caseInsensitive) {
|
1736
1989
|
if (typeof pattern == "string") {
|
1737
1990
|
function cased(str) {return caseInsensitive ? str.toLowerCase() : str;}
|
@@ -1750,18 +2003,95 @@ var CodeMirror = (function() {
|
|
1750
2003
|
};
|
1751
2004
|
CodeMirror.StringStream = StringStream;
|
1752
2005
|
|
2006
|
+
function MarkedText(from, to, className, set) {
|
2007
|
+
this.from = from; this.to = to; this.style = className; this.set = set;
|
2008
|
+
}
|
2009
|
+
MarkedText.prototype = {
|
2010
|
+
attach: function(line) { this.set.push(line); },
|
2011
|
+
detach: function(line) {
|
2012
|
+
var ix = indexOf(this.set, line);
|
2013
|
+
if (ix > -1) this.set.splice(ix, 1);
|
2014
|
+
},
|
2015
|
+
split: function(pos, lenBefore) {
|
2016
|
+
if (this.to <= pos && this.to != null) return null;
|
2017
|
+
var from = this.from < pos || this.from == null ? null : this.from - pos + lenBefore;
|
2018
|
+
var to = this.to == null ? null : this.to - pos + lenBefore;
|
2019
|
+
return new MarkedText(from, to, this.style, this.set);
|
2020
|
+
},
|
2021
|
+
dup: function() { return new MarkedText(null, null, this.style, this.set); },
|
2022
|
+
clipTo: function(fromOpen, from, toOpen, to, diff) {
|
2023
|
+
if (this.from != null && this.from >= from)
|
2024
|
+
this.from = Math.max(to, this.from) + diff;
|
2025
|
+
if (this.to != null && this.to > from)
|
2026
|
+
this.to = to < this.to ? this.to + diff : from;
|
2027
|
+
if (fromOpen && to > this.from && (to < this.to || this.to == null))
|
2028
|
+
this.from = null;
|
2029
|
+
if (toOpen && (from < this.to || this.to == null) && (from > this.from || this.from == null))
|
2030
|
+
this.to = null;
|
2031
|
+
},
|
2032
|
+
isDead: function() { return this.from != null && this.to != null && this.from >= this.to; },
|
2033
|
+
sameSet: function(x) { return this.set == x.set; }
|
2034
|
+
};
|
2035
|
+
|
2036
|
+
function Bookmark(pos) {
|
2037
|
+
this.from = pos; this.to = pos; this.line = null;
|
2038
|
+
}
|
2039
|
+
Bookmark.prototype = {
|
2040
|
+
attach: function(line) { this.line = line; },
|
2041
|
+
detach: function(line) { if (this.line == line) this.line = null; },
|
2042
|
+
split: function(pos, lenBefore) {
|
2043
|
+
if (pos < this.from) {
|
2044
|
+
this.from = this.to = (this.from - pos) + lenBefore;
|
2045
|
+
return this;
|
2046
|
+
}
|
2047
|
+
},
|
2048
|
+
isDead: function() { return this.from > this.to; },
|
2049
|
+
clipTo: function(fromOpen, from, toOpen, to, diff) {
|
2050
|
+
if ((fromOpen || from < this.from) && (toOpen || to > this.to)) {
|
2051
|
+
this.from = 0; this.to = -1;
|
2052
|
+
} else if (this.from > from) {
|
2053
|
+
this.from = this.to = Math.max(to, this.from) + diff;
|
2054
|
+
}
|
2055
|
+
},
|
2056
|
+
sameSet: function(x) { return false; },
|
2057
|
+
find: function() {
|
2058
|
+
if (!this.line || !this.line.parent) return null;
|
2059
|
+
return {line: lineNo(this.line), ch: this.from};
|
2060
|
+
},
|
2061
|
+
clear: function() {
|
2062
|
+
if (this.line) {
|
2063
|
+
var found = indexOf(this.line.marked, this);
|
2064
|
+
if (found != -1) this.line.marked.splice(found, 1);
|
2065
|
+
this.line = null;
|
2066
|
+
}
|
2067
|
+
}
|
2068
|
+
};
|
2069
|
+
|
1753
2070
|
// Line objects. These hold state related to a line, including
|
1754
2071
|
// highlighting info (the styles array).
|
1755
2072
|
function Line(text, styles) {
|
1756
2073
|
this.styles = styles || [text, null];
|
1757
|
-
this.stateAfter = null;
|
1758
2074
|
this.text = text;
|
1759
|
-
this.
|
2075
|
+
this.height = 1;
|
2076
|
+
this.marked = this.gutterMarker = this.className = this.handlers = null;
|
2077
|
+
this.stateAfter = this.parent = this.hidden = null;
|
2078
|
+
}
|
2079
|
+
Line.inheritMarks = function(text, orig) {
|
2080
|
+
var ln = new Line(text), mk = orig && orig.marked;
|
2081
|
+
if (mk) {
|
2082
|
+
for (var i = 0; i < mk.length; ++i) {
|
2083
|
+
if (mk[i].to == null && mk[i].style) {
|
2084
|
+
var newmk = ln.marked || (ln.marked = []), mark = mk[i];
|
2085
|
+
var nmark = mark.dup(); newmk.push(nmark); nmark.attach(ln);
|
2086
|
+
}
|
2087
|
+
}
|
2088
|
+
}
|
2089
|
+
return ln;
|
1760
2090
|
}
|
1761
2091
|
Line.prototype = {
|
1762
2092
|
// Replace a piece of a line, keeping the styles around it intact.
|
1763
|
-
replace: function(from,
|
1764
|
-
var st = [], mk = this.marked;
|
2093
|
+
replace: function(from, to_, text) {
|
2094
|
+
var st = [], mk = this.marked, to = to_ == null ? this.text.length : to_;
|
1765
2095
|
copyStyles(0, from, this.styles, st);
|
1766
2096
|
if (text) st.push(text, null);
|
1767
2097
|
copyStyles(to, this.text.length, this.styles, st);
|
@@ -1769,40 +2099,91 @@ var CodeMirror = (function() {
|
|
1769
2099
|
this.text = this.text.slice(0, from) + text + this.text.slice(to);
|
1770
2100
|
this.stateAfter = null;
|
1771
2101
|
if (mk) {
|
1772
|
-
var diff = text.length - (to - from)
|
1773
|
-
|
1774
|
-
|
1775
|
-
|
1776
|
-
if (mark.from >= end) del = true;
|
1777
|
-
else {mark.from = fix(mark.from); if (mark.to != null) mark.to = fix(mark.to);}
|
1778
|
-
if (del || mark.from >= mark.to) {mk.splice(i, 1); i--;}
|
2102
|
+
var diff = text.length - (to - from);
|
2103
|
+
for (var i = 0, mark = mk[i]; i < mk.length; ++i) {
|
2104
|
+
mark.clipTo(from == null, from || 0, to_ == null, to, diff);
|
2105
|
+
if (mark.isDead()) {mark.detach(this); mk.splice(i--, 1);}
|
1779
2106
|
}
|
1780
2107
|
}
|
1781
2108
|
},
|
1782
|
-
// Split a
|
2109
|
+
// Split a part off a line, keeping styles and markers intact.
|
1783
2110
|
split: function(pos, textBefore) {
|
1784
|
-
var st = [textBefore, null];
|
2111
|
+
var st = [textBefore, null], mk = this.marked;
|
1785
2112
|
copyStyles(pos, this.text.length, this.styles, st);
|
1786
|
-
|
2113
|
+
var taken = new Line(textBefore + this.text.slice(pos), st);
|
2114
|
+
if (mk) {
|
2115
|
+
for (var i = 0; i < mk.length; ++i) {
|
2116
|
+
var mark = mk[i];
|
2117
|
+
var newmark = mark.split(pos, textBefore.length);
|
2118
|
+
if (newmark) {
|
2119
|
+
if (!taken.marked) taken.marked = [];
|
2120
|
+
taken.marked.push(newmark); newmark.attach(taken);
|
2121
|
+
}
|
2122
|
+
}
|
2123
|
+
}
|
2124
|
+
return taken;
|
1787
2125
|
},
|
1788
|
-
|
1789
|
-
var
|
1790
|
-
|
1791
|
-
this.
|
1792
|
-
|
1793
|
-
|
2126
|
+
append: function(line) {
|
2127
|
+
var mylen = this.text.length, mk = line.marked, mymk = this.marked;
|
2128
|
+
this.text += line.text;
|
2129
|
+
copyStyles(0, line.text.length, line.styles, this.styles);
|
2130
|
+
if (mymk) {
|
2131
|
+
for (var i = 0; i < mymk.length; ++i)
|
2132
|
+
if (mymk[i].to == null) mymk[i].to = mylen;
|
2133
|
+
}
|
2134
|
+
if (mk && mk.length) {
|
2135
|
+
if (!mymk) this.marked = mymk = [];
|
2136
|
+
outer: for (var i = 0; i < mk.length; ++i) {
|
2137
|
+
var mark = mk[i];
|
2138
|
+
if (!mark.from) {
|
2139
|
+
for (var j = 0; j < mymk.length; ++j) {
|
2140
|
+
var mymark = mymk[j];
|
2141
|
+
if (mymark.to == mylen && mymark.sameSet(mark)) {
|
2142
|
+
mymark.to = mark.to == null ? null : mark.to + mylen;
|
2143
|
+
if (mymark.isDead()) {
|
2144
|
+
mymark.detach(this);
|
2145
|
+
mk.splice(i--, 1);
|
2146
|
+
}
|
2147
|
+
continue outer;
|
2148
|
+
}
|
2149
|
+
}
|
2150
|
+
}
|
2151
|
+
mymk.push(mark);
|
2152
|
+
mark.attach(this);
|
2153
|
+
mark.from += mylen;
|
2154
|
+
if (mark.to != null) mark.to += mylen;
|
2155
|
+
}
|
2156
|
+
}
|
2157
|
+
},
|
2158
|
+
fixMarkEnds: function(other) {
|
2159
|
+
var mk = this.marked, omk = other.marked;
|
2160
|
+
if (!mk) return;
|
2161
|
+
for (var i = 0; i < mk.length; ++i) {
|
2162
|
+
var mark = mk[i], close = mark.to == null;
|
2163
|
+
if (close && omk) {
|
2164
|
+
for (var j = 0; j < omk.length; ++j)
|
2165
|
+
if (omk[j].sameSet(mark)) {close = false; break;}
|
2166
|
+
}
|
2167
|
+
if (close) mark.to = this.text.length;
|
2168
|
+
}
|
1794
2169
|
},
|
1795
|
-
|
2170
|
+
fixMarkStarts: function() {
|
1796
2171
|
var mk = this.marked;
|
1797
2172
|
if (!mk) return;
|
1798
2173
|
for (var i = 0; i < mk.length; ++i)
|
1799
|
-
if (mk[i] ==
|
2174
|
+
if (mk[i].from == null) mk[i].from = 0;
|
2175
|
+
},
|
2176
|
+
addMark: function(mark) {
|
2177
|
+
mark.attach(this);
|
2178
|
+
if (this.marked == null) this.marked = [];
|
2179
|
+
this.marked.push(mark);
|
2180
|
+
this.marked.sort(function(a, b){return (a.from || 0) - (b.from || 0);});
|
1800
2181
|
},
|
1801
2182
|
// Run the given mode's parser over a line, update the styles
|
1802
2183
|
// array, which contains alternating fragments of text and CSS
|
1803
2184
|
// classes.
|
1804
|
-
highlight: function(mode, state) {
|
1805
|
-
var stream = new StringStream(this.text), st = this.styles, pos = 0;
|
2185
|
+
highlight: function(mode, state, tabSize) {
|
2186
|
+
var stream = new StringStream(this.text, tabSize), st = this.styles, pos = 0;
|
1806
2187
|
var changed = false, curWord = st[0], prevWord;
|
1807
2188
|
if (this.text == "" && mode.blankLine) mode.blankLine(state);
|
1808
2189
|
while (!stream.eol()) {
|
@@ -1843,17 +2224,20 @@ var CodeMirror = (function() {
|
|
1843
2224
|
className: style || null,
|
1844
2225
|
state: state};
|
1845
2226
|
},
|
1846
|
-
indentation: function() {return countColumn(this.text);},
|
2227
|
+
indentation: function(tabSize) {return countColumn(this.text, null, tabSize);},
|
1847
2228
|
// Produces an HTML fragment for the line, taking selection,
|
1848
2229
|
// marking, and highlighting into account.
|
1849
|
-
getHTML: function(sfrom, sto, includePre, endAt) {
|
1850
|
-
var html = [];
|
2230
|
+
getHTML: function(sfrom, sto, includePre, tabText, endAt) {
|
2231
|
+
var html = [], first = true;
|
1851
2232
|
if (includePre)
|
1852
2233
|
html.push(this.className ? '<pre class="' + this.className + '">': "<pre>");
|
1853
2234
|
function span(text, style) {
|
1854
2235
|
if (!text) return;
|
1855
|
-
|
1856
|
-
|
2236
|
+
// Work around a bug where, in some compat modes, IE ignores leading spaces
|
2237
|
+
if (first && ie && text.charAt(0) == " ") text = "\u00a0" + text.slice(1);
|
2238
|
+
first = false;
|
2239
|
+
if (style) html.push('<span class="', style, '">', htmlEscape(text).replace(/\t/g, tabText), "</span>");
|
2240
|
+
else html.push(htmlEscape(text).replace(/\t/g, tabText));
|
1857
2241
|
}
|
1858
2242
|
var st = this.styles, allText = this.text, marked = this.marked;
|
1859
2243
|
if (sfrom == sto) sfrom = null;
|
@@ -1864,10 +2248,10 @@ var CodeMirror = (function() {
|
|
1864
2248
|
span(" ", sfrom != null && sto == null ? "CodeMirror-selected" : null);
|
1865
2249
|
else if (!marked && sfrom == null)
|
1866
2250
|
for (var i = 0, ch = 0; ch < len; i+=2) {
|
1867
|
-
var str = st[i], l = str.length;
|
2251
|
+
var str = st[i], style = st[i+1], l = str.length;
|
1868
2252
|
if (ch + l > len) str = str.slice(0, len - ch);
|
1869
2253
|
ch += l;
|
1870
|
-
span(str, "cm-" +
|
2254
|
+
span(str, style && "cm-" + style);
|
1871
2255
|
}
|
1872
2256
|
else {
|
1873
2257
|
var pos = 0, i = 0, text = "", style, sg = 0;
|
@@ -1911,6 +2295,11 @@ var CodeMirror = (function() {
|
|
1911
2295
|
}
|
1912
2296
|
if (includePre) html.push("</pre>");
|
1913
2297
|
return html.join("");
|
2298
|
+
},
|
2299
|
+
cleanUp: function() {
|
2300
|
+
this.parent = null;
|
2301
|
+
if (this.marked)
|
2302
|
+
for (var i = 0, e = this.marked.length; i < e; ++i) this.marked[i].detach(this);
|
1914
2303
|
}
|
1915
2304
|
};
|
1916
2305
|
// Utility used by replace and split above
|
@@ -1929,6 +2318,193 @@ var CodeMirror = (function() {
|
|
1929
2318
|
}
|
1930
2319
|
}
|
1931
2320
|
|
2321
|
+
// Data structure that holds the sequence of lines.
|
2322
|
+
function LeafChunk(lines) {
|
2323
|
+
this.lines = lines;
|
2324
|
+
this.parent = null;
|
2325
|
+
for (var i = 0, e = lines.length, height = 0; i < e; ++i) {
|
2326
|
+
lines[i].parent = this;
|
2327
|
+
height += lines[i].height;
|
2328
|
+
}
|
2329
|
+
this.height = height;
|
2330
|
+
}
|
2331
|
+
LeafChunk.prototype = {
|
2332
|
+
chunkSize: function() { return this.lines.length; },
|
2333
|
+
remove: function(at, n, callbacks) {
|
2334
|
+
for (var i = at, e = at + n; i < e; ++i) {
|
2335
|
+
var line = this.lines[i];
|
2336
|
+
this.height -= line.height;
|
2337
|
+
line.cleanUp();
|
2338
|
+
if (line.handlers)
|
2339
|
+
for (var j = 0; j < line.handlers.length; ++j) callbacks.push(line.handlers[j]);
|
2340
|
+
}
|
2341
|
+
this.lines.splice(at, n);
|
2342
|
+
},
|
2343
|
+
collapse: function(lines) {
|
2344
|
+
lines.splice.apply(lines, [lines.length, 0].concat(this.lines));
|
2345
|
+
},
|
2346
|
+
insertHeight: function(at, lines, height) {
|
2347
|
+
this.height += height;
|
2348
|
+
this.lines.splice.apply(this.lines, [at, 0].concat(lines));
|
2349
|
+
for (var i = 0, e = lines.length; i < e; ++i) lines[i].parent = this;
|
2350
|
+
},
|
2351
|
+
iterN: function(at, n, op) {
|
2352
|
+
for (var e = at + n; at < e; ++at)
|
2353
|
+
if (op(this.lines[at])) return true;
|
2354
|
+
}
|
2355
|
+
};
|
2356
|
+
function BranchChunk(children) {
|
2357
|
+
this.children = children;
|
2358
|
+
var size = 0, height = 0;
|
2359
|
+
for (var i = 0, e = children.length; i < e; ++i) {
|
2360
|
+
var ch = children[i];
|
2361
|
+
size += ch.chunkSize(); height += ch.height;
|
2362
|
+
ch.parent = this;
|
2363
|
+
}
|
2364
|
+
this.size = size;
|
2365
|
+
this.height = height;
|
2366
|
+
this.parent = null;
|
2367
|
+
}
|
2368
|
+
BranchChunk.prototype = {
|
2369
|
+
chunkSize: function() { return this.size; },
|
2370
|
+
remove: function(at, n, callbacks) {
|
2371
|
+
this.size -= n;
|
2372
|
+
for (var i = 0; i < this.children.length; ++i) {
|
2373
|
+
var child = this.children[i], sz = child.chunkSize();
|
2374
|
+
if (at < sz) {
|
2375
|
+
var rm = Math.min(n, sz - at), oldHeight = child.height;
|
2376
|
+
child.remove(at, rm, callbacks);
|
2377
|
+
this.height -= oldHeight - child.height;
|
2378
|
+
if (sz == rm) { this.children.splice(i--, 1); child.parent = null; }
|
2379
|
+
if ((n -= rm) == 0) break;
|
2380
|
+
at = 0;
|
2381
|
+
} else at -= sz;
|
2382
|
+
}
|
2383
|
+
if (this.size - n < 25) {
|
2384
|
+
var lines = [];
|
2385
|
+
this.collapse(lines);
|
2386
|
+
this.children = [new LeafChunk(lines)];
|
2387
|
+
}
|
2388
|
+
},
|
2389
|
+
collapse: function(lines) {
|
2390
|
+
for (var i = 0, e = this.children.length; i < e; ++i) this.children[i].collapse(lines);
|
2391
|
+
},
|
2392
|
+
insert: function(at, lines) {
|
2393
|
+
var height = 0;
|
2394
|
+
for (var i = 0, e = lines.length; i < e; ++i) height += lines[i].height;
|
2395
|
+
this.insertHeight(at, lines, height);
|
2396
|
+
},
|
2397
|
+
insertHeight: function(at, lines, height) {
|
2398
|
+
this.size += lines.length;
|
2399
|
+
this.height += height;
|
2400
|
+
for (var i = 0, e = this.children.length; i < e; ++i) {
|
2401
|
+
var child = this.children[i], sz = child.chunkSize();
|
2402
|
+
if (at <= sz) {
|
2403
|
+
child.insertHeight(at, lines, height);
|
2404
|
+
if (child.lines && child.lines.length > 50) {
|
2405
|
+
while (child.lines.length > 50) {
|
2406
|
+
var spilled = child.lines.splice(child.lines.length - 25, 25);
|
2407
|
+
var newleaf = new LeafChunk(spilled);
|
2408
|
+
child.height -= newleaf.height;
|
2409
|
+
this.children.splice(i + 1, 0, newleaf);
|
2410
|
+
newleaf.parent = this;
|
2411
|
+
}
|
2412
|
+
this.maybeSpill();
|
2413
|
+
}
|
2414
|
+
break;
|
2415
|
+
}
|
2416
|
+
at -= sz;
|
2417
|
+
}
|
2418
|
+
},
|
2419
|
+
maybeSpill: function() {
|
2420
|
+
if (this.children.length <= 10) return;
|
2421
|
+
var me = this;
|
2422
|
+
do {
|
2423
|
+
var spilled = me.children.splice(me.children.length - 5, 5);
|
2424
|
+
var sibling = new BranchChunk(spilled);
|
2425
|
+
if (!me.parent) { // Become the parent node
|
2426
|
+
var copy = new BranchChunk(me.children);
|
2427
|
+
copy.parent = me;
|
2428
|
+
me.children = [copy, sibling];
|
2429
|
+
me = copy;
|
2430
|
+
} else {
|
2431
|
+
me.size -= sibling.size;
|
2432
|
+
me.height -= sibling.height;
|
2433
|
+
var myIndex = indexOf(me.parent.children, me);
|
2434
|
+
me.parent.children.splice(myIndex + 1, 0, sibling);
|
2435
|
+
}
|
2436
|
+
sibling.parent = me.parent;
|
2437
|
+
} while (me.children.length > 10);
|
2438
|
+
me.parent.maybeSpill();
|
2439
|
+
},
|
2440
|
+
iter: function(from, to, op) { this.iterN(from, to - from, op); },
|
2441
|
+
iterN: function(at, n, op) {
|
2442
|
+
for (var i = 0, e = this.children.length; i < e; ++i) {
|
2443
|
+
var child = this.children[i], sz = child.chunkSize();
|
2444
|
+
if (at < sz) {
|
2445
|
+
var used = Math.min(n, sz - at);
|
2446
|
+
if (child.iterN(at, used, op)) return true;
|
2447
|
+
if ((n -= used) == 0) break;
|
2448
|
+
at = 0;
|
2449
|
+
} else at -= sz;
|
2450
|
+
}
|
2451
|
+
}
|
2452
|
+
};
|
2453
|
+
|
2454
|
+
function getLineAt(chunk, n) {
|
2455
|
+
while (!chunk.lines) {
|
2456
|
+
for (var i = 0;; ++i) {
|
2457
|
+
var child = chunk.children[i], sz = child.chunkSize();
|
2458
|
+
if (n < sz) { chunk = child; break; }
|
2459
|
+
n -= sz;
|
2460
|
+
}
|
2461
|
+
}
|
2462
|
+
return chunk.lines[n];
|
2463
|
+
}
|
2464
|
+
function lineNo(line) {
|
2465
|
+
if (line.parent == null) return null;
|
2466
|
+
var cur = line.parent, no = indexOf(cur.lines, line);
|
2467
|
+
for (var chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) {
|
2468
|
+
for (var i = 0, e = chunk.children.length; ; ++i) {
|
2469
|
+
if (chunk.children[i] == cur) break;
|
2470
|
+
no += chunk.children[i].chunkSize();
|
2471
|
+
}
|
2472
|
+
}
|
2473
|
+
return no;
|
2474
|
+
}
|
2475
|
+
function lineAtHeight(chunk, h) {
|
2476
|
+
var n = 0;
|
2477
|
+
outer: do {
|
2478
|
+
for (var i = 0, e = chunk.children.length; i < e; ++i) {
|
2479
|
+
var child = chunk.children[i], ch = child.height;
|
2480
|
+
if (h < ch) { chunk = child; continue outer; }
|
2481
|
+
h -= ch;
|
2482
|
+
n += child.chunkSize();
|
2483
|
+
}
|
2484
|
+
return n;
|
2485
|
+
} while (!chunk.lines);
|
2486
|
+
for (var i = 0, e = chunk.lines.length; i < e; ++i) {
|
2487
|
+
var line = chunk.lines[i], lh = line.height;
|
2488
|
+
if (h < lh) break;
|
2489
|
+
h -= lh;
|
2490
|
+
}
|
2491
|
+
return n + i;
|
2492
|
+
}
|
2493
|
+
function heightAtLine(chunk, n) {
|
2494
|
+
var h = 0;
|
2495
|
+
outer: do {
|
2496
|
+
for (var i = 0, e = chunk.children.length; i < e; ++i) {
|
2497
|
+
var child = chunk.children[i], sz = child.chunkSize();
|
2498
|
+
if (n < sz) { chunk = child; continue outer; }
|
2499
|
+
n -= sz;
|
2500
|
+
h += child.height;
|
2501
|
+
}
|
2502
|
+
return h;
|
2503
|
+
} while (!chunk.lines);
|
2504
|
+
for (var i = 0; i < n; ++i) h += chunk.lines[i].height;
|
2505
|
+
return h;
|
2506
|
+
}
|
2507
|
+
|
1932
2508
|
// The history object 'chunks' changes that are made close together
|
1933
2509
|
// and at almost the same time into bigger undoable units.
|
1934
2510
|
function History() {
|
@@ -1978,6 +2554,10 @@ var CodeMirror = (function() {
|
|
1978
2554
|
else e.cancelBubble = true;
|
1979
2555
|
}
|
1980
2556
|
function e_stop(e) {e_preventDefault(e); e_stopPropagation(e);}
|
2557
|
+
CodeMirror.e_stop = e_stop;
|
2558
|
+
CodeMirror.e_preventDefault = e_preventDefault;
|
2559
|
+
CodeMirror.e_stopPropagation = e_stopPropagation;
|
2560
|
+
|
1981
2561
|
function e_target(e) {return e.target || e.srcElement;}
|
1982
2562
|
function e_button(e) {
|
1983
2563
|
if (e.which) return e.which;
|
@@ -1989,39 +2569,33 @@ var CodeMirror = (function() {
|
|
1989
2569
|
// Event handler registration. If disconnect is true, it'll return a
|
1990
2570
|
// function that unregisters the handler.
|
1991
2571
|
function connect(node, type, handler, disconnect) {
|
1992
|
-
function wrapHandler(event) {handler(event || window.event);}
|
1993
2572
|
if (typeof node.addEventListener == "function") {
|
1994
|
-
node.addEventListener(type,
|
1995
|
-
if (disconnect) return function() {node.removeEventListener(type,
|
2573
|
+
node.addEventListener(type, handler, false);
|
2574
|
+
if (disconnect) return function() {node.removeEventListener(type, handler, false);};
|
1996
2575
|
}
|
1997
2576
|
else {
|
2577
|
+
var wrapHandler = function(event) {handler(event || window.event);};
|
1998
2578
|
node.attachEvent("on" + type, wrapHandler);
|
1999
2579
|
if (disconnect) return function() {node.detachEvent("on" + type, wrapHandler);};
|
2000
2580
|
}
|
2001
2581
|
}
|
2582
|
+
CodeMirror.connect = connect;
|
2002
2583
|
|
2003
2584
|
function Delayed() {this.id = null;}
|
2004
2585
|
Delayed.prototype = {set: function(ms, f) {clearTimeout(this.id); this.id = setTimeout(f, ms);}};
|
2005
2586
|
|
2006
|
-
// Some IE versions don't preserve whitespace when setting the
|
2007
|
-
// innerHTML of a PRE tag.
|
2008
|
-
var badInnerHTML = (function() {
|
2009
|
-
var pre = document.createElement("pre");
|
2010
|
-
pre.innerHTML = " "; return !pre.innerHTML;
|
2011
|
-
})();
|
2012
|
-
|
2013
2587
|
// Detect drag-and-drop
|
2014
|
-
var dragAndDrop =
|
2588
|
+
var dragAndDrop = function() {
|
2015
2589
|
// IE8 has ondragstart and ondrop properties, but doesn't seem to
|
2016
2590
|
// actually support ondragstart the way it's supposed to work.
|
2017
2591
|
if (/MSIE [1-8]\b/.test(navigator.userAgent)) return false;
|
2018
2592
|
var div = document.createElement('div');
|
2019
|
-
return "
|
2020
|
-
}
|
2593
|
+
return "draggable" in div;
|
2594
|
+
}();
|
2021
2595
|
|
2022
2596
|
var gecko = /gecko\/\d{7}/i.test(navigator.userAgent);
|
2023
2597
|
var ie = /MSIE \d/.test(navigator.userAgent);
|
2024
|
-
var
|
2598
|
+
var webkit = /WebKit\//.test(navigator.userAgent);
|
2025
2599
|
|
2026
2600
|
var lineSep = "\n";
|
2027
2601
|
// Feature-detect whether newlines in textareas are converted to \r\n
|
@@ -2031,15 +2605,9 @@ var CodeMirror = (function() {
|
|
2031
2605
|
if (te.value.indexOf("\r") > -1) lineSep = "\r\n";
|
2032
2606
|
}());
|
2033
2607
|
|
2034
|
-
var tabSize = 8;
|
2035
|
-
var mac = /Mac/.test(navigator.platform);
|
2036
|
-
var movementKeys = {};
|
2037
|
-
for (var i = 35; i <= 40; ++i)
|
2038
|
-
movementKeys[i] = movementKeys["c" + i] = true;
|
2039
|
-
|
2040
2608
|
// Counts the column offset in a string, taking tabs into account.
|
2041
2609
|
// Used mostly to find indentation.
|
2042
|
-
function countColumn(string, end) {
|
2610
|
+
function countColumn(string, end, tabSize) {
|
2043
2611
|
if (end == null) {
|
2044
2612
|
end = string.search(/[^\s\u00a0]/);
|
2045
2613
|
if (end == -1) end = string.length;
|
@@ -2055,21 +2623,44 @@ var CodeMirror = (function() {
|
|
2055
2623
|
if (elt.currentStyle) return elt.currentStyle;
|
2056
2624
|
return window.getComputedStyle(elt, null);
|
2057
2625
|
}
|
2626
|
+
|
2058
2627
|
// Find the position of an element by following the offsetParent chain.
|
2059
2628
|
// If screen==true, it returns screen (rather than page) coordinates.
|
2060
2629
|
function eltOffset(node, screen) {
|
2061
|
-
var
|
2062
|
-
var x = 0, y = 0,
|
2630
|
+
var bod = node.ownerDocument.body;
|
2631
|
+
var x = 0, y = 0, skipBody = false;
|
2063
2632
|
for (var n = node; n; n = n.offsetParent) {
|
2064
|
-
|
2633
|
+
var ol = n.offsetLeft, ot = n.offsetTop;
|
2634
|
+
// Firefox reports weird inverted offsets when the body has a border.
|
2635
|
+
if (n == bod) { x += Math.abs(ol); y += Math.abs(ot); }
|
2636
|
+
else { x += ol, y += ot; }
|
2065
2637
|
if (screen && computedStyle(n).position == "fixed")
|
2066
|
-
|
2638
|
+
skipBody = true;
|
2067
2639
|
}
|
2068
|
-
var e = screen && !
|
2640
|
+
var e = screen && !skipBody ? null : bod;
|
2069
2641
|
for (var n = node.parentNode; n != e; n = n.parentNode)
|
2070
2642
|
if (n.scrollLeft != null) { x -= n.scrollLeft; y -= n.scrollTop;}
|
2071
2643
|
return {left: x, top: y};
|
2072
2644
|
}
|
2645
|
+
// Use the faster and saner getBoundingClientRect method when possible.
|
2646
|
+
if (document.documentElement.getBoundingClientRect != null) eltOffset = function(node, screen) {
|
2647
|
+
// Take the parts of bounding client rect that we are interested in so we are able to edit if need be,
|
2648
|
+
// since the returned value cannot be changed externally (they are kept in sync as the element moves within the page)
|
2649
|
+
try { var box = node.getBoundingClientRect(); box = { top: box.top, left: box.left }; }
|
2650
|
+
catch(e) { box = {top: 0, left: 0}; }
|
2651
|
+
if (!screen) {
|
2652
|
+
// Get the toplevel scroll, working around browser differences.
|
2653
|
+
if (window.pageYOffset == null) {
|
2654
|
+
var t = document.documentElement || document.body.parentNode;
|
2655
|
+
if (t.scrollTop == null) t = document.body;
|
2656
|
+
box.top += t.scrollTop; box.left += t.scrollLeft;
|
2657
|
+
} else {
|
2658
|
+
box.top += window.pageYOffset; box.left += window.pageXOffset;
|
2659
|
+
}
|
2660
|
+
}
|
2661
|
+
return box;
|
2662
|
+
};
|
2663
|
+
|
2073
2664
|
// Get a node's text content.
|
2074
2665
|
function eltText(node) {
|
2075
2666
|
return node.textContent || node.innerText || node.nodeValue || "";
|
@@ -2080,11 +2671,25 @@ var CodeMirror = (function() {
|
|
2080
2671
|
function posLess(a, b) {return a.line < b.line || (a.line == b.line && a.ch < b.ch);}
|
2081
2672
|
function copyPos(x) {return {line: x.line, ch: x.ch};}
|
2082
2673
|
|
2083
|
-
var escapeElement = document.createElement("
|
2674
|
+
var escapeElement = document.createElement("pre");
|
2084
2675
|
function htmlEscape(str) {
|
2085
|
-
escapeElement.
|
2676
|
+
escapeElement.textContent = str;
|
2086
2677
|
return escapeElement.innerHTML;
|
2087
2678
|
}
|
2679
|
+
// Recent (late 2011) Opera betas insert bogus newlines at the start
|
2680
|
+
// of the textContent, so we strip those.
|
2681
|
+
if (htmlEscape("a") == "\na")
|
2682
|
+
htmlEscape = function(str) {
|
2683
|
+
escapeElement.textContent = str;
|
2684
|
+
return escapeElement.innerHTML.slice(1);
|
2685
|
+
};
|
2686
|
+
// Some IEs don't preserve tabs through innerHTML
|
2687
|
+
else if (htmlEscape("\t") != "\t")
|
2688
|
+
htmlEscape = function(str) {
|
2689
|
+
escapeElement.innerHTML = "";
|
2690
|
+
escapeElement.appendChild(document.createTextNode(str));
|
2691
|
+
return escapeElement.innerHTML;
|
2692
|
+
};
|
2088
2693
|
CodeMirror.htmlEscape = htmlEscape;
|
2089
2694
|
|
2090
2695
|
// Used to position the cursor after an undo/redo by finding the
|
@@ -2103,95 +2708,54 @@ var CodeMirror = (function() {
|
|
2103
2708
|
if (collection[i] == elt) return i;
|
2104
2709
|
return -1;
|
2105
2710
|
}
|
2711
|
+
function isWordChar(ch) {
|
2712
|
+
return /\w/.test(ch) || ch.toUpperCase() != ch.toLowerCase();
|
2713
|
+
}
|
2106
2714
|
|
2107
2715
|
// See if "".split is the broken IE version, if so, provide an
|
2108
2716
|
// alternative way to split lines.
|
2109
|
-
var splitLines
|
2110
|
-
|
2111
|
-
|
2112
|
-
|
2113
|
-
|
2114
|
-
|
2115
|
-
|
2116
|
-
|
2117
|
-
|
2118
|
-
return result;
|
2119
|
-
};
|
2120
|
-
else
|
2121
|
-
splitLines = function(string){return string.split(/\r?\n/);};
|
2717
|
+
var splitLines = "\n\nb".split(/\n/).length != 3 ? function(string) {
|
2718
|
+
var pos = 0, nl, result = [];
|
2719
|
+
while ((nl = string.indexOf("\n", pos)) > -1) {
|
2720
|
+
result.push(string.slice(pos, string.charAt(nl-1) == "\r" ? nl - 1 : nl));
|
2721
|
+
pos = nl + 1;
|
2722
|
+
}
|
2723
|
+
result.push(string.slice(pos));
|
2724
|
+
return result;
|
2725
|
+
} : function(string){return string.split(/\r?\n/);};
|
2122
2726
|
CodeMirror.splitLines = splitLines;
|
2123
2727
|
|
2124
|
-
|
2125
|
-
|
2126
|
-
|
2127
|
-
|
2128
|
-
|
2129
|
-
}
|
2130
|
-
if (
|
2131
|
-
|
2132
|
-
|
2133
|
-
// the anchor is put at the end, and the selection expanded to
|
2134
|
-
// the left. If you press shift-right, the anchor ends up at the
|
2135
|
-
// front. This is not what CodeMirror wants, so it does a
|
2136
|
-
// spurious modify() call to get out of limbo.
|
2137
|
-
setSelRange = function(te, start, end) {
|
2138
|
-
if (start == end)
|
2139
|
-
te.setSelectionRange(start, end);
|
2140
|
-
else {
|
2141
|
-
te.setSelectionRange(start, end - 1);
|
2142
|
-
window.getSelection().modify("extend", "forward", "character");
|
2143
|
-
}
|
2144
|
-
};
|
2145
|
-
else
|
2146
|
-
setSelRange = function(te, start, end) {
|
2147
|
-
try {te.setSelectionRange(start, end);}
|
2148
|
-
catch(e) {} // Fails on Firefox when textarea isn't part of the document
|
2149
|
-
};
|
2150
|
-
}
|
2151
|
-
// IE model. Don't ask.
|
2152
|
-
else {
|
2153
|
-
selRange = function(te) {
|
2154
|
-
try {var range = te.ownerDocument.selection.createRange();}
|
2155
|
-
catch(e) {return null;}
|
2156
|
-
if (!range || range.parentElement() != te) return null;
|
2157
|
-
var val = te.value, len = val.length, localRange = te.createTextRange();
|
2158
|
-
localRange.moveToBookmark(range.getBookmark());
|
2159
|
-
var endRange = te.createTextRange();
|
2160
|
-
endRange.collapse(false);
|
2161
|
-
|
2162
|
-
if (localRange.compareEndPoints("StartToEnd", endRange) > -1)
|
2163
|
-
return {start: len, end: len};
|
2164
|
-
|
2165
|
-
var start = -localRange.moveStart("character", -len);
|
2166
|
-
for (var i = val.indexOf("\r"); i > -1 && i < start; i = val.indexOf("\r", i+1), start++) {}
|
2167
|
-
|
2168
|
-
if (localRange.compareEndPoints("EndToEnd", endRange) > -1)
|
2169
|
-
return {start: start, end: len};
|
2170
|
-
|
2171
|
-
var end = -localRange.moveEnd("character", -len);
|
2172
|
-
for (var i = val.indexOf("\r"); i > -1 && i < end; i = val.indexOf("\r", i+1), end++) {}
|
2173
|
-
return {start: start, end: end};
|
2174
|
-
};
|
2175
|
-
setSelRange = function(te, start, end) {
|
2176
|
-
var range = te.createTextRange();
|
2177
|
-
range.collapse(true);
|
2178
|
-
var endrange = range.duplicate();
|
2179
|
-
var newlines = 0, txt = te.value;
|
2180
|
-
for (var pos = txt.indexOf("\n"); pos > -1 && pos < start; pos = txt.indexOf("\n", pos + 1))
|
2181
|
-
++newlines;
|
2182
|
-
range.move("character", start - newlines);
|
2183
|
-
for (; pos > -1 && pos < end; pos = txt.indexOf("\n", pos + 1))
|
2184
|
-
++newlines;
|
2185
|
-
endrange.move("character", end - newlines);
|
2186
|
-
range.setEndPoint("EndToEnd", endrange);
|
2187
|
-
range.select();
|
2188
|
-
};
|
2189
|
-
}
|
2728
|
+
var hasSelection = window.getSelection ? function(te) {
|
2729
|
+
try { return te.selectionStart != te.selectionEnd; }
|
2730
|
+
catch(e) { return false; }
|
2731
|
+
} : function(te) {
|
2732
|
+
try {var range = te.ownerDocument.selection.createRange();}
|
2733
|
+
catch(e) {}
|
2734
|
+
if (!range || range.parentElement() != te) return false;
|
2735
|
+
return range.compareEndPoints("StartToEnd", range) != 0;
|
2736
|
+
};
|
2190
2737
|
|
2191
2738
|
CodeMirror.defineMode("null", function() {
|
2192
2739
|
return {token: function(stream) {stream.skipToEnd();}};
|
2193
2740
|
});
|
2194
2741
|
CodeMirror.defineMIME("text/plain", "null");
|
2195
2742
|
|
2743
|
+
var keyNames = {3: "Enter", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt",
|
2744
|
+
19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End",
|
2745
|
+
36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert",
|
2746
|
+
46: "Delete", 59: ";", 91: "Mod", 92: "Mod", 93: "Mod", 186: ";", 187: "=", 188: ",",
|
2747
|
+
189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\", 221: "]", 222: "'", 63276: "PageUp",
|
2748
|
+
63277: "PageDown", 63275: "End", 63273: "Home", 63234: "Left", 63232: "Up", 63235: "Right",
|
2749
|
+
63233: "Down", 63302: "Insert", 63272: "Delete"};
|
2750
|
+
CodeMirror.keyNames = keyNames;
|
2751
|
+
(function() {
|
2752
|
+
// Number keys
|
2753
|
+
for (var i = 0; i < 10; i++) keyNames[i + 48] = String(i);
|
2754
|
+
// Alphabetic keys
|
2755
|
+
for (var i = 65; i <= 90; i++) keyNames[i] = String.fromCharCode(i);
|
2756
|
+
// Function keys
|
2757
|
+
for (var i = 1; i <= 12; i++) keyNames[i + 111] = keyNames[i + 63235] = "F" + i;
|
2758
|
+
})();
|
2759
|
+
|
2196
2760
|
return CodeMirror;
|
2197
2761
|
})();
|