2014-05-21 00:04:59 +04:00
_ = require ' underscore-plus '
{ extend , flatten , toArray , last } = _
2014-09-23 01:35:13 +04:00
TextEditorView = require ' ../src/text-editor-view '
TextEditorComponent = require ' ../src/text-editor-component '
2014-04-17 21:56:50 +04:00
nbsp = String . fromCharCode ( 160 )
2014-03-28 05:02:24 +04:00
2014-09-23 01:35:13 +04:00
describe " TextEditorComponent " , ->
2014-07-13 21:56:14 +04:00
[ contentNode , editor , wrapperView , wrapperNode , component , componentNode , verticalScrollbarNode , horizontalScrollbarNode ] = [ ]
2014-07-26 03:38:55 +04:00
[ lineHeightInPixels , charWidth , nextAnimationFrame , noAnimationFrame , lineOverdrawMargin ] = [ ]
2014-03-28 05:02:24 +04:00
beforeEach ->
2014-05-16 20:44:49 +04:00
lineOverdrawMargin = 2
2014-04-02 20:59:57 +04:00
waitsForPromise ->
atom . packages . activatePackage ( ' language-javascript ' )
2014-04-02 03:06:59 +04:00
2014-04-02 20:59:57 +04:00
runs ->
2014-04-12 02:43:04 +04:00
spyOn ( window , " setInterval " ) . andCallFake window . fakeSetInterval
spyOn ( window , " clearInterval " ) . andCallFake window . fakeClearInterval
2014-07-26 03:38:55 +04:00
noAnimationFrame = -> throw new Error ( ' No animation frame requested ' )
nextAnimationFrame = noAnimationFrame
2014-06-20 02:48:40 +04:00
2014-04-07 01:47:50 +04:00
spyOn ( window , ' requestAnimationFrame ' ) . andCallFake (fn) ->
2014-07-26 03:38:55 +04:00
nextAnimationFrame = ->
nextAnimationFrame = noAnimationFrame
2014-04-07 01:47:50 +04:00
fn ( )
2014-03-28 05:02:24 +04:00
2014-04-25 01:14:10 +04:00
waitsForPromise ->
atom . project . open ( ' sample.js ' ) . then (o) -> editor = o
runs ->
2014-05-02 14:12:17 +04:00
contentNode = document . querySelector ( ' # jasmine-content ' )
contentNode.style.width = ' 1000px '
2014-09-23 01:35:13 +04:00
wrapperView = new TextEditorView ( editor , { lineOverdrawMargin } )
2014-04-10 22:46:58 +04:00
wrapperView . attachToDom ( )
2014-07-13 21:56:14 +04:00
wrapperNode = wrapperView . element
2014-11-27 03:51:13 +03:00
wrapperNode . setUpdatedSynchronously ( false )
2014-06-20 02:48:40 +04:00
2014-04-10 22:46:58 +04:00
{ component } = wrapperView
2014-07-28 21:23:36 +04:00
component . setFontFamily ( ' monospace ' )
2014-04-02 20:59:57 +04:00
component . setLineHeight ( 1.3 )
component . setFontSize ( 20 )
2014-04-14 23:48:54 +04:00
2014-05-22 04:04:44 +04:00
lineHeightInPixels = editor . getLineHeightInPixels ( )
2014-04-14 23:48:54 +04:00
charWidth = editor . getDefaultCharWidth ( )
2014-07-13 21:56:14 +04:00
componentNode = component . getDOMNode ( )
verticalScrollbarNode = componentNode . querySelector ( ' .vertical-scrollbar ' )
horizontalScrollbarNode = componentNode . querySelector ( ' .horizontal-scrollbar ' )
2014-03-28 05:02:24 +04:00
2014-07-14 22:16:32 +04:00
component . measureHeightAndWidth ( )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-05-13 01:30:21 +04:00
2014-05-02 14:12:17 +04:00
afterEach ->
contentNode.style.width = ' '
2014-04-08 03:52:48 +04:00
describe " line rendering " , ->
2014-05-16 20:44:49 +04:00
it " renders the currently-visible lines plus the overdraw margin " , ->
2014-07-13 21:56:14 +04:00
wrapperNode.style.height = 4.5 * lineHeightInPixels + ' px '
2014-07-14 22:16:32 +04:00
component . measureHeightAndWidth ( )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-04-04 22:58:12 +04:00
2014-07-13 21:56:14 +04:00
linesNode = componentNode . querySelector ( ' .lines ' )
2014-05-16 20:44:49 +04:00
expect ( linesNode . style [ ' -webkit-transform ' ] ) . toBe " translate3d(0px, 0px, 0px) "
2014-07-13 21:56:14 +04:00
expect ( componentNode . querySelectorAll ( ' .line ' ) . length ) . toBe 6 + 2 # no margin above
2014-09-04 00:49:52 +04:00
expect ( component . lineNodeForScreenRow ( 0 ) . textContent ) . toBe editor . tokenizedLineForScreenRow ( 0 ) . text
2014-05-16 20:44:49 +04:00
expect ( component . lineNodeForScreenRow ( 0 ) . offsetTop ) . toBe 0
2014-09-04 00:49:52 +04:00
expect ( component . lineNodeForScreenRow ( 5 ) . textContent ) . toBe editor . tokenizedLineForScreenRow ( 5 ) . text
2014-05-16 20:44:49 +04:00
expect ( component . lineNodeForScreenRow ( 5 ) . offsetTop ) . toBe 5 * lineHeightInPixels
verticalScrollbarNode.scrollTop = 4.5 * lineHeightInPixels
2014-04-14 23:06:15 +04:00
verticalScrollbarNode . dispatchEvent ( new UIEvent ( ' scroll ' ) )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-03-28 05:02:24 +04:00
2014-05-16 20:44:49 +04:00
expect ( linesNode . style [ ' -webkit-transform ' ] ) . toBe " translate3d(0px, #{ - 4.5 * lineHeightInPixels } px, 0px) "
2014-07-13 21:56:14 +04:00
expect ( componentNode . querySelectorAll ( ' .line ' ) . length ) . toBe 6 + 4 # margin above and below
2014-05-16 20:44:49 +04:00
expect ( component . lineNodeForScreenRow ( 2 ) . offsetTop ) . toBe 2 * lineHeightInPixels
2014-09-04 00:49:52 +04:00
expect ( component . lineNodeForScreenRow ( 2 ) . textContent ) . toBe editor . tokenizedLineForScreenRow ( 2 ) . text
2014-05-16 20:44:49 +04:00
expect ( component . lineNodeForScreenRow ( 9 ) . offsetTop ) . toBe 9 * lineHeightInPixels
2014-09-04 00:49:52 +04:00
expect ( component . lineNodeForScreenRow ( 9 ) . textContent ) . toBe editor . tokenizedLineForScreenRow ( 9 ) . text
2014-03-28 05:02:24 +04:00
2014-05-16 20:44:49 +04:00
it " updates the top position of subsequent lines when lines are inserted or removed " , ->
2014-04-22 21:10:48 +04:00
editor . getBuffer ( ) . deleteRows ( 0 , 1 )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-06-20 02:48:40 +04:00
2014-07-13 21:56:14 +04:00
lineNodes = componentNode . querySelectorAll ( ' .line ' )
2014-05-16 20:44:49 +04:00
expect ( component . lineNodeForScreenRow ( 0 ) . offsetTop ) . toBe 0
expect ( component . lineNodeForScreenRow ( 1 ) . offsetTop ) . toBe 1 * lineHeightInPixels
expect ( component . lineNodeForScreenRow ( 2 ) . offsetTop ) . toBe 2 * lineHeightInPixels
2014-04-22 21:10:48 +04:00
editor . getBuffer ( ) . insert ( [ 0 , 0 ] , ' \n \n ' )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-06-20 02:48:40 +04:00
2014-07-13 21:56:14 +04:00
lineNodes = componentNode . querySelectorAll ( ' .line ' )
2014-05-16 20:44:49 +04:00
expect ( component . lineNodeForScreenRow ( 0 ) . offsetTop ) . toBe 0 * lineHeightInPixels
expect ( component . lineNodeForScreenRow ( 1 ) . offsetTop ) . toBe 1 * lineHeightInPixels
expect ( component . lineNodeForScreenRow ( 2 ) . offsetTop ) . toBe 2 * lineHeightInPixels
expect ( component . lineNodeForScreenRow ( 3 ) . offsetTop ) . toBe 3 * lineHeightInPixels
expect ( component . lineNodeForScreenRow ( 4 ) . offsetTop ) . toBe 4 * lineHeightInPixels
2014-04-22 21:10:48 +04:00
2014-07-03 21:22:46 +04:00
it " updates the lines when lines are inserted or removed above the rendered row range " , ->
2014-07-13 21:56:14 +04:00
wrapperNode.style.height = 4.5 * lineHeightInPixels + ' px '
2014-07-14 22:16:32 +04:00
component . measureHeightAndWidth ( )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-07-03 21:22:46 +04:00
verticalScrollbarNode.scrollTop = 5 * lineHeightInPixels
verticalScrollbarNode . dispatchEvent ( new UIEvent ( ' scroll ' ) )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-07-03 21:22:46 +04:00
buffer = editor . getBuffer ( )
buffer . insert ( [ 0 , 0 ] , ' \n \n ' )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-09-04 00:49:52 +04:00
expect ( component . lineNodeForScreenRow ( 3 ) . textContent ) . toBe editor . tokenizedLineForScreenRow ( 3 ) . text
2014-07-03 21:22:46 +04:00
buffer . delete ( [ [ 0 , 0 ] , [ 3 , 0 ] ] )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-09-04 00:49:52 +04:00
expect ( component . lineNodeForScreenRow ( 3 ) . textContent ) . toBe editor . tokenizedLineForScreenRow ( 3 ) . text
2014-07-03 21:22:46 +04:00
2014-05-22 04:21:27 +04:00
it " updates the top position of lines when the line height changes " , ->
initialLineHeightInPixels = editor . getLineHeightInPixels ( )
component . setLineHeight ( 2 )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-05-22 04:21:27 +04:00
newLineHeightInPixels = editor . getLineHeightInPixels ( )
expect ( newLineHeightInPixels ) . not . toBe initialLineHeightInPixels
expect ( component . lineNodeForScreenRow ( 1 ) . offsetTop ) . toBe 1 * newLineHeightInPixels
2014-05-22 04:21:44 +04:00
it " updates the top position of lines when the font size changes " , ->
initialLineHeightInPixels = editor . getLineHeightInPixels ( )
component . setFontSize ( 10 )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-05-22 04:21:44 +04:00
newLineHeightInPixels = editor . getLineHeightInPixels ( )
expect ( newLineHeightInPixels ) . not . toBe initialLineHeightInPixels
expect ( component . lineNodeForScreenRow ( 1 ) . offsetTop ) . toBe 1 * newLineHeightInPixels
it " updates the top position of lines when the font family changes " , ->
# Can't find a font that changes the line height, but we think one might exist
2014-06-01 10:24:59 +04:00
linesComponent = component . refs . lines
2014-06-02 12:52:10 +04:00
spyOn ( linesComponent , ' measureLineHeightAndDefaultCharWidth ' ) . andCallFake -> editor . setLineHeightInPixels ( 10 )
2014-05-22 04:21:44 +04:00
initialLineHeightInPixels = editor . getLineHeightInPixels ( )
component . setFontFamily ( ' sans-serif ' )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-05-22 04:21:44 +04:00
2014-06-02 12:52:10 +04:00
expect ( linesComponent . measureLineHeightAndDefaultCharWidth ) . toHaveBeenCalled ( )
2014-05-22 04:21:44 +04:00
newLineHeightInPixels = editor . getLineHeightInPixels ( )
expect ( newLineHeightInPixels ) . not . toBe initialLineHeightInPixels
expect ( component . lineNodeForScreenRow ( 1 ) . offsetTop ) . toBe 1 * newLineHeightInPixels
2014-05-22 05:07:03 +04:00
it " renders the .lines div at the full height of the editor if there aren ' t enough lines to scroll vertically " , ->
editor . setText ( ' ' )
2014-07-13 21:56:14 +04:00
wrapperNode.style.height = ' 300px '
2014-07-14 22:16:32 +04:00
component . measureHeightAndWidth ( )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-05-22 05:07:03 +04:00
2014-07-13 21:56:14 +04:00
linesNode = componentNode . querySelector ( ' .lines ' )
2014-05-22 05:07:03 +04:00
expect ( linesNode . offsetHeight ) . toBe 300
2014-06-26 05:13:50 +04:00
it " assigns the width of each line so it extends across the full width of the editor " , ->
2014-07-13 21:56:14 +04:00
gutterWidth = componentNode . querySelector ( ' .gutter ' ) . offsetWidth
scrollViewNode = componentNode . querySelector ( ' .scroll-view ' )
lineNodes = componentNode . querySelectorAll ( ' .line ' )
2014-06-26 05:13:50 +04:00
2014-07-13 21:56:14 +04:00
componentNode.style.width = gutterWidth + ( 30 * charWidth ) + ' px '
2014-07-14 22:16:32 +04:00
component . measureHeightAndWidth ( )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-06-26 05:13:50 +04:00
expect ( editor . getScrollWidth ( ) ) . toBeGreaterThan scrollViewNode . offsetWidth
# At the time of writing, using width: 100% to achieve the full-width
# lines caused full-screen repaints after switching away from an editor
# and back again Please ensure you don't cause a performance regression if
# you change this behavior.
for lineNode in lineNodes
expect ( lineNode . style . width ) . toBe editor . getScrollWidth ( ) + ' px '
2014-07-13 21:56:14 +04:00
componentNode.style.width = gutterWidth + editor . getScrollWidth ( ) + 100 + ' px '
2014-07-14 22:16:32 +04:00
component . measureHeightAndWidth ( )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-06-26 05:13:50 +04:00
scrollViewWidth = scrollViewNode . offsetWidth
for lineNode in lineNodes
expect ( lineNode . style . width ) . toBe scrollViewWidth + ' px '
2014-07-23 20:30:52 +04:00
it " renders an nbsp on empty lines when no line-ending character is defined " , ->
atom . config . set ( " editor.showInvisibles " , false )
expect ( component . lineNodeForScreenRow ( 10 ) . textContent ) . toBe nbsp
2014-07-25 03:54:01 +04:00
it " gives the lines div the same background color as the editor to improve GPU performance " , ->
linesNode = componentNode . querySelector ( ' .lines ' )
backgroundColor = getComputedStyle ( wrapperNode ) . backgroundColor
expect ( linesNode . style . backgroundColor ) . toBe backgroundColor
wrapperNode.style.backgroundColor = ' rgb(255, 0, 0) '
advanceClock ( component . domPollingInterval )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-07-25 03:54:01 +04:00
expect ( linesNode . style . backgroundColor ) . toBe ' rgb(255, 0, 0) '
2014-09-17 04:46:25 +04:00
it " applies .leading-whitespace for lines with leading spaces and/or tabs " , ->
editor . setText ( ' a ' )
2014-09-17 04:42:34 +04:00
nextAnimationFrame ( )
leafNodes = getLeafNodes ( component . lineNodeForScreenRow ( 0 ) )
2014-09-17 04:46:25 +04:00
expect ( leafNodes [ 0 ] . classList . contains ( ' leading-whitespace ' ) ) . toBe true
2014-09-17 20:50:51 +04:00
expect ( leafNodes [ 0 ] . classList . contains ( ' trailing-whitespace ' ) ) . toBe false
2014-09-17 04:42:34 +04:00
2014-09-17 04:46:25 +04:00
editor . setText ( ' \t a ' )
2014-09-17 04:42:34 +04:00
nextAnimationFrame ( )
leafNodes = getLeafNodes ( component . lineNodeForScreenRow ( 0 ) )
2014-09-17 04:46:25 +04:00
expect ( leafNodes [ 0 ] . classList . contains ( ' leading-whitespace ' ) ) . toBe true
2014-09-17 20:50:51 +04:00
expect ( leafNodes [ 0 ] . classList . contains ( ' trailing-whitespace ' ) ) . toBe false
2014-09-17 04:42:34 +04:00
2014-09-17 04:46:25 +04:00
it " applies .trailing-whitespace for lines with trailing spaces and/or tabs " , ->
2014-09-17 20:47:40 +04:00
editor . setText ( ' ' )
nextAnimationFrame ( )
leafNodes = getLeafNodes ( component . lineNodeForScreenRow ( 0 ) )
expect ( leafNodes [ 0 ] . classList . contains ( ' trailing-whitespace ' ) ) . toBe true
2014-09-17 20:50:51 +04:00
expect ( leafNodes [ 0 ] . classList . contains ( ' leading-whitespace ' ) ) . toBe false
2014-09-17 20:47:40 +04:00
editor . setText ( ' \t ' )
nextAnimationFrame ( )
leafNodes = getLeafNodes ( component . lineNodeForScreenRow ( 0 ) )
expect ( leafNodes [ 0 ] . classList . contains ( ' trailing-whitespace ' ) ) . toBe true
2014-09-17 20:50:51 +04:00
expect ( leafNodes [ 0 ] . classList . contains ( ' leading-whitespace ' ) ) . toBe false
2014-09-17 20:47:40 +04:00
2014-09-17 04:46:25 +04:00
editor . setText ( ' a ' )
2014-09-17 04:42:34 +04:00
nextAnimationFrame ( )
leafNodes = getLeafNodes ( component . lineNodeForScreenRow ( 0 ) )
2014-09-17 04:46:25 +04:00
expect ( leafNodes [ 0 ] . classList . contains ( ' trailing-whitespace ' ) ) . toBe true
2014-09-17 20:50:51 +04:00
expect ( leafNodes [ 0 ] . classList . contains ( ' leading-whitespace ' ) ) . toBe false
2014-09-17 04:42:34 +04:00
2014-09-17 04:46:25 +04:00
editor . setText ( ' a \t ' )
2014-09-17 04:42:34 +04:00
nextAnimationFrame ( )
leafNodes = getLeafNodes ( component . lineNodeForScreenRow ( 0 ) )
2014-09-17 04:46:25 +04:00
expect ( leafNodes [ 0 ] . classList . contains ( ' trailing-whitespace ' ) ) . toBe true
2014-09-17 20:50:51 +04:00
expect ( leafNodes [ 0 ] . classList . contains ( ' leading-whitespace ' ) ) . toBe false
2014-09-17 04:42:34 +04:00
2014-05-20 03:20:51 +04:00
describe " when showInvisibles is enabled " , ->
2014-05-20 03:56:53 +04:00
invisibles = null
2014-05-20 03:20:51 +04:00
beforeEach ->
2014-05-20 03:56:53 +04:00
invisibles =
eol: ' E '
space: ' S '
tab: ' T '
cr: ' C '
2014-05-20 04:10:04 +04:00
atom . config . set ( " editor.showInvisibles " , true )
atom . config . set ( " editor.invisibles " , invisibles )
2014-08-13 00:18:20 +04:00
nextAnimationFrame ( )
2014-05-20 03:20:51 +04:00
2014-05-21 01:17:19 +04:00
it " re-renders the lines when the showInvisibles config option changes " , ->
2014-08-12 21:20:12 +04:00
editor . setText " a line with tabs \t and spaces \n "
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-06-19 03:46:20 +04:00
expect ( component . lineNodeForScreenRow ( 0 ) . textContent ) . toBe " #{ invisibles . space } a line with tabs #{ invisibles . tab } and spaces #{ invisibles . space } #{ invisibles . eol } "
2014-06-20 02:48:40 +04:00
2014-05-20 23:34:43 +04:00
atom . config . set ( " editor.showInvisibles " , false )
2014-08-12 23:45:57 +04:00
nextAnimationFrame ( )
2014-06-19 03:46:20 +04:00
expect ( component . lineNodeForScreenRow ( 0 ) . textContent ) . toBe " a line with tabs and spaces "
2014-06-20 02:48:40 +04:00
2014-05-20 23:34:43 +04:00
atom . config . set ( " editor.showInvisibles " , true )
2014-08-12 23:45:57 +04:00
nextAnimationFrame ( )
2014-06-19 03:46:20 +04:00
expect ( component . lineNodeForScreenRow ( 0 ) . textContent ) . toBe " #{ invisibles . space } a line with tabs #{ invisibles . tab } and spaces #{ invisibles . space } #{ invisibles . eol } "
2014-05-20 23:34:43 +04:00
2014-08-12 00:39:04 +04:00
it " displays leading/trailing spaces, tabs, and newlines as visible characters " , ->
2014-08-12 21:20:12 +04:00
editor . setText " a line with tabs \t and spaces \n "
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-06-19 03:46:20 +04:00
expect ( component . lineNodeForScreenRow ( 0 ) . textContent ) . toBe " #{ invisibles . space } a line with tabs #{ invisibles . tab } and spaces #{ invisibles . space } #{ invisibles . eol } "
2014-05-20 03:20:51 +04:00
2014-08-12 00:39:04 +04:00
leafNodes = getLeafNodes ( component . lineNodeForScreenRow ( 0 ) )
expect ( leafNodes [ 0 ] . classList . contains ( ' invisible-character ' ) ) . toBe true
expect ( leafNodes [ leafNodes . length - 1 ] . classList . contains ( ' invisible-character ' ) ) . toBe true
2014-10-14 03:30:41 +04:00
it " displays newlines as their own token outside of the other tokens ' scopeDescriptor " , ->
2014-08-12 21:20:12 +04:00
editor . setText " var \n "
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-05-20 04:27:59 +04:00
expect ( component . lineNodeForScreenRow ( 0 ) . innerHTML ) . toBe " <span class= \" source js \" ><span class= \" storage modifier js \" >var</span></span><span class= \" invisible-character \" > #{ invisibles . eol } </span> "
2014-05-20 03:20:51 +04:00
2014-05-21 01:17:19 +04:00
it " displays trailing carriage returns using a visible, non-empty value " , ->
2014-05-20 21:38:01 +04:00
editor . setText " a line that ends with a carriage return \r \n "
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-05-20 21:38:01 +04:00
expect ( component . lineNodeForScreenRow ( 0 ) . textContent ) . toBe " a line that ends with a carriage return #{ invisibles . cr } #{ invisibles . eol } "
2014-05-20 03:20:51 +04:00
2014-07-09 02:51:41 +04:00
it " renders invisible line-ending characters on empty lines " , ->
2014-07-23 20:30:52 +04:00
expect ( component . lineNodeForScreenRow ( 10 ) . textContent ) . toBe invisibles . eol
it " renders an nbsp on empty lines when the line-ending character is an empty string " , ->
atom . config . set ( " editor.invisibles " , eol: ' ' )
2014-08-13 01:32:21 +04:00
nextAnimationFrame ( )
2014-07-23 20:30:52 +04:00
expect ( component . lineNodeForScreenRow ( 10 ) . textContent ) . toBe nbsp
2014-08-13 01:32:21 +04:00
it " renders an nbsp on empty lines when the line-ending character is false " , ->
atom . config . set ( " editor.invisibles " , eol: false )
nextAnimationFrame ( )
2014-07-23 20:30:52 +04:00
expect ( component . lineNodeForScreenRow ( 10 ) . textContent ) . toBe nbsp
2014-07-09 02:51:41 +04:00
it " interleaves invisible line-ending characters with indent guides on empty lines " , ->
component . setShowIndentGuide ( true )
2014-11-26 02:05:07 +03:00
editor . setTextInBufferRange ( [ [ 10 , 0 ] , [ 11 , 0 ] ] , " \r \n " , normalizeLineEndings: false )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-07-09 02:51:41 +04:00
expect ( component . lineNodeForScreenRow ( 10 ) . innerHTML ) . toBe ' <span class= " indent-guide " ><span class= " invisible-character " >C</span><span class= " invisible-character " >E</span></span> '
editor . setTabLength ( 3 )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-07-09 02:51:41 +04:00
expect ( component . lineNodeForScreenRow ( 10 ) . innerHTML ) . toBe ' <span class= " indent-guide " ><span class= " invisible-character " >C</span><span class= " invisible-character " >E</span> </span> '
editor . setTabLength ( 1 )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-07-09 02:51:41 +04:00
expect ( component . lineNodeForScreenRow ( 10 ) . innerHTML ) . toBe ' <span class= " indent-guide " ><span class= " invisible-character " >C</span></span><span class= " indent-guide " ><span class= " invisible-character " >E</span></span> '
editor . setTextInBufferRange ( [ [ 9 , 0 ] , [ 9 , Infinity ] ] , ' ' )
editor . setTextInBufferRange ( [ [ 11 , 0 ] , [ 11 , Infinity ] ] , ' ' )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-07-09 02:51:41 +04:00
expect ( component . lineNodeForScreenRow ( 10 ) . innerHTML ) . toBe ' <span class= " indent-guide " ><span class= " invisible-character " >C</span></span><span class= " invisible-character " >E</span> '
2014-05-20 03:20:51 +04:00
describe " when soft wrapping is enabled " , ->
beforeEach ->
2014-08-12 21:20:12 +04:00
editor . setText " a line that wraps \n "
2014-09-04 18:42:32 +04:00
editor . setSoftWrapped ( true )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-07-12 02:12:41 +04:00
componentNode.style.width = 16 * charWidth + editor . getVerticalScrollbarWidth ( ) + ' px '
2014-07-14 22:16:32 +04:00
component . measureHeightAndWidth ( )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-05-20 03:20:51 +04:00
2014-05-20 22:44:55 +04:00
it " doesn ' t show end of line invisibles at the end of wrapped lines " , ->
expect ( component . lineNodeForScreenRow ( 0 ) . textContent ) . toBe " a line that "
expect ( component . lineNodeForScreenRow ( 1 ) . textContent ) . toBe " wraps #{ invisibles . space } #{ invisibles . eol } "
2014-05-20 03:20:51 +04:00
2014-04-09 23:49:56 +04:00
describe " when indent guides are enabled " , ->
2014-04-10 03:07:04 +04:00
beforeEach ->
2014-04-09 23:49:56 +04:00
component . setShowIndentGuide ( true )
2014-04-10 03:07:04 +04:00
it " adds an ' indent-guide ' class to spans comprising the leading whitespace " , ->
2014-05-16 20:44:49 +04:00
line1LeafNodes = getLeafNodes ( component . lineNodeForScreenRow ( 1 ) )
2014-04-09 23:49:56 +04:00
expect ( line1LeafNodes [ 0 ] . textContent ) . toBe ' '
expect ( line1LeafNodes [ 0 ] . classList . contains ( ' indent-guide ' ) ) . toBe true
expect ( line1LeafNodes [ 1 ] . classList . contains ( ' indent-guide ' ) ) . toBe false
2014-05-17 01:21:12 +04:00
line2LeafNodes = getLeafNodes ( component . lineNodeForScreenRow ( 2 ) )
2014-04-09 23:49:56 +04:00
expect ( line2LeafNodes [ 0 ] . textContent ) . toBe ' '
expect ( line2LeafNodes [ 0 ] . classList . contains ( ' indent-guide ' ) ) . toBe true
expect ( line2LeafNodes [ 1 ] . textContent ) . toBe ' '
expect ( line2LeafNodes [ 1 ] . classList . contains ( ' indent-guide ' ) ) . toBe true
expect ( line2LeafNodes [ 2 ] . classList . contains ( ' indent-guide ' ) ) . toBe false
2014-04-10 03:07:04 +04:00
it " renders leading whitespace spans with the ' indent-guide ' class for empty lines " , ->
editor . getBuffer ( ) . insert ( [ 1 , Infinity ] , ' \n ' )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-04-10 03:07:04 +04:00
2014-05-17 01:21:12 +04:00
line2LeafNodes = getLeafNodes ( component . lineNodeForScreenRow ( 2 ) )
2014-04-10 03:07:04 +04:00
2014-07-08 03:53:02 +04:00
expect ( line2LeafNodes . length ) . toBe 2
2014-04-10 03:07:04 +04:00
expect ( line2LeafNodes [ 0 ] . textContent ) . toBe ' '
expect ( line2LeafNodes [ 0 ] . classList . contains ( ' indent-guide ' ) ) . toBe true
expect ( line2LeafNodes [ 1 ] . textContent ) . toBe ' '
expect ( line2LeafNodes [ 1 ] . classList . contains ( ' indent-guide ' ) ) . toBe true
2014-04-10 03:20:22 +04:00
it " renders indent guides correctly on lines containing only whitespace " , ->
editor . getBuffer ( ) . insert ( [ 1 , Infinity ] , ' \n ' )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-06-20 02:48:40 +04:00
2014-05-17 01:21:12 +04:00
line2LeafNodes = getLeafNodes ( component . lineNodeForScreenRow ( 2 ) )
2014-04-10 03:20:22 +04:00
expect ( line2LeafNodes . length ) . toBe 3
expect ( line2LeafNodes [ 0 ] . textContent ) . toBe ' '
expect ( line2LeafNodes [ 0 ] . classList . contains ( ' indent-guide ' ) ) . toBe true
expect ( line2LeafNodes [ 1 ] . textContent ) . toBe ' '
expect ( line2LeafNodes [ 1 ] . classList . contains ( ' indent-guide ' ) ) . toBe true
expect ( line2LeafNodes [ 2 ] . textContent ) . toBe ' '
expect ( line2LeafNodes [ 2 ] . classList . contains ( ' indent-guide ' ) ) . toBe true
2014-04-16 22:37:35 +04:00
it " does not render indent guides in trailing whitespace for lines containing non whitespace characters " , ->
2014-05-17 01:21:12 +04:00
editor . getBuffer ( ) . setText " hi "
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-06-20 02:48:40 +04:00
2014-05-17 01:21:12 +04:00
line0LeafNodes = getLeafNodes ( component . lineNodeForScreenRow ( 0 ) )
2014-04-16 22:37:35 +04:00
expect ( line0LeafNodes [ 0 ] . textContent ) . toBe ' '
expect ( line0LeafNodes [ 0 ] . classList . contains ( ' indent-guide ' ) ) . toBe true
expect ( line0LeafNodes [ 1 ] . textContent ) . toBe ' '
expect ( line0LeafNodes [ 1 ] . classList . contains ( ' indent-guide ' ) ) . toBe false
2014-07-01 22:41:44 +04:00
it " updates the indent guides on empty lines preceding an indentation change " , ->
2014-06-25 01:45:54 +04:00
editor . getBuffer ( ) . insert ( [ 12 , 0 ] , ' \n ' )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-07-08 03:53:02 +04:00
editor . getBuffer ( ) . insert ( [ 13 , 0 ] , ' ' )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-06-25 01:45:54 +04:00
line12LeafNodes = getLeafNodes ( component . lineNodeForScreenRow ( 12 ) )
expect ( line12LeafNodes [ 0 ] . textContent ) . toBe ' '
expect ( line12LeafNodes [ 0 ] . classList . contains ( ' indent-guide ' ) ) . toBe true
expect ( line12LeafNodes [ 1 ] . textContent ) . toBe ' '
expect ( line12LeafNodes [ 1 ] . classList . contains ( ' indent-guide ' ) ) . toBe true
2014-07-01 22:41:44 +04:00
it " updates the indent guides on empty lines following an indentation change " , ->
editor . getBuffer ( ) . insert ( [ 12 , 2 ] , ' \n ' )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-07-08 03:53:02 +04:00
editor . getBuffer ( ) . insert ( [ 12 , 0 ] , ' ' )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-07-01 22:41:44 +04:00
line13LeafNodes = getLeafNodes ( component . lineNodeForScreenRow ( 13 ) )
expect ( line13LeafNodes [ 0 ] . textContent ) . toBe ' '
expect ( line13LeafNodes [ 0 ] . classList . contains ( ' indent-guide ' ) ) . toBe true
expect ( line13LeafNodes [ 1 ] . textContent ) . toBe ' '
expect ( line13LeafNodes [ 1 ] . classList . contains ( ' indent-guide ' ) ) . toBe true
2014-07-14 00:11:02 +04:00
describe " when indent guides are disabled " , ->
beforeEach ->
component . setShowIndentGuide ( false )
it " does not render indent guides on lines containing only whitespace " , ->
editor . getBuffer ( ) . insert ( [ 1 , Infinity ] , ' \n ' )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-07-14 00:11:02 +04:00
line2LeafNodes = getLeafNodes ( component . lineNodeForScreenRow ( 2 ) )
expect ( line2LeafNodes . length ) . toBe 3
expect ( line2LeafNodes [ 0 ] . textContent ) . toBe ' '
expect ( line2LeafNodes [ 0 ] . classList . contains ( ' indent-guide ' ) ) . toBe false
expect ( line2LeafNodes [ 1 ] . textContent ) . toBe ' '
expect ( line2LeafNodes [ 1 ] . classList . contains ( ' indent-guide ' ) ) . toBe false
expect ( line2LeafNodes [ 2 ] . textContent ) . toBe ' '
expect ( line2LeafNodes [ 2 ] . classList . contains ( ' indent-guide ' ) ) . toBe false
2014-06-11 17:18:52 +04:00
describe " when the buffer contains null bytes " , ->
it " excludes the null byte from character measurement " , ->
editor . setText ( " a \0 b " )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2015-01-09 19:22:45 +03:00
expect ( wrapperNode . pixelPositionForScreenPosition ( [ 0 , Infinity ] ) . left ) . toEqual 2 * charWidth
2014-06-11 17:18:52 +04:00
2014-06-19 00:47:38 +04:00
describe " when there is a fold " , ->
it " renders a fold marker on the folded line " , ->
foldedLineNode = component . lineNodeForScreenRow ( 4 )
expect ( foldedLineNode . querySelector ( ' .fold-marker ' ) ) . toBeFalsy ( )
editor . foldBufferRow ( 4 )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-06-19 00:47:38 +04:00
foldedLineNode = component . lineNodeForScreenRow ( 4 )
expect ( foldedLineNode . querySelector ( ' .fold-marker ' ) ) . toBeTruthy ( )
editor . unfoldBufferRow ( 4 )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-06-19 00:47:38 +04:00
foldedLineNode = component . lineNodeForScreenRow ( 4 )
expect ( foldedLineNode . querySelector ( ' .fold-marker ' ) ) . toBeFalsy ( )
2014-04-08 04:13:19 +04:00
describe " gutter rendering " , ->
2014-06-13 05:11:55 +04:00
[ gutter ] = [ ]
2014-06-06 01:03:55 +04:00
beforeEach ->
{ gutter } = component . refs
2014-04-08 04:13:19 +04:00
it " renders the currently-visible line numbers " , ->
2014-07-13 21:56:14 +04:00
wrapperNode.style.height = 4.5 * lineHeightInPixels + ' px '
2014-07-14 22:16:32 +04:00
component . measureHeightAndWidth ( )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-04-08 04:13:19 +04:00
2014-07-13 21:56:14 +04:00
expect ( componentNode . querySelectorAll ( ' .line-number ' ) . length ) . toBe 6 + 2 + 1 # line overdraw margin below + dummy line number
2014-05-17 00:52:24 +04:00
expect ( component . lineNumberNodeForScreenRow ( 0 ) . textContent ) . toBe " #{ nbsp } 1 "
expect ( component . lineNumberNodeForScreenRow ( 5 ) . textContent ) . toBe " #{ nbsp } 6 "
2014-04-08 04:13:19 +04:00
2014-04-14 23:06:15 +04:00
verticalScrollbarNode.scrollTop = 2.5 * lineHeightInPixels
verticalScrollbarNode . dispatchEvent ( new UIEvent ( ' scroll ' ) )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-04-08 04:13:19 +04:00
2014-07-13 21:56:14 +04:00
expect ( componentNode . querySelectorAll ( ' .line-number ' ) . length ) . toBe 6 + 4 + 1 # line overdraw margin above/below + dummy line number
2014-05-12 22:45:58 +04:00
2014-05-17 00:52:24 +04:00
expect ( component . lineNumberNodeForScreenRow ( 2 ) . textContent ) . toBe " #{ nbsp } 3 "
expect ( component . lineNumberNodeForScreenRow ( 2 ) . offsetTop ) . toBe 2 * lineHeightInPixels
expect ( component . lineNumberNodeForScreenRow ( 7 ) . textContent ) . toBe " #{ nbsp } 8 "
expect ( component . lineNumberNodeForScreenRow ( 7 ) . offsetTop ) . toBe 7 * lineHeightInPixels
2014-04-08 04:13:19 +04:00
2014-05-12 22:45:58 +04:00
it " updates the translation of subsequent line numbers when lines are inserted or removed " , ->
2014-04-22 21:10:48 +04:00
editor . getBuffer ( ) . insert ( [ 0 , 0 ] , ' \n \n ' )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-04-22 21:10:48 +04:00
2014-07-13 21:56:14 +04:00
lineNumberNodes = componentNode . querySelectorAll ( ' .line-number ' )
2014-05-17 00:52:24 +04:00
expect ( component . lineNumberNodeForScreenRow ( 0 ) . offsetTop ) . toBe 0
expect ( component . lineNumberNodeForScreenRow ( 1 ) . offsetTop ) . toBe 1 * lineHeightInPixels
expect ( component . lineNumberNodeForScreenRow ( 2 ) . offsetTop ) . toBe 2 * lineHeightInPixels
expect ( component . lineNumberNodeForScreenRow ( 3 ) . offsetTop ) . toBe 3 * lineHeightInPixels
expect ( component . lineNumberNodeForScreenRow ( 4 ) . offsetTop ) . toBe 4 * lineHeightInPixels
2014-04-22 21:10:48 +04:00
editor . getBuffer ( ) . insert ( [ 0 , 0 ] , ' \n \n ' )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-06-20 02:48:40 +04:00
2014-05-17 00:52:24 +04:00
expect ( component . lineNumberNodeForScreenRow ( 0 ) . offsetTop ) . toBe 0
expect ( component . lineNumberNodeForScreenRow ( 1 ) . offsetTop ) . toBe 1 * lineHeightInPixels
expect ( component . lineNumberNodeForScreenRow ( 2 ) . offsetTop ) . toBe 2 * lineHeightInPixels
expect ( component . lineNumberNodeForScreenRow ( 3 ) . offsetTop ) . toBe 3 * lineHeightInPixels
expect ( component . lineNumberNodeForScreenRow ( 4 ) . offsetTop ) . toBe 4 * lineHeightInPixels
expect ( component . lineNumberNodeForScreenRow ( 5 ) . offsetTop ) . toBe 5 * lineHeightInPixels
expect ( component . lineNumberNodeForScreenRow ( 6 ) . offsetTop ) . toBe 6 * lineHeightInPixels
2014-04-22 21:10:48 +04:00
2014-04-08 05:06:39 +04:00
it " renders • characters for soft-wrapped lines " , ->
2014-09-04 18:42:32 +04:00
editor . setSoftWrapped ( true )
2014-07-13 21:56:14 +04:00
wrapperNode.style.height = 4.5 * lineHeightInPixels + ' px '
wrapperNode.style.width = 30 * charWidth + ' px '
2014-07-14 22:16:32 +04:00
component . measureHeightAndWidth ( )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-04-08 05:06:39 +04:00
2014-07-13 21:56:14 +04:00
expect ( componentNode . querySelectorAll ( ' .line-number ' ) . length ) . toBe 6 + lineOverdrawMargin + 1 # 1 dummy line componentNode
2014-05-17 00:52:24 +04:00
expect ( component . lineNumberNodeForScreenRow ( 0 ) . textContent ) . toBe " #{ nbsp } 1 "
expect ( component . lineNumberNodeForScreenRow ( 1 ) . textContent ) . toBe " #{ nbsp } • "
expect ( component . lineNumberNodeForScreenRow ( 2 ) . textContent ) . toBe " #{ nbsp } 2 "
expect ( component . lineNumberNodeForScreenRow ( 3 ) . textContent ) . toBe " #{ nbsp } • "
expect ( component . lineNumberNodeForScreenRow ( 4 ) . textContent ) . toBe " #{ nbsp } 3 "
expect ( component . lineNumberNodeForScreenRow ( 5 ) . textContent ) . toBe " #{ nbsp } • "
2014-04-08 05:06:39 +04:00
2014-05-17 00:52:24 +04:00
it " pads line numbers to be right-justified based on the maximum number of line number digits " , ->
2014-04-24 21:09:51 +04:00
editor . getBuffer ( ) . setText ( [ 1 . . 10 ] . join ( ' \n ' ) )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-05-17 00:52:24 +04:00
for screenRow in [ 0 . . 8 ]
expect ( component . lineNumberNodeForScreenRow ( screenRow ) . textContent ) . toBe " #{ nbsp } #{ screenRow + 1 } "
expect ( component . lineNumberNodeForScreenRow ( 9 ) . textContent ) . toBe " 10 "
2014-04-24 21:09:51 +04:00
2014-07-13 21:56:14 +04:00
gutterNode = componentNode . querySelector ( ' .gutter ' )
2014-05-17 01:56:18 +04:00
initialGutterWidth = gutterNode . offsetWidth
2014-04-24 21:09:51 +04:00
# Removes padding when the max number of digits goes down
editor . getBuffer ( ) . delete ( [ [ 1 , 0 ] , [ 2 , 0 ] ] )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-05-17 00:52:24 +04:00
for screenRow in [ 0 . . 8 ]
expect ( component . lineNumberNodeForScreenRow ( screenRow ) . textContent ) . toBe " #{ screenRow + 1 } "
2014-05-17 01:56:18 +04:00
expect ( gutterNode . offsetWidth ) . toBeLessThan initialGutterWidth
# Increases padding when the max number of digits goes up
editor . getBuffer ( ) . insert ( [ 0 , 0 ] , ' \n \n ' )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-05-17 01:56:18 +04:00
for screenRow in [ 0 . . 8 ]
expect ( component . lineNumberNodeForScreenRow ( screenRow ) . textContent ) . toBe " #{ nbsp } #{ screenRow + 1 } "
expect ( component . lineNumberNodeForScreenRow ( 9 ) . textContent ) . toBe " 10 "
expect ( gutterNode . offsetWidth ) . toBe initialGutterWidth
2014-04-24 21:09:51 +04:00
2014-06-21 11:58:11 +04:00
it " renders the .line-numbers div at the full height of the editor even if it ' s taller than its content " , ->
2014-07-13 21:56:14 +04:00
wrapperNode.style.height = componentNode . offsetHeight + 100 + ' px '
2014-07-14 22:16:32 +04:00
component . measureHeightAndWidth ( )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-07-13 21:56:14 +04:00
expect ( componentNode . querySelector ( ' .line-numbers ' ) . offsetHeight ) . toBe componentNode . offsetHeight
2014-06-21 11:58:11 +04:00
2014-07-25 04:23:04 +04:00
it " applies the background color of the gutter or the editor to the line numbers to improve GPU performance " , ->
gutterNode = componentNode . querySelector ( ' .gutter ' )
lineNumbersNode = gutterNode . querySelector ( ' .line-numbers ' )
{ backgroundColor } = getComputedStyle ( wrapperNode )
expect ( lineNumbersNode . style . backgroundColor ) . toBe backgroundColor
# favor gutter color if it's assigned
gutterNode.style.backgroundColor = ' rgb(255, 0, 0) '
advanceClock ( component . domPollingInterval )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-07-25 04:23:04 +04:00
expect ( lineNumbersNode . style . backgroundColor ) . toBe ' rgb(255, 0, 0) '
2015-01-12 21:10:58 +03:00
it " hides or shows the gutter based on the ' ::isGutterVisible ' property on the model and the global ' editor.showLineNumbers ' config setting " , ->
expect ( component . refs . gutter ? ) . toBe true
editor . setGutterVisible ( false )
nextAnimationFrame ( )
expect ( component . refs . gutter ? ) . toBe false
atom . config . set ( " editor.showLineNumbers " , false )
2015-01-12 21:13:51 +03:00
expect ( nextAnimationFrame ) . toBe noAnimationFrame
2015-01-12 21:10:58 +03:00
expect ( component . refs . gutter ? ) . toBe false
editor . setGutterVisible ( true )
2015-01-12 21:13:51 +03:00
expect ( nextAnimationFrame ) . toBe noAnimationFrame
2015-01-12 21:10:58 +03:00
expect ( component . refs . gutter ? ) . toBe false
atom . config . set ( " editor.showLineNumbers " , true )
nextAnimationFrame ( )
expect ( component . refs . gutter ? ) . toBe true
expect ( component . lineNumberNodeForScreenRow ( 3 ) ? ) . toBe true
2014-06-24 02:22:16 +04:00
2014-06-06 23:23:12 +04:00
describe " fold decorations " , ->
describe " rendering fold decorations " , ->
it " adds the foldable class to line numbers when the line is foldable " , ->
expect ( lineNumberHasClass ( 0 , ' foldable ' ) ) . toBe true
expect ( lineNumberHasClass ( 1 , ' foldable ' ) ) . toBe true
expect ( lineNumberHasClass ( 2 , ' foldable ' ) ) . toBe false
expect ( lineNumberHasClass ( 3 , ' foldable ' ) ) . toBe false
expect ( lineNumberHasClass ( 4 , ' foldable ' ) ) . toBe true
expect ( lineNumberHasClass ( 5 , ' foldable ' ) ) . toBe false
it " updates the foldable class on the correct line numbers when the foldable positions change " , ->
editor . getBuffer ( ) . insert ( [ 0 , 0 ] , ' \n ' )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-06-06 23:23:12 +04:00
expect ( lineNumberHasClass ( 0 , ' foldable ' ) ) . toBe false
expect ( lineNumberHasClass ( 1 , ' foldable ' ) ) . toBe true
expect ( lineNumberHasClass ( 2 , ' foldable ' ) ) . toBe true
expect ( lineNumberHasClass ( 3 , ' foldable ' ) ) . toBe false
expect ( lineNumberHasClass ( 4 , ' foldable ' ) ) . toBe false
expect ( lineNumberHasClass ( 5 , ' foldable ' ) ) . toBe true
expect ( lineNumberHasClass ( 6 , ' foldable ' ) ) . toBe false
2014-06-10 01:03:53 +04:00
it " updates the foldable class on a line number that becomes foldable " , ->
expect ( lineNumberHasClass ( 11 , ' foldable ' ) ) . toBe false
editor . getBuffer ( ) . insert ( [ 11 , 44 ] , ' \n fold me ' )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-06-10 01:03:53 +04:00
expect ( lineNumberHasClass ( 11 , ' foldable ' ) ) . toBe true
editor . undo ( )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-06-10 01:03:53 +04:00
expect ( lineNumberHasClass ( 11 , ' foldable ' ) ) . toBe false
2014-07-13 21:56:14 +04:00
it " adds, updates and removes the folded class on the correct line number componentNodes " , ->
2014-06-06 23:23:12 +04:00
editor . foldBufferRow ( 4 )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-06-06 23:23:12 +04:00
expect ( lineNumberHasClass ( 4 , ' folded ' ) ) . toBe true
editor . getBuffer ( ) . insert ( [ 0 , 0 ] , ' \n ' )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-06-06 23:23:12 +04:00
expect ( lineNumberHasClass ( 4 , ' folded ' ) ) . toBe false
expect ( lineNumberHasClass ( 5 , ' folded ' ) ) . toBe true
editor . unfoldBufferRow ( 5 )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-06-06 23:23:12 +04:00
expect ( lineNumberHasClass ( 5 , ' folded ' ) ) . toBe false
describe " mouse interactions with fold indicators " , ->
[ gutterNode ] = [ ]
buildClickEvent = (target) ->
2014-06-10 00:56:23 +04:00
buildMouseEvent ( ' click ' , { target } )
2014-06-06 23:23:12 +04:00
beforeEach ->
2014-07-13 21:56:14 +04:00
gutterNode = componentNode . querySelector ( ' .gutter ' )
2014-06-06 23:23:12 +04:00
it " folds and unfolds the block represented by the fold indicator when clicked " , ->
2014-06-10 00:56:23 +04:00
expect ( lineNumberHasClass ( 1 , ' folded ' ) ) . toBe false
2014-06-06 23:23:12 +04:00
2014-06-10 00:56:23 +04:00
lineNumber = component . lineNumberNodeForScreenRow ( 1 )
2014-06-06 23:23:12 +04:00
target = lineNumber . querySelector ( ' .icon-right ' )
2014-06-10 00:56:23 +04:00
target . dispatchEvent ( buildClickEvent ( target ) )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-06-10 00:56:23 +04:00
expect ( lineNumberHasClass ( 1 , ' folded ' ) ) . toBe true
lineNumber = component . lineNumberNodeForScreenRow ( 1 )
2014-06-06 23:23:12 +04:00
target = lineNumber . querySelector ( ' .icon-right ' )
2014-06-10 00:56:23 +04:00
target . dispatchEvent ( buildClickEvent ( target ) )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-06-10 00:56:23 +04:00
expect ( lineNumberHasClass ( 1 , ' folded ' ) ) . toBe false
2014-06-06 23:23:12 +04:00
2014-07-13 21:56:14 +04:00
it " does not fold when the line number componentNode is clicked " , ->
2014-06-10 01:11:41 +04:00
lineNumber = component . lineNumberNodeForScreenRow ( 1 )
lineNumber . dispatchEvent ( buildClickEvent ( lineNumber ) )
2014-07-26 03:38:55 +04:00
expect ( nextAnimationFrame ) . toBe noAnimationFrame
2014-06-10 00:56:23 +04:00
expect ( lineNumberHasClass ( 1 , ' folded ' ) ) . toBe false
2014-06-05 03:36:41 +04:00
2014-04-04 03:04:19 +04:00
describe " cursor rendering " , ->
2014-05-13 01:13:56 +04:00
it " renders the currently visible cursors, translated relative to the scroll position " , ->
2014-08-29 05:10:18 +04:00
cursor1 = editor . getLastCursor ( )
2014-04-04 03:04:19 +04:00
cursor1 . setScreenPosition ( [ 0 , 5 ] )
2014-07-13 21:56:14 +04:00
wrapperNode.style.height = 4.5 * lineHeightInPixels + ' px '
wrapperNode.style.width = 20 * lineHeightInPixels + ' px '
2014-07-14 22:16:32 +04:00
component . measureHeightAndWidth ( )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-04-04 03:04:19 +04:00
2014-07-13 21:56:14 +04:00
cursorNodes = componentNode . querySelectorAll ( ' .cursor ' )
2014-04-04 03:04:19 +04:00
expect ( cursorNodes . length ) . toBe 1
expect ( cursorNodes [ 0 ] . offsetHeight ) . toBe lineHeightInPixels
expect ( cursorNodes [ 0 ] . offsetWidth ) . toBe charWidth
2014-07-27 21:37:36 +04:00
expect ( cursorNodes [ 0 ] . style [ ' -webkit-transform ' ] ) . toBe " translate( #{ 5 * charWidth } px, #{ 0 * lineHeightInPixels } px) "
2014-04-04 03:04:19 +04:00
2014-06-02 06:09:29 +04:00
cursor2 = editor . addCursorAtScreenPosition ( [ 8 , 11 ] )
2014-04-04 03:04:19 +04:00
cursor3 = editor . addCursorAtScreenPosition ( [ 4 , 10 ] )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-04-04 03:04:19 +04:00
2014-07-13 21:56:14 +04:00
cursorNodes = componentNode . querySelectorAll ( ' .cursor ' )
2014-04-04 03:04:19 +04:00
expect ( cursorNodes . length ) . toBe 2
expect ( cursorNodes [ 0 ] . offsetTop ) . toBe 0
2014-07-27 21:37:36 +04:00
expect ( cursorNodes [ 0 ] . style [ ' -webkit-transform ' ] ) . toBe " translate( #{ 5 * charWidth } px, #{ 0 * lineHeightInPixels } px) "
expect ( cursorNodes [ 1 ] . style [ ' -webkit-transform ' ] ) . toBe " translate( #{ 10 * charWidth } px, #{ 4 * lineHeightInPixels } px) "
2014-04-04 03:04:19 +04:00
2014-06-02 06:09:29 +04:00
verticalScrollbarNode.scrollTop = 4.5 * lineHeightInPixels
2014-04-14 23:06:15 +04:00
verticalScrollbarNode . dispatchEvent ( new UIEvent ( ' scroll ' ) )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-05-13 01:13:56 +04:00
horizontalScrollbarNode.scrollLeft = 3.5 * charWidth
horizontalScrollbarNode . dispatchEvent ( new UIEvent ( ' scroll ' ) )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-04-04 03:04:19 +04:00
2014-07-13 21:56:14 +04:00
cursorNodes = componentNode . querySelectorAll ( ' .cursor ' )
2014-04-04 03:04:19 +04:00
expect ( cursorNodes . length ) . toBe 2
2014-07-27 21:37:36 +04:00
expect ( cursorNodes [ 0 ] . style [ ' -webkit-transform ' ] ) . toBe " translate( #{ 11 * charWidth } px, #{ 8 * lineHeightInPixels } px) "
expect ( cursorNodes [ 1 ] . style [ ' -webkit-transform ' ] ) . toBe " translate( #{ 10 * charWidth } px, #{ 4 * lineHeightInPixels } px) "
2014-04-04 03:04:19 +04:00
cursor3 . destroy ( )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-07-13 21:56:14 +04:00
cursorNodes = componentNode . querySelectorAll ( ' .cursor ' )
2014-04-04 03:04:19 +04:00
expect ( cursorNodes . length ) . toBe 1
2014-07-27 21:37:36 +04:00
expect ( cursorNodes [ 0 ] . style [ ' -webkit-transform ' ] ) . toBe " translate( #{ 11 * charWidth } px, #{ 8 * lineHeightInPixels } px) "
2014-04-04 03:04:19 +04:00
it " accounts for character widths when positioning cursors " , ->
atom . config . set ( ' editor.fontFamily ' , ' sans-serif ' )
editor . setCursorScreenPosition ( [ 0 , 16 ] )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-04-04 03:04:19 +04:00
2014-07-13 21:56:14 +04:00
cursor = componentNode . querySelector ( ' .cursor ' )
2014-04-04 03:04:19 +04:00
cursorRect = cursor . getBoundingClientRect ( )
2014-05-16 21:00:05 +04:00
cursorLocationTextNode = component . lineNodeForScreenRow ( 0 ) . querySelector ( ' .storage.type.function.js ' ) . firstChild
2014-04-04 03:04:19 +04:00
range = document . createRange ( )
range . setStart ( cursorLocationTextNode , 0 )
range . setEnd ( cursorLocationTextNode , 1 )
rangeRect = range . getBoundingClientRect ( )
expect ( cursorRect . left ) . toBe rangeRect . left
expect ( cursorRect . width ) . toBe rangeRect . width
2014-10-24 23:06:51 +04:00
it " accounts for the width of paired characters when positioning cursors " , ->
atom . config . set ( ' editor.fontFamily ' , ' sans-serif ' )
editor . setText ( ' he \u 0301y ' ) # e with an accent mark
editor . setCursorBufferPosition ( [ 0 , 3 ] )
nextAnimationFrame ( )
cursor = componentNode . querySelector ( ' .cursor ' )
cursorRect = cursor . getBoundingClientRect ( )
cursorLocationTextNode = component . lineNodeForScreenRow ( 0 ) . querySelector ( ' .source.js ' ) . firstChild
range = document . createRange ( )
range . setStart ( cursorLocationTextNode , 3 )
range . setEnd ( cursorLocationTextNode , 4 )
rangeRect = range . getBoundingClientRect ( )
expect ( cursorRect . left ) . toBe rangeRect . left
expect ( cursorRect . width ) . toBe rangeRect . width
2014-07-08 01:39:00 +04:00
it " positions cursors correctly after character widths are changed via a stylesheet change " , ->
atom . config . set ( ' editor.fontFamily ' , ' sans-serif ' )
editor . setCursorScreenPosition ( [ 0 , 16 ] )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-07-08 01:39:00 +04:00
2014-10-16 22:42:29 +04:00
atom . styles . addStyleSheet """
2014-07-08 01:39:00 +04:00
. function . js {
font - weight: bold ;
}
2014-10-16 22:42:29 +04:00
""" , context: ' atom-text-editor '
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( ) # update based on new measurements
2014-07-08 01:39:00 +04:00
2014-07-13 21:56:14 +04:00
cursor = componentNode . querySelector ( ' .cursor ' )
2014-07-08 01:39:00 +04:00
cursorRect = cursor . getBoundingClientRect ( )
cursorLocationTextNode = component . lineNodeForScreenRow ( 0 ) . querySelector ( ' .storage.type.function.js ' ) . firstChild
range = document . createRange ( )
range . setStart ( cursorLocationTextNode , 0 )
range . setEnd ( cursorLocationTextNode , 1 )
rangeRect = range . getBoundingClientRect ( )
expect ( cursorRect . left ) . toBe rangeRect . left
expect ( cursorRect . width ) . toBe rangeRect . width
2014-07-08 21:55:50 +04:00
atom . themes . removeStylesheet ( ' test ' )
2014-06-11 21:53:51 +04:00
it " sets the cursor to the default character width at the end of a line " , ->
editor . setCursorScreenPosition ( [ 0 , Infinity ] )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-07-13 21:56:14 +04:00
cursorNode = componentNode . querySelector ( ' .cursor ' )
2014-06-11 21:53:51 +04:00
expect ( cursorNode . offsetWidth ) . toBe charWidth
2014-06-11 21:40:57 +04:00
it " gives the cursor a non-zero width even if it ' s inside atomic tokens " , ->
editor . setCursorScreenPosition ( [ 1 , 0 ] )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-07-13 21:56:14 +04:00
cursorNode = componentNode . querySelector ( ' .cursor ' )
2014-06-11 21:40:57 +04:00
expect ( cursorNode . offsetWidth ) . toBe charWidth
2014-04-08 00:28:44 +04:00
it " blinks cursors when they aren ' t moving " , ->
2014-07-13 21:56:14 +04:00
cursorsNode = componentNode . querySelector ( ' .cursors ' )
2014-05-21 00:04:59 +04:00
expect ( cursorsNode . classList . contains ( ' blink-off ' ) ) . toBe false
advanceClock ( component . props . cursorBlinkPeriod / 2 )
expect ( cursorsNode . classList . contains ( ' blink-off ' ) ) . toBe true
2014-06-02 06:09:29 +04:00
2014-05-21 00:04:59 +04:00
advanceClock ( component . props . cursorBlinkPeriod / 2 )
expect ( cursorsNode . classList . contains ( ' blink-off ' ) ) . toBe false
2014-04-07 23:51:25 +04:00
2014-05-13 22:12:04 +04:00
# Stop blinking after moving the cursor
2014-08-29 02:25:12 +04:00
editor . moveRight ( )
2014-05-21 00:04:59 +04:00
expect ( cursorsNode . classList . contains ( ' blink-off ' ) ) . toBe false
2014-04-08 00:28:44 +04:00
2014-05-21 00:04:59 +04:00
advanceClock ( component . props . cursorBlinkResumeDelay )
advanceClock ( component . props . cursorBlinkPeriod / 2 )
expect ( cursorsNode . classList . contains ( ' blink-off ' ) ) . toBe true
2014-04-08 00:28:44 +04:00
2014-04-18 00:19:40 +04:00
it " does not render cursors that are associated with non-empty selections " , ->
editor . setSelectedScreenRange ( [ [ 0 , 4 ] , [ 4 , 6 ] ] )
editor . addCursorAtScreenPosition ( [ 6 , 8 ] )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-04-18 00:19:40 +04:00
2014-07-13 21:56:14 +04:00
cursorNodes = componentNode . querySelectorAll ( ' .cursor ' )
2014-04-18 00:19:40 +04:00
expect ( cursorNodes . length ) . toBe 1
2014-07-27 21:37:36 +04:00
expect ( cursorNodes [ 0 ] . style [ ' -webkit-transform ' ] ) . toBe " translate( #{ 8 * charWidth } px, #{ 6 * lineHeightInPixels } px) "
2014-04-18 00:19:40 +04:00
2014-06-02 06:33:16 +04:00
it " updates cursor positions when the line height changes " , ->
editor . setCursorBufferPosition ( [ 1 , 10 ] )
component . setLineHeight ( 2 )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-07-13 21:56:14 +04:00
cursorNode = componentNode . querySelector ( ' .cursor ' )
2014-07-27 21:37:36 +04:00
expect ( cursorNode . style [ ' -webkit-transform ' ] ) . toBe " translate( #{ 10 * editor . getDefaultCharWidth ( ) } px, #{ editor . getLineHeightInPixels ( ) } px) "
2014-06-02 12:52:31 +04:00
it " updates cursor positions when the font size changes " , ->
editor . setCursorBufferPosition ( [ 1 , 10 ] )
component . setFontSize ( 10 )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-07-13 21:56:14 +04:00
cursorNode = componentNode . querySelector ( ' .cursor ' )
2014-07-27 21:37:36 +04:00
expect ( cursorNode . style [ ' -webkit-transform ' ] ) . toBe " translate( #{ 10 * editor . getDefaultCharWidth ( ) } px, #{ editor . getLineHeightInPixels ( ) } px) "
2014-06-02 06:33:16 +04:00
2014-06-02 12:55:14 +04:00
it " updates cursor positions when the font family changes " , ->
editor . setCursorBufferPosition ( [ 1 , 10 ] )
component . setFontFamily ( ' sans-serif ' )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-07-13 21:56:14 +04:00
cursorNode = componentNode . querySelector ( ' .cursor ' )
2014-06-02 12:55:14 +04:00
2015-01-09 19:22:45 +03:00
{ left } = wrapperNode . pixelPositionForScreenPosition ( [ 1 , 10 ] )
2014-07-27 21:37:36 +04:00
expect ( cursorNode . style [ ' -webkit-transform ' ] ) . toBe " translate( #{ left } px, #{ editor . getLineHeightInPixels ( ) } px) "
2014-06-02 12:55:14 +04:00
2014-04-04 03:04:19 +04:00
describe " selection rendering " , ->
2014-05-13 20:32:20 +04:00
[ scrollViewNode , scrollViewClientLeft ] = [ ]
2014-04-08 04:13:19 +04:00
beforeEach ->
2014-07-13 21:56:14 +04:00
scrollViewNode = componentNode . querySelector ( ' .scroll-view ' )
scrollViewClientLeft = componentNode . querySelector ( ' .scroll-view ' ) . getBoundingClientRect ( ) . left
2014-04-08 04:13:19 +04:00
2014-05-16 21:10:39 +04:00
it " renders 1 region for 1-line selections " , ->
# 1-line selection
editor . setSelectedScreenRange ( [ [ 1 , 6 ] , [ 1 , 10 ] ] )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-07-13 21:56:14 +04:00
regions = componentNode . querySelectorAll ( ' .selection .region ' )
2014-05-16 21:10:39 +04:00
expect ( regions . length ) . toBe 1
regionRect = regions [ 0 ] . getBoundingClientRect ( )
expect ( regionRect . top ) . toBe 1 * lineHeightInPixels
expect ( regionRect . height ) . toBe 1 * lineHeightInPixels
expect ( regionRect . left ) . toBe scrollViewClientLeft + 6 * charWidth
expect ( regionRect . width ) . toBe 4 * charWidth
it " renders 2 regions for 2-line selections " , ->
editor . setSelectedScreenRange ( [ [ 1 , 6 ] , [ 2 , 10 ] ] )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-07-13 21:56:14 +04:00
regions = componentNode . querySelectorAll ( ' .selection .region ' )
2014-05-16 21:10:39 +04:00
expect ( regions . length ) . toBe 2
region1Rect = regions [ 0 ] . getBoundingClientRect ( )
expect ( region1Rect . top ) . toBe 1 * lineHeightInPixels
expect ( region1Rect . height ) . toBe 1 * lineHeightInPixels
expect ( region1Rect . left ) . toBe scrollViewClientLeft + 6 * charWidth
expect ( region1Rect . right ) . toBe scrollViewNode . getBoundingClientRect ( ) . right
region2Rect = regions [ 1 ] . getBoundingClientRect ( )
expect ( region2Rect . top ) . toBe 2 * lineHeightInPixels
expect ( region2Rect . height ) . toBe 1 * lineHeightInPixels
expect ( region2Rect . left ) . toBe scrollViewClientLeft + 0
expect ( region2Rect . width ) . toBe 10 * charWidth
it " renders 3 regions for selections with more than 2 lines " , ->
editor . setSelectedScreenRange ( [ [ 1 , 6 ] , [ 5 , 10 ] ] )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-07-13 21:56:14 +04:00
regions = componentNode . querySelectorAll ( ' .selection .region ' )
2014-05-16 21:10:39 +04:00
expect ( regions . length ) . toBe 3
region1Rect = regions [ 0 ] . getBoundingClientRect ( )
expect ( region1Rect . top ) . toBe 1 * lineHeightInPixels
expect ( region1Rect . height ) . toBe 1 * lineHeightInPixels
expect ( region1Rect . left ) . toBe scrollViewClientLeft + 6 * charWidth
expect ( region1Rect . right ) . toBe scrollViewNode . getBoundingClientRect ( ) . right
region2Rect = regions [ 1 ] . getBoundingClientRect ( )
expect ( region2Rect . top ) . toBe 2 * lineHeightInPixels
expect ( region2Rect . height ) . toBe 3 * lineHeightInPixels
expect ( region2Rect . left ) . toBe scrollViewClientLeft + 0
expect ( region2Rect . right ) . toBe scrollViewNode . getBoundingClientRect ( ) . right
region3Rect = regions [ 2 ] . getBoundingClientRect ( )
expect ( region3Rect . top ) . toBe 5 * lineHeightInPixels
expect ( region3Rect . height ) . toBe 1 * lineHeightInPixels
expect ( region3Rect . left ) . toBe scrollViewClientLeft + 0
expect ( region3Rect . width ) . toBe 10 * charWidth
2014-06-18 00:47:12 +04:00
it " does not render empty selections " , ->
2014-05-16 21:10:39 +04:00
editor . addSelectionForBufferRange ( [ [ 2 , 2 ] , [ 2 , 2 ] ] )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-08-29 05:16:33 +04:00
expect ( editor . getSelections ( ) [ 0 ] . isEmpty ( ) ) . toBe true
expect ( editor . getSelections ( ) [ 1 ] . isEmpty ( ) ) . toBe true
2014-05-16 21:10:39 +04:00
2014-07-13 21:56:14 +04:00
expect ( componentNode . querySelectorAll ( ' .selection ' ) . length ) . toBe 0
2014-04-18 00:19:52 +04:00
2014-06-02 07:48:19 +04:00
it " updates selections when the line height changes " , ->
editor . setSelectedBufferRange ( [ [ 1 , 6 ] , [ 1 , 10 ] ] )
component . setLineHeight ( 2 )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-07-13 21:56:14 +04:00
selectionNode = componentNode . querySelector ( ' .region ' )
2014-06-02 07:48:19 +04:00
expect ( selectionNode . offsetTop ) . toBe editor . getLineHeightInPixels ( )
2014-06-02 13:37:21 +04:00
it " updates selections when the font size changes " , ->
editor . setSelectedBufferRange ( [ [ 1 , 6 ] , [ 1 , 10 ] ] )
component . setFontSize ( 10 )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-07-13 21:56:14 +04:00
selectionNode = componentNode . querySelector ( ' .region ' )
2014-06-02 13:37:21 +04:00
expect ( selectionNode . offsetTop ) . toBe editor . getLineHeightInPixels ( )
expect ( selectionNode . offsetLeft ) . toBe 6 * editor . getDefaultCharWidth ( )
it " updates selections when the font family changes " , ->
editor . setSelectedBufferRange ( [ [ 1 , 6 ] , [ 1 , 10 ] ] )
component . setFontFamily ( ' sans-serif ' )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-07-13 21:56:14 +04:00
selectionNode = componentNode . querySelector ( ' .region ' )
2014-06-02 13:37:21 +04:00
expect ( selectionNode . offsetTop ) . toBe editor . getLineHeightInPixels ( )
2015-01-09 19:22:45 +03:00
expect ( selectionNode . offsetLeft ) . toBe wrapperNode . pixelPositionForScreenPosition ( [ 1 , 6 ] ) . left
2014-06-02 13:37:21 +04:00
2014-07-03 22:56:09 +04:00
it " will flash the selection when flash:true is passed to editor::setSelectedBufferRange " , ->
editor . setSelectedBufferRange ( [ [ 1 , 6 ] , [ 1 , 10 ] ] , flash: true )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
nextAnimationFrame ( ) # flash starts on its own frame
2014-07-13 21:56:14 +04:00
selectionNode = componentNode . querySelector ( ' .selection ' )
2014-07-04 03:16:46 +04:00
expect ( selectionNode . classList . contains ( ' flash ' ) ) . toBe true
2014-07-03 22:56:09 +04:00
2014-07-04 04:36:45 +04:00
advanceClock editor . selectionFlashDuration
2014-07-04 03:16:46 +04:00
expect ( selectionNode . classList . contains ( ' flash ' ) ) . toBe false
2014-07-03 22:56:09 +04:00
editor . setSelectedBufferRange ( [ [ 1 , 5 ] , [ 1 , 7 ] ] , flash: true )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-07-04 03:16:46 +04:00
expect ( selectionNode . classList . contains ( ' flash ' ) ) . toBe true
2014-07-03 22:56:09 +04:00
2014-06-28 13:12:40 +04:00
describe " line decoration rendering " , ->
2014-07-08 01:43:56 +04:00
[ marker , decoration , decorationParams ] = [ ]
2014-06-28 13:12:40 +04:00
beforeEach ->
marker = editor . displayBuffer . markBufferRange ( [ [ 2 , 13 ] , [ 3 , 15 ] ] , invalidate: ' inside ' )
2015-01-15 05:14:28 +03:00
decorationParams = { type: [ ' line-number ' , ' line ' ] , class : ' a ' }
2014-07-08 01:43:56 +04:00
decoration = editor . decorateMarker ( marker , decorationParams )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-06-28 13:12:40 +04:00
it " applies line decoration classes to lines and line numbers " , ->
expect ( lineAndLineNumberHaveClass ( 2 , ' a ' ) ) . toBe true
expect ( lineAndLineNumberHaveClass ( 3 , ' a ' ) ) . toBe true
# Shrink editor vertically
2014-07-13 21:56:14 +04:00
wrapperNode.style.height = 4.5 * lineHeightInPixels + ' px '
2014-07-14 22:16:32 +04:00
component . measureHeightAndWidth ( )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-06-28 13:12:40 +04:00
# Add decorations that are out of range
marker2 = editor . displayBuffer . markBufferRange ( [ [ 9 , 0 ] , [ 9 , 0 ] ] )
2015-01-15 05:14:28 +03:00
editor . decorateMarker ( marker2 , type: [ ' line-number ' , ' line ' ] , class : ' b ' )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-06-28 13:12:40 +04:00
# Scroll decorations into view
verticalScrollbarNode.scrollTop = 2.5 * lineHeightInPixels
verticalScrollbarNode . dispatchEvent ( new UIEvent ( ' scroll ' ) )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-06-28 13:12:40 +04:00
expect ( lineAndLineNumberHaveClass ( 9 , ' b ' ) ) . toBe true
# Fold a line to move the decorations
editor . foldBufferRow ( 5 )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-06-28 13:12:40 +04:00
expect ( lineAndLineNumberHaveClass ( 9 , ' b ' ) ) . toBe false
expect ( lineAndLineNumberHaveClass ( 6 , ' b ' ) ) . toBe true
it " only applies decorations to screen rows that are spanned by their marker when lines are soft-wrapped " , ->
editor . setText ( " a line that wraps, ok " )
2014-09-04 18:42:32 +04:00
editor . setSoftWrapped ( true )
2014-07-13 21:56:14 +04:00
componentNode.style.width = 16 * charWidth + ' px '
2014-07-14 22:16:32 +04:00
component . measureHeightAndWidth ( )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-06-28 13:12:40 +04:00
marker . destroy ( )
marker = editor . markBufferRange ( [ [ 0 , 0 ] , [ 0 , 2 ] ] )
2015-01-15 05:14:28 +03:00
editor . decorateMarker ( marker , type: [ ' line-number ' , ' line ' ] , class : ' b ' )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-06-28 13:12:40 +04:00
expect ( lineNumberHasClass ( 0 , ' b ' ) ) . toBe true
expect ( lineNumberHasClass ( 1 , ' b ' ) ) . toBe false
marker . setBufferRange ( [ [ 0 , 0 ] , [ 0 , Infinity ] ] )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-06-28 13:12:40 +04:00
expect ( lineNumberHasClass ( 0 , ' b ' ) ) . toBe true
expect ( lineNumberHasClass ( 1 , ' b ' ) ) . toBe true
it " updates decorations when markers move " , ->
expect ( lineAndLineNumberHaveClass ( 1 , ' a ' ) ) . toBe false
expect ( lineAndLineNumberHaveClass ( 2 , ' a ' ) ) . toBe true
expect ( lineAndLineNumberHaveClass ( 3 , ' a ' ) ) . toBe true
expect ( lineAndLineNumberHaveClass ( 4 , ' a ' ) ) . toBe false
editor . getBuffer ( ) . insert ( [ 0 , 0 ] , ' \n ' )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-06-28 13:12:40 +04:00
expect ( lineAndLineNumberHaveClass ( 2 , ' a ' ) ) . toBe false
expect ( lineAndLineNumberHaveClass ( 3 , ' a ' ) ) . toBe true
expect ( lineAndLineNumberHaveClass ( 4 , ' a ' ) ) . toBe true
expect ( lineAndLineNumberHaveClass ( 5 , ' a ' ) ) . toBe false
marker . setBufferRange ( [ [ 4 , 4 ] , [ 6 , 4 ] ] )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-06-28 13:12:40 +04:00
expect ( lineAndLineNumberHaveClass ( 2 , ' a ' ) ) . toBe false
expect ( lineAndLineNumberHaveClass ( 3 , ' a ' ) ) . toBe false
expect ( lineAndLineNumberHaveClass ( 4 , ' a ' ) ) . toBe true
expect ( lineAndLineNumberHaveClass ( 5 , ' a ' ) ) . toBe true
expect ( lineAndLineNumberHaveClass ( 6 , ' a ' ) ) . toBe true
expect ( lineAndLineNumberHaveClass ( 7 , ' a ' ) ) . toBe false
it " remove decoration classes and unsubscribes from markers decorations are removed " , ->
expect ( marker . getSubscriptionCount ( ' changed ' ) )
2014-07-08 01:43:56 +04:00
decoration . destroy ( )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-06-28 13:12:40 +04:00
expect ( lineNumberHasClass ( 1 , ' a ' ) ) . toBe false
expect ( lineNumberHasClass ( 2 , ' a ' ) ) . toBe false
expect ( lineNumberHasClass ( 3 , ' a ' ) ) . toBe false
expect ( lineNumberHasClass ( 4 , ' a ' ) ) . toBe false
expect ( marker . getSubscriptionCount ( ' changed ' ) ) . toBe 0
it " removes decorations when their marker is invalidated " , ->
editor . getBuffer ( ) . insert ( [ 3 , 2 ] , ' n ' )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-06-28 13:12:40 +04:00
expect ( marker . isValid ( ) ) . toBe false
expect ( lineAndLineNumberHaveClass ( 1 , ' a ' ) ) . toBe false
expect ( lineAndLineNumberHaveClass ( 2 , ' a ' ) ) . toBe false
expect ( lineAndLineNumberHaveClass ( 3 , ' a ' ) ) . toBe false
expect ( lineAndLineNumberHaveClass ( 4 , ' a ' ) ) . toBe false
editor . undo ( )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-06-28 13:12:40 +04:00
expect ( marker . isValid ( ) ) . toBe true
expect ( lineAndLineNumberHaveClass ( 1 , ' a ' ) ) . toBe false
expect ( lineAndLineNumberHaveClass ( 2 , ' a ' ) ) . toBe true
expect ( lineAndLineNumberHaveClass ( 3 , ' a ' ) ) . toBe true
expect ( lineAndLineNumberHaveClass ( 4 , ' a ' ) ) . toBe false
it " removes decorations when their marker is destroyed " , ->
marker . destroy ( )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-06-28 13:12:40 +04:00
expect ( lineNumberHasClass ( 1 , ' a ' ) ) . toBe false
expect ( lineNumberHasClass ( 2 , ' a ' ) ) . toBe false
expect ( lineNumberHasClass ( 3 , ' a ' ) ) . toBe false
expect ( lineNumberHasClass ( 4 , ' a ' ) ) . toBe false
describe " when the decoration ' s ' onlyHead ' property is true " , ->
it " only applies the decoration ' s class to lines containing the marker ' s head " , ->
2015-01-15 05:14:28 +03:00
editor . decorateMarker ( marker , type: [ ' line-number ' , ' line ' ] , class : ' only-head ' , onlyHead: true )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-06-28 13:12:40 +04:00
expect ( lineAndLineNumberHaveClass ( 1 , ' only-head ' ) ) . toBe false
expect ( lineAndLineNumberHaveClass ( 2 , ' only-head ' ) ) . toBe false
expect ( lineAndLineNumberHaveClass ( 3 , ' only-head ' ) ) . toBe true
expect ( lineAndLineNumberHaveClass ( 4 , ' only-head ' ) ) . toBe false
describe " when the decoration ' s ' onlyEmpty ' property is true " , ->
it " only applies the decoration when its marker is empty " , ->
2015-01-15 05:14:28 +03:00
editor . decorateMarker ( marker , type: [ ' line-number ' , ' line ' ] , class : ' only-empty ' , onlyEmpty: true )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-06-28 13:12:40 +04:00
expect ( lineAndLineNumberHaveClass ( 2 , ' only-empty ' ) ) . toBe false
expect ( lineAndLineNumberHaveClass ( 3 , ' only-empty ' ) ) . toBe false
marker . clearTail ( )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-06-28 13:12:40 +04:00
expect ( lineAndLineNumberHaveClass ( 2 , ' only-empty ' ) ) . toBe false
expect ( lineAndLineNumberHaveClass ( 3 , ' only-empty ' ) ) . toBe true
describe " when the decoration ' s ' onlyNonEmpty ' property is true " , ->
it " only applies the decoration when its marker is non-empty " , ->
2015-01-15 05:14:28 +03:00
editor . decorateMarker ( marker , type: [ ' line-number ' , ' line ' ] , class : ' only-non-empty ' , onlyNonEmpty: true )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-06-28 13:12:40 +04:00
expect ( lineAndLineNumberHaveClass ( 2 , ' only-non-empty ' ) ) . toBe true
expect ( lineAndLineNumberHaveClass ( 3 , ' only-non-empty ' ) ) . toBe true
marker . clearTail ( )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-06-28 13:12:40 +04:00
expect ( lineAndLineNumberHaveClass ( 2 , ' only-non-empty ' ) ) . toBe false
expect ( lineAndLineNumberHaveClass ( 3 , ' only-non-empty ' ) ) . toBe false
2014-06-18 00:47:49 +04:00
describe " highlight decoration rendering " , ->
2014-07-03 05:36:29 +04:00
[ marker , decoration , decorationParams , scrollViewClientLeft ] = [ ]
2014-06-17 02:24:01 +04:00
beforeEach ->
2014-07-13 21:56:14 +04:00
scrollViewClientLeft = componentNode . querySelector ( ' .scroll-view ' ) . getBoundingClientRect ( ) . left
2014-06-17 02:24:01 +04:00
marker = editor . displayBuffer . markBufferRange ( [ [ 2 , 13 ] , [ 3 , 15 ] ] , invalidate: ' inside ' )
2014-07-03 05:36:29 +04:00
decorationParams = { type: ' highlight ' , class : ' test-highlight ' }
2014-07-08 01:43:56 +04:00
decoration = editor . decorateMarker ( marker , decorationParams )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-06-17 02:24:01 +04:00
it " does not render highlights for off-screen lines until they come on-screen " , ->
2014-07-13 21:56:14 +04:00
wrapperNode.style.height = 2.5 * lineHeightInPixels + ' px '
2014-07-14 22:16:32 +04:00
component . measureHeightAndWidth ( )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-06-17 02:24:01 +04:00
marker = editor . displayBuffer . markBufferRange ( [ [ 9 , 2 ] , [ 9 , 4 ] ] , invalidate: ' inside ' )
2014-07-08 01:43:56 +04:00
editor . decorateMarker ( marker , type: ' highlight ' , class : ' some-highlight ' )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-06-17 02:24:01 +04:00
2014-06-20 02:48:40 +04:00
# Should not be rendering range containing the marker
expect ( component . getRenderedRowRange ( ) [ 1 ] ) . toBeLessThan 9
2014-06-17 02:24:01 +04:00
2014-07-13 21:56:14 +04:00
regions = componentNode . querySelectorAll ( ' .some-highlight .region ' )
2014-06-17 02:24:01 +04:00
2014-06-20 02:48:40 +04:00
# Nothing when outside the rendered row range
expect ( regions . length ) . toBe 0
2014-06-17 02:24:01 +04:00
2014-06-20 02:48:40 +04:00
verticalScrollbarNode.scrollTop = 3.5 * lineHeightInPixels
verticalScrollbarNode . dispatchEvent ( new UIEvent ( ' scroll ' ) )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-06-17 02:24:01 +04:00
2014-07-13 21:56:14 +04:00
regions = componentNode . querySelectorAll ( ' .some-highlight .region ' )
2014-06-17 02:24:01 +04:00
2014-06-20 02:48:40 +04:00
expect ( regions . length ) . toBe 1
regionRect = regions [ 0 ] . style
expect ( regionRect . top ) . toBe 9 * lineHeightInPixels + ' px '
expect ( regionRect . height ) . toBe 1 * lineHeightInPixels + ' px '
expect ( regionRect . left ) . toBe 2 * charWidth + ' px '
expect ( regionRect . width ) . toBe 2 * charWidth + ' px '
2014-06-17 02:24:01 +04:00
it " renders highlights decoration ' s marker is added " , ->
2014-07-13 21:56:14 +04:00
regions = componentNode . querySelectorAll ( ' .test-highlight .region ' )
2014-06-17 02:24:01 +04:00
expect ( regions . length ) . toBe 2
it " removes highlights when a decoration is removed " , ->
2014-07-08 01:43:56 +04:00
decoration . destroy ( )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-07-13 21:56:14 +04:00
regions = componentNode . querySelectorAll ( ' .test-highlight .region ' )
2014-06-20 02:48:40 +04:00
expect ( regions . length ) . toBe 0
2014-06-17 02:24:01 +04:00
2014-06-17 02:56:55 +04:00
it " does not render a highlight that is within a fold " , ->
editor . foldBufferRow ( 1 )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-07-13 21:56:14 +04:00
expect ( componentNode . querySelectorAll ( ' .test-highlight ' ) . length ) . toBe 0
2014-06-17 02:56:55 +04:00
2014-06-17 02:24:01 +04:00
it " removes highlights when a decoration ' s marker is destroyed " , ->
marker . destroy ( )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-07-13 21:56:14 +04:00
regions = componentNode . querySelectorAll ( ' .test-highlight .region ' )
2014-06-20 02:48:40 +04:00
expect ( regions . length ) . toBe 0
2014-06-17 02:24:01 +04:00
it " only renders highlights when a decoration ' s marker is valid " , ->
editor . getBuffer ( ) . insert ( [ 3 , 2 ] , ' n ' )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-06-17 02:24:01 +04:00
2014-06-20 02:48:40 +04:00
expect ( marker . isValid ( ) ) . toBe false
2014-07-13 21:56:14 +04:00
regions = componentNode . querySelectorAll ( ' .test-highlight .region ' )
2014-06-20 02:48:40 +04:00
expect ( regions . length ) . toBe 0
2014-06-17 02:24:01 +04:00
2014-06-20 02:48:40 +04:00
editor . getBuffer ( ) . undo ( )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-06-17 02:24:01 +04:00
2014-06-20 02:48:40 +04:00
expect ( marker . isValid ( ) ) . toBe true
2014-07-13 21:56:14 +04:00
regions = componentNode . querySelectorAll ( ' .test-highlight .region ' )
2014-06-20 02:48:40 +04:00
expect ( regions . length ) . toBe 2
2014-06-17 02:24:01 +04:00
2014-11-11 22:11:12 +03:00
it " renders classes on the regions directly if ' deprecatedRegionClass ' option is defined " , ->
decoration = editor . decorateMarker ( marker , type: ' highlight ' , class : ' test-highlight ' , deprecatedRegionClass: ' test-highlight-region ' )
nextAnimationFrame ( )
regions = componentNode . querySelectorAll ( ' .test-highlight .region.test-highlight-region ' )
expect ( regions . length ) . toBe 2
2014-07-08 02:13:04 +04:00
describe " when flashing a decoration via Decoration::flash() " , ->
2014-07-03 05:36:29 +04:00
highlightNode = null
beforeEach ->
2014-07-13 21:56:14 +04:00
highlightNode = componentNode . querySelector ( ' .test-highlight ' )
2014-07-03 05:36:29 +04:00
it " adds and removes the flash class specified in ::flash " , ->
2014-07-04 03:52:35 +04:00
expect ( highlightNode . classList . contains ( ' flash-class ' ) ) . toBe false
2014-07-03 05:36:29 +04:00
2014-07-04 03:52:35 +04:00
decoration . flash ( ' flash-class ' , 10 )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-07-04 03:52:35 +04:00
expect ( highlightNode . classList . contains ( ' flash-class ' ) ) . toBe true
2014-07-03 05:36:29 +04:00
advanceClock ( 10 )
2014-07-04 03:52:35 +04:00
expect ( highlightNode . classList . contains ( ' flash-class ' ) ) . toBe false
2014-07-03 05:36:29 +04:00
describe " when ::flash is called again before the first has finished " , ->
it " removes the class from the decoration highlight before adding it for the second ::flash call " , ->
2014-07-04 03:52:35 +04:00
decoration . flash ( ' flash-class ' , 10 )
2014-07-04 19:56:51 +04:00
nextAnimationFrame ( )
2014-07-04 03:52:35 +04:00
expect ( highlightNode . classList . contains ( ' flash-class ' ) ) . toBe true
2014-07-03 05:36:29 +04:00
advanceClock ( 2 )
2014-07-04 19:56:51 +04:00
decoration . flash ( ' flash-class ' , 10 )
# Removed for 1 frame to force CSS transition to restart
expect ( highlightNode . classList . contains ( ' flash-class ' ) ) . toBe false
2014-07-03 05:36:29 +04:00
2014-07-04 19:56:51 +04:00
nextAnimationFrame ( )
2014-07-04 03:52:35 +04:00
expect ( highlightNode . classList . contains ( ' flash-class ' ) ) . toBe true
2014-07-03 05:36:29 +04:00
advanceClock ( 10 )
2014-07-04 03:52:35 +04:00
expect ( highlightNode . classList . contains ( ' flash-class ' ) ) . toBe false
2014-07-03 05:36:29 +04:00
2014-06-24 03:00:36 +04:00
describe " when a decoration ' s marker moves " , ->
it " moves rendered highlights when the buffer is changed " , ->
2014-07-13 21:56:14 +04:00
regionStyle = componentNode . querySelector ( ' .test-highlight .region ' ) . style
2014-06-24 03:00:36 +04:00
originalTop = parseInt ( regionStyle . top )
editor . getBuffer ( ) . insert ( [ 0 , 0 ] , ' \n ' )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-06-24 03:00:36 +04:00
2014-07-13 21:56:14 +04:00
regionStyle = componentNode . querySelector ( ' .test-highlight .region ' ) . style
2014-06-24 03:00:36 +04:00
newTop = parseInt ( regionStyle . top )
expect ( newTop ) . toBe originalTop + lineHeightInPixels
it " moves rendered highlights when the marker is manually moved " , ->
2014-07-13 21:56:14 +04:00
regionStyle = componentNode . querySelector ( ' .test-highlight .region ' ) . style
2014-06-24 03:00:36 +04:00
expect ( parseInt ( regionStyle . top ) ) . toBe 2 * lineHeightInPixels
marker . setBufferRange ( [ [ 5 , 8 ] , [ 5 , 13 ] ] )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-06-24 03:00:36 +04:00
2014-07-13 21:56:14 +04:00
regionStyle = componentNode . querySelector ( ' .test-highlight .region ' ) . style
2014-06-24 03:00:36 +04:00
expect ( parseInt ( regionStyle . top ) ) . toBe 5 * lineHeightInPixels
2014-07-08 02:13:04 +04:00
describe " when a decoration is updated via Decoration::update " , ->
it " renders the decoration ' s new params " , ->
2014-07-13 21:56:14 +04:00
expect ( componentNode . querySelector ( ' .test-highlight ' ) ) . toBeTruthy ( )
2014-07-08 02:13:04 +04:00
2014-09-06 03:35:55 +04:00
decoration . setProperties ( type: ' highlight ' , class : ' new-test-highlight ' )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-07-08 02:13:04 +04:00
2014-07-13 21:56:14 +04:00
expect ( componentNode . querySelector ( ' .test-highlight ' ) ) . toBeFalsy ( )
expect ( componentNode . querySelector ( ' .new-test-highlight ' ) ) . toBeTruthy ( )
2014-07-08 02:13:04 +04:00
2014-11-12 02:52:21 +03:00
describe " overlay decoration rendering " , ->
2014-11-12 02:45:57 +03:00
[ item ] = [ ]
2014-11-11 22:20:30 +03:00
beforeEach ->
item = document . createElement ( ' div ' )
2014-11-12 00:08:05 +03:00
item . classList . add ' overlay-test '
2014-11-11 22:20:30 +03:00
2014-11-12 00:32:13 +03:00
describe " when the marker is empty " , ->
it " renders an overlay decoration when added and removes the overlay when the decoration is destroyed " , ->
marker = editor . displayBuffer . markBufferRange ( [ [ 2 , 13 ] , [ 2 , 13 ] ] , invalidate: ' never ' )
decoration = editor . decorateMarker ( marker , { type: ' overlay ' , item } )
nextAnimationFrame ( )
2014-11-11 22:20:30 +03:00
2014-11-12 00:32:13 +03:00
overlay = component . getTopmostDOMNode ( ) . querySelector ( ' atom-overlay .overlay-test ' )
expect ( overlay ) . toBe item
2014-11-11 22:20:30 +03:00
2014-11-12 00:32:13 +03:00
decoration . destroy ( )
nextAnimationFrame ( )
2014-11-11 22:20:30 +03:00
2014-11-12 00:32:13 +03:00
overlay = component . getTopmostDOMNode ( ) . querySelector ( ' atom-overlay .overlay-test ' )
expect ( overlay ) . toBe null
2014-11-11 22:20:30 +03:00
2014-11-12 00:32:13 +03:00
it " renders in the correct position on initial display and when the marker moves " , ->
editor . setCursorBufferPosition ( [ 2 , 5 ] )
2014-11-12 00:08:05 +03:00
2014-11-12 00:32:13 +03:00
marker = editor . getLastCursor ( ) . getMarker ( )
decoration = editor . decorateMarker ( marker , { type: ' overlay ' , item } )
nextAnimationFrame ( )
2014-11-12 00:08:05 +03:00
2015-01-09 19:22:45 +03:00
position = wrapperNode . pixelPositionForBufferPosition ( [ 2 , 5 ] )
2014-11-12 00:08:05 +03:00
2014-11-12 00:32:13 +03:00
overlay = component . getTopmostDOMNode ( ) . querySelector ( ' atom-overlay ' )
expect ( overlay . style . left ) . toBe position . left + ' px '
expect ( overlay . style . top ) . toBe position . top + editor . getLineHeightInPixels ( ) + ' px '
2014-11-12 00:08:05 +03:00
2014-11-12 00:32:13 +03:00
editor . moveRight ( )
editor . moveRight ( )
nextAnimationFrame ( )
2015-01-09 19:22:45 +03:00
position = wrapperNode . pixelPositionForBufferPosition ( [ 2 , 7 ] )
2014-11-12 00:32:13 +03:00
expect ( overlay . style . left ) . toBe position . left + ' px '
expect ( overlay . style . top ) . toBe position . top + editor . getLineHeightInPixels ( ) + ' px '
describe " when the marker is not empty " , ->
2014-11-26 19:29:29 +03:00
it " renders at the head of the marker by default " , ->
2014-11-12 00:32:13 +03:00
marker = editor . displayBuffer . markBufferRange ( [ [ 2 , 5 ] , [ 2 , 10 ] ] , invalidate: ' never ' )
decoration = editor . decorateMarker ( marker , { type: ' overlay ' , item } )
nextAnimationFrame ( )
2015-01-09 19:22:45 +03:00
position = wrapperNode . pixelPositionForBufferPosition ( [ 2 , 10 ] )
2014-11-12 00:32:13 +03:00
overlay = component . getTopmostDOMNode ( ) . querySelector ( ' atom-overlay ' )
expect ( overlay . style . left ) . toBe position . left + ' px '
expect ( overlay . style . top ) . toBe position . top + editor . getLineHeightInPixels ( ) + ' px '
2014-11-13 01:43:26 +03:00
it " renders at the head of the marker when the marker is reversed " , ->
2014-11-12 00:32:13 +03:00
marker = editor . displayBuffer . markBufferRange ( [ [ 2 , 5 ] , [ 2 , 10 ] ] , invalidate: ' never ' , reversed: true )
decoration = editor . decorateMarker ( marker , { type: ' overlay ' , item } )
nextAnimationFrame ( )
2014-11-12 00:08:05 +03:00
2015-01-09 19:22:45 +03:00
position = wrapperNode . pixelPositionForBufferPosition ( [ 2 , 5 ] )
2014-11-26 19:29:29 +03:00
overlay = component . getTopmostDOMNode ( ) . querySelector ( ' atom-overlay ' )
expect ( overlay . style . left ) . toBe position . left + ' px '
expect ( overlay . style . top ) . toBe position . top + editor . getLineHeightInPixels ( ) + ' px '
it " renders at the tail of the marker when the ' position ' option is ' tail ' " , ->
marker = editor . displayBuffer . markBufferRange ( [ [ 2 , 5 ] , [ 2 , 10 ] ] , invalidate: ' never ' )
decoration = editor . decorateMarker ( marker , { type: ' overlay ' , position: ' tail ' , item } )
nextAnimationFrame ( )
2015-01-09 19:22:45 +03:00
position = wrapperNode . pixelPositionForBufferPosition ( [ 2 , 5 ] )
2014-11-12 00:08:05 +03:00
2014-11-12 00:32:13 +03:00
overlay = component . getTopmostDOMNode ( ) . querySelector ( ' atom-overlay ' )
expect ( overlay . style . left ) . toBe position . left + ' px '
expect ( overlay . style . top ) . toBe position . top + editor . getLineHeightInPixels ( ) + ' px '
2014-11-12 00:08:05 +03:00
2014-11-12 02:52:44 +03:00
describe " positioning the overlay when near the edge of the editor " , ->
2014-11-12 02:46:27 +03:00
[ itemWidth , itemHeight ] = [ ]
beforeEach ->
itemWidth = 4 * editor . getDefaultCharWidth ( )
itemHeight = 4 * editor . getLineHeightInPixels ( )
gutterWidth = componentNode . querySelector ( ' .gutter ' ) . offsetWidth
windowWidth = gutterWidth + 30 * editor . getDefaultCharWidth ( )
windowHeight = 9 * editor . getLineHeightInPixels ( )
item.style.width = itemWidth + ' px '
item.style.height = itemHeight + ' px '
wrapperNode.style.width = windowWidth + ' px '
wrapperNode.style.height = windowHeight + ' px '
component . measureHeightAndWidth ( )
nextAnimationFrame ( )
it " flips horizontally when near the right edge " , ->
marker = editor . displayBuffer . markBufferRange ( [ [ 0 , 26 ] , [ 0 , 26 ] ] , invalidate: ' never ' )
decoration = editor . decorateMarker ( marker , { type: ' overlay ' , item } )
nextAnimationFrame ( )
2015-01-09 19:22:45 +03:00
position = wrapperNode . pixelPositionForBufferPosition ( [ 0 , 26 ] )
2014-11-12 02:46:27 +03:00
overlay = component . getTopmostDOMNode ( ) . querySelector ( ' atom-overlay ' )
expect ( overlay . style . left ) . toBe position . left + ' px '
expect ( overlay . style . top ) . toBe position . top + editor . getLineHeightInPixels ( ) + ' px '
editor . insertText ( ' a ' )
nextAnimationFrame ( )
2015-01-09 19:22:45 +03:00
position = wrapperNode . pixelPositionForBufferPosition ( [ 0 , 27 ] )
2014-11-12 02:46:27 +03:00
expect ( overlay . style . left ) . toBe position . left - itemWidth + ' px '
expect ( overlay . style . top ) . toBe position . top + editor . getLineHeightInPixels ( ) + ' px '
it " flips vertically when near the bottom edge " , ->
marker = editor . displayBuffer . markBufferRange ( [ [ 4 , 0 ] , [ 4 , 0 ] ] , invalidate: ' never ' )
decoration = editor . decorateMarker ( marker , { type: ' overlay ' , item } )
nextAnimationFrame ( )
2015-01-09 19:22:45 +03:00
position = wrapperNode . pixelPositionForBufferPosition ( [ 4 , 0 ] )
2014-11-12 02:46:27 +03:00
overlay = component . getTopmostDOMNode ( ) . querySelector ( ' atom-overlay ' )
expect ( overlay . style . left ) . toBe position . left + ' px '
expect ( overlay . style . top ) . toBe position . top + editor . getLineHeightInPixels ( ) + ' px '
editor . insertNewline ( )
nextAnimationFrame ( )
2015-01-09 19:22:45 +03:00
position = wrapperNode . pixelPositionForBufferPosition ( [ 5 , 0 ] )
2014-11-12 02:46:27 +03:00
expect ( overlay . style . left ) . toBe position . left + ' px '
expect ( overlay . style . top ) . toBe position . top - itemHeight + ' px '
2014-11-12 22:50:22 +03:00
describe " when the editor is very small " , ->
beforeEach ->
gutterWidth = componentNode . querySelector ( ' .gutter ' ) . offsetWidth
windowWidth = gutterWidth + 6 * editor . getDefaultCharWidth ( )
windowHeight = 6 * editor . getLineHeightInPixels ( )
wrapperNode.style.width = windowWidth + ' px '
wrapperNode.style.height = windowHeight + ' px '
component . measureHeightAndWidth ( )
nextAnimationFrame ( )
it " does not flip horizontally and force the overlay to have a negative left " , ->
marker = editor . displayBuffer . markBufferRange ( [ [ 0 , 2 ] , [ 0 , 2 ] ] , invalidate: ' never ' )
decoration = editor . decorateMarker ( marker , { type: ' overlay ' , item } )
nextAnimationFrame ( )
2015-01-09 19:22:45 +03:00
position = wrapperNode . pixelPositionForBufferPosition ( [ 0 , 2 ] )
2014-11-12 22:50:22 +03:00
overlay = component . getTopmostDOMNode ( ) . querySelector ( ' atom-overlay ' )
expect ( overlay . style . left ) . toBe position . left + ' px '
expect ( overlay . style . top ) . toBe position . top + editor . getLineHeightInPixels ( ) + ' px '
editor . insertText ( ' a ' )
nextAnimationFrame ( )
2015-01-09 19:22:45 +03:00
position = wrapperNode . pixelPositionForBufferPosition ( [ 0 , 3 ] )
2014-11-12 22:50:22 +03:00
expect ( overlay . style . left ) . toBe position . left + ' px '
expect ( overlay . style . top ) . toBe position . top + editor . getLineHeightInPixels ( ) + ' px '
it " does not flip vertically and force the overlay to have a negative top " , ->
marker = editor . displayBuffer . markBufferRange ( [ [ 1 , 0 ] , [ 1 , 0 ] ] , invalidate: ' never ' )
decoration = editor . decorateMarker ( marker , { type: ' overlay ' , item } )
nextAnimationFrame ( )
2015-01-09 19:22:45 +03:00
position = wrapperNode . pixelPositionForBufferPosition ( [ 1 , 0 ] )
2014-11-12 22:50:22 +03:00
overlay = component . getTopmostDOMNode ( ) . querySelector ( ' atom-overlay ' )
expect ( overlay . style . left ) . toBe position . left + ' px '
expect ( overlay . style . top ) . toBe position . top + editor . getLineHeightInPixels ( ) + ' px '
editor . insertNewline ( )
nextAnimationFrame ( )
2015-01-09 19:22:45 +03:00
position = wrapperNode . pixelPositionForBufferPosition ( [ 2 , 0 ] )
2014-11-12 22:50:22 +03:00
expect ( overlay . style . left ) . toBe position . left + ' px '
expect ( overlay . style . top ) . toBe position . top + editor . getLineHeightInPixels ( ) + ' px '
2014-11-12 02:52:44 +03:00
describe " when editor scroll position is not 0 " , ->
2014-11-12 02:46:27 +03:00
it " flips horizontally when near the right edge " , ->
editor . setScrollLeft ( 2 * editor . getDefaultCharWidth ( ) )
marker = editor . displayBuffer . markBufferRange ( [ [ 0 , 28 ] , [ 0 , 28 ] ] , invalidate: ' never ' )
decoration = editor . decorateMarker ( marker , { type: ' overlay ' , item } )
nextAnimationFrame ( )
2015-01-09 19:22:45 +03:00
position = wrapperNode . pixelPositionForBufferPosition ( [ 0 , 28 ] )
2014-11-12 02:46:27 +03:00
overlay = component . getTopmostDOMNode ( ) . querySelector ( ' atom-overlay ' )
expect ( overlay . style . left ) . toBe position . left + ' px '
expect ( overlay . style . top ) . toBe position . top + editor . getLineHeightInPixels ( ) + ' px '
editor . insertText ( ' a ' )
nextAnimationFrame ( )
2015-01-09 19:22:45 +03:00
position = wrapperNode . pixelPositionForBufferPosition ( [ 0 , 29 ] )
2014-11-12 02:46:27 +03:00
expect ( overlay . style . left ) . toBe position . left - itemWidth + ' px '
expect ( overlay . style . top ) . toBe position . top + editor . getLineHeightInPixels ( ) + ' px '
it " flips vertically when near the bottom edge " , ->
editor . setScrollTop ( 2 * editor . getLineHeightInPixels ( ) )
marker = editor . displayBuffer . markBufferRange ( [ [ 6 , 0 ] , [ 6 , 0 ] ] , invalidate: ' never ' )
decoration = editor . decorateMarker ( marker , { type: ' overlay ' , item } )
nextAnimationFrame ( )
2015-01-09 19:22:45 +03:00
position = wrapperNode . pixelPositionForBufferPosition ( [ 6 , 0 ] )
2014-11-12 02:46:27 +03:00
overlay = component . getTopmostDOMNode ( ) . querySelector ( ' atom-overlay ' )
expect ( overlay . style . left ) . toBe position . left + ' px '
expect ( overlay . style . top ) . toBe position . top + editor . getLineHeightInPixels ( ) + ' px '
editor . insertNewline ( )
nextAnimationFrame ( )
2015-01-09 19:22:45 +03:00
position = wrapperNode . pixelPositionForBufferPosition ( [ 7 , 0 ] )
2014-11-12 02:46:27 +03:00
expect ( overlay . style . left ) . toBe position . left + ' px '
expect ( overlay . style . top ) . toBe position . top - itemHeight + ' px '
2014-05-23 05:54:57 +04:00
describe " hidden input field " , ->
it " renders the hidden input field at the position of the last cursor if the cursor is on screen and the editor is focused " , ->
editor . setVerticalScrollMargin ( 0 )
editor . setHorizontalScrollMargin ( 0 )
2014-07-13 21:56:14 +04:00
inputNode = componentNode . querySelector ( ' .hidden-input ' )
wrapperNode.style.height = 5 * lineHeightInPixels + ' px '
wrapperNode.style.width = 10 * charWidth + ' px '
2014-07-14 22:16:32 +04:00
component . measureHeightAndWidth ( )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-05-23 05:54:57 +04:00
expect ( editor . getCursorScreenPosition ( ) ) . toEqual [ 0 , 0 ]
editor . setScrollTop ( 3 * lineHeightInPixels )
editor . setScrollLeft ( 3 * charWidth )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-05-23 05:54:57 +04:00
expect ( inputNode . offsetTop ) . toBe 0
expect ( inputNode . offsetLeft ) . toBe 0
# In bounds, not focused
editor . setCursorBufferPosition ( [ 5 , 4 ] )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-05-23 05:54:57 +04:00
expect ( inputNode . offsetTop ) . toBe 0
expect ( inputNode . offsetLeft ) . toBe 0
# In bounds and focused
2014-06-20 02:48:40 +04:00
inputNode . focus ( ) # updates via state change
2014-05-23 05:54:57 +04:00
expect ( inputNode . offsetTop ) . toBe ( 5 * lineHeightInPixels ) - editor . getScrollTop ( )
expect ( inputNode . offsetLeft ) . toBe ( 4 * charWidth ) - editor . getScrollLeft ( )
# In bounds, not focused
2014-06-20 02:48:40 +04:00
inputNode . blur ( ) # updates via state change
2014-05-23 05:54:57 +04:00
expect ( inputNode . offsetTop ) . toBe 0
expect ( inputNode . offsetLeft ) . toBe 0
# Out of bounds, not focused
editor . setCursorBufferPosition ( [ 1 , 2 ] )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-05-23 05:54:57 +04:00
expect ( inputNode . offsetTop ) . toBe 0
expect ( inputNode . offsetLeft ) . toBe 0
# Out of bounds, focused
2014-06-20 02:48:40 +04:00
inputNode . focus ( ) # updates via state change
2014-05-23 05:54:57 +04:00
expect ( inputNode . offsetTop ) . toBe 0
expect ( inputNode . offsetLeft ) . toBe 0
2014-07-07 23:55:32 +04:00
describe " mouse interactions on the lines " , ->
2014-04-07 01:47:08 +04:00
linesNode = null
beforeEach ->
2014-07-13 21:56:14 +04:00
linesNode = componentNode . querySelector ( ' .lines ' )
2014-04-07 01:47:08 +04:00
2014-10-17 04:17:33 +04:00
describe " when the mouse is single-clicked below the last line " , ->
2014-10-17 04:16:18 +04:00
it " moves the cursor to the end of file buffer position " , ->
editor . setText ( ' foo ' )
editor . setCursorBufferPosition ( [ 0 , 0 ] )
height = 4.5 * lineHeightInPixels
wrapperNode.style.height = height + ' px '
wrapperNode.style.width = 10 * charWidth + ' px '
component . measureHeightAndWidth ( )
nextAnimationFrame ( )
coordinates = clientCoordinatesForScreenPosition ( [ 0 , 2 ] )
coordinates.clientY = height * 2
linesNode . dispatchEvent ( buildMouseEvent ( ' mousedown ' , coordinates ) )
nextAnimationFrame ( )
expect ( editor . getCursorScreenPosition ( ) ) . toEqual [ 0 , 3 ]
2014-04-06 23:56:38 +04:00
describe " when a non-folded line is single-clicked " , ->
describe " when no modifier keys are held down " , ->
2014-04-07 00:01:48 +04:00
it " moves the cursor to the nearest screen position " , ->
2014-07-13 21:56:14 +04:00
wrapperNode.style.height = 4.5 * lineHeightInPixels + ' px '
wrapperNode.style.width = 10 * charWidth + ' px '
2014-07-14 22:16:32 +04:00
component . measureHeightAndWidth ( )
2014-04-06 23:56:38 +04:00
editor . setScrollTop ( 3.5 * lineHeightInPixels )
2014-04-10 22:28:58 +04:00
editor . setScrollLeft ( 2 * charWidth )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-04-06 23:56:38 +04:00
2014-04-07 01:47:08 +04:00
linesNode . dispatchEvent ( buildMouseEvent ( ' mousedown ' , clientCoordinatesForScreenPosition ( [ 4 , 8 ] ) ) )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-04-06 23:56:38 +04:00
expect ( editor . getCursorScreenPosition ( ) ) . toEqual [ 4 , 8 ]
describe " when the shift key is held down " , ->
2014-04-07 00:01:48 +04:00
it " selects to the nearest screen position " , ->
2014-04-06 23:56:38 +04:00
editor . setCursorScreenPosition ( [ 3 , 4 ] )
2014-04-07 01:47:08 +04:00
linesNode . dispatchEvent ( buildMouseEvent ( ' mousedown ' , clientCoordinatesForScreenPosition ( [ 5 , 6 ] ) , shiftKey: true ) )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-04-06 23:56:38 +04:00
expect ( editor . getSelectedScreenRange ( ) ) . toEqual [ [ 3 , 4 ] , [ 5 , 6 ] ]
2014-04-07 00:01:19 +04:00
describe " when the command key is held down " , ->
2014-04-07 00:01:48 +04:00
it " adds a cursor at the nearest screen position " , ->
2014-04-07 00:01:19 +04:00
editor . setCursorScreenPosition ( [ 3 , 4 ] )
2014-04-07 01:47:08 +04:00
linesNode . dispatchEvent ( buildMouseEvent ( ' mousedown ' , clientCoordinatesForScreenPosition ( [ 5 , 6 ] ) , metaKey: true ) )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-04-07 00:01:19 +04:00
expect ( editor . getSelectedScreenRanges ( ) ) . toEqual [ [ [ 3 , 4 ] , [ 3 , 4 ] ] , [ [ 5 , 6 ] , [ 5 , 6 ] ] ]
2014-04-07 21:10:14 +04:00
describe " when a non-folded line is double-clicked " , ->
2014-07-04 01:18:30 +04:00
describe " when no modifier keys are held down " , ->
it " selects the word containing the nearest screen position " , ->
linesNode . dispatchEvent ( buildMouseEvent ( ' mousedown ' , clientCoordinatesForScreenPosition ( [ 5 , 10 ] ) , detail: 1 ) )
linesNode . dispatchEvent ( buildMouseEvent ( ' mouseup ' ) )
linesNode . dispatchEvent ( buildMouseEvent ( ' mousedown ' , clientCoordinatesForScreenPosition ( [ 5 , 10 ] ) , detail: 2 ) )
linesNode . dispatchEvent ( buildMouseEvent ( ' mouseup ' ) )
expect ( editor . getSelectedScreenRange ( ) ) . toEqual [ [ 5 , 6 ] , [ 5 , 13 ] ]
2014-04-07 21:10:14 +04:00
2014-07-04 01:18:30 +04:00
linesNode . dispatchEvent ( buildMouseEvent ( ' mousedown ' , clientCoordinatesForScreenPosition ( [ 6 , 6 ] ) , detail: 1 ) )
linesNode . dispatchEvent ( buildMouseEvent ( ' mouseup ' ) )
expect ( editor . getSelectedScreenRange ( ) ) . toEqual [ [ 6 , 6 ] , [ 6 , 6 ] ]
2014-04-07 21:10:14 +04:00
2014-07-04 01:18:30 +04:00
linesNode . dispatchEvent ( buildMouseEvent ( ' mousedown ' , clientCoordinatesForScreenPosition ( [ 8 , 8 ] ) , detail: 1 , shiftKey: true ) )
linesNode . dispatchEvent ( buildMouseEvent ( ' mouseup ' ) )
expect ( editor . getSelectedScreenRange ( ) ) . toEqual [ [ 6 , 6 ] , [ 8 , 8 ] ]
2014-04-07 21:57:53 +04:00
2014-07-04 01:18:30 +04:00
describe " when the command key is held down " , ->
it " selects the word containing the newly-added cursor " , ->
linesNode . dispatchEvent ( buildMouseEvent ( ' mousedown ' , clientCoordinatesForScreenPosition ( [ 5 , 10 ] ) , detail: 1 , metaKey: true ) )
linesNode . dispatchEvent ( buildMouseEvent ( ' mouseup ' ) )
linesNode . dispatchEvent ( buildMouseEvent ( ' mousedown ' , clientCoordinatesForScreenPosition ( [ 5 , 10 ] ) , detail: 2 , metaKey: true ) )
linesNode . dispatchEvent ( buildMouseEvent ( ' mouseup ' ) )
2014-04-07 22:04:59 +04:00
2014-07-04 01:18:30 +04:00
expect ( editor . getSelectedScreenRanges ( ) ) . toEqual [ [ [ 0 , 0 ] , [ 0 , 0 ] ] , [ [ 5 , 6 ] , [ 5 , 13 ] ] ]
2014-04-07 22:04:59 +04:00
2014-07-04 01:18:30 +04:00
describe " when a non-folded line is triple-clicked " , ->
describe " when no modifier keys are held down " , ->
it " selects the line containing the nearest screen position " , ->
linesNode . dispatchEvent ( buildMouseEvent ( ' mousedown ' , clientCoordinatesForScreenPosition ( [ 5 , 10 ] ) , detail: 1 ) )
linesNode . dispatchEvent ( buildMouseEvent ( ' mouseup ' ) )
linesNode . dispatchEvent ( buildMouseEvent ( ' mousedown ' , clientCoordinatesForScreenPosition ( [ 5 , 10 ] ) , detail: 2 ) )
linesNode . dispatchEvent ( buildMouseEvent ( ' mouseup ' ) )
linesNode . dispatchEvent ( buildMouseEvent ( ' mousedown ' , clientCoordinatesForScreenPosition ( [ 5 , 10 ] ) , detail: 3 ) )
linesNode . dispatchEvent ( buildMouseEvent ( ' mouseup ' ) )
expect ( editor . getSelectedScreenRange ( ) ) . toEqual [ [ 5 , 0 ] , [ 6 , 0 ] ]
linesNode . dispatchEvent ( buildMouseEvent ( ' mousedown ' , clientCoordinatesForScreenPosition ( [ 6 , 6 ] ) , detail: 1 , shiftKey: true ) )
linesNode . dispatchEvent ( buildMouseEvent ( ' mouseup ' ) )
expect ( editor . getSelectedScreenRange ( ) ) . toEqual [ [ 5 , 0 ] , [ 7 , 0 ] ]
linesNode . dispatchEvent ( buildMouseEvent ( ' mousedown ' , clientCoordinatesForScreenPosition ( [ 7 , 5 ] ) , detail: 1 ) )
linesNode . dispatchEvent ( buildMouseEvent ( ' mouseup ' ) )
linesNode . dispatchEvent ( buildMouseEvent ( ' mousedown ' , clientCoordinatesForScreenPosition ( [ 8 , 8 ] ) , detail: 1 , shiftKey: true ) )
linesNode . dispatchEvent ( buildMouseEvent ( ' mouseup ' ) )
expect ( editor . getSelectedScreenRange ( ) ) . toEqual [ [ 7 , 5 ] , [ 8 , 8 ] ]
describe " when the command key is held down " , ->
it " selects the line containing the newly-added cursor " , ->
linesNode . dispatchEvent ( buildMouseEvent ( ' mousedown ' , clientCoordinatesForScreenPosition ( [ 5 , 10 ] ) , detail: 1 , metaKey: true ) )
linesNode . dispatchEvent ( buildMouseEvent ( ' mouseup ' ) )
linesNode . dispatchEvent ( buildMouseEvent ( ' mousedown ' , clientCoordinatesForScreenPosition ( [ 5 , 10 ] ) , detail: 2 , metaKey: true ) )
linesNode . dispatchEvent ( buildMouseEvent ( ' mouseup ' ) )
linesNode . dispatchEvent ( buildMouseEvent ( ' mousedown ' , clientCoordinatesForScreenPosition ( [ 5 , 10 ] ) , detail: 3 , metaKey: true ) )
linesNode . dispatchEvent ( buildMouseEvent ( ' mouseup ' ) )
expect ( editor . getSelectedScreenRanges ( ) ) . toEqual [ [ [ 0 , 0 ] , [ 0 , 0 ] ] , [ [ 5 , 0 ] , [ 6 , 0 ] ] ]
2014-04-07 22:04:59 +04:00
2014-04-07 01:47:50 +04:00
describe " when the mouse is clicked and dragged " , ->
it " selects to the nearest screen position until the mouse button is released " , ->
linesNode . dispatchEvent ( buildMouseEvent ( ' mousedown ' , clientCoordinatesForScreenPosition ( [ 2 , 4 ] ) , which: 1 ) )
linesNode . dispatchEvent ( buildMouseEvent ( ' mousemove ' , clientCoordinatesForScreenPosition ( [ 6 , 8 ] ) , which: 1 ) )
nextAnimationFrame ( )
expect ( editor . getSelectedScreenRange ( ) ) . toEqual [ [ 2 , 4 ] , [ 6 , 8 ] ]
linesNode . dispatchEvent ( buildMouseEvent ( ' mousemove ' , clientCoordinatesForScreenPosition ( [ 10 , 0 ] ) , which: 1 ) )
nextAnimationFrame ( )
expect ( editor . getSelectedScreenRange ( ) ) . toEqual [ [ 2 , 4 ] , [ 10 , 0 ] ]
linesNode . dispatchEvent ( buildMouseEvent ( ' mouseup ' ) )
linesNode . dispatchEvent ( buildMouseEvent ( ' mousemove ' , clientCoordinatesForScreenPosition ( [ 12 , 0 ] ) , which: 1 ) )
nextAnimationFrame ( )
expect ( editor . getSelectedScreenRange ( ) ) . toEqual [ [ 2 , 4 ] , [ 10 , 0 ] ]
it " stops selecting if the mouse is dragged into the dev tools " , ->
linesNode . dispatchEvent ( buildMouseEvent ( ' mousedown ' , clientCoordinatesForScreenPosition ( [ 2 , 4 ] ) , which: 1 ) )
linesNode . dispatchEvent ( buildMouseEvent ( ' mousemove ' , clientCoordinatesForScreenPosition ( [ 6 , 8 ] ) , which: 1 ) )
nextAnimationFrame ( )
expect ( editor . getSelectedScreenRange ( ) ) . toEqual [ [ 2 , 4 ] , [ 6 , 8 ] ]
linesNode . dispatchEvent ( buildMouseEvent ( ' mousemove ' , clientCoordinatesForScreenPosition ( [ 10 , 0 ] ) , which: 0 ) )
nextAnimationFrame ( )
expect ( editor . getSelectedScreenRange ( ) ) . toEqual [ [ 2 , 4 ] , [ 6 , 8 ] ]
linesNode . dispatchEvent ( buildMouseEvent ( ' mousemove ' , clientCoordinatesForScreenPosition ( [ 8 , 0 ] ) , which: 1 ) )
2014-07-26 03:38:55 +04:00
expect ( nextAnimationFrame ) . toBe noAnimationFrame
2014-04-07 01:47:50 +04:00
expect ( editor . getSelectedScreenRange ( ) ) . toEqual [ [ 2 , 4 ] , [ 6 , 8 ] ]
2014-12-17 01:27:11 +03:00
describe " when the editor is destroyed while dragging " , ->
it " cleans up the handlers for window.mouseup and window.mousemove " , ->
linesNode . dispatchEvent ( buildMouseEvent ( ' mousedown ' , clientCoordinatesForScreenPosition ( [ 2 , 4 ] ) , which: 1 ) )
linesNode . dispatchEvent ( buildMouseEvent ( ' mousemove ' , clientCoordinatesForScreenPosition ( [ 6 , 8 ] ) , which: 1 ) )
nextAnimationFrame ( )
spyOn ( window , ' removeEventListener ' ) . andCallThrough ( )
linesNode . dispatchEvent ( buildMouseEvent ( ' mousemove ' , clientCoordinatesForScreenPosition ( [ 6 , 10 ] ) , which: 1 ) )
editor . destroy ( )
nextAnimationFrame ( )
call . args . pop ( ) for call in window . removeEventListener . calls
expect ( window . removeEventListener ) . toHaveBeenCalledWith ( ' mouseup ' )
expect ( window . removeEventListener ) . toHaveBeenCalledWith ( ' mousemove ' )
2014-06-19 01:07:06 +04:00
describe " when a line is folded " , ->
beforeEach ->
editor . foldBufferRow 4
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-06-19 01:07:06 +04:00
describe " when the folded line ' s fold-marker is clicked " , ->
it " unfolds the buffer row " , ->
target = component . lineNodeForScreenRow ( 4 ) . querySelector ' .fold-marker '
linesNode . dispatchEvent ( buildMouseEvent ( ' mousedown ' , clientCoordinatesForScreenPosition ( [ 4 , 8 ] ) , { target } ) )
expect ( editor . isFoldedAtBufferRow 4 ) . toBe false
2014-08-21 21:40:16 +04:00
describe " when the horizontal scrollbar is interacted with " , ->
it " clicking on the scrollbar does not move the cursor " , ->
target = horizontalScrollbarNode
linesNode . dispatchEvent ( buildMouseEvent ( ' mousedown ' , clientCoordinatesForScreenPosition ( [ 4 , 8 ] ) , { target } ) )
expect ( editor . getCursorScreenPosition ( ) ) . toEqual [ 0 , 0 ]
2014-06-19 03:54:08 +04:00
describe " mouse interactions on the gutter " , ->
gutterNode = null
beforeEach ->
2014-07-13 21:56:14 +04:00
gutterNode = componentNode . querySelector ( ' .gutter ' )
2014-06-19 03:54:08 +04:00
describe " when the gutter is clicked " , ->
2014-08-30 22:20:09 +04:00
it " selects the clicked row " , ->
2014-06-19 03:54:08 +04:00
gutterNode . dispatchEvent ( buildMouseEvent ( ' mousedown ' , clientCoordinatesForScreenRowInGutter ( 4 ) ) )
2014-08-30 22:20:09 +04:00
expect ( editor . getSelectedScreenRange ( ) ) . toEqual [ [ 4 , 0 ] , [ 5 , 0 ] ]
2014-06-19 03:54:08 +04:00
2014-08-31 23:39:16 +04:00
describe " when the gutter is meta-clicked " , ->
it " creates a new selection for the clicked row " , ->
editor . setSelectedScreenRange ( [ [ 3 , 0 ] , [ 3 , 2 ] ] )
gutterNode . dispatchEvent ( buildMouseEvent ( ' mousedown ' , clientCoordinatesForScreenRowInGutter ( 4 ) , metaKey: true ) )
expect ( editor . getSelectedScreenRanges ( ) ) . toEqual [ [ [ 3 , 0 ] , [ 3 , 2 ] ] , [ [ 4 , 0 ] , [ 5 , 0 ] ] ]
gutterNode . dispatchEvent ( buildMouseEvent ( ' mousedown ' , clientCoordinatesForScreenRowInGutter ( 6 ) , metaKey: true ) )
expect ( editor . getSelectedScreenRanges ( ) ) . toEqual [ [ [ 3 , 0 ] , [ 3 , 2 ] ] , [ [ 4 , 0 ] , [ 5 , 0 ] ] , [ [ 6 , 0 ] , [ 7 , 0 ] ] ]
2014-06-19 03:54:08 +04:00
describe " when the gutter is shift-clicked " , ->
beforeEach ->
editor . setSelectedScreenRange ( [ [ 3 , 4 ] , [ 4 , 5 ] ] )
describe " when the clicked row is before the current selection ' s tail " , ->
it " selects to the beginning of the clicked row " , ->
gutterNode . dispatchEvent ( buildMouseEvent ( ' mousedown ' , clientCoordinatesForScreenRowInGutter ( 1 ) , shiftKey: true ) )
expect ( editor . getSelectedScreenRange ( ) ) . toEqual [ [ 1 , 0 ] , [ 3 , 4 ] ]
describe " when the clicked row is after the current selection ' s tail " , ->
it " selects to the beginning of the row following the clicked row " , ->
gutterNode . dispatchEvent ( buildMouseEvent ( ' mousedown ' , clientCoordinatesForScreenRowInGutter ( 6 ) , shiftKey: true ) )
expect ( editor . getSelectedScreenRange ( ) ) . toEqual [ [ 3 , 4 ] , [ 7 , 0 ] ]
2014-04-06 23:34:50 +04:00
2014-06-19 04:48:28 +04:00
describe " when the gutter is clicked and dragged " , ->
describe " when dragging downward " , ->
it " selects the rows between the start and end of the drag " , ->
gutterNode . dispatchEvent ( buildMouseEvent ( ' mousedown ' , clientCoordinatesForScreenRowInGutter ( 2 ) ) )
gutterNode . dispatchEvent ( buildMouseEvent ( ' mousemove ' , clientCoordinatesForScreenRowInGutter ( 6 ) ) )
nextAnimationFrame ( )
gutterNode . dispatchEvent ( buildMouseEvent ( ' mouseup ' , clientCoordinatesForScreenRowInGutter ( 6 ) ) )
2014-06-19 23:06:09 +04:00
expect ( editor . getSelectedScreenRange ( ) ) . toEqual [ [ 2 , 0 ] , [ 7 , 0 ] ]
2014-06-19 04:48:28 +04:00
describe " when dragging upward " , ->
it " selects the rows between the start and end of the drag " , ->
gutterNode . dispatchEvent ( buildMouseEvent ( ' mousedown ' , clientCoordinatesForScreenRowInGutter ( 6 ) ) )
gutterNode . dispatchEvent ( buildMouseEvent ( ' mousemove ' , clientCoordinatesForScreenRowInGutter ( 2 ) ) )
nextAnimationFrame ( )
gutterNode . dispatchEvent ( buildMouseEvent ( ' mouseup ' , clientCoordinatesForScreenRowInGutter ( 2 ) ) )
2014-06-19 23:06:09 +04:00
expect ( editor . getSelectedScreenRange ( ) ) . toEqual [ [ 2 , 0 ] , [ 7 , 0 ] ]
2014-06-19 04:48:28 +04:00
2014-08-31 23:39:16 +04:00
describe " when the gutter is meta-clicked and dragged " , ->
beforeEach ->
editor . setSelectedScreenRange ( [ [ 3 , 0 ] , [ 3 , 2 ] ] )
describe " when dragging downward " , ->
it " selects the rows between the start and end of the drag " , ->
gutterNode . dispatchEvent ( buildMouseEvent ( ' mousedown ' , clientCoordinatesForScreenRowInGutter ( 4 ) , metaKey: true ) )
gutterNode . dispatchEvent ( buildMouseEvent ( ' mousemove ' , clientCoordinatesForScreenRowInGutter ( 6 ) , metaKey: true ) )
nextAnimationFrame ( )
gutterNode . dispatchEvent ( buildMouseEvent ( ' mouseup ' , clientCoordinatesForScreenRowInGutter ( 6 ) , metaKey: true ) )
expect ( editor . getSelectedScreenRanges ( ) ) . toEqual [ [ [ 3 , 0 ] , [ 3 , 2 ] ] , [ [ 4 , 0 ] , [ 7 , 0 ] ] ]
it " merges overlapping selections " , ->
gutterNode . dispatchEvent ( buildMouseEvent ( ' mousedown ' , clientCoordinatesForScreenRowInGutter ( 2 ) , metaKey: true ) )
gutterNode . dispatchEvent ( buildMouseEvent ( ' mousemove ' , clientCoordinatesForScreenRowInGutter ( 6 ) , metaKey: true ) )
nextAnimationFrame ( )
gutterNode . dispatchEvent ( buildMouseEvent ( ' mouseup ' , clientCoordinatesForScreenRowInGutter ( 6 ) , metaKey: true ) )
expect ( editor . getSelectedScreenRanges ( ) ) . toEqual [ [ [ 2 , 0 ] , [ 7 , 0 ] ] ]
describe " when dragging upward " , ->
it " selects the rows between the start and end of the drag " , ->
gutterNode . dispatchEvent ( buildMouseEvent ( ' mousedown ' , clientCoordinatesForScreenRowInGutter ( 6 ) , metaKey: true ) )
gutterNode . dispatchEvent ( buildMouseEvent ( ' mousemove ' , clientCoordinatesForScreenRowInGutter ( 4 ) , metaKey: true ) )
nextAnimationFrame ( )
gutterNode . dispatchEvent ( buildMouseEvent ( ' mouseup ' , clientCoordinatesForScreenRowInGutter ( 4 ) , metaKey: true ) )
expect ( editor . getSelectedScreenRanges ( ) ) . toEqual [ [ [ 3 , 0 ] , [ 3 , 2 ] ] , [ [ 4 , 0 ] , [ 7 , 0 ] ] ]
it " merges overlapping selections " , ->
gutterNode . dispatchEvent ( buildMouseEvent ( ' mousedown ' , clientCoordinatesForScreenRowInGutter ( 6 ) , metaKey: true ) )
gutterNode . dispatchEvent ( buildMouseEvent ( ' mousemove ' , clientCoordinatesForScreenRowInGutter ( 2 ) , metaKey: true ) )
nextAnimationFrame ( )
gutterNode . dispatchEvent ( buildMouseEvent ( ' mouseup ' , clientCoordinatesForScreenRowInGutter ( 2 ) , metaKey: true ) )
expect ( editor . getSelectedScreenRanges ( ) ) . toEqual [ [ [ 2 , 0 ] , [ 7 , 0 ] ] ]
2014-06-19 04:48:28 +04:00
describe " when the gutter is shift-clicked and dragged " , ->
describe " when the shift-click is below the existing selection ' s tail " , ->
describe " when dragging downward " , ->
it " selects the rows between the existing selection ' s tail and the end of the drag " , ->
editor . setSelectedScreenRange ( [ [ 3 , 4 ] , [ 4 , 5 ] ] )
gutterNode . dispatchEvent ( buildMouseEvent ( ' mousedown ' , clientCoordinatesForScreenRowInGutter ( 7 ) , shiftKey: true ) )
gutterNode . dispatchEvent ( buildMouseEvent ( ' mousemove ' , clientCoordinatesForScreenRowInGutter ( 8 ) ) )
nextAnimationFrame ( )
2014-06-19 23:06:09 +04:00
expect ( editor . getSelectedScreenRange ( ) ) . toEqual [ [ 3 , 4 ] , [ 9 , 0 ] ]
2014-06-19 04:48:28 +04:00
describe " when dragging upward " , ->
it " selects the rows between the end of the drag and the tail of the existing selection " , ->
editor . setSelectedScreenRange ( [ [ 4 , 4 ] , [ 5 , 5 ] ] )
gutterNode . dispatchEvent ( buildMouseEvent ( ' mousedown ' , clientCoordinatesForScreenRowInGutter ( 7 ) , shiftKey: true ) )
gutterNode . dispatchEvent ( buildMouseEvent ( ' mousemove ' , clientCoordinatesForScreenRowInGutter ( 5 ) ) )
nextAnimationFrame ( )
2014-06-19 23:06:09 +04:00
expect ( editor . getSelectedScreenRange ( ) ) . toEqual [ [ 4 , 4 ] , [ 6 , 0 ] ]
2014-06-19 04:48:28 +04:00
gutterNode . dispatchEvent ( buildMouseEvent ( ' mousemove ' , clientCoordinatesForScreenRowInGutter ( 1 ) ) )
nextAnimationFrame ( )
expect ( editor . getSelectedScreenRange ( ) ) . toEqual [ [ 1 , 0 ] , [ 4 , 4 ] ]
describe " when the shift-click is above the existing selection ' s tail " , ->
describe " when dragging upward " , ->
it " selects the rows between the end of the drag and the tail of the existing selection " , ->
editor . setSelectedScreenRange ( [ [ 4 , 4 ] , [ 5 , 5 ] ] )
gutterNode . dispatchEvent ( buildMouseEvent ( ' mousedown ' , clientCoordinatesForScreenRowInGutter ( 2 ) , shiftKey: true ) )
gutterNode . dispatchEvent ( buildMouseEvent ( ' mousemove ' , clientCoordinatesForScreenRowInGutter ( 1 ) ) )
nextAnimationFrame ( )
expect ( editor . getSelectedScreenRange ( ) ) . toEqual [ [ 1 , 0 ] , [ 4 , 4 ] ]
describe " when dragging downward " , ->
it " selects the rows between the existing selection ' s tail and the end of the drag " , ->
editor . setSelectedScreenRange ( [ [ 3 , 4 ] , [ 4 , 5 ] ] )
gutterNode . dispatchEvent ( buildMouseEvent ( ' mousedown ' , clientCoordinatesForScreenRowInGutter ( 1 ) , shiftKey: true ) )
gutterNode . dispatchEvent ( buildMouseEvent ( ' mousemove ' , clientCoordinatesForScreenRowInGutter ( 2 ) ) )
nextAnimationFrame ( )
expect ( editor . getSelectedScreenRange ( ) ) . toEqual [ [ 2 , 0 ] , [ 3 , 4 ] ]
gutterNode . dispatchEvent ( buildMouseEvent ( ' mousemove ' , clientCoordinatesForScreenRowInGutter ( 8 ) ) )
nextAnimationFrame ( )
2014-06-19 23:06:09 +04:00
expect ( editor . getSelectedScreenRange ( ) ) . toEqual [ [ 3 , 4 ] , [ 9 , 0 ] ]
2014-06-19 04:48:28 +04:00
2014-04-07 23:06:40 +04:00
describe " focus handling " , ->
inputNode = null
beforeEach ->
2014-07-13 21:56:14 +04:00
inputNode = componentNode . querySelector ( ' .hidden-input ' )
2014-04-07 23:06:40 +04:00
it " transfers focus to the hidden input " , ->
expect ( document . activeElement ) . toBe document . body
2014-11-05 01:02:08 +03:00
wrapperNode . focus ( )
2014-10-16 22:42:42 +04:00
expect ( document . activeElement ) . toBe wrapperNode
expect ( wrapperNode . shadowRoot . activeElement ) . toBe inputNode
2014-04-07 23:06:40 +04:00
it " adds the ' is-focused ' class to the editor when the hidden input is focused " , ->
expect ( document . activeElement ) . toBe document . body
inputNode . focus ( )
2014-07-13 21:56:14 +04:00
expect ( componentNode . classList . contains ( ' is-focused ' ) ) . toBe true
2014-06-11 18:37:16 +04:00
expect ( wrapperView . hasClass ( ' is-focused ' ) ) . toBe true
2014-04-07 23:06:40 +04:00
inputNode . blur ( )
2014-07-13 21:56:14 +04:00
expect ( componentNode . classList . contains ( ' is-focused ' ) ) . toBe false
2014-06-11 18:37:16 +04:00
expect ( wrapperView . hasClass ( ' is-focused ' ) ) . toBe false
2014-04-08 03:52:48 +04:00
2014-06-10 02:45:32 +04:00
describe " selection handling " , ->
cursor = null
beforeEach ->
2014-08-29 05:10:18 +04:00
cursor = editor . getLastCursor ( )
2014-06-10 02:45:32 +04:00
cursor . setScreenPosition ( [ 0 , 0 ] )
it " adds the ' has-selection ' class to the editor when there is a selection " , ->
2014-07-13 21:56:14 +04:00
expect ( componentNode . classList . contains ( ' has-selection ' ) ) . toBe false
2014-06-10 02:45:32 +04:00
editor . selectDown ( )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-07-13 21:56:14 +04:00
expect ( componentNode . classList . contains ( ' has-selection ' ) ) . toBe true
2014-06-10 02:45:32 +04:00
cursor . moveDown ( )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-07-13 21:56:14 +04:00
expect ( componentNode . classList . contains ( ' has-selection ' ) ) . toBe false
2014-06-10 02:45:32 +04:00
2014-04-08 22:04:27 +04:00
describe " scrolling " , ->
it " updates the vertical scrollbar when the scrollTop is changed in the model " , ->
2014-07-13 21:56:14 +04:00
wrapperNode.style.height = 4.5 * lineHeightInPixels + ' px '
2014-07-14 22:16:32 +04:00
component . measureHeightAndWidth ( )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-04-08 22:04:27 +04:00
2014-04-14 23:06:15 +04:00
expect ( verticalScrollbarNode . scrollTop ) . toBe 0
2014-04-08 03:52:48 +04:00
2014-04-08 22:04:27 +04:00
editor . setScrollTop ( 10 )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-04-14 23:06:15 +04:00
expect ( verticalScrollbarNode . scrollTop ) . toBe 10
2014-04-08 22:04:27 +04:00
2014-05-13 01:18:28 +04:00
it " updates the horizontal scrollbar and the x transform of the lines based on the scrollLeft of the model " , ->
2014-07-13 21:56:14 +04:00
componentNode.style.width = 30 * charWidth + ' px '
2014-07-14 22:16:32 +04:00
component . measureHeightAndWidth ( )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-04-08 03:52:48 +04:00
2014-07-13 21:56:14 +04:00
linesNode = componentNode . querySelector ( ' .lines ' )
2014-05-16 20:44:49 +04:00
expect ( linesNode . style [ ' -webkit-transform ' ] ) . toBe " translate3d(0px, 0px, 0px) "
2014-04-09 00:12:41 +04:00
expect ( horizontalScrollbarNode . scrollLeft ) . toBe 0
2014-04-08 22:04:27 +04:00
editor . setScrollLeft ( 100 )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-05-16 20:44:49 +04:00
expect ( linesNode . style [ ' -webkit-transform ' ] ) . toBe " translate3d(-100px, 0px, 0px) "
2014-04-09 00:12:41 +04:00
expect ( horizontalScrollbarNode . scrollLeft ) . toBe 100
2014-04-09 00:20:19 +04:00
it " updates the scrollLeft of the model when the scrollLeft of the horizontal scrollbar changes " , ->
2014-07-13 21:56:14 +04:00
componentNode.style.width = 30 * charWidth + ' px '
2014-07-14 22:16:32 +04:00
component . measureHeightAndWidth ( )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-04-09 00:20:19 +04:00
expect ( editor . getScrollLeft ( ) ) . toBe 0
2014-04-14 23:06:15 +04:00
horizontalScrollbarNode.scrollLeft = 100
horizontalScrollbarNode . dispatchEvent ( new UIEvent ( ' scroll ' ) )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-04-09 00:20:19 +04:00
expect ( editor . getScrollLeft ( ) ) . toBe 100
2014-04-09 00:38:06 +04:00
2014-04-24 20:18:41 +04:00
it " does not obscure the last line with the horizontal scrollbar " , ->
2014-07-13 21:56:14 +04:00
wrapperNode.style.height = 4.5 * lineHeightInPixels + ' px '
wrapperNode.style.width = 10 * charWidth + ' px '
2014-07-14 22:16:32 +04:00
component . measureHeightAndWidth ( )
2014-04-24 20:18:41 +04:00
editor . setScrollBottom ( editor . getScrollHeight ( ) )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-05-16 20:44:49 +04:00
lastLineNode = component . lineNodeForScreenRow ( editor . getLastScreenRow ( ) )
2014-04-24 20:18:41 +04:00
bottomOfLastLine = lastLineNode . getBoundingClientRect ( ) . bottom
topOfHorizontalScrollbar = horizontalScrollbarNode . getBoundingClientRect ( ) . top
expect ( bottomOfLastLine ) . toBe topOfHorizontalScrollbar
2014-04-24 00:13:19 +04:00
# Scroll so there's no space below the last line when the horizontal scrollbar disappears
2014-07-13 21:56:14 +04:00
wrapperNode.style.width = 100 * charWidth + ' px '
2014-07-14 22:16:32 +04:00
component . measureHeightAndWidth ( )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-04-24 20:18:41 +04:00
bottomOfLastLine = lastLineNode . getBoundingClientRect ( ) . bottom
2014-07-13 21:56:14 +04:00
bottomOfEditor = componentNode . getBoundingClientRect ( ) . bottom
2014-04-24 20:18:41 +04:00
expect ( bottomOfLastLine ) . toBe bottomOfEditor
2014-04-25 07:20:25 +04:00
it " does not obscure the last character of the longest line with the vertical scrollbar " , ->
2014-07-13 21:56:14 +04:00
wrapperNode.style.height = 7 * lineHeightInPixels + ' px '
wrapperNode.style.width = 10 * charWidth + ' px '
2014-07-14 22:16:32 +04:00
component . measureHeightAndWidth ( )
2014-04-25 07:20:25 +04:00
editor . setScrollLeft ( Infinity )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-04-25 07:20:25 +04:00
2014-06-24 20:29:13 +04:00
rightOfLongestLine = component . lineNodeForScreenRow ( 6 ) . querySelector ( ' .line > span:last-child ' ) . getBoundingClientRect ( ) . right
2014-04-25 07:20:25 +04:00
leftOfVerticalScrollbar = verticalScrollbarNode . getBoundingClientRect ( ) . left
2014-05-16 20:44:49 +04:00
expect ( Math . round ( rightOfLongestLine ) ) . toBe leftOfVerticalScrollbar - 1 # Leave 1 px so the cursor is visible on the end of the line
2014-04-25 07:20:25 +04:00
2014-05-02 15:11:14 +04:00
it " only displays dummy scrollbars when scrollable in that direction " , ->
expect ( verticalScrollbarNode . style . display ) . toBe ' none '
expect ( horizontalScrollbarNode . style . display ) . toBe ' none '
2014-07-13 21:56:14 +04:00
wrapperNode.style.height = 4.5 * lineHeightInPixels + ' px '
wrapperNode.style.width = ' 1000px '
2014-07-14 22:16:32 +04:00
component . measureHeightAndWidth ( )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-05-02 15:11:14 +04:00
expect ( verticalScrollbarNode . style . display ) . toBe ' '
expect ( horizontalScrollbarNode . style . display ) . toBe ' none '
2014-07-13 21:56:14 +04:00
componentNode.style.width = 10 * charWidth + ' px '
2014-07-14 22:16:32 +04:00
component . measureHeightAndWidth ( )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-05-02 15:11:14 +04:00
expect ( verticalScrollbarNode . style . display ) . toBe ' '
expect ( horizontalScrollbarNode . style . display ) . toBe ' '
2014-07-13 21:56:14 +04:00
wrapperNode.style.height = 20 * lineHeightInPixels + ' px '
2014-07-14 22:16:32 +04:00
component . measureHeightAndWidth ( )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-05-02 15:11:14 +04:00
expect ( verticalScrollbarNode . style . display ) . toBe ' none '
expect ( horizontalScrollbarNode . style . display ) . toBe ' '
2014-05-02 23:32:03 +04:00
it " makes the dummy scrollbar divs only as tall/wide as the actual scrollbars " , ->
2014-07-13 21:56:14 +04:00
wrapperNode.style.height = 4 * lineHeightInPixels + ' px '
wrapperNode.style.width = 10 * charWidth + ' px '
2014-07-14 22:16:32 +04:00
component . measureHeightAndWidth ( )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-05-03 00:28:29 +04:00
2014-10-16 22:42:29 +04:00
atom . styles . addStyleSheet """
2014-05-02 23:32:03 +04:00
: : - webkit - scrollbar {
width: 8 px ;
height: 8 px ;
}
2014-10-16 22:42:29 +04:00
""" , context: ' atom-text-editor '
2014-12-10 00:56:20 +03:00
nextAnimationFrame ( ) # handle stylesheet change event
nextAnimationFrame ( ) # perform requested update
2014-05-02 23:32:03 +04:00
2014-07-13 21:56:14 +04:00
scrollbarCornerNode = componentNode . querySelector ( ' .scrollbar-corner ' )
2014-05-02 23:32:03 +04:00
expect ( verticalScrollbarNode . offsetWidth ) . toBe 8
expect ( horizontalScrollbarNode . offsetHeight ) . toBe 8
2014-05-03 00:28:29 +04:00
expect ( scrollbarCornerNode . offsetWidth ) . toBe 8
expect ( scrollbarCornerNode . offsetHeight ) . toBe 8
2014-05-02 23:32:03 +04:00
2014-07-18 04:56:06 +04:00
atom . themes . removeStylesheet ( ' test ' )
2014-04-25 07:20:25 +04:00
it " assigns the bottom/right of the scrollbars to the width of the opposite scrollbar if it is visible " , ->
2014-07-13 21:56:14 +04:00
scrollbarCornerNode = componentNode . querySelector ( ' .scrollbar-corner ' )
2014-05-02 14:15:42 +04:00
2014-04-25 07:20:25 +04:00
expect ( verticalScrollbarNode . style . bottom ) . toBe ' '
expect ( horizontalScrollbarNode . style . right ) . toBe ' '
2014-04-24 04:10:26 +04:00
2014-07-13 21:56:14 +04:00
wrapperNode.style.height = 4.5 * lineHeightInPixels + ' px '
wrapperNode.style.width = ' 1000px '
2014-07-14 22:16:32 +04:00
component . measureHeightAndWidth ( )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-04-25 07:20:25 +04:00
expect ( verticalScrollbarNode . style . bottom ) . toBe ' '
expect ( horizontalScrollbarNode . style . right ) . toBe verticalScrollbarNode . offsetWidth + ' px '
2014-05-02 14:15:42 +04:00
expect ( scrollbarCornerNode . style . display ) . toBe ' none '
2014-04-24 04:10:26 +04:00
2014-07-13 21:56:14 +04:00
componentNode.style.width = 10 * charWidth + ' px '
2014-07-14 22:16:32 +04:00
component . measureHeightAndWidth ( )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-04-25 07:20:25 +04:00
expect ( verticalScrollbarNode . style . bottom ) . toBe horizontalScrollbarNode . offsetHeight + ' px '
expect ( horizontalScrollbarNode . style . right ) . toBe verticalScrollbarNode . offsetWidth + ' px '
2014-05-02 14:15:42 +04:00
expect ( scrollbarCornerNode . style . display ) . toBe ' '
2014-04-24 04:10:26 +04:00
2014-07-13 21:56:14 +04:00
wrapperNode.style.height = 20 * lineHeightInPixels + ' px '
2014-07-14 22:16:32 +04:00
component . measureHeightAndWidth ( )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-04-25 07:20:25 +04:00
expect ( verticalScrollbarNode . style . bottom ) . toBe horizontalScrollbarNode . offsetHeight + ' px '
expect ( horizontalScrollbarNode . style . right ) . toBe ' '
2014-05-02 14:15:42 +04:00
expect ( scrollbarCornerNode . style . display ) . toBe ' none '
2014-04-24 04:10:26 +04:00
2014-04-25 05:07:17 +04:00
it " accounts for the width of the gutter in the scrollWidth of the horizontal scrollbar " , ->
2014-07-13 21:56:14 +04:00
gutterNode = componentNode . querySelector ( ' .gutter ' )
componentNode.style.width = 10 * charWidth + ' px '
2014-07-14 22:16:32 +04:00
component . measureHeightAndWidth ( )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-04-25 05:07:17 +04:00
2014-07-23 02:21:41 +04:00
expect ( horizontalScrollbarNode . scrollWidth ) . toBe editor . getScrollWidth ( )
expect ( horizontalScrollbarNode . style . left ) . toBe ' 0px '
2014-04-25 05:07:17 +04:00
2014-05-31 13:20:27 +04:00
describe " mousewheel events " , ->
2014-06-10 05:05:54 +04:00
beforeEach ->
atom . config . set ( ' editor.scrollSensitivity ' , 100 )
2014-04-09 00:38:06 +04:00
2014-06-10 05:05:54 +04:00
describe " updating scrollTop and scrollLeft " , ->
beforeEach ->
2014-07-13 21:56:14 +04:00
wrapperNode.style.height = 4.5 * lineHeightInPixels + ' px '
wrapperNode.style.width = 20 * charWidth + ' px '
2014-07-14 22:16:32 +04:00
component . measureHeightAndWidth ( )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-04-10 22:41:36 +04:00
2014-06-10 05:05:54 +04:00
it " updates the scrollLeft or scrollTop on mousewheel events depending on which delta is greater (x or y) " , ->
expect ( verticalScrollbarNode . scrollTop ) . toBe 0
expect ( horizontalScrollbarNode . scrollLeft ) . toBe 0
2014-07-13 21:56:14 +04:00
componentNode . dispatchEvent ( new WheelEvent ( ' mousewheel ' , wheelDeltaX: - 5 , wheelDeltaY: - 10 ) )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-06-10 05:05:54 +04:00
expect ( verticalScrollbarNode . scrollTop ) . toBe 10
expect ( horizontalScrollbarNode . scrollLeft ) . toBe 0
2014-07-13 21:56:14 +04:00
componentNode . dispatchEvent ( new WheelEvent ( ' mousewheel ' , wheelDeltaX: - 15 , wheelDeltaY: - 5 ) )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-06-10 05:05:54 +04:00
expect ( verticalScrollbarNode . scrollTop ) . toBe 10
expect ( horizontalScrollbarNode . scrollLeft ) . toBe 15
it " updates the scrollLeft or scrollTop according to the scroll sensitivity " , ->
atom . config . set ( ' editor.scrollSensitivity ' , 50 )
2014-07-13 21:56:14 +04:00
componentNode . dispatchEvent ( new WheelEvent ( ' mousewheel ' , wheelDeltaX: - 5 , wheelDeltaY: - 10 ) )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-06-10 05:05:54 +04:00
expect ( horizontalScrollbarNode . scrollLeft ) . toBe 0
2014-07-13 21:56:14 +04:00
componentNode . dispatchEvent ( new WheelEvent ( ' mousewheel ' , wheelDeltaX: - 15 , wheelDeltaY: - 5 ) )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-06-10 05:05:54 +04:00
expect ( verticalScrollbarNode . scrollTop ) . toBe 5
expect ( horizontalScrollbarNode . scrollLeft ) . toBe 7
it " uses the previous scrollSensitivity when the value is not an int " , ->
atom . config . set ( ' editor.scrollSensitivity ' , ' nope ' )
2014-07-13 21:56:14 +04:00
componentNode . dispatchEvent ( new WheelEvent ( ' mousewheel ' , wheelDeltaX: 0 , wheelDeltaY: - 10 ) )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-06-10 05:05:54 +04:00
expect ( verticalScrollbarNode . scrollTop ) . toBe 10
2014-09-25 03:24:26 +04:00
it " parses negative scrollSensitivity values at the minimum " , ->
2014-06-10 05:05:54 +04:00
atom . config . set ( ' editor.scrollSensitivity ' , - 50 )
2014-07-13 21:56:14 +04:00
componentNode . dispatchEvent ( new WheelEvent ( ' mousewheel ' , wheelDeltaX: 0 , wheelDeltaY: - 10 ) )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-09-25 03:24:26 +04:00
expect ( verticalScrollbarNode . scrollTop ) . toBe 1
2014-05-31 13:20:27 +04:00
describe " when the mousewheel event ' s target is a line " , ->
it " keeps the line on the DOM if it is scrolled off-screen " , ->
2014-07-13 21:56:14 +04:00
wrapperNode.style.height = 4.5 * lineHeightInPixels + ' px '
wrapperNode.style.width = 20 * charWidth + ' px '
2014-07-14 22:16:32 +04:00
component . measureHeightAndWidth ( )
2014-05-20 00:33:17 +04:00
2014-07-13 21:56:14 +04:00
lineNode = componentNode . querySelector ( ' .line ' )
2014-05-31 13:20:27 +04:00
wheelEvent = new WheelEvent ( ' mousewheel ' , wheelDeltaX: 0 , wheelDeltaY: - 500 )
Object . defineProperty ( wheelEvent , ' target ' , get: -> lineNode )
2014-07-13 21:56:14 +04:00
componentNode . dispatchEvent ( wheelEvent )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-05-20 00:33:17 +04:00
2014-07-13 21:56:14 +04:00
expect ( componentNode . contains ( lineNode ) ) . toBe true
2014-05-20 00:33:17 +04:00
2014-05-31 13:20:27 +04:00
it " does not set the mouseWheelScreenRow if scrolling horizontally " , ->
2014-07-13 21:56:14 +04:00
wrapperNode.style.height = 4.5 * lineHeightInPixels + ' px '
wrapperNode.style.width = 20 * charWidth + ' px '
2014-07-14 22:16:32 +04:00
component . measureHeightAndWidth ( )
2014-05-30 01:49:34 +04:00
2014-07-13 21:56:14 +04:00
lineNode = componentNode . querySelector ( ' .line ' )
2014-05-31 13:20:27 +04:00
wheelEvent = new WheelEvent ( ' mousewheel ' , wheelDeltaX: 10 , wheelDeltaY: 0 )
Object . defineProperty ( wheelEvent , ' target ' , get: -> lineNode )
2014-07-13 21:56:14 +04:00
componentNode . dispatchEvent ( wheelEvent )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-05-30 01:49:34 +04:00
2014-05-31 13:20:27 +04:00
expect ( component . mouseWheelScreenRow ) . toBe null
2014-05-30 01:49:34 +04:00
2014-05-31 13:36:59 +04:00
it " clears the mouseWheelScreenRow after a delay even if the event does not cause scrolling " , ->
expect ( editor . getScrollTop ( ) ) . toBe 0
2014-07-13 21:56:14 +04:00
lineNode = componentNode . querySelector ( ' .line ' )
2014-05-31 13:36:59 +04:00
wheelEvent = new WheelEvent ( ' mousewheel ' , wheelDeltaX: 0 , wheelDeltaY: 10 )
Object . defineProperty ( wheelEvent , ' target ' , get: -> lineNode )
2014-07-13 21:56:14 +04:00
componentNode . dispatchEvent ( wheelEvent )
2014-07-26 03:38:55 +04:00
expect ( nextAnimationFrame ) . toBe noAnimationFrame
2014-05-31 13:36:59 +04:00
expect ( editor . getScrollTop ( ) ) . toBe 0
expect ( component . mouseWheelScreenRow ) . toBe 0
advanceClock ( component . mouseWheelScreenRowClearDelay )
expect ( component . mouseWheelScreenRow ) . toBe null
2014-05-31 13:20:27 +04:00
it " does not preserve the line if it is on screen " , ->
2014-07-13 21:56:14 +04:00
expect ( componentNode . querySelectorAll ( ' .line-number ' ) . length ) . toBe 14 # dummy line
lineNodes = componentNode . querySelectorAll ( ' .line ' )
2014-05-31 13:20:27 +04:00
expect ( lineNodes . length ) . toBe 13
lineNode = lineNodes [ 0 ]
2014-05-30 06:54:29 +04:00
2014-05-31 13:20:27 +04:00
wheelEvent = new WheelEvent ( ' mousewheel ' , wheelDeltaX: 0 , wheelDeltaY: 100 ) # goes nowhere, we're already at scrollTop 0
Object . defineProperty ( wheelEvent , ' target ' , get: -> lineNode )
2014-07-13 21:56:14 +04:00
componentNode . dispatchEvent ( wheelEvent )
2014-07-26 03:38:55 +04:00
expect ( nextAnimationFrame ) . toBe noAnimationFrame
2014-05-30 06:54:29 +04:00
2014-05-31 13:20:27 +04:00
expect ( component . mouseWheelScreenRow ) . toBe 0
editor . insertText ( " hello " )
2014-07-13 21:56:14 +04:00
expect ( componentNode . querySelectorAll ( ' .line-number ' ) . length ) . toBe 14 # dummy line
expect ( componentNode . querySelectorAll ( ' .line ' ) . length ) . toBe 13
2014-05-30 06:54:29 +04:00
2014-05-31 13:20:27 +04:00
describe " when the mousewheel event ' s target is a line number " , ->
it " keeps the line number on the DOM if it is scrolled off-screen " , ->
2014-07-13 21:56:14 +04:00
wrapperNode.style.height = 4.5 * lineHeightInPixels + ' px '
wrapperNode.style.width = 20 * charWidth + ' px '
2014-07-14 22:16:32 +04:00
component . measureHeightAndWidth ( )
2014-05-20 00:33:17 +04:00
2014-07-13 21:56:14 +04:00
lineNumberNode = componentNode . querySelectorAll ( ' .line-number ' ) [ 1 ]
2014-05-31 13:20:27 +04:00
wheelEvent = new WheelEvent ( ' mousewheel ' , wheelDeltaX: 0 , wheelDeltaY: - 500 )
Object . defineProperty ( wheelEvent , ' target ' , get: -> lineNumberNode )
2014-07-13 21:56:14 +04:00
componentNode . dispatchEvent ( wheelEvent )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-05-20 00:33:17 +04:00
2014-07-13 21:56:14 +04:00
expect ( componentNode . contains ( lineNumberNode ) ) . toBe true
2014-05-20 00:33:17 +04:00
2014-07-14 23:06:14 +04:00
it " only prevents the default action of the mousewheel event if it actually lead to scrolling " , ->
spyOn ( WheelEvent : : , ' preventDefault ' ) . andCallThrough ( )
wrapperNode.style.height = 4.5 * lineHeightInPixels + ' px '
wrapperNode.style.width = 20 * charWidth + ' px '
component . measureHeightAndWidth ( )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-07-14 23:06:14 +04:00
2014-07-22 21:16:43 +04:00
# try to scroll past the top, which is impossible
2014-07-14 23:06:14 +04:00
componentNode . dispatchEvent ( new WheelEvent ( ' mousewheel ' , wheelDeltaX: 0 , wheelDeltaY: 50 ) )
expect ( editor . getScrollTop ( ) ) . toBe 0
expect ( WheelEvent : : preventDefault ) . not . toHaveBeenCalled ( )
2014-07-22 21:16:43 +04:00
# scroll to the bottom in one huge event
2014-07-14 23:06:14 +04:00
componentNode . dispatchEvent ( new WheelEvent ( ' mousewheel ' , wheelDeltaX: 0 , wheelDeltaY: - 3000 ) )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-07-22 21:16:43 +04:00
maxScrollTop = editor . getScrollTop ( )
2014-07-14 23:06:14 +04:00
expect ( WheelEvent : : preventDefault ) . toHaveBeenCalled ( )
WheelEvent : : preventDefault . reset ( )
2014-07-22 21:16:43 +04:00
# try to scroll past the bottom, which is impossible
2014-07-14 23:06:14 +04:00
componentNode . dispatchEvent ( new WheelEvent ( ' mousewheel ' , wheelDeltaX: 0 , wheelDeltaY: - 30 ) )
2014-07-22 21:16:43 +04:00
expect ( editor . getScrollTop ( ) ) . toBe maxScrollTop
2014-07-14 23:06:14 +04:00
expect ( WheelEvent : : preventDefault ) . not . toHaveBeenCalled ( )
2014-07-22 21:16:43 +04:00
# try to scroll past the left side, which is impossible
2014-07-14 23:06:14 +04:00
componentNode . dispatchEvent ( new WheelEvent ( ' mousewheel ' , wheelDeltaX: 50 , wheelDeltaY: 0 ) )
expect ( editor . getScrollLeft ( ) ) . toBe 0
expect ( WheelEvent : : preventDefault ) . not . toHaveBeenCalled ( )
2014-07-22 21:16:43 +04:00
# scroll all the way right
2014-07-14 23:06:14 +04:00
componentNode . dispatchEvent ( new WheelEvent ( ' mousewheel ' , wheelDeltaX: - 3000 , wheelDeltaY: 0 ) )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-07-22 21:16:43 +04:00
maxScrollLeft = editor . getScrollLeft ( )
2014-07-14 23:06:14 +04:00
expect ( WheelEvent : : preventDefault ) . toHaveBeenCalled ( )
WheelEvent : : preventDefault . reset ( )
2014-07-22 21:16:43 +04:00
# try to scroll past the right side, which is impossible
2014-07-14 23:06:14 +04:00
componentNode . dispatchEvent ( new WheelEvent ( ' mousewheel ' , wheelDeltaX: - 30 , wheelDeltaY: 0 ) )
2014-07-22 21:16:43 +04:00
expect ( editor . getScrollLeft ( ) ) . toBe maxScrollLeft
2014-07-14 23:06:14 +04:00
expect ( WheelEvent : : preventDefault ) . not . toHaveBeenCalled ( )
2014-04-10 22:41:36 +04:00
describe " input events " , ->
2014-04-14 23:06:15 +04:00
inputNode = null
beforeEach ->
2014-07-13 21:56:14 +04:00
inputNode = componentNode . querySelector ( ' .hidden-input ' )
2014-04-14 23:06:15 +04:00
2014-06-12 02:40:46 +04:00
buildTextInputEvent = ({data, target}) ->
event = new Event ( ' textInput ' )
event.data = data
Object . defineProperty ( event , ' target ' , get: -> target )
event
2014-04-14 23:06:15 +04:00
it " inserts the newest character in the input ' s value into the buffer " , ->
2014-07-13 21:56:14 +04:00
componentNode . dispatchEvent ( buildTextInputEvent ( data: ' x ' , target: inputNode ) )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-09-04 00:51:57 +04:00
expect ( editor . lineTextForBufferRow ( 0 ) ) . toBe ' xvar quicksort = function () { '
2014-04-10 22:41:36 +04:00
2014-07-13 21:56:14 +04:00
componentNode . dispatchEvent ( buildTextInputEvent ( data: ' y ' , target: inputNode ) )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-09-04 00:51:57 +04:00
expect ( editor . lineTextForBufferRow ( 0 ) ) . toBe ' xyvar quicksort = function () { '
2014-04-14 23:06:15 +04:00
2014-04-15 00:10:56 +04:00
it " replaces the last character if the length of the input ' s value doesn ' t increase, as occurs with the accented character menu " , ->
2014-07-13 21:56:14 +04:00
componentNode . dispatchEvent ( buildTextInputEvent ( data: ' u ' , target: inputNode ) )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-09-04 00:51:57 +04:00
expect ( editor . lineTextForBufferRow ( 0 ) ) . toBe ' uvar quicksort = function () { '
2014-04-14 23:06:15 +04:00
2014-06-12 02:40:46 +04:00
# simulate the accented character suggestion's selection of the previous character
inputNode . setSelectionRange ( 0 , 1 )
2014-07-13 21:56:14 +04:00
componentNode . dispatchEvent ( buildTextInputEvent ( data: ' ü ' , target: inputNode ) )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-09-04 00:51:57 +04:00
expect ( editor . lineTextForBufferRow ( 0 ) ) . toBe ' üvar quicksort = function () { '
2014-04-17 22:43:13 +04:00
2014-06-11 16:07:41 +04:00
it " does not handle input events when input is disabled " , ->
component . setInputEnabled ( false )
2014-07-13 21:56:14 +04:00
componentNode . dispatchEvent ( buildTextInputEvent ( data: ' x ' , target: inputNode ) )
2014-07-26 03:38:55 +04:00
expect ( nextAnimationFrame ) . toBe noAnimationFrame
2014-09-04 00:51:57 +04:00
expect ( editor . lineTextForBufferRow ( 0 ) ) . toBe ' var quicksort = function () { '
2014-06-11 16:07:41 +04:00
2014-11-05 22:18:20 +03:00
it " groups events that occur close together in time into single undo entries " , ->
2014-11-06 20:25:10 +03:00
currentTime = 0
spyOn ( Date , ' now ' ) . andCallFake -> currentTime
atom . config . set ( ' editor.undoGroupingInterval ' , 100 )
2014-11-05 22:18:20 +03:00
editor . setText ( " " )
componentNode . dispatchEvent ( buildTextInputEvent ( data: ' x ' , target: inputNode ) )
2014-11-06 20:25:10 +03:00
currentTime += 99
2014-11-05 22:18:20 +03:00
componentNode . dispatchEvent ( buildTextInputEvent ( data: ' y ' , target: inputNode ) )
2014-11-06 20:25:10 +03:00
currentTime += 99
2014-11-05 22:18:20 +03:00
componentNode . dispatchEvent ( new CustomEvent ( ' editor:duplicate-lines ' , bubbles: true , cancelable: true ) )
2014-11-06 20:25:10 +03:00
currentTime += 100
componentNode . dispatchEvent ( new CustomEvent ( ' editor:duplicate-lines ' , bubbles: true , cancelable: true ) )
expect ( editor . getText ( ) ) . toBe " xy \n xy \n xy "
componentNode . dispatchEvent ( new CustomEvent ( ' core:undo ' , bubbles: true , cancelable: true ) )
expect ( editor . getText ( ) ) . toBe " xy \n xy "
2014-11-05 22:18:20 +03:00
2014-11-06 03:59:06 +03:00
componentNode . dispatchEvent ( new CustomEvent ( ' core:undo ' , bubbles: true , cancelable: true ) )
2014-11-05 22:18:20 +03:00
expect ( editor . getText ( ) ) . toBe " "
2014-06-20 02:03:34 +04:00
describe " when IME composition is used to insert international characters " , ->
2014-06-25 21:12:16 +04:00
inputNode = null
buildIMECompositionEvent = (event, {data, target}={}) ->
2014-06-20 02:03:34 +04:00
event = new Event ( event )
event.data = data
2014-06-25 21:12:16 +04:00
Object . defineProperty ( event , ' target ' , get: -> target )
2014-06-20 02:03:34 +04:00
event
2014-06-25 21:12:16 +04:00
beforeEach ->
2014-07-13 21:56:14 +04:00
inputNode = inputNode = componentNode . querySelector ( ' .hidden-input ' )
2014-06-25 21:12:16 +04:00
2014-06-20 02:03:34 +04:00
describe " when nothing is selected " , ->
it " inserts the chosen completion " , ->
2014-07-13 21:56:14 +04:00
componentNode . dispatchEvent ( buildIMECompositionEvent ( ' compositionstart ' , target: inputNode ) )
componentNode . dispatchEvent ( buildIMECompositionEvent ( ' compositionupdate ' , data: ' s ' , target: inputNode ) )
2014-09-04 00:51:57 +04:00
expect ( editor . lineTextForBufferRow ( 0 ) ) . toBe ' svar quicksort = function () { '
2014-06-20 02:03:34 +04:00
2014-07-13 21:56:14 +04:00
componentNode . dispatchEvent ( buildIMECompositionEvent ( ' compositionupdate ' , data: ' sd ' , target: inputNode ) )
2014-09-04 00:51:57 +04:00
expect ( editor . lineTextForBufferRow ( 0 ) ) . toBe ' sdvar quicksort = function () { '
2014-06-20 02:03:34 +04:00
2014-07-13 21:56:14 +04:00
componentNode . dispatchEvent ( buildIMECompositionEvent ( ' compositionend ' , target: inputNode ) )
componentNode . dispatchEvent ( buildTextInputEvent ( data: ' 速度 ' , target: inputNode ) )
2014-09-04 00:51:57 +04:00
expect ( editor . lineTextForBufferRow ( 0 ) ) . toBe ' 速度var quicksort = function () { '
2014-06-20 02:03:34 +04:00
it " reverts back to the original text when the completion helper is dismissed " , ->
2014-07-13 21:56:14 +04:00
componentNode . dispatchEvent ( buildIMECompositionEvent ( ' compositionstart ' , target: inputNode ) )
componentNode . dispatchEvent ( buildIMECompositionEvent ( ' compositionupdate ' , data: ' s ' , target: inputNode ) )
2014-09-04 00:51:57 +04:00
expect ( editor . lineTextForBufferRow ( 0 ) ) . toBe ' svar quicksort = function () { '
2014-06-20 02:03:34 +04:00
2014-07-13 21:56:14 +04:00
componentNode . dispatchEvent ( buildIMECompositionEvent ( ' compositionupdate ' , data: ' sd ' , target: inputNode ) )
2014-09-04 00:51:57 +04:00
expect ( editor . lineTextForBufferRow ( 0 ) ) . toBe ' sdvar quicksort = function () { '
2014-06-20 02:03:34 +04:00
2014-07-13 21:56:14 +04:00
componentNode . dispatchEvent ( buildIMECompositionEvent ( ' compositionend ' , target: inputNode ) )
2014-09-04 00:51:57 +04:00
expect ( editor . lineTextForBufferRow ( 0 ) ) . toBe ' var quicksort = function () { '
2014-06-20 02:03:34 +04:00
2014-06-25 21:12:16 +04:00
it " allows multiple accented character to be inserted with the ' on a US international layout " , ->
inputNode.value = " ' "
inputNode . setSelectionRange ( 0 , 1 )
2014-07-13 21:56:14 +04:00
componentNode . dispatchEvent ( buildIMECompositionEvent ( ' compositionstart ' , target: inputNode ) )
componentNode . dispatchEvent ( buildIMECompositionEvent ( ' compositionupdate ' , data: " ' " , target: inputNode ) )
2014-09-04 00:51:57 +04:00
expect ( editor . lineTextForBufferRow ( 0 ) ) . toBe " ' var quicksort = function () { "
2014-06-25 21:12:16 +04:00
2014-07-13 21:56:14 +04:00
componentNode . dispatchEvent ( buildIMECompositionEvent ( ' compositionend ' , target: inputNode ) )
componentNode . dispatchEvent ( buildTextInputEvent ( data: ' á ' , target: inputNode ) )
2014-09-04 00:51:57 +04:00
expect ( editor . lineTextForBufferRow ( 0 ) ) . toBe " ávar quicksort = function () { "
2014-06-25 21:12:16 +04:00
inputNode.value = " ' "
inputNode . setSelectionRange ( 0 , 1 )
2014-07-13 21:56:14 +04:00
componentNode . dispatchEvent ( buildIMECompositionEvent ( ' compositionstart ' , target: inputNode ) )
componentNode . dispatchEvent ( buildIMECompositionEvent ( ' compositionupdate ' , data: " ' " , target: inputNode ) )
2014-09-04 00:51:57 +04:00
expect ( editor . lineTextForBufferRow ( 0 ) ) . toBe " á ' var quicksort = function () { "
2014-06-25 21:12:16 +04:00
2014-07-13 21:56:14 +04:00
componentNode . dispatchEvent ( buildIMECompositionEvent ( ' compositionend ' , target: inputNode ) )
componentNode . dispatchEvent ( buildTextInputEvent ( data: ' á ' , target: inputNode ) )
2014-09-04 00:51:57 +04:00
expect ( editor . lineTextForBufferRow ( 0 ) ) . toBe " áávar quicksort = function () { "
2014-06-25 21:12:16 +04:00
2014-06-20 02:03:34 +04:00
describe " when a string is selected " , ->
beforeEach ->
editor . setSelectedBufferRange [ [ 0 , 4 ] , [ 0 , 9 ] ] # select 'quick'
it " inserts the chosen completion " , ->
2014-07-13 21:56:14 +04:00
componentNode . dispatchEvent ( buildIMECompositionEvent ( ' compositionstart ' , target: inputNode ) )
componentNode . dispatchEvent ( buildIMECompositionEvent ( ' compositionupdate ' , data: ' s ' , target: inputNode ) )
2014-09-04 00:51:57 +04:00
expect ( editor . lineTextForBufferRow ( 0 ) ) . toBe ' var ssort = function () { '
2014-06-20 02:03:34 +04:00
2014-07-13 21:56:14 +04:00
componentNode . dispatchEvent ( buildIMECompositionEvent ( ' compositionupdate ' , data: ' sd ' , target: inputNode ) )
2014-09-04 00:51:57 +04:00
expect ( editor . lineTextForBufferRow ( 0 ) ) . toBe ' var sdsort = function () { '
2014-06-20 02:03:34 +04:00
2014-07-13 21:56:14 +04:00
componentNode . dispatchEvent ( buildIMECompositionEvent ( ' compositionend ' , target: inputNode ) )
componentNode . dispatchEvent ( buildTextInputEvent ( data: ' 速度 ' , target: inputNode ) )
2014-09-04 00:51:57 +04:00
expect ( editor . lineTextForBufferRow ( 0 ) ) . toBe ' var 速度sort = function () { '
2014-06-20 02:03:34 +04:00
it " reverts back to the original text when the completion helper is dismissed " , ->
2014-07-13 21:56:14 +04:00
componentNode . dispatchEvent ( buildIMECompositionEvent ( ' compositionstart ' , target: inputNode ) )
componentNode . dispatchEvent ( buildIMECompositionEvent ( ' compositionupdate ' , data: ' s ' , target: inputNode ) )
2014-09-04 00:51:57 +04:00
expect ( editor . lineTextForBufferRow ( 0 ) ) . toBe ' var ssort = function () { '
2014-06-20 02:03:34 +04:00
2014-07-13 21:56:14 +04:00
componentNode . dispatchEvent ( buildIMECompositionEvent ( ' compositionupdate ' , data: ' sd ' , target: inputNode ) )
2014-09-04 00:51:57 +04:00
expect ( editor . lineTextForBufferRow ( 0 ) ) . toBe ' var sdsort = function () { '
2014-06-20 02:03:34 +04:00
2014-07-13 21:56:14 +04:00
componentNode . dispatchEvent ( buildIMECompositionEvent ( ' compositionend ' , target: inputNode ) )
2014-09-04 00:51:57 +04:00
expect ( editor . lineTextForBufferRow ( 0 ) ) . toBe ' var quicksort = function () { '
2014-06-20 02:03:34 +04:00
2014-04-17 22:43:13 +04:00
describe " commands " , ->
describe " editor:consolidate-selections " , ->
it " consolidates selections on the editor model, aborting the key binding if there is only one selection " , ->
spyOn ( editor , ' consolidateSelections ' ) . andCallThrough ( )
event = new CustomEvent ( ' editor:consolidate-selections ' , bubbles: true , cancelable: true )
event.abortKeyBinding = jasmine . createSpy ( " event.abortKeyBinding " )
2014-07-13 21:56:14 +04:00
componentNode . dispatchEvent ( event )
2014-04-17 22:43:13 +04:00
expect ( editor . consolidateSelections ) . toHaveBeenCalled ( )
expect ( event . abortKeyBinding ) . toHaveBeenCalled ( )
2014-05-22 03:15:47 +04:00
describe " hiding and showing the editor " , ->
2014-07-18 04:56:06 +04:00
describe " when the editor is hidden when it is mounted " , ->
it " defers measurement and rendering until the editor becomes visible " , ->
wrapperView . remove ( )
hiddenParent = document . createElement ( ' div ' )
hiddenParent.style.display = ' none '
contentNode . appendChild ( hiddenParent )
2014-09-23 01:35:13 +04:00
wrapperView = new TextEditorView ( editor , { lineOverdrawMargin } )
2014-07-18 04:56:06 +04:00
wrapperNode = wrapperView . element
wrapperView . appendTo ( hiddenParent )
{ component } = wrapperView
componentNode = component . getDOMNode ( )
expect ( componentNode . querySelectorAll ( ' .line ' ) . length ) . toBe 0
hiddenParent.style.display = ' block '
advanceClock ( component . domPollingInterval )
expect ( componentNode . querySelectorAll ( ' .line ' ) . length ) . toBeGreaterThan 0
2014-06-19 13:35:35 +04:00
describe " when the lineHeight changes while the editor is hidden " , ->
it " does not attempt to measure the lineHeightInPixels until the editor becomes visible again " , ->
2014-05-22 03:15:47 +04:00
wrapperView . hide ( )
2014-05-22 04:04:44 +04:00
initialLineHeightInPixels = editor . getLineHeightInPixels ( )
2014-05-22 03:15:47 +04:00
component . setLineHeight ( 2 )
2014-05-22 04:04:44 +04:00
expect ( editor . getLineHeightInPixels ( ) ) . toBe initialLineHeightInPixels
2014-06-19 13:35:35 +04:00
wrapperView . show ( )
expect ( editor . getLineHeightInPixels ( ) ) . not . toBe initialLineHeightInPixels
describe " when the fontSize changes while the editor is hidden " , ->
it " does not attempt to measure the lineHeightInPixels or defaultCharWidth until the editor becomes visible again " , ->
wrapperView . hide ( )
initialLineHeightInPixels = editor . getLineHeightInPixels ( )
initialCharWidth = editor . getDefaultCharWidth ( )
2014-05-22 03:15:47 +04:00
component . setFontSize ( 22 )
2014-05-22 04:04:44 +04:00
expect ( editor . getLineHeightInPixels ( ) ) . toBe initialLineHeightInPixels
2014-05-22 03:15:47 +04:00
expect ( editor . getDefaultCharWidth ( ) ) . toBe initialCharWidth
wrapperView . show ( )
2014-05-22 04:04:44 +04:00
expect ( editor . getLineHeightInPixels ( ) ) . not . toBe initialLineHeightInPixels
2014-05-22 03:15:47 +04:00
expect ( editor . getDefaultCharWidth ( ) ) . not . toBe initialCharWidth
2014-05-24 01:59:31 +04:00
2014-06-19 14:19:51 +04:00
it " does not re-measure character widths until the editor is shown again " , ->
wrapperView . hide ( )
component . setFontSize ( 22 )
2014-08-26 04:03:32 +04:00
editor . getBuffer ( ) . insert ( [ 0 , 0 ] , ' a ' ) # regression test against atom/atom#3318
2014-06-19 14:19:51 +04:00
wrapperView . show ( )
editor . setCursorBufferPosition ( [ 0 , Infinity ] )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-06-19 14:19:51 +04:00
2014-07-13 21:56:14 +04:00
cursorLeft = componentNode . querySelector ( ' .cursor ' ) . getBoundingClientRect ( ) . left
line0Right = componentNode . querySelector ( ' .line > span:last-child ' ) . getBoundingClientRect ( ) . right
2014-06-19 14:19:51 +04:00
expect ( cursorLeft ) . toBe line0Right
2014-06-19 13:35:35 +04:00
describe " when the fontFamily changes while the editor is hidden " , ->
it " does not attempt to measure the defaultCharWidth until the editor becomes visible again " , ->
wrapperView . hide ( )
initialLineHeightInPixels = editor . getLineHeightInPixels ( )
initialCharWidth = editor . getDefaultCharWidth ( )
component . setFontFamily ( ' sans-serif ' )
expect ( editor . getDefaultCharWidth ( ) ) . toBe initialCharWidth
wrapperView . show ( )
expect ( editor . getDefaultCharWidth ( ) ) . not . toBe initialCharWidth
2014-06-19 14:19:51 +04:00
it " does not re-measure character widths until the editor is shown again " , ->
wrapperView . hide ( )
component . setFontFamily ( ' sans-serif ' )
wrapperView . show ( )
editor . setCursorBufferPosition ( [ 0 , Infinity ] )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-06-19 14:19:51 +04:00
2014-07-13 21:56:14 +04:00
cursorLeft = componentNode . querySelector ( ' .cursor ' ) . getBoundingClientRect ( ) . left
line0Right = componentNode . querySelector ( ' .line > span:last-child ' ) . getBoundingClientRect ( ) . right
2014-06-19 14:19:51 +04:00
expect ( cursorLeft ) . toBe line0Right
2014-07-08 21:45:24 +04:00
describe " when stylesheets change while the editor is hidden " , ->
2014-07-08 21:55:50 +04:00
afterEach ->
atom . themes . removeStylesheet ( ' test ' )
2014-07-30 02:51:59 +04:00
it " does not re-measure character widths until the editor is shown again " , ->
2014-07-08 21:45:24 +04:00
atom . config . set ( ' editor.fontFamily ' , ' sans-serif ' )
wrapperView . hide ( )
atom . themes . applyStylesheet ' test ' , """
. function . js {
font - weight: bold ;
}
"""
wrapperView . show ( )
editor . setCursorBufferPosition ( [ 0 , Infinity ] )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-07-08 21:45:24 +04:00
2014-07-13 21:56:14 +04:00
cursorLeft = componentNode . querySelector ( ' .cursor ' ) . getBoundingClientRect ( ) . left
line0Right = componentNode . querySelector ( ' .line > span:last-child ' ) . getBoundingClientRect ( ) . right
2014-07-08 21:45:24 +04:00
expect ( cursorLeft ) . toBe line0Right
2014-05-24 01:59:31 +04:00
describe " when lines are changed while the editor is hidden " , ->
it " does not measure new characters until the editor is shown again " , ->
editor . setText ( ' ' )
wrapperView . hide ( )
editor . setText ( ' var z = 1 ' )
editor . setCursorBufferPosition ( [ 0 , Infinity ] )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-05-24 01:59:31 +04:00
wrapperView . show ( )
2014-07-27 21:37:36 +04:00
expect ( componentNode . querySelector ( ' .cursor ' ) . style [ ' -webkit-transform ' ] ) . toBe " translate( #{ 9 * charWidth } px, 0px) "
2014-06-10 00:56:23 +04:00
2014-07-08 00:02:24 +04:00
describe " soft wrapping " , ->
beforeEach ->
2014-09-04 18:42:32 +04:00
editor . setSoftWrapped ( true )
2014-07-27 21:34:29 +04:00
nextAnimationFrame ( )
2014-07-08 00:02:24 +04:00
it " updates the wrap location when the editor is resized " , ->
2014-06-18 03:07:07 +04:00
newHeight = 4 * editor . getLineHeightInPixels ( ) + " px "
2014-07-13 23:50:33 +04:00
expect ( parseInt ( newHeight ) ) . toBeLessThan wrapperNode . offsetHeight
2014-07-13 21:56:14 +04:00
wrapperNode.style.height = newHeight
2014-06-18 00:26:56 +04:00
2014-07-14 22:10:39 +04:00
advanceClock ( component . domPollingInterval )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-07-13 21:56:14 +04:00
expect ( componentNode . querySelectorAll ( ' .line ' ) ) . toHaveLength ( 4 + lineOverdrawMargin + 1 )
2014-06-18 00:26:56 +04:00
2014-07-13 21:56:14 +04:00
gutterWidth = componentNode . querySelector ( ' .gutter ' ) . offsetWidth
2014-07-12 02:12:41 +04:00
componentNode.style.width = gutterWidth + 14 * charWidth + editor . getVerticalScrollbarWidth ( ) + ' px '
2014-07-14 22:10:39 +04:00
advanceClock ( component . domPollingInterval )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-07-13 21:56:14 +04:00
expect ( componentNode . querySelector ( ' .line ' ) . textContent ) . toBe " var quicksort "
2014-06-13 03:17:18 +04:00
2014-07-08 00:02:24 +04:00
it " accounts for the scroll view ' s padding when determining the wrap location " , ->
2014-07-13 21:56:14 +04:00
scrollViewNode = componentNode . querySelector ( ' .scroll-view ' )
2014-07-08 00:02:24 +04:00
scrollViewNode.style.paddingLeft = 20 + ' px '
2014-07-13 21:56:14 +04:00
componentNode.style.width = 30 * charWidth + ' px '
2014-07-08 00:02:24 +04:00
2014-07-14 22:10:39 +04:00
advanceClock ( component . domPollingInterval )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-07-08 00:02:24 +04:00
expect ( component . lineNodeForScreenRow ( 0 ) . textContent ) . toBe " var quicksort = "
2014-06-27 16:03:56 +04:00
describe " default decorations " , ->
2014-06-28 14:09:45 +04:00
it " applies .cursor-line decorations for line numbers overlapping selections " , ->
2014-06-27 16:03:56 +04:00
editor . setCursorScreenPosition ( [ 4 , 4 ] )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-06-27 16:03:56 +04:00
expect ( lineNumberHasClass ( 3 , ' cursor-line ' ) ) . toBe false
expect ( lineNumberHasClass ( 4 , ' cursor-line ' ) ) . toBe true
expect ( lineNumberHasClass ( 5 , ' cursor-line ' ) ) . toBe false
editor . setSelectedScreenRange ( [ [ 3 , 4 ] , [ 4 , 4 ] ] )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-06-28 14:09:45 +04:00
expect ( lineNumberHasClass ( 3 , ' cursor-line ' ) ) . toBe true
2014-06-27 16:03:56 +04:00
expect ( lineNumberHasClass ( 4 , ' cursor-line ' ) ) . toBe true
2014-06-28 14:09:45 +04:00
editor . setSelectedScreenRange ( [ [ 3 , 4 ] , [ 4 , 0 ] ] )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-06-28 14:09:45 +04:00
expect ( lineNumberHasClass ( 3 , ' cursor-line ' ) ) . toBe true
expect ( lineNumberHasClass ( 4 , ' cursor-line ' ) ) . toBe false
it " does not apply .cursor-line to the last line of a selection if it ' s empty " , ->
editor . setSelectedScreenRange ( [ [ 3 , 4 ] , [ 5 , 0 ] ] )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-06-28 14:09:45 +04:00
expect ( lineNumberHasClass ( 3 , ' cursor-line ' ) ) . toBe true
expect ( lineNumberHasClass ( 4 , ' cursor-line ' ) ) . toBe true
expect ( lineNumberHasClass ( 5 , ' cursor-line ' ) ) . toBe false
it " applies .cursor-line decorations for lines containing the cursor in non-empty selections " , ->
2014-06-27 16:03:56 +04:00
editor . setCursorScreenPosition ( [ 4 , 4 ] )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-06-28 14:09:45 +04:00
expect ( lineHasClass ( 3 , ' cursor-line ' ) ) . toBe false
expect ( lineHasClass ( 4 , ' cursor-line ' ) ) . toBe true
expect ( lineHasClass ( 5 , ' cursor-line ' ) ) . toBe false
editor . setSelectedScreenRange ( [ [ 3 , 4 ] , [ 4 , 4 ] ] )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-06-28 14:09:45 +04:00
expect ( lineHasClass ( 2 , ' cursor-line ' ) ) . toBe false
expect ( lineHasClass ( 3 , ' cursor-line ' ) ) . toBe false
expect ( lineHasClass ( 4 , ' cursor-line ' ) ) . toBe false
expect ( lineHasClass ( 5 , ' cursor-line ' ) ) . toBe false
2014-06-27 16:03:56 +04:00
it " applies .cursor-line-no-selection to line numbers for rows containing the cursor when the selection is empty " , ->
editor . setCursorScreenPosition ( [ 4 , 4 ] )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-06-27 16:03:56 +04:00
expect ( lineNumberHasClass ( 4 , ' cursor-line-no-selection ' ) ) . toBe true
editor . setSelectedScreenRange ( [ [ 3 , 4 ] , [ 4 , 4 ] ] )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-06-27 16:03:56 +04:00
expect ( lineNumberHasClass ( 4 , ' cursor-line-no-selection ' ) ) . toBe false
2014-07-13 23:50:33 +04:00
describe " height " , ->
describe " when the wrapper view has an explicit height " , ->
it " does not assign a height on the component node " , ->
wrapperNode.style.height = ' 200px '
2014-07-14 22:16:32 +04:00
component . measureHeightAndWidth ( )
2014-07-13 23:50:33 +04:00
expect ( componentNode . style . height ) . toBe ' '
describe " when the wrapper view does not have an explicit height " , ->
it " assigns a height on the component node based on the editor ' s content " , ->
expect ( wrapperNode . style . height ) . toBe ' '
expect ( componentNode . style . height ) . toBe editor . getScreenLineCount ( ) * lineHeightInPixels + ' px '
2014-07-13 23:06:37 +04:00
describe " when the ' mini ' property is true " , ->
beforeEach ->
2015-01-12 21:13:51 +03:00
editor . setMini ( true )
nextAnimationFrame ( )
2014-07-13 23:06:37 +04:00
it " does not render the gutter " , ->
expect ( componentNode . querySelector ( ' .gutter ' ) ) . toBeNull ( )
2014-07-13 23:59:32 +04:00
it " adds the ' mini ' class to the wrapper view " , ->
expect ( wrapperNode . classList . contains ( ' mini ' ) ) . toBe true
2014-08-13 01:52:59 +04:00
it " does not have an opaque background on lines " , ->
expect ( component . refs . lines . getDOMNode ( ) . getAttribute ( ' style ' ) ) . not . toContain ' background-color '
2014-07-14 00:03:51 +04:00
it " does not render invisible characters " , ->
2014-08-14 02:18:38 +04:00
atom . config . set ( ' editor.invisibles ' , eol: ' E ' )
atom . config . set ( ' editor.showInvisibles ' , true )
2014-07-14 00:03:51 +04:00
expect ( component . lineNodeForScreenRow ( 0 ) . textContent ) . toBe ' var quicksort = function () { '
2014-07-14 00:45:38 +04:00
it " does not assign an explicit line-height on the editor contents " , ->
expect ( componentNode . style . lineHeight ) . toBe ' '
2014-07-14 00:56:34 +04:00
it " does not apply cursor-line decorations " , ->
expect ( component . lineNodeForScreenRow ( 0 ) . classList . contains ( ' cursor-line ' ) ) . toBe false
2014-07-14 01:08:48 +04:00
describe " when placholderText is specified " , ->
it " renders the placeholder text when the buffer is empty " , ->
2014-09-23 04:21:42 +04:00
editor . setPlaceholderText ( ' Hello World ' )
2014-07-14 01:08:48 +04:00
expect ( componentNode . querySelector ( ' .placeholder-text ' ) ) . toBeNull ( )
editor . setText ( ' ' )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-07-14 01:08:48 +04:00
expect ( componentNode . querySelector ( ' .placeholder-text ' ) . textContent ) . toBe " Hello World "
editor . setText ( ' hey ' )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-07-14 01:08:48 +04:00
expect ( componentNode . querySelector ( ' .placeholder-text ' ) ) . toBeNull ( )
2014-06-20 03:55:34 +04:00
describe " legacy editor compatibility " , ->
it " triggers the screen-lines-changed event before the editor:display-update event " , ->
2014-09-04 18:42:32 +04:00
editor . setSoftWrapped ( true )
2014-06-20 03:55:34 +04:00
callingOrder = [ ]
2014-09-16 04:30:52 +04:00
editor . onDidChange -> callingOrder . push ' screen-lines-changed '
2014-06-20 03:55:34 +04:00
wrapperView . on ' editor:display-updated ' , -> callingOrder . push ' editor:display-updated '
editor . insertText ( " HELLO! HELLO! \n HELLO! HELLO! HELLO! HELLO! HELLO! HELLO! HELLO! HELLO! HELLO! HELLO! HELLO! HELLO! HELLO! HELLO! HELLO! HELLO! HELLO! HELLO! " )
2014-07-26 03:38:55 +04:00
nextAnimationFrame ( )
2014-06-20 03:55:34 +04:00
expect ( callingOrder ) . toEqual [ ' screen-lines-changed ' , ' editor:display-updated ' ]
2014-07-22 02:04:44 +04:00
it " works with the ::setEditorHeightInLines and ::setEditorWidthInChars helpers " , ->
setEditorHeightInLines ( wrapperView , 7 )
expect ( componentNode . offsetHeight ) . toBe lineHeightInPixels * 7
setEditorWidthInChars ( wrapperView , 10 )
expect ( componentNode . querySelector ( ' .scroll-view ' ) . offsetWidth ) . toBe charWidth * 10
2014-08-25 21:22:44 +04:00
describe " grammar data attributes " , ->
it " adds and updates the grammar data attribute based on the current grammar " , ->
expect ( wrapperNode . dataset . grammar ) . toBe ' source js '
2014-11-20 21:42:49 +03:00
editor . setGrammar ( atom . grammars . nullGrammar )
2014-08-25 21:22:44 +04:00
expect ( wrapperNode . dataset . grammar ) . toBe ' text plain null-grammar '
2014-10-30 02:01:14 +03:00
describe " encoding data attributes " , ->
it " adds and updates the encoding data attribute based on the current encoding " , ->
expect ( wrapperNode . dataset . encoding ) . toBe ' utf8 '
editor . setEncoding ( ' utf16le ' )
expect ( wrapperNode . dataset . encoding ) . toBe ' utf16le '
2014-09-27 01:13:07 +04:00
describe " detaching and reattaching the editor (regression) " , ->
it " does not throw an exception " , ->
wrapperView . detach ( )
wrapperView . attachToDom ( )
2014-09-30 20:45:55 +04:00
wrapperView . trigger ( ' core:move-right ' )
expect ( editor . getCursorBufferPosition ( ) ) . toEqual [ 0 , 1 ]
2014-10-07 03:37:43 +04:00
describe ' scoped config settings ' , ->
[ coffeeEditor , coffeeComponent ] = [ ]
beforeEach ->
waitsForPromise ->
atom . packages . activatePackage ( ' language-coffee-script ' )
waitsForPromise ->
atom . project . open ( ' coffee.coffee ' , autoIndent: false ) . then (o) -> coffeeEditor = o
afterEach: ->
atom . packages . deactivatePackages ( )
atom . packages . unloadPackages ( )
describe ' soft wrap settings ' , ->
beforeEach ->
2014-12-20 00:41:01 +03:00
atom . config . set ' editor.softWrap ' , true , scopeSelector: ' .source.coffee '
atom . config . set ' editor.preferredLineLength ' , 17 , scopeSelector: ' .source.coffee '
atom . config . set ' editor.softWrapAtPreferredLineLength ' , true , scopeSelector: ' .source.coffee '
2014-10-07 03:37:43 +04:00
editor . setEditorWidthInChars ( 20 )
coffeeEditor . setEditorWidthInChars ( 20 )
2014-10-09 02:17:04 +04:00
it " wraps lines when editor.softWrap is true for a matching scope " , ->
2014-10-07 03:37:43 +04:00
expect ( editor . lineTextForScreenRow ( 2 ) ) . toEqual ' if (items.length <= 1) return items; '
expect ( coffeeEditor . lineTextForScreenRow ( 3 ) ) . toEqual ' return items '
it ' updates the wrapped lines when editor.preferredLineLength changes ' , ->
2014-12-20 00:41:01 +03:00
atom . config . set ' editor.preferredLineLength ' , 20 , scopeSelector: ' .source.coffee '
2014-10-07 03:37:43 +04:00
expect ( coffeeEditor . lineTextForScreenRow ( 2 ) ) . toEqual ' return items if '
it ' updates the wrapped lines when editor.softWrapAtPreferredLineLength changes ' , ->
2014-12-20 00:41:01 +03:00
atom . config . set ' editor.softWrapAtPreferredLineLength ' , false , scopeSelector: ' .source.coffee '
2014-10-07 03:37:43 +04:00
expect ( coffeeEditor . lineTextForScreenRow ( 2 ) ) . toEqual ' return items if '
it ' updates the wrapped lines when editor.softWrap changes ' , ->
2014-12-20 00:41:01 +03:00
atom . config . set ' editor.softWrap ' , false , scopeSelector: ' .source.coffee '
2014-10-07 03:37:43 +04:00
expect ( coffeeEditor . lineTextForScreenRow ( 2 ) ) . toEqual ' return items if items.length <= 1 '
2014-12-20 00:41:01 +03:00
atom . config . set ' editor.softWrap ' , true , scopeSelector: ' .source.coffee '
2014-10-07 03:37:43 +04:00
expect ( coffeeEditor . lineTextForScreenRow ( 3 ) ) . toEqual ' return items '
it ' updates the wrapped lines when the grammar changes ' , ->
editor . setGrammar ( coffeeEditor . getGrammar ( ) )
expect ( editor . isSoftWrapped ( ) ) . toBe true
expect ( editor . lineTextForScreenRow ( 0 ) ) . toEqual ' var quicksort = '
2014-10-08 03:10:00 +04:00
describe ' ::isSoftWrapped() ' , ->
it ' returns the correct value based on the scoped settings ' , ->
expect ( editor . isSoftWrapped ( ) ) . toBe false
expect ( coffeeEditor . isSoftWrapped ( ) ) . toBe true
2014-10-07 03:37:43 +04:00
describe ' invisibles settings ' , ->
[ jsInvisibles , coffeeInvisibles ] = [ ]
beforeEach ->
jsInvisibles =
eol: ' J '
space: ' A '
tab: ' V '
cr: ' A '
coffeeInvisibles =
eol: ' C '
space: ' O '
tab: ' F '
cr: ' E '
2014-12-20 00:41:01 +03:00
atom . config . set ' editor.showInvisibles ' , true , scopeSelector: ' .source.js '
atom . config . set ' editor.invisibles ' , jsInvisibles , scopeSelector: ' .source.js '
2014-10-07 03:37:43 +04:00
2014-12-20 00:41:01 +03:00
atom . config . set ' editor.showInvisibles ' , false , scopeSelector: ' .source.coffee '
atom . config . set ' editor.invisibles ' , coffeeInvisibles , scopeSelector: ' .source.coffee '
2014-10-07 03:37:43 +04:00
editor . setText " a line with tabs \t and spaces \n "
nextAnimationFrame ( )
2014-10-09 02:17:04 +04:00
it " renders the invisibles when editor.showInvisibles is true for a given grammar " , ->
2014-10-07 03:37:43 +04:00
expect ( component . lineNodeForScreenRow ( 0 ) . textContent ) . toBe " #{ jsInvisibles . space } a line with tabs #{ jsInvisibles . tab } and spaces #{ jsInvisibles . space } #{ jsInvisibles . eol } "
2014-10-09 02:17:04 +04:00
it " does not render the invisibles when editor.showInvisibles is false for a given grammar " , ->
2014-10-07 03:37:43 +04:00
editor . setGrammar ( coffeeEditor . getGrammar ( ) )
nextAnimationFrame ( )
expect ( component . lineNodeForScreenRow ( 0 ) . textContent ) . toBe " a line with tabs and spaces "
2014-10-08 04:30:36 +04:00
it " re-renders the invisibles when the invisible settings change " , ->
2014-10-07 03:37:43 +04:00
jsGrammar = editor . getGrammar ( )
editor . setGrammar ( coffeeEditor . getGrammar ( ) )
2014-12-20 00:41:01 +03:00
atom . config . set ' editor.showInvisibles ' , true , scopeSelector: ' .source.coffee '
2014-10-07 03:37:43 +04:00
nextAnimationFrame ( )
expect ( component . lineNodeForScreenRow ( 0 ) . textContent ) . toBe " #{ coffeeInvisibles . space } a line with tabs #{ coffeeInvisibles . tab } and spaces #{ coffeeInvisibles . space } #{ coffeeInvisibles . eol } "
newInvisibles =
eol: ' N '
space: ' E '
tab: ' W '
cr: ' I '
2014-12-20 00:41:01 +03:00
atom . config . set ' editor.invisibles ' , newInvisibles , scopeSelector: ' .source.coffee '
2014-10-07 03:37:43 +04:00
nextAnimationFrame ( )
expect ( component . lineNodeForScreenRow ( 0 ) . textContent ) . toBe " #{ newInvisibles . space } a line with tabs #{ newInvisibles . tab } and spaces #{ newInvisibles . space } #{ newInvisibles . eol } "
editor . setGrammar ( jsGrammar )
nextAnimationFrame ( )
expect ( component . lineNodeForScreenRow ( 0 ) . textContent ) . toBe " #{ jsInvisibles . space } a line with tabs #{ jsInvisibles . tab } and spaces #{ jsInvisibles . space } #{ jsInvisibles . eol } "
2014-10-07 21:34:33 +04:00
describe ' editor.showIndentGuide ' , ->
beforeEach ->
2014-12-20 00:41:01 +03:00
atom . config . set ' editor.showIndentGuide ' , true , scopeSelector: ' .source.js '
atom . config . set ' editor.showIndentGuide ' , false , scopeSelector: ' .source.coffee '
2014-10-07 21:34:33 +04:00
2014-10-08 03:10:00 +04:00
it " has an ' indent-guide ' class when scoped editor.showIndentGuide is true, but not when scoped editor.showIndentGuide is false " , ->
2014-10-07 21:34:33 +04:00
line1LeafNodes = getLeafNodes ( component . lineNodeForScreenRow ( 1 ) )
expect ( line1LeafNodes [ 0 ] . textContent ) . toBe ' '
expect ( line1LeafNodes [ 0 ] . classList . contains ( ' indent-guide ' ) ) . toBe true
expect ( line1LeafNodes [ 1 ] . classList . contains ( ' indent-guide ' ) ) . toBe false
editor . setGrammar ( coffeeEditor . getGrammar ( ) )
line1LeafNodes = getLeafNodes ( component . lineNodeForScreenRow ( 1 ) )
expect ( line1LeafNodes [ 0 ] . textContent ) . toBe ' '
expect ( line1LeafNodes [ 0 ] . classList . contains ( ' indent-guide ' ) ) . toBe false
expect ( line1LeafNodes [ 1 ] . classList . contains ( ' indent-guide ' ) ) . toBe false
2014-10-09 02:17:04 +04:00
it " removes the ' indent-guide ' class when editor.showIndentGuide to false " , ->
2014-10-07 21:34:33 +04:00
line1LeafNodes = getLeafNodes ( component . lineNodeForScreenRow ( 1 ) )
expect ( line1LeafNodes [ 0 ] . textContent ) . toBe ' '
expect ( line1LeafNodes [ 0 ] . classList . contains ( ' indent-guide ' ) ) . toBe true
expect ( line1LeafNodes [ 1 ] . classList . contains ( ' indent-guide ' ) ) . toBe false
2014-12-20 00:41:01 +03:00
atom . config . set ' editor.showIndentGuide ' , false , scopeSelector: ' .source.js '
2014-10-07 21:34:33 +04:00
line1LeafNodes = getLeafNodes ( component . lineNodeForScreenRow ( 1 ) )
expect ( line1LeafNodes [ 0 ] . textContent ) . toBe ' '
expect ( line1LeafNodes [ 0 ] . classList . contains ( ' indent-guide ' ) ) . toBe false
expect ( line1LeafNodes [ 1 ] . classList . contains ( ' indent-guide ' ) ) . toBe false
2014-11-17 19:54:37 +03:00
describe " middle mouse paste on Linux " , ->
2014-12-18 22:04:52 +03:00
originalPlatform = null
beforeEach ->
originalPlatform = process . platform
Object . defineProperty process , ' platform ' , value: ' linux '
afterEach ->
Object . defineProperty process , ' platform ' , value: originalPlatform
it " pastes the previously selected text at the clicked location " , ->
jasmine . unspy ( window , ' setTimeout ' )
clipboardWrittenTo = false
2014-11-19 22:32:05 +03:00
spyOn ( require ( ' ipc ' ) , ' send ' ) . andCallFake (eventName, selectedText) ->
if eventName is ' write-text-to-selection-clipboard '
require ( ' clipboard ' ) . writeText ( selectedText , ' selection ' )
2014-12-18 22:04:52 +03:00
clipboardWrittenTo = true
2014-11-19 22:32:05 +03:00
2014-11-17 20:08:29 +03:00
atom . clipboard . write ( ' ' )
2014-12-18 22:04:52 +03:00
component . trackSelectionClipboard ( )
2014-11-17 19:54:37 +03:00
editor . setSelectedBufferRange ( [ [ 1 , 6 ] , [ 1 , 10 ] ] )
2014-12-18 22:04:52 +03:00
waitsFor ->
clipboardWrittenTo
runs ->
componentNode . querySelector ( ' .scroll-view ' ) . dispatchEvent ( buildMouseEvent ( ' mousedown ' , clientCoordinatesForScreenPosition ( [ 10 , 0 ] ) , button: 1 ) )
componentNode . querySelector ( ' .scroll-view ' ) . dispatchEvent ( buildMouseEvent ( ' mouseup ' , clientCoordinatesForScreenPosition ( [ 10 , 0 ] ) , which: 2 ) )
expect ( atom . clipboard . read ( ) ) . toBe ' sort '
expect ( editor . lineTextForBufferRow ( 10 ) ) . toBe ' sort '
2014-11-17 19:54:37 +03:00
2014-06-10 00:56:23 +04:00
buildMouseEvent = (type, properties...) ->
properties = extend ( { bubbles: true , cancelable: true } , properties . . . )
2014-07-04 01:18:30 +04:00
properties . detail ? = 1
2014-06-10 00:56:23 +04:00
event = new MouseEvent ( type , properties )
Object . defineProperty ( event , ' which ' , get: -> properties . which ) if properties . which ?
if properties . target ?
Object . defineProperty ( event , ' target ' , get: -> properties . target )
Object . defineProperty ( event , ' srcObject ' , get: -> properties . target )
event
2014-06-19 03:52:31 +04:00
clientCoordinatesForScreenPosition = (screenPosition) ->
2015-01-09 19:22:45 +03:00
positionOffset = wrapperNode . pixelPositionForScreenPosition ( screenPosition )
2014-07-13 21:56:14 +04:00
scrollViewClientRect = componentNode . querySelector ( ' .scroll-view ' ) . getBoundingClientRect ( )
2014-06-19 03:52:31 +04:00
clientX = scrollViewClientRect . left + positionOffset . left - editor . getScrollLeft ( )
clientY = scrollViewClientRect . top + positionOffset . top - editor . getScrollTop ( )
{ clientX , clientY }
2014-06-19 03:54:08 +04:00
clientCoordinatesForScreenRowInGutter = (screenRow) ->
2015-01-09 19:22:45 +03:00
positionOffset = wrapperNode . pixelPositionForScreenPosition ( [ screenRow , 1 ] )
2014-07-13 21:56:14 +04:00
gutterClientRect = componentNode . querySelector ( ' .gutter ' ) . getBoundingClientRect ( )
2014-06-19 03:54:08 +04:00
clientX = gutterClientRect . left + positionOffset . left - editor . getScrollLeft ( )
clientY = gutterClientRect . top + positionOffset . top - editor . getScrollTop ( )
{ clientX , clientY }
2014-06-27 16:03:56 +04:00
2014-06-28 13:12:40 +04:00
lineAndLineNumberHaveClass = (screenRow, klass) ->
lineHasClass ( screenRow , klass ) and lineNumberHasClass ( screenRow , klass )
2014-06-27 16:03:56 +04:00
lineNumberHasClass = (screenRow, klass) ->
component . lineNumberNodeForScreenRow ( screenRow ) . classList . contains ( klass )
lineNumberForBufferRowHasClass = (bufferRow, klass) ->
screenRow = editor . displayBuffer . screenRowForBufferRow ( bufferRow )
component . lineNumberNodeForScreenRow ( screenRow ) . classList . contains ( klass )
lineHasClass = (screenRow, klass) ->
component . lineNodeForScreenRow ( screenRow ) . classList . contains ( klass )
2014-10-07 21:34:33 +04:00
getLeafNodes = (node) ->
if node . children . length > 0
flatten ( toArray ( node . children ) . map ( getLeafNodes ) )
else
[ node ]