@@ -5,30 +5,30 @@ var event = require("../lib/event");
5
5
var useragent = require ( "../lib/useragent" ) ;
6
6
var EventEmitter = require ( "../lib/event_emitter" ) . EventEmitter ;
7
7
8
- var CHAR_COUNT = 256 ;
8
+ var DEFAULT_CHAR_COUNT = 250 ;
9
9
var USE_OBSERVER = typeof ResizeObserver == "function" ;
10
10
var L = 200 ;
11
11
12
- var FontMetrics = exports . FontMetrics = function ( parentEl ) {
12
+ var FontMetrics = exports . FontMetrics = function ( parentEl , charCount ) {
13
+ this . charCount = charCount || DEFAULT_CHAR_COUNT ;
14
+
13
15
this . el = dom . createElement ( "div" ) ;
14
16
this . $setMeasureNodeStyles ( this . el . style , true ) ;
15
-
17
+
16
18
this . $main = dom . createElement ( "div" ) ;
17
19
this . $setMeasureNodeStyles ( this . $main . style ) ;
18
-
20
+
19
21
this . $measureNode = dom . createElement ( "div" ) ;
20
22
this . $setMeasureNodeStyles ( this . $measureNode . style ) ;
21
-
22
-
23
+
23
24
this . el . appendChild ( this . $main ) ;
24
25
this . el . appendChild ( this . $measureNode ) ;
25
26
parentEl . appendChild ( this . el ) ;
26
-
27
- this . $measureNode . textContent = lang . stringRepeat ( "X" , CHAR_COUNT ) ;
28
-
27
+
28
+ this . $measureNode . textContent = lang . stringRepeat ( "X" , this . charCount ) ;
29
+
29
30
this . $characterSize = { width : 0 , height : 0 } ;
30
-
31
-
31
+
32
32
if ( USE_OBSERVER )
33
33
this . $addObserver ( ) ;
34
34
else
@@ -38,9 +38,9 @@ var FontMetrics = exports.FontMetrics = function(parentEl) {
38
38
( function ( ) {
39
39
40
40
oop . implement ( this , EventEmitter ) ;
41
-
41
+
42
42
this . $characterSize = { width : 0 , height : 0 } ;
43
-
43
+
44
44
this . $setMeasureNodeStyles = function ( style , isRoot ) {
45
45
style . width = style . height = "auto" ;
46
46
style . left = style . top = "0px" ;
@@ -69,7 +69,7 @@ var FontMetrics = exports.FontMetrics = function(parentEl) {
69
69
this . _emit ( "changeCharacterSize" , { data : size } ) ;
70
70
}
71
71
} ;
72
-
72
+
73
73
this . $addObserver = function ( ) {
74
74
var self = this ;
75
75
this . $observer = new window . ResizeObserver ( function ( e ) {
@@ -83,13 +83,13 @@ var FontMetrics = exports.FontMetrics = function(parentEl) {
83
83
if ( this . $pollSizeChangesTimer || this . $observer )
84
84
return this . $pollSizeChangesTimer ;
85
85
var self = this ;
86
-
86
+
87
87
return this . $pollSizeChangesTimer = event . onIdle ( function cb ( ) {
88
88
self . checkForSizeChanges ( ) ;
89
89
event . onIdle ( cb , 500 ) ;
90
90
} , 500 ) ;
91
91
} ;
92
-
92
+
93
93
this . setPolling = function ( val ) {
94
94
if ( val ) {
95
95
this . $pollSizeChanges ( ) ;
@@ -100,24 +100,32 @@ var FontMetrics = exports.FontMetrics = function(parentEl) {
100
100
} ;
101
101
102
102
this . $measureSizes = function ( node ) {
103
- var size = {
104
- height : ( node || this . $measureNode ) . clientHeight ,
105
- width : ( node || this . $measureNode ) . clientWidth / CHAR_COUNT
103
+ node = node || this . $measureNode ;
104
+
105
+ // Avoid `Element.clientWidth` since it is rounded to an integer (see
106
+ // https://developer.mozilla.org/en-US/docs/Web/API/Element/clientWidth).
107
+ // Using it here can result in a noticeable cursor offset for long lines.
108
+ const rect = node . getBoundingClientRect ( ) ;
109
+ const charSize = {
110
+ height : rect . height ,
111
+ width : rect . width / this . charCount
106
112
} ;
107
-
113
+
108
114
// Size and width can be null if the editor is not visible or
109
115
// detached from the document
110
- if ( size . width === 0 || size . height === 0 )
116
+ if ( charSize . width === 0 || charSize . height === 0 )
111
117
return null ;
112
- return size ;
118
+ return charSize ;
113
119
} ;
114
120
115
121
this . $measureCharWidth = function ( ch ) {
116
- this . $main . textContent = lang . stringRepeat ( ch , CHAR_COUNT ) ;
122
+ this . $main . textContent = lang . stringRepeat ( ch , this . charCount ) ;
123
+ // Avoid `Element.clientWidth` since it is rounded to an integer (see
124
+ // https://developer.mozilla.org/en-US/docs/Web/API/Element/clientWidth).
117
125
var rect = this . $main . getBoundingClientRect ( ) ;
118
- return rect . width / CHAR_COUNT ;
126
+ return rect . width / this . charCount ;
119
127
} ;
120
-
128
+
121
129
this . getCharacterWidth = function ( ch ) {
122
130
var w = this . charSizes [ ch ] ;
123
131
if ( w === undefined ) {
@@ -134,7 +142,7 @@ var FontMetrics = exports.FontMetrics = function(parentEl) {
134
142
this . el . parentNode . removeChild ( this . el ) ;
135
143
} ;
136
144
137
-
145
+
138
146
this . $getZoom = function getZoom ( element ) {
139
147
if ( ! element || ! element . parentElement ) return 1 ;
140
148
return ( window . getComputedStyle ( element ) . zoom || 1 ) * getZoom ( element . parentElement ) ;
@@ -171,7 +179,7 @@ var FontMetrics = exports.FontMetrics = function(parentEl) {
171
179
172
180
if ( ! this . els )
173
181
this . $initTransformMeasureNodes ( ) ;
174
-
182
+
175
183
function p ( el ) {
176
184
var r = el . getBoundingClientRect ( ) ;
177
185
return [ r . left , r . top ] ;
@@ -186,7 +194,7 @@ var FontMetrics = exports.FontMetrics = function(parentEl) {
186
194
187
195
var m1 = mul ( 1 + h [ 0 ] , sub ( b , a ) ) ;
188
196
var m2 = mul ( 1 + h [ 1 ] , sub ( c , a ) ) ;
189
-
197
+
190
198
if ( elPos ) {
191
199
var x = elPos ;
192
200
var k = h [ 0 ] * x [ 0 ] / L + h [ 1 ] * x [ 1 ] / L + 1 ;
@@ -197,5 +205,5 @@ var FontMetrics = exports.FontMetrics = function(parentEl) {
197
205
var f = solve ( sub ( m1 , mul ( h [ 0 ] , u ) ) , sub ( m2 , mul ( h [ 1 ] , u ) ) , u ) ;
198
206
return mul ( L , f ) ;
199
207
} ;
200
-
208
+
201
209
} ) . call ( FontMetrics . prototype ) ;
0 commit comments