Comparar commits

...

173 Commits

Autor SHA1 Mensagem Data
Kevin Sawicki 88df4d2f3e Merge pull request #2692 from atom/ks-select-page-up-down
Select page up/down
2014-06-20 13:22:04 -07:00
Kevin Sawicki 4136ff566b 📝 Correct spec description 2014-06-20 13:09:51 -07:00
Kevin Sawicki 2fe523a664 Add spec for Editor::selectPageUp/Down 2014-06-20 13:09:51 -07:00
Kevin Sawicki 148180adda Implement select page up/down in Editor 2014-06-20 13:09:27 -07:00
Justin Bradford 01a4032895 Add text selection using page up/down keys to the React-based editor 2014-06-20 13:09:16 -07:00
Justin Bradford 2b2b65ec82 Add command to select text using page up/down keys 2014-06-20 13:08:48 -07:00
Ben Ogle 5ffc063f0c Merge pull request #2693 from atom/bo-fix-gutter-prev-decorations
Fix caching of gutter's previous decorations
2014-06-20 12:52:22 -07:00
Ben Ogle dee0771dd7 Use ? in the conditional. 2014-06-20 12:50:42 -07:00
Ben Ogle aef6991ca8 Set the decorations and previousDecorations vars before loops
Both loops use both vars, so both need to be available before the 
diffing!
2014-06-20 11:29:18 -07:00
Ben Ogle e8db3e97ce Remove cached decorations for removed lines 2014-06-20 11:28:01 -07:00
Ben Ogle b1dd4f2e8e Index the previous decoration cache by lineNumberId rather than screenRow
Why? Screen rows change. If some operation (folding?) changes the 
screen rows and the decorations at the same time, the previous 
decorations will no longer be valid and can no longer be diffed against 
the decorations to-be-rendered.
2014-06-20 11:27:39 -07:00
Ben Ogle 633b08b9de Merge pull request #2682 from atom/bo-line-decorations
Render line decorations
2014-06-20 11:02:35 -07:00
Ben Ogle 1228435f9b 💄 2014-06-20 11:00:40 -07:00
Kevin Sawicki 02b5280587 Upgrade to language-ruby@0.30 2014-06-20 10:59:15 -07:00
Ben Ogle 2e47207701 Upgrade to git-diff 0.34.0 to fix spec 2014-06-20 10:54:10 -07:00
Ben Ogle 593b5b4e36 Use _.deepContains 2014-06-20 10:50:12 -07:00
Kevin Sawicki 1896c94775 Upgrade to language-c@0.21 2014-06-20 10:46:50 -07:00
Kevin Sawicki 4dc20e2027 Upgrade to tree-view@0.104 2014-06-20 09:18:53 -07:00
Kevin Sawicki 238e291888 Upgrade to language-javascript@0.28 2014-06-20 09:06:39 -07:00
Kevin Sawicki 5ccac143aa Upgrade to language-c@0.20 2014-06-20 08:38:10 -07:00
Kevin Sawicki ae97244664 📝 Tweak python install location note 2014-06-20 08:06:22 -07:00
Kevin Sawicki 859e3cd038 Merge pull request #2687 from grenade/patch-1
Added a hint about the required Python path.
2014-06-20 08:04:05 -07:00
Rob Thijssen 07577ff944 Added a hint about the required Python path.
In order to alert users that the Atom build scripts expect to find Python in the default installation folder.
2014-06-20 15:19:55 +01:00
Ben Ogle 72b1821828 Render line decorations. 2014-06-19 17:20:05 -07:00
Kevin Sawicki 781a51ac53 Merge pull request #2675 from atom/ks-bundle-dlls
Bundle DLLs
2014-06-19 16:27:38 -07:00
Kevin Sawicki 9055e650c6 Specify full destination path 2014-06-19 16:16:16 -07:00
Kevin Sawicki 5195a4aaa7 Copy dlls during build task 2014-06-19 16:16:16 -07:00
Kevin Sawicki 5e4f34f92c Update dlls 2014-06-19 16:16:16 -07:00
Kevin Sawicki dc7b549017 Add msvcr100.dll to win resources 2014-06-19 16:16:16 -07:00
Kevin Sawicki 29d5f63cd9 Update msvcp100.dll 2014-06-19 16:16:15 -07:00
Kevin Sawicki 33b4ec8e25 Add msvcp100.dll to win resources 2014-06-19 16:16:15 -07:00
Ben Ogle 85abed2406 Merge pull request #2676 from atom/bo-cursor-gutter
Don’t render decorations on the last empty line when selection not empty
2014-06-19 15:10:18 -07:00
Ben Ogle 520ece4b13 💄 Wording 2014-06-19 15:09:49 -07:00
Ben Ogle 184068dc55 Add handlers for IME composition 2014-06-19 15:03:34 -07:00
Ben Ogle f27b897e91 Change message in scroll canary 2014-06-19 14:27:20 -07:00
Ben Ogle 6e201104bc Only fix the scroll position when the editor is mounted
fixes #2664
2014-06-19 14:24:47 -07:00
Ben Ogle 908a2978ae Don’t render decorations on the last empty line when selection not empty 2014-06-19 14:20:32 -07:00
Ben Ogle 2eb5ef0816 Merge pull request #2665 from atom/bo-gutter-selection
Implement gutter clicking and dragging to change selection on react editor
2014-06-19 13:48:40 -07:00
probablycorey 0c5f2cd067 Upgrade to language-gfm@0.40.0 2014-06-19 13:38:25 -07:00
Kevin Sawicki d1e60fb2a5 Upgrade to language-c@0.19 2014-06-19 13:22:16 -07:00
Nathan Sobo bee7be1d1a Fix errors refreshing directly after adding selection below/above 2014-06-19 13:43:10 -06:00
Ben Ogle bee4c9df8a Revert "Select to the end of the last row rather than beginning of row + 1"
This reverts commit c5815d2af9.
2014-06-19 12:06:09 -07:00
Ben Ogle c5815d2af9 Select to the end of the last row rather than beginning of row + 1 2014-06-19 12:05:17 -07:00
Ben Ogle 77717d3eff Fix spec 2014-06-19 12:05:17 -07:00
Ben Ogle & Nathan Sobo d3e0005b33 💄 Break out separate methods for gutter click and shift-click 2014-06-19 12:05:17 -07:00
Ben Ogle & Nathan Sobo 8295019891 Throw error when no animation frame was requested 2014-06-19 12:05:17 -07:00
Ben Ogle & Nathan Sobo 2edcc517b1 Handle dragging in the gutter
Including shift-click dragging better than the old editor!!!!!!
2014-06-19 12:05:17 -07:00
Ben Ogle & Nathan Sobo 9083103bb3 Add click and shift-click in gutter 2014-06-19 12:04:36 -07:00
Ben Ogle & Nathan Sobo bc391094df 💄 Move helper 2014-06-19 12:03:18 -07:00
Kevin Sawicki ffba81a962 Prepare 0.106 2014-06-19 10:03:26 -07:00
Kevin Sawicki 1e1f4cf173 Merge pull request #2662 from atom/ks-align-hard-tabs
Align hard tabs
2014-06-19 09:41:15 -07:00
Ivan Žužak 7f04149f8d Upgrade to find-and-replace@0.120.0 2014-06-19 18:03:00 +02:00
Nathan Sobo cd1fb99142 Merge pull request #2667 from atom/ns-react-dont-measure-when-hidden
Don't measure character widths when editor is hidden
2014-06-19 06:34:07 -06:00
Nathan Sobo 388763e7cd Wait to measure characters if editor is hidden
Also, when characters *are* measured, request a display update
2014-06-19 04:19:51 -06:00
Nathan Sobo f22e4225c3 Break into separate specs for lineHeight, fontSize, and fontFamily 2014-06-19 03:35:35 -06:00
Cheng Zhao db4b99d27b Merge branch 'atom-shell-v0.13.2' 2014-06-19 15:16:06 +08:00
Nathan Sobo 17f9cc49f2 Honor the center: true option in scrollTo* methods
Fixes #2648
2014-06-18 21:48:14 -06:00
Nathan Sobo 609855af3c Decide to measure gutter's width in gutter
The gutter is in a better position to determine if the max line number
length has changed because it's a property that gets passed in so we
can compare current with previous.

Fixes #2659
2014-06-18 21:07:13 -06:00
Cheng Zhao 8410d8587b Upgrade to atom-shell@0.13.2 2014-06-19 09:38:27 +08:00
Kevin Sawicki 33c9d5ae24 Upgrade to find-and-replace@0.119 2014-06-18 18:17:01 -07:00
Kevin Sawicki 0c48821465 Upgrade to command-palette@0.23 2014-06-18 17:50:46 -07:00
Kevin Sawicki 43259f5c51 Upgrade to link@0.24 2014-06-18 17:47:42 -07:00
Kevin Sawicki fe2cb046c3 Verify token screen and buffer delta 2014-06-18 17:26:38 -07:00
Kevin Sawicki de132d79a4 Add parens for clarity 2014-06-18 17:16:50 -07:00
Kevin Sawicki 5af181ffb5 Default column to 0 2014-06-18 17:11:34 -07:00
Kevin Sawicki 355f54ba00 Test odd-numbered tab length alignment 2014-06-18 17:10:10 -07:00
Kevin Sawicki b56d4c6181 Update more expected tab lengths 2014-06-18 16:54:56 -07:00
Kevin Sawicki ad9e2ab869 Update specs where tab is now only 1 space 2014-06-18 16:48:00 -07:00
Kevin Sawicki a497b0f90f 💄 Use trailing for loop 2014-06-18 16:42:36 -07:00
Kevin Sawicki e4fd80399d Add token value directly to column 2014-06-18 16:41:10 -07:00
Kevin Sawicki c4c5d72bf1 Test multiple tab stops per line 2014-06-18 16:37:23 -07:00
Kevin Sawicki 0504244066 Test multiple tab lengths 2014-06-18 16:25:31 -07:00
Kevin Sawicki 8560526158 Add initial spec of hard tabs aligning 2014-06-18 16:24:35 -07:00
Kevin Sawicki ec8805e99e Merge branch 'master' of https://github.com/a-m-s/atom into ks-align-hard-tabs
Conflicts:
	src/tokenized-line.coffee
2014-06-18 16:09:07 -07:00
Kevin Sawicki f8ec2e6da4 Upgrade to settings-view@0.128 2014-06-18 15:23:46 -07:00
Ben Ogle 2a28eafd04 Merge pull request #2661 from atom/bo-fold-markers
Add fold markers to folded lines
2014-06-18 14:58:32 -07:00
Kevin Sawicki a6bf1af2d5 Upgrade to go-to-line@0.23 2014-06-18 14:26:40 -07:00
Kevin Sawicki 8eae66fc49 Upgrade to git-diff@0.33 2014-06-18 14:23:57 -07:00
Ben Ogle 02757fc2de 💄 2014-06-18 14:22:05 -07:00
Kevin Sawicki fc37ac37bd Upgrade to archive-view@0.33 2014-06-18 14:21:33 -07:00
Ben Ogle 63587abe97 Give fold markers a pointer on hover 2014-06-18 14:21:03 -07:00
Ben Ogle d5ea766541 Make click of fold marker unfold the row 2014-06-18 14:07:55 -07:00
Kevin Sawicki bd0643eda4 Upgrade to apm 0.69 2014-06-18 13:56:18 -07:00
Ben Ogle 7a9710b8c3 Add fold markers to folded lines
Fixes #2634
2014-06-18 13:47:38 -07:00
probablycorey 468c6598db Make sure the overlayer class is only used once.
Using it twice causes context menus with the .overlay selector to
appear twice.
Closes #2601
2014-06-18 13:24:15 -07:00
Ben Ogle ddb3cdc76f Merge pull request #2656 from atom/bo-upgrade-fnr
Upgrade find and replace to use decorations for marker views
2014-06-18 13:11:11 -07:00
Ben Ogle 1c8e716cfd Upgrade find-and-replace to use decorations for marker views 2014-06-18 12:56:05 -07:00
Nathan Sobo f1f83a7d36 Add a comment explaining the .editor-colors class on .lines 2014-06-18 12:36:40 -06:00
Nathan Sobo 8d87eb2ed6 Style the .line-numbers div to be compatible w/ both themes and the GPU
The .line-numbers div has to have an opaque background because it's
sent as a texture to the GPU, and otherwise it will have isuses with
subpixel antialiasing.

However, themes style the background of the .gutter div, which was
getting obscured by the opaque background of the line numbers. This
commit adds the .gutter class to the .line-numbers div as well and
ensures it always fills the entire height of the editor.
2014-06-18 12:36:40 -06:00
Kevin Sawicki 29d26a4fae Increase timeout on Windows CI 2014-06-18 10:04:41 -07:00
Ben Ogle 5c6f711bf3 Upgrade syntax themes to add css for new find decorations 2014-06-18 09:53:17 -07:00
Nathan Sobo 53c363b853 Merge pull request #2615 from adnelson/feature/delete-to-end-of-line
added delete to end of line
2014-06-18 10:46:48 -06:00
probablycorey bacd612c71 Apply editor-colors style to gutter
Closes #2596
2014-06-18 09:42:33 -07:00
Kevin Sawicki 2cded15c4c Upgrade to language-xml@0.15 2014-06-18 09:30:44 -07:00
Corey Johnson 2e73a46cbc Merge pull request #2603 from atom/cj-expose-resizing-to-editor
Expose resizing to editor
2014-06-18 09:19:43 -07:00
Kevin Sawicki 64ae7bcdcc Upgrade to tabs@0.42 2014-06-18 08:35:08 -07:00
Kevin Sawicki abca7f778c Upgrade to status-bar@0.41 2014-06-18 08:27:10 -07:00
Kevin Sawicki b7197145c8 Upgrade to spell-check@0.38 2014-06-18 08:19:36 -07:00
Kevin Sawicki 7f53bb5753 Upgrade to text-buffer 2.4.1 2014-06-18 08:18:41 -07:00
Kevin Sawicki 8d38fc77d6 Upgrade to snippets@0.46 2014-06-18 08:15:32 -07:00
Kevin Sawicki 1975882b9e Upgrade to fuzzy-finder@0.55 2014-06-17 18:16:53 -07:00
Kevin Sawicki 0bd4d31ab0 💄 Remove semicolon 2014-06-17 18:06:38 -07:00
Cheng Zhao 4192c121e5 Merge pull request #2630 from deprint/desktop-file
Install Atom.desktop when installing to /usr/local
2014-06-18 08:58:50 +08:00
Ben Ogle e89e2141d7 Merge pull request #2605 from atom/bo-ns-highlights
Add highlight decorations
2014-06-17 17:42:18 -07:00
Kevin Sawicki 25c5458bd2 Upgrade to wrap-guide@0.19 2014-06-17 17:30:58 -07:00
probablycorey ff0cddfd1d Merge remote-tracking branch 'origin/master' into cj-expose-resizing-to-editor 2014-06-17 17:22:10 -07:00
Ben Ogle a7ec7497e2 nof 2014-06-17 17:09:26 -07:00
Ben Ogle 3790cdb262 Reset highlights so they don’t interfere with styleguide text.highlight 2014-06-17 17:08:53 -07:00
Ben Ogle 4308ce7bb0 Warnings when you pass in a bs marker 2014-06-17 17:08:53 -07:00
Kevin Sawicki b302fdc553 Upgrade to markdown-preview@0.82 2014-06-17 16:41:31 -07:00
Nathan Sobo 2fffbba503 Eliminate Decoration class and use plain objects instead 2014-06-17 17:35:56 -06:00
probablycorey 9be1427891 Request scrollView measurement on resize events 2014-06-17 16:25:47 -07:00
Corey Johnson & Nathan Sobo 4564a39392 Remove measureScrollView helper 2014-06-17 16:09:23 -07:00
Corey Johnson & Nathan Sobo 068c1e6249 Use polling to detect editor resize 2014-06-17 16:07:07 -07:00
Kevin Sawicki 06a55250b9 Upgrade to bookmarks@0.25 2014-06-17 15:11:54 -07:00
Ben Ogle d8240628a7 Update doc comments 2014-06-17 14:53:11 -07:00
Kevin Sawicki eaa7593b27 Add single selection menu item 2014-06-17 14:42:57 -07:00
Kevin Sawicki 72e4be60c0 Upgrade to markdown-preview@0.81 2014-06-17 14:40:49 -07:00
Corey Johnson f3a4d32a32 Remove width setting of overflowExpander 2014-06-17 14:07:26 -07:00
Nathan Sobo edadedce7b Give highlight decorations unique id's to avoid potential React errors
Using the marker's id plus the decoration class can cause an error in
the event we apply a decoration with the same class twice to the same
marker. This is admittedly unlikely, but I think it's cleaner to just
allocate unique id's for decoration objects.
2014-06-17 15:03:45 -06:00
Nathan Sobo 89be77b0a9 💄 2014-06-17 14:47:49 -06:00
Nathan Sobo 99ba20ae0d Don't render empty highlights 2014-06-17 14:47:12 -06:00
Corey Johnson 0255e44f00 Remove suppressUpdates 2014-06-17 13:38:08 -07:00
Nathan Sobo 4832d36ac1 Rename filterDecorationsByScreenRow to getLineDecorations
Also rename local variable to lineDecorations for clarity
2014-06-17 14:35:56 -06:00
Nathan Sobo 5d15af943e Rename filterDecorationsByMarkerId to getHighlightDecorations
And rename local variable to highlightDecorations to clarify intent
2014-06-17 14:31:56 -06:00
Corey Johnson 0a671fc386 Add a div that triggers overflowchanged events on resize 2014-06-17 13:28:05 -07:00
Corey Johnson c06f5911c6 Update editor height change spec 2014-06-17 13:26:56 -07:00
Kevin Sawicki 2af8404ea9 Upgrade to bracket-matcher@0.47 2014-06-17 13:07:58 -07:00
Nathan Sobo 345d20dc4a Prepare 0.105.0 release 2014-06-17 14:05:11 -06:00
Ben Ogle ef09fbbfb2 Remove softWrap decoration option 2014-06-17 11:24:27 -07:00
Ben Ogle a3784500ec Fix editor-view tests 2014-06-17 11:13:15 -07:00
Allen Nelson ef7f0ed9ed updated docstring 2014-06-17 12:18:34 -05:00
Fabian Stiewitz 72c87bfbc9 Install Atom.desktop when installDir is not temporary 2014-06-17 16:25:10 +02:00
Fabian Stiewitz 54c7c1e98d 🐧 Install Atom.desktop when installing to /usr/local 2014-06-17 12:40:10 +02:00
Ben Ogle 359793c0b0 Upgrade git-diff for use with new decoration apis. 2014-06-16 17:47:28 -07:00
Ben Ogle 34ec15862f Filter decorations in the components.
This reduces the number of intermediate objects we need to create. The 
downside is a bit more code complexity in the components.
2014-06-16 16:54:21 -07:00
Ben Ogle 04bbe393d4 Remove log lines 2014-06-16 16:51:46 -07:00
Ben Ogle 5b84aa7b18 💄 per nathan’s request 2014-06-16 16:06:18 -07:00
Ben Ogle 7ba498a170 Fix id generation 2014-06-16 16:06:01 -07:00
Ben Ogle c21f8a5a6c 💄 clean up spec names 2014-06-16 16:05:48 -07:00
Ben Ogle 0312609e19 Make highlights render as absolute position 2014-06-16 15:57:23 -07:00
Ben Ogle 32fba97c3a Add a spec for highlights in folds 2014-06-16 15:56:55 -07:00
Ben Ogle 5259d5b750 Dont render invalid highlight decorations 2014-06-16 15:40:54 -07:00
Ben Ogle dff27eba18 Specs for highlights 2014-06-16 15:36:09 -07:00
Ben Ogle d2908c75fc Rename redundant describe 2014-06-16 15:36:09 -07:00
Nathan Sobo 2c04bff0fa Only return decorations for markers intersecting the screen row range
This relies on a fix to the interval-skip-list provided with the upgrade
to text-buffer@2.4.0.
2014-06-16 15:36:09 -07:00
Ben Ogle 4f2f158d0d Make selection updating work properly 2014-06-16 15:36:09 -07:00
Ben Ogle 351dc58354 Remove getSelectionScreenRanges() 2014-06-16 15:36:09 -07:00
Ben Ogle 2867dd98e5 Remove getGutterDecorations() 2014-06-16 15:36:09 -07:00
Ben Ogle 32a0804b9a Remove Decorations object 2014-06-16 15:36:09 -07:00
Ben Ogle 408e62a993 Pass highlightDecorations into the HighlightsComponent 2014-06-16 15:36:08 -07:00
Ben Ogle d4057d21c7 Pass the decoration hash into the gutterComponent 2014-06-16 15:36:08 -07:00
Ben Ogle 031ec9798a No more typeless decorations
Now you can specify a list of types with your decoration.
2014-06-16 15:36:08 -07:00
Ben Ogle 1ebdd801f5 Use decorations to render selections 2014-06-16 15:36:08 -07:00
Ben Ogle 084632a985 Rename Selection(s)Component to Highlight(s)Component 2014-06-16 15:36:08 -07:00
Ben Ogle 002e14990b Rename bufferRowHasClass 2014-06-16 15:36:08 -07:00
Ben Ogle 3a3fc4b614 Add a spec for screen line changes 2014-06-16 15:36:08 -07:00
Ben Ogle b028673b5d Only render on updates 2014-06-16 15:36:08 -07:00
Ben Ogle 6394814142 Make foldable decorations work again 2014-06-16 15:36:08 -07:00
Ben Ogle e128212410 Fix decoration specs to work with markers only 2014-06-16 15:36:08 -07:00
Ben Ogle 2d4360dcf0 Decorations can now only be attached to markers.
The basics work. It will render them on the gutter.
2014-06-16 15:36:08 -07:00
Ben Ogle & Nathan Sobo 120e2a3bdb Move decoration Editor specs to DisplayBuffer 2014-06-16 15:36:08 -07:00
Ben Ogle & Nathan Sobo 7142022f05 Add intersection support to DisplayBuffer::findMarkers 2014-06-16 15:36:08 -07:00
Ben Ogle & Nathan Sobo 25520a4cad Add containedInScreenRange to DisplayBuffer::findMarkers 2014-06-16 15:36:08 -07:00
Ben Ogle & Nathan Sobo 56da6399b8 Add startScreenRow and endScreenRow to DisplayBuffer::findMarkers 2014-06-16 15:36:08 -07:00
Ben Ogle & Nathan Sobo a7379b067a Use ::getMarker for fold decorations instead of new DisplayBufferMarker 2014-06-16 15:36:07 -07:00
Allen Nelson 8411d41621 deleting only selection if selection is not empty 2014-06-16 16:13:40 -05:00
Allen Nelson fc462fcd21 added delete to end of line 2014-06-16 10:59:55 -05:00
Nathan Sobo 9e6756ed6d Don't perform an update in response scroll view dimension changes
We always measure the scroll view in the ::componentWillUpdate hook, so
performing *another* update in response to the measurement causes an
invariant violation in react. Whenever we are measuring, we are always
already updating.
2014-06-13 17:07:41 -06:00
probablycorey 8d84a97b2b Move scroll view measurements to componentWillUpdate 2014-06-13 11:36:40 -07:00
Corey Johnson e96d2dbd17 Add the simplest resize fix that will work. 2014-06-12 16:17:45 -07:00
Corey Johnson 025370b9f8 Add editor component resize spec 2014-06-12 16:17:18 -07:00
Andrew Stubbs 1fe6c498ac Make hard tabs align to columns. 2014-05-14 13:31:03 +01:00
37 arquivos alterados com 1319 adições e 626 exclusões
+1 -1
Ver Arquivo
@@ -6,6 +6,6 @@
"url": "https://github.com/atom/atom.git"
},
"dependencies": {
"atom-package-manager": "0.68.0"
"atom-package-manager": "0.69.0"
}
}
+4
Ver Arquivo
@@ -86,6 +86,10 @@ module.exports = (grunt) ->
unless /.+\.plist/.test(sourcePath)
grunt.file.copy(sourcePath, path.resolve(appDir, '..', subDirectory, filename))
if process.platform is 'win32'
cp path.join('resources', 'win', 'msvcp100.dll'), path.join(shellAppDir, 'msvcp100.dll')
cp path.join('resources', 'win', 'msvcr100.dll'), path.join(shellAppDir, 'msvcr100.dll')
dependencies = ['compile', "generate-license:save"]
dependencies.push('copy-info-plist') if process.platform is 'darwin'
dependencies.push('set-exe-icon') if process.platform is 'win32'
+17
Ver Arquivo
@@ -1,8 +1,14 @@
fs = require 'fs'
path = require 'path'
_ = require 'underscore-plus'
fs = require 'fs-plus'
runas = null
fillTemplate = (filePath, data) ->
template = _.template(String(fs.readFileSync(filePath + '.in')))
filled = template(data)
fs.writeFileSync(filePath, filled)
module.exports = (grunt) ->
{cp, mkdir, rm} = require('./task-helpers')(grunt)
@@ -25,12 +31,23 @@ module.exports = (grunt) ->
binDir = path.join(installDir, 'bin')
shareDir = path.join(installDir, 'share', 'atom')
iconName = path.join(shareDir,'resources','app','resources','atom.png')
desktopFile = path.join('resources', 'linux', 'Atom.desktop')
mkdir binDir
cp 'atom.sh', path.join(binDir, 'atom')
rm shareDir
mkdir path.dirname(shareDir)
cp shellAppDir, shareDir
# Create Atom.desktop if installation in '/usr/local'
applicationsDir = path.join('/usr','share','applications')
tmpDir = if process.env.TMPDIR? then process.env.TMPDIR else '/tmp'
if installDir.indexOf(tmpDir) isnt 0 and fs.isDirectorySync(applicationsDir)
{description} = grunt.file.readJSON('package.json')
fillTemplate(desktopFile, {description, installDir, iconName})
cp desktopFile, path.join(applicationsDir,'Atom.desktop')
# Create relative symbol link for apm.
process.chdir(binDir)
rm('apm')
+3 -1
Ver Arquivo
@@ -23,7 +23,9 @@ module.exports = (grunt) ->
{name, version, description} = grunt.file.readJSON('package.json')
section = 'devel'
maintainer = 'GitHub <atom@github.com>'
data = {name, version, description, section, arch, maintainer}
installDir = '/usr'
iconName = 'atom'
data = {name, version, description, section, arch, maintainer, installDir, iconName}
control = path.join('resources', 'linux', 'debian', 'control')
fillTemplate(control, data)
+5 -2
Ver Arquivo
@@ -9,8 +9,11 @@
* For 64-bit builds of node and native modules you **must** have the
[Windows 7 64-bit SDK](http://www.microsoft.com/en-us/download/details.aspx?id=8279).
You may also need the [compiler update for the Windows SDK 7.1](http://www.microsoft.com/en-us/download/details.aspx?id=4422)
* [Python](http://www.python.org/download/) v2.7.x
* [Python](http://www.python.org/download/) v2.7.
* The python.exe must be available at `%SystemDrive%\Python27\python.exe`.
If it is installed elsewhere, you can create a symbolic link to the
directory containing the python.exe using:
`mklink /d %SystemDrive%\Python27 D:\elsewhere\Python27`
* [GitHub for Windows](http://windows.github.com/)
### On Windows 8
+2
Ver Arquivo
@@ -53,6 +53,8 @@
'shift-down': 'core:select-down'
'shift-left': 'core:select-left'
'shift-right': 'core:select-right'
'shift-pageup': 'core:select-page-up'
'shift-pagedown': 'core:select-page-down'
'delete': 'core:delete'
'shift-delete': 'core:delete'
'pageup': 'core:page-up'
+2
Ver Arquivo
@@ -35,6 +35,8 @@
'shift-down': 'core:select-down'
'shift-left': 'core:select-left'
'shift-right': 'core:select-right'
'shift-pageup': 'core:select-page-up'
'shift-pagedown': 'core:select-page-down'
'delete': 'core:delete'
'shift-delete': 'core:delete'
'pageup': 'core:page-up'
+2
Ver Arquivo
@@ -37,6 +37,8 @@
'shift-down': 'core:select-down'
'shift-left': 'core:select-left'
'shift-right': 'core:select-right'
'shift-pageup': 'core:select-page-up'
'shift-pagedown': 'core:select-page-down'
'delete': 'core:delete'
'shift-delete': 'core:delete'
'pageup': 'core:page-up'
+1
Ver Arquivo
@@ -109,6 +109,7 @@
submenu: [
{ label: 'Add Selection Above', command: 'editor:add-selection-above' }
{ label: 'Add Selection Below', command: 'editor:add-selection-below' }
{ label: 'Single Selection', command: 'editor:consolidate-selections'}
{ label: 'Split into Lines', command: 'editor:split-selections-into-lines'}
{ type: 'separator' }
{ label: 'Select to Top', command: 'core:select-to-top' }
+1
Ver Arquivo
@@ -108,6 +108,7 @@
{ label: 'Add Selection &Above', command: 'editor:add-selection-above' }
{ label: 'Add Selection &Below', command: 'editor:add-selection-below' }
{ label: 'S&plit into Lines', command: 'editor:split-selections-into-lines'}
{ label: 'Single Selection', command: 'editor:consolidate-selections'}
{ type: 'separator' }
{ label: 'Select to &Top', command: 'core:select-to-top' }
{ label: 'Select to Botto&m', command: 'core:select-to-bottom' }
+1
Ver Arquivo
@@ -127,6 +127,7 @@
{ label: 'Add Selection &Above', command: 'editor:add-selection-above' }
{ label: 'Add Selection &Below', command: 'editor:add-selection-below' }
{ label: 'S&plit into Lines', command: 'editor:split-selections-into-lines'}
{ label: 'Single Selection', command: 'editor:consolidate-selections'}
{ type: 'separator' }
{ label: 'Select to &Top', command: 'core:select-to-top' }
{ label: 'Select to Botto&m', command: 'core:select-to-bottom' }
+31 -31
Ver Arquivo
@@ -1,7 +1,7 @@
{
"name": "atom",
"productName": "Atom",
"version": "0.104.0",
"version": "0.106.0",
"description": "A hackable text editor for the 21st Century.",
"main": "./src/browser/main.js",
"repository": {
@@ -17,7 +17,7 @@
"url": "http://github.com/atom/atom/raw/master/LICENSE.md"
}
],
"atomShellVersion": "0.13.1",
"atomShellVersion": "0.13.2",
"dependencies": {
"async": "0.2.6",
"atom-keymap": "^0.27.0",
@@ -56,67 +56,67 @@
"serializable": "^1",
"space-pen": "3.2.0",
"temp": "0.7.0",
"text-buffer": "^2.3.0",
"text-buffer": "^2.4.2",
"theorist": "^1",
"underscore-plus": "^1.4.1",
"underscore-plus": "^1.5.0",
"vm-compatibility-layer": "0.1.0"
},
"packageDependencies": {
"atom-dark-syntax": "0.16.0",
"atom-dark-syntax": "0.17.0",
"atom-dark-ui": "0.29.0",
"atom-light-syntax": "0.17.0",
"atom-light-syntax": "0.18.0",
"atom-light-ui": "0.25.0",
"base16-tomorrow-dark-theme": "0.16.0",
"solarized-dark-syntax": "0.17.0",
"solarized-light-syntax": "0.8.0",
"archive-view": "0.32.0",
"base16-tomorrow-dark-theme": "0.17.0",
"solarized-dark-syntax": "0.18.0",
"solarized-light-syntax": "0.9.0",
"archive-view": "0.33.0",
"autocomplete": "0.28.0",
"autoflow": "0.17.0",
"autosave": "0.14.0",
"background-tips": "0.14.0",
"bookmarks": "0.24.0",
"bracket-matcher": "0.46.0",
"command-palette": "0.22.0",
"bookmarks": "0.25.0",
"bracket-matcher": "0.47.0",
"command-palette": "0.23.0",
"deprecation-cop": "0.7.0",
"dev-live-reload": "0.31.0",
"exception-reporting": "0.18.0",
"feedback": "0.33.0",
"find-and-replace": "0.117.0",
"fuzzy-finder": "0.54.0",
"git-diff": "0.31.0",
"go-to-line": "0.22.0",
"find-and-replace": "0.120.0",
"fuzzy-finder": "0.55.0",
"git-diff": "0.34.0",
"go-to-line": "0.23.0",
"grammar-selector": "0.27.0",
"image-view": "0.35.0",
"keybinding-resolver": "0.18.0",
"link": "0.22.0",
"markdown-preview": "0.80.0",
"link": "0.24.0",
"markdown-preview": "0.82.0",
"metrics": "0.32.0",
"open-on-github": "0.28.0",
"package-generator": "0.31.0",
"release-notes": "0.32.0",
"settings-view": "0.127.0",
"snippets": "0.45.0",
"spell-check": "0.37.0",
"status-bar": "0.40.0",
"settings-view": "0.128.0",
"snippets": "0.46.0",
"spell-check": "0.38.0",
"status-bar": "0.41.0",
"styleguide": "0.29.0",
"symbols-view": "0.56.0",
"tabs": "0.41.0",
"tabs": "0.42.0",
"timecop": "0.19.0",
"tree-view": "0.103.0",
"tree-view": "0.104.0",
"update-package-dependencies": "0.6.0",
"welcome": "0.16.0",
"whitespace": "0.22.0",
"wrap-guide": "0.18.0",
"language-c": "0.18.0",
"wrap-guide": "0.19.0",
"language-c": "0.21.0",
"language-coffee-script": "0.22.0",
"language-css": "0.17.0",
"language-gfm": "0.39.0",
"language-gfm": "0.40.0",
"language-git": "0.9.0",
"language-go": "0.12.0",
"language-html": "0.22.0",
"language-hyperlink": "0.10.0",
"language-java": "0.10.0",
"language-javascript": "0.27.0",
"language-javascript": "0.28.0",
"language-json": "0.8.0",
"language-less": "0.9.0",
"language-make": "0.10.0",
@@ -125,7 +125,7 @@
"language-php": "0.15.0",
"language-property-list": "0.7.0",
"language-python": "0.18.0",
"language-ruby": "0.29.0",
"language-ruby": "0.30.0",
"language-ruby-on-rails": "0.14.0",
"language-sass": "0.13.0",
"language-shellscript": "0.8.0",
@@ -134,7 +134,7 @@
"language-text": "0.6.0",
"language-todo": "0.10.0",
"language-toml": "0.12.0",
"language-xml": "0.14.0",
"language-xml": "0.15.0",
"language-yaml": "0.7.0"
},
"private": true,
+2 -2
Ver Arquivo
@@ -1,8 +1,8 @@
[Desktop Entry]
Name=Atom
Comment=<%= description %>
Exec=/usr/share/atom/atom %U
Icon=atom
Exec=<%= installDir %>/share/atom/atom %U
Icon=<%= iconName %>
Type=Application
StartupNotify=true
Categories=GNOME;GTK;Utility;TextEditor;
Arquivo binário não exibido.
Arquivo binário não exibido.
+60 -4
Ver Arquivo
@@ -912,6 +912,42 @@ describe "DisplayBuffer", ->
expect(displayBuffer.findMarkers(class: 'a', startBufferRow: 0, endBufferRow: 3)).toEqual [marker1]
expect(displayBuffer.findMarkers(endBufferRow: 10)).toEqual [marker3]
it "allows the startScreenRow and endScreenRow to be specified", ->
marker1 = displayBuffer.markBufferRange([[6, 0], [7, 0]], class: 'a')
marker2 = displayBuffer.markBufferRange([[9, 0], [10, 0]], class: 'a')
displayBuffer.createFold(4, 7)
expect(displayBuffer.findMarkers(class: 'a', startScreenRow: 6, endScreenRow: 7)).toEqual [marker2]
it "allows intersectsBufferRowRange to be specified", ->
marker1 = displayBuffer.markBufferRange([[5, 0], [5, 0]], class: 'a')
marker2 = displayBuffer.markBufferRange([[8, 0], [8, 0]], class: 'a')
displayBuffer.createFold(4, 7)
expect(displayBuffer.findMarkers(class: 'a', intersectsBufferRowRange: [5, 6])).toEqual [marker1]
it "allows intersectsScreenRowRange to be specified", ->
marker1 = displayBuffer.markBufferRange([[5, 0], [5, 0]], class: 'a')
marker2 = displayBuffer.markBufferRange([[8, 0], [8, 0]], class: 'a')
displayBuffer.createFold(4, 7)
expect(displayBuffer.findMarkers(class: 'a', intersectsScreenRowRange: [5, 10])).toEqual [marker2]
it "allows containedInScreenRange to be specified", ->
marker1 = displayBuffer.markBufferRange([[5, 0], [5, 0]], class: 'a')
marker2 = displayBuffer.markBufferRange([[8, 0], [8, 0]], class: 'a')
displayBuffer.createFold(4, 7)
expect(displayBuffer.findMarkers(class: 'a', containedInScreenRange: [[5, 0], [7, 0]])).toEqual [marker2]
it "allows intersectsBufferRange to be specified", ->
marker1 = displayBuffer.markBufferRange([[5, 0], [5, 0]], class: 'a')
marker2 = displayBuffer.markBufferRange([[8, 0], [8, 0]], class: 'a')
displayBuffer.createFold(4, 7)
expect(displayBuffer.findMarkers(class: 'a', intersectsBufferRange: [[5, 0], [6, 0]])).toEqual [marker1]
it "allows intersectsScreenRange to be specified", ->
marker1 = displayBuffer.markBufferRange([[5, 0], [5, 0]], class: 'a')
marker2 = displayBuffer.markBufferRange([[8, 0], [8, 0]], class: 'a')
displayBuffer.createFold(4, 7)
expect(displayBuffer.findMarkers(class: 'a', intersectsScreenRange: [[5, 0], [10, 0]])).toEqual [marker2]
describe "marker destruction", ->
it "allows markers to be destroyed", ->
marker = displayBuffer.markScreenRange([[5, 4], [5, 10]])
@@ -956,6 +992,17 @@ describe "DisplayBuffer", ->
expect(start.top).toBe 5 * 20
expect(start.left).toBe (4 * 10) + (6 * 11)
describe "decorations", ->
it "can add decorations associated with markers and remove them", ->
decoration = {type: 'gutter', class: 'one'}
marker = displayBuffer.markBufferRange([[2, 13], [3, 15]])
displayBuffer.addDecorationForMarker(marker, decoration)
expect(displayBuffer.decorationsForScreenRowRange(2, 3)[marker.id][0]).toBe decoration
displayBuffer.removeDecorationForMarker(marker, decoration)
expect(displayBuffer.decorationsForScreenRowRange(2, 3)[marker.id]).not.toBeDefined()
describe "::setScrollTop", ->
beforeEach ->
displayBuffer.manageScrollPosition = true
@@ -997,17 +1044,26 @@ describe "DisplayBuffer", ->
expect(displayBuffer.setScrollLeft(maxScrollLeft + 50)).toBe maxScrollLeft
expect(displayBuffer.getScrollLeft()).toBe maxScrollLeft
describe "::scrollToScreenPosition(position)", ->
it "sets the scroll top and scroll left so the given screen position is in view", ->
describe "::scrollToScreenPosition(position, [options])", ->
beforeEach ->
displayBuffer.manageScrollPosition = true
displayBuffer.setLineHeightInPixels(10)
displayBuffer.setDefaultCharWidth(10)
displayBuffer.setHorizontalScrollbarHeight(0)
displayBuffer.setHeight(50)
displayBuffer.setWidth(50)
maxScrollTop = displayBuffer.getScrollHeight() - displayBuffer.getHeight()
it "sets the scroll top and scroll left so the given screen position is in view", ->
displayBuffer.scrollToScreenPosition([8, 20])
expect(displayBuffer.getScrollBottom()).toBe (9 + displayBuffer.getVerticalScrollMargin()) * 10
expect(displayBuffer.getScrollRight()).toBe (20 + displayBuffer.getHorizontalScrollMargin()) * 10
describe "when the 'center' option is true", ->
it "vertically scrolls to center the given position vertically", ->
displayBuffer.scrollToScreenPosition([8, 20], center: true)
expect(displayBuffer.getScrollTop()).toBe (8 * 10) + 5 - (50 / 2)
expect(displayBuffer.getScrollRight()).toBe (20 + displayBuffer.getHorizontalScrollMargin()) * 10
it "does not scroll vertically if the position is already in view", ->
displayBuffer.scrollToScreenPosition([4, 20], center: true)
expect(displayBuffer.getScrollTop()).toBe 0
+563 -146
Ver Arquivo
@@ -19,7 +19,7 @@ describe "EditorComponent", ->
spyOn(window, "clearInterval").andCallFake window.fakeClearInterval
delayAnimationFrames = false
nextAnimationFrame = null
nextAnimationFrame = -> throw new Error('No animation frame requested')
spyOn(window, 'requestAnimationFrame').andCallFake (fn) ->
if delayAnimationFrames
nextAnimationFrame = fn
@@ -143,15 +143,15 @@ describe "EditorComponent", ->
it "re-renders the lines when the showInvisibles config option changes", ->
editor.setText " a line with tabs\tand spaces "
expect(component.lineNodeForScreenRow(0).textContent).toBe "#{invisibles.space}a line with tabs#{invisibles.tab} and spaces#{invisibles.space}#{invisibles.eol}"
expect(component.lineNodeForScreenRow(0).textContent).toBe "#{invisibles.space}a line with tabs#{invisibles.tab}and spaces#{invisibles.space}#{invisibles.eol}"
atom.config.set("editor.showInvisibles", false)
expect(component.lineNodeForScreenRow(0).textContent).toBe " a line with tabs and spaces "
expect(component.lineNodeForScreenRow(0).textContent).toBe " a line with tabs and spaces "
atom.config.set("editor.showInvisibles", true)
expect(component.lineNodeForScreenRow(0).textContent).toBe "#{invisibles.space}a line with tabs#{invisibles.tab} and spaces#{invisibles.space}#{invisibles.eol}"
expect(component.lineNodeForScreenRow(0).textContent).toBe "#{invisibles.space}a line with tabs#{invisibles.tab}and spaces#{invisibles.space}#{invisibles.eol}"
it "displays spaces, tabs, and newlines as visible characters", ->
editor.setText " a line with tabs\tand spaces "
expect(component.lineNodeForScreenRow(0).textContent).toBe "#{invisibles.space}a line with tabs#{invisibles.tab} and spaces#{invisibles.space}#{invisibles.eol}"
expect(component.lineNodeForScreenRow(0).textContent).toBe "#{invisibles.space}a line with tabs#{invisibles.tab}and spaces#{invisibles.space}#{invisibles.eol}"
it "displays newlines as their own token outside of the other tokens' scopes", ->
editor.setText "var"
@@ -232,13 +232,113 @@ describe "EditorComponent", ->
editor.setText("a\0b")
expect(editor.pixelPositionForScreenPosition([0, Infinity]).left).toEqual 2 * charWidth
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)
foldedLineNode = component.lineNodeForScreenRow(4)
expect(foldedLineNode.querySelector('.fold-marker')).toBeTruthy()
editor.unfoldBufferRow(4)
foldedLineNode = component.lineNodeForScreenRow(4)
expect(foldedLineNode.querySelector('.fold-marker')).toBeFalsy()
describe "when line decorations are attached to markers", ->
{marker, decoration} = {}
lineHasClass = (screenRow, klass) ->
component.lineNodeForScreenRow(screenRow).classList.contains(klass)
beforeEach ->
marker = editor.displayBuffer.markBufferRange([[2, 13], [3, 15]], invalidate: 'inside')
decoration = {type: 'line', class: 'someclass'}
editor.addDecorationForMarker(marker, decoration)
waitsFor -> not component.decorationChangedImmediate?
it "does not render off-screen lines with line number classes until they are with in the rendered row range", ->
node.style.height = 4.5 * lineHeightInPixels + 'px'
component.measureScrollView()
expect(component.lineNodeForScreenRow(9)).not.toBeDefined()
marker = editor.displayBuffer.markBufferRange([[9, 0], [9, 0]], invalidate: 'inside')
editor.addDecorationForMarker(marker, type: 'line', class: 'fancy-class')
editor.addDecorationForMarker(marker, type: 'gutter', class: 'nope-class')
verticalScrollbarNode.scrollTop = 2.5 * lineHeightInPixels
verticalScrollbarNode.dispatchEvent(new UIEvent('scroll'))
expect(lineHasClass(9, 'fancy-class')).toBe true
expect(lineHasClass(9, 'nope-class')).toBe false
it "renders the specified decoration class on the correct lines", ->
expect(lineHasClass(1, 'someclass')).toBe false
expect(lineHasClass(2, 'someclass')).toBe true
expect(lineHasClass(3, 'someclass')).toBe true
expect(lineHasClass(4, 'someclass')).toBe false
it "removes line classes when a decoration's marker is invalidated", ->
editor.getBuffer().insert([3, 2], 'n')
waitsFor -> not component.decorationChangedImmediate?
runs ->
expect(marker.isValid()).toBe false
expect(lineHasClass(1, 'someclass')).toBe false
expect(lineHasClass(2, 'someclass')).toBe false
expect(lineHasClass(3, 'someclass')).toBe false
expect(lineHasClass(4, 'someclass')).toBe false
editor.getBuffer().undo()
waitsFor -> not component.decorationChangedImmediate?
runs ->
expect(marker.isValid()).toBe true
expect(lineHasClass(1, 'someclass')).toBe false
expect(lineHasClass(2, 'someclass')).toBe true
expect(lineHasClass(3, 'someclass')).toBe true
expect(lineHasClass(4, 'someclass')).toBe false
it "removes the classes and unsubscribes from the marker when decoration is removed", ->
editor.removeDecorationForMarker(marker, decoration)
waitsFor -> not component.decorationChangedImmediate?
runs ->
expect(lineHasClass(1, 'someclass')).toBe false
expect(lineHasClass(2, 'someclass')).toBe false
expect(lineHasClass(3, 'someclass')).toBe false
expect(lineHasClass(4, 'someclass')).toBe false
editor.getBuffer().insert([0, 0], '\n')
waitsFor -> not component.decorationChangedImmediate?
runs ->
expect(lineHasClass(2, 'someclass')).toBe false
expect(lineHasClass(3, 'someclass')).toBe false
it "removes the line number classes when the decoration's marker is destroyed", ->
marker.destroy()
waitsFor -> not component.decorationChangedImmediate?
runs ->
expect(lineHasClass(1, 'someclass')).toBe false
expect(lineHasClass(2, 'someclass')).toBe false
expect(lineHasClass(3, 'someclass')).toBe false
expect(lineHasClass(4, 'someclass')).toBe false
describe "gutter rendering", ->
[lineNumberHasClass, gutter] = []
[gutter] = []
lineNumberHasClass = (screenRow, klass) ->
component.lineNumberNodeForScreenRow(screenRow).classList.contains(klass)
lineNumberForBufferRowHasClass = (bufferRow, klass) ->
screenRow = editor.displayBuffer.screenRowForBufferRow(bufferRow)
component.lineNumberNodeForScreenRow(screenRow).classList.contains(klass)
beforeEach ->
{gutter} = component.refs
lineNumberHasClass = (screenRow, klass) ->
component.lineNumberNodeForScreenRow(screenRow).classList.contains(klass)
it "renders the currently-visible line numbers", ->
node.style.height = 4.5 * lineHeightInPixels + 'px'
@@ -314,6 +414,11 @@ describe "EditorComponent", ->
expect(component.lineNumberNodeForScreenRow(9).textContent).toBe "10"
expect(gutterNode.offsetWidth).toBe initialGutterWidth
it "renders the .line-numbers div at the full height of the editor even if it's taller than its content", ->
node.style.height = node.offsetHeight + 100 + 'px'
component.measureScrollView()
expect(node.querySelector('.line-numbers').offsetHeight).toBe node.offsetHeight
describe "fold decorations", ->
describe "rendering fold decorations", ->
it "adds the foldable class to line numbers when the line is foldable", ->
@@ -416,7 +521,7 @@ describe "EditorComponent", ->
expect(lineNumberHasClass(10, 'cursor-line')).toBe false
it "adds cursor-line decorations to multiple lines when a selection is performed", ->
cursor.setScreenPosition([1, 0])
cursor.setScreenPosition([1, 5])
editor.selectDown(2)
expect(lineNumberHasClass(0, 'cursor-line')).toBe false
expect(lineNumberHasClass(1, 'cursor-line')).toBe true
@@ -424,156 +529,149 @@ describe "EditorComponent", ->
expect(lineNumberHasClass(3, 'cursor-line')).toBe true
expect(lineNumberHasClass(4, 'cursor-line')).toBe false
describe "when decorations are used", ->
describe "when decorations are applied to buffer rows", ->
it "renders line number classes based on the decorations on their buffer row", ->
node.style.height = 4.5 * lineHeightInPixels + 'px'
component.measureScrollView()
it "does not render a cursor-line decoration for the last line of a multi-line selection of the selection ends at column 0", ->
cursor.setScreenPosition([1, 0])
editor.selectDown(2)
expect(lineNumberHasClass(0, 'cursor-line')).toBe false
expect(lineNumberHasClass(1, 'cursor-line')).toBe true
expect(lineNumberHasClass(2, 'cursor-line')).toBe true
expect(lineNumberHasClass(3, 'cursor-line')).toBe false
expect(component.lineNumberNodeForScreenRow(9)).not.toBeDefined()
describe "when gutter decorations are attached to markers", ->
{marker, decoration} = {}
beforeEach ->
marker = editor.displayBuffer.markBufferRange([[2, 13], [3, 15]], invalidate: 'inside')
decoration = {type: 'gutter', class: 'someclass'}
editor.addDecorationForMarker(marker, decoration)
waitsFor -> not component.decorationChangedImmediate?
editor.addDecorationToBufferRow(9, type: 'gutter', class: 'fancy-class')
editor.addDecorationToBufferRow(9, type: 'someother-type', class: 'nope-class')
it "does not render off-screen lines with line number classes until they are with in the rendered row range", ->
node.style.height = 4.5 * lineHeightInPixels + 'px'
component.measureScrollView()
verticalScrollbarNode.scrollTop = 2.5 * lineHeightInPixels
verticalScrollbarNode.dispatchEvent(new UIEvent('scroll'))
expect(component.lineNumberNodeForScreenRow(9)).not.toBeDefined()
expect(lineNumberHasClass(9, 'fancy-class')).toBe true
expect(lineNumberHasClass(9, 'nope-class')).toBe false
marker = editor.displayBuffer.markBufferRange([[9, 0], [9, 0]], invalidate: 'inside')
editor.addDecorationForMarker(marker, type: 'gutter', class: 'fancy-class')
editor.addDecorationForMarker(marker, type: 'someother-type', class: 'nope-class')
it "renders updates to gutter decorations", ->
editor.addDecorationToBufferRow(2, type: 'gutter', class: 'fancy-class')
editor.addDecorationToBufferRow(2, type: 'someother-type', class: 'nope-class')
verticalScrollbarNode.scrollTop = 2.5 * lineHeightInPixels
verticalScrollbarNode.dispatchEvent(new UIEvent('scroll'))
waitsFor -> not component.decorationChangedImmediate?
runs ->
expect(lineNumberHasClass(2, 'fancy-class')).toBe true
expect(lineNumberHasClass(2, 'nope-class')).toBe false
expect(lineNumberHasClass(9, 'fancy-class')).toBe true
expect(lineNumberHasClass(9, 'nope-class')).toBe false
editor.removeDecorationFromBufferRow(2, type: 'gutter', class: 'fancy-class')
editor.removeDecorationFromBufferRow(2, type: 'someother-type', class: 'nope-class')
it "renders classes on correct screen lines when the user folds a block of code", ->
marker = editor.displayBuffer.markBufferRange([[9, 0], [9, 0]], invalidate: 'inside')
editor.addDecorationForMarker(marker, decoration)
waitsFor -> not component.decorationChangedImmediate?
runs ->
expect(lineNumberHasClass(2, 'fancy-class')).toBe false
expect(lineNumberHasClass(2, 'nope-class')).toBe false
waitsFor -> not component.decorationChangedImmediate?
runs ->
expect(lineNumberForBufferRowHasClass(9, 'someclass')).toBe true
editor.foldBufferRow(5)
editor.removeDecorationForMarker(marker, decoration)
it "renders decorations on soft-wrapped line numbers when softWrap is true", ->
editor.addDecorationToBufferRow(1, type: 'gutter', class: 'no-wrap')
editor.addDecorationToBufferRow(1, type: 'gutter', class: 'wrap-me', softWrap: true)
waitsFor -> not component.decorationChangedImmediate?
runs ->
expect(lineNumberForBufferRowHasClass(9, 'someclass')).toBe false
editor.setSoftWrap(true)
node.style.height = 4.5 * lineHeightInPixels + 'px'
node.style.width = 30 * charWidth + 'px'
component.measureScrollView()
it "updates line number classes when the marker moves", ->
expect(lineNumberHasClass(1, 'someclass')).toBe false
expect(lineNumberHasClass(2, 'someclass')).toBe true
expect(lineNumberHasClass(3, 'someclass')).toBe true
expect(lineNumberHasClass(4, 'someclass')).toBe false
expect(lineNumberHasClass(2, 'no-wrap')).toBe true
expect(lineNumberHasClass(2, 'wrap-me')).toBe true
expect(lineNumberHasClass(3, 'no-wrap')).toBe false
expect(lineNumberHasClass(3, 'wrap-me')).toBe true
editor.getBuffer().insert([0, 0], '\n')
# should remove the wrapped decorations
editor.removeDecorationFromBufferRow(1, type: 'gutter', class: 'no-wrap')
editor.removeDecorationFromBufferRow(1, type: 'gutter', class: 'wrap-me')
waitsFor -> not component.decorationChangedImmediate?
runs ->
expect(lineNumberHasClass(2, 'someclass')).toBe false
expect(lineNumberHasClass(3, 'someclass')).toBe true
expect(lineNumberHasClass(4, 'someclass')).toBe true
expect(lineNumberHasClass(5, 'someclass')).toBe false
waitsFor -> not component.decorationChangedImmediate?
runs ->
expect(lineNumberHasClass(2, 'no-wrap')).toBe false
expect(lineNumberHasClass(2, 'wrap-me')).toBe false
expect(lineNumberHasClass(3, 'no-wrap')).toBe false
expect(lineNumberHasClass(3, 'wrap-me')).toBe false
editor.getBuffer().deleteRows(0, 1)
# should add them back when the nodes are not recreated
editor.addDecorationToBufferRow(1, type: 'gutter', class: 'no-wrap')
editor.addDecorationToBufferRow(1, type: 'gutter', class: 'wrap-me', softWrap: true)
waitsFor -> not component.decorationChangedImmediate?
runs ->
expect(lineNumberHasClass(0, 'someclass')).toBe false
expect(lineNumberHasClass(1, 'someclass')).toBe true
expect(lineNumberHasClass(2, 'someclass')).toBe true
expect(lineNumberHasClass(3, 'someclass')).toBe false
waitsFor -> not component.decorationChangedImmediate?
runs ->
expect(lineNumberHasClass(2, 'no-wrap')).toBe true
expect(lineNumberHasClass(2, 'wrap-me')).toBe true
expect(lineNumberHasClass(3, 'no-wrap')).toBe false
expect(lineNumberHasClass(3, 'wrap-me')).toBe true
it "removes line number classes when a decoration's marker is invalidated", ->
editor.getBuffer().insert([3, 2], 'n')
describe "when decorations are applied to markers", ->
{marker, decoration} = {}
beforeEach ->
marker = editor.displayBuffer.markBufferRange([[2, 13], [3, 15]], class: 'my-marker', invalidate: 'inside')
decoration = {type: 'gutter', class: 'someclass'}
editor.addDecorationForMarker(marker, decoration)
waitsFor -> not component.decorationChangedImmediate?
waitsFor -> not component.decorationChangedImmediate?
runs ->
it "updates line number classes when the marker moves", ->
expect(marker.isValid()).toBe false
expect(lineNumberHasClass(1, 'someclass')).toBe false
expect(lineNumberHasClass(2, 'someclass')).toBe false
expect(lineNumberHasClass(3, 'someclass')).toBe false
expect(lineNumberHasClass(4, 'someclass')).toBe false
editor.getBuffer().undo()
waitsFor -> not component.decorationChangedImmediate?
runs ->
expect(marker.isValid()).toBe true
expect(lineNumberHasClass(1, 'someclass')).toBe false
expect(lineNumberHasClass(2, 'someclass')).toBe true
expect(lineNumberHasClass(3, 'someclass')).toBe true
expect(lineNumberHasClass(4, 'someclass')).toBe false
editor.getBuffer().insert([0, 0], '\n')
it "removes the classes and unsubscribes from the marker when decoration is removed", ->
editor.removeDecorationForMarker(marker, decoration)
waitsFor -> not component.decorationChangedImmediate?
runs ->
expect(lineNumberHasClass(1, 'someclass')).toBe false
expect(lineNumberHasClass(2, 'someclass')).toBe false
expect(lineNumberHasClass(3, 'someclass')).toBe false
expect(lineNumberHasClass(4, 'someclass')).toBe false
editor.getBuffer().insert([0, 0], '\n')
waitsFor -> not component.decorationChangedImmediate?
runs ->
expect(lineNumberHasClass(2, 'someclass')).toBe false
expect(lineNumberHasClass(3, 'someclass')).toBe false
it "removes the line number classes when the decoration's marker is destroyed", ->
marker.destroy()
waitsFor -> not component.decorationChangedImmediate?
runs ->
expect(lineNumberHasClass(1, 'someclass')).toBe false
expect(lineNumberHasClass(2, 'someclass')).toBe false
expect(lineNumberHasClass(3, 'someclass')).toBe false
expect(lineNumberHasClass(4, 'someclass')).toBe false
describe "when soft wrapping is enabled", ->
beforeEach ->
editor.setText "a line that wraps, ok"
editor.setSoftWrap(true)
node.style.width = 16 * charWidth + 'px'
component.measureScrollView()
it "applies decoration only to the first row when marker range does not wrap", ->
marker = editor.displayBuffer.markBufferRange([[0, 0], [0, 0]])
editor.addDecorationForMarker(marker, type: 'gutter', class: 'someclass')
waitsFor -> not component.decorationChangedImmediate?
runs ->
expect(lineNumberHasClass(2, 'someclass')).toBe false
expect(lineNumberHasClass(3, 'someclass')).toBe true
expect(lineNumberHasClass(4, 'someclass')).toBe true
expect(lineNumberHasClass(5, 'someclass')).toBe false
expect(lineNumberHasClass(0, 'someclass')).toBe true
expect(lineNumberHasClass(1, 'someclass')).toBe false
editor.getBuffer().deleteRows(0, 1)
it "applies decoration to both rows when marker wraps", ->
marker = editor.displayBuffer.markBufferRange([[0, 0], [0, Infinity]])
editor.addDecorationForMarker(marker, type: 'gutter', class: 'someclass')
waitsFor -> not component.decorationChangedImmediate?
runs ->
expect(lineNumberHasClass(0, 'someclass')).toBe false
expect(lineNumberHasClass(0, 'someclass')).toBe true
expect(lineNumberHasClass(1, 'someclass')).toBe true
expect(lineNumberHasClass(2, 'someclass')).toBe true
expect(lineNumberHasClass(3, 'someclass')).toBe false
it "removes line number classes when a decoration's marker is invalidated", ->
editor.getBuffer().insert([3, 2], 'n')
waitsFor -> not component.decorationChangedImmediate?
runs ->
expect(marker.isValid()).toBe false
expect(lineNumberHasClass(1, 'someclass')).toBe false
expect(lineNumberHasClass(2, 'someclass')).toBe false
expect(lineNumberHasClass(3, 'someclass')).toBe false
expect(lineNumberHasClass(4, 'someclass')).toBe false
editor.getBuffer().undo()
waitsFor -> not component.decorationChangedImmediate?
runs ->
expect(marker.isValid()).toBe true
expect(lineNumberHasClass(1, 'someclass')).toBe false
expect(lineNumberHasClass(2, 'someclass')).toBe true
expect(lineNumberHasClass(3, 'someclass')).toBe true
expect(lineNumberHasClass(4, 'someclass')).toBe false
it "removes the classes and unsubscribes from the marker when decoration is removed", ->
editor.removeDecorationForMarker(marker, decoration)
waitsFor -> not component.decorationChangedImmediate?
runs ->
expect(lineNumberHasClass(1, 'someclass')).toBe false
expect(lineNumberHasClass(2, 'someclass')).toBe false
expect(lineNumberHasClass(3, 'someclass')).toBe false
expect(lineNumberHasClass(4, 'someclass')).toBe false
editor.getBuffer().insert([0, 0], '\n')
waitsFor -> not component.decorationChangedImmediate?
runs ->
expect(lineNumberHasClass(2, 'someclass')).toBe false
expect(lineNumberHasClass(3, 'someclass')).toBe false
it "removes the line number classes when the decoration's marker is destroyed", ->
marker.destroy()
waitsFor -> not component.decorationChangedImmediate?
runs ->
expect(lineNumberHasClass(1, 'someclass')).toBe false
expect(lineNumberHasClass(2, 'someclass')).toBe false
expect(lineNumberHasClass(3, 'someclass')).toBe false
expect(lineNumberHasClass(4, 'someclass')).toBe false
describe "cursor rendering", ->
it "renders the currently visible cursors, translated relative to the scroll position", ->
@@ -746,12 +844,12 @@ describe "EditorComponent", ->
expect(region3Rect.left).toBe scrollViewClientLeft + 0
expect(region3Rect.width).toBe 10 * charWidth
it "does not render empty selections unless they are the first selection (to prevent a Chromium rendering artifact caused by removing it)", ->
it "does not render empty selections", ->
editor.addSelectionForBufferRange([[2, 2], [2, 2]])
expect(editor.getSelection(0).isEmpty()).toBe true
expect(editor.getSelection(1).isEmpty()).toBe true
expect(node.querySelectorAll('.selection').length).toBe 1
expect(node.querySelectorAll('.selection').length).toBe 0
it "updates selections when the line height changes", ->
editor.setSelectedBufferRange([[1, 6], [1, 10]])
@@ -773,6 +871,99 @@ describe "EditorComponent", ->
expect(selectionNode.offsetTop).toBe editor.getLineHeightInPixels()
expect(selectionNode.offsetLeft).toBe editor.pixelPositionForScreenPosition([1, 6]).left
describe "highlight decoration rendering", ->
[marker, decoration, scrollViewClientLeft] = []
beforeEach ->
scrollViewClientLeft = node.querySelector('.scroll-view').getBoundingClientRect().left
marker = editor.displayBuffer.markBufferRange([[2, 13], [3, 15]], invalidate: 'inside')
decoration = {type: 'highlight', class: 'test-highlight'}
editor.addDecorationForMarker(marker, decoration)
waitsFor -> not component.decorationChangedImmediate?
it "does not render highlights for off-screen lines until they come on-screen", ->
node.style.height = 2.5 * lineHeightInPixels + 'px'
component.measureScrollView()
marker = editor.displayBuffer.markBufferRange([[9, 2], [9, 4]], invalidate: 'inside')
editor.addDecorationForMarker(marker, type: 'highlight', class: 'some-highlight')
waitsFor -> not component.decorationChangedImmediate?
runs ->
# Should not be rendering range containing the marker
expect(component.getRenderedRowRange()[1]).toBeLessThan 9
regions = node.querySelectorAll('.some-highlight .region')
# Nothing when outside the rendered row range
expect(regions.length).toBe 0
verticalScrollbarNode.scrollTop = 3.5 * lineHeightInPixels
verticalScrollbarNode.dispatchEvent(new UIEvent('scroll'))
regions = node.querySelectorAll('.some-highlight .region')
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'
it "renders highlights decoration's marker is added", ->
regions = node.querySelectorAll('.test-highlight .region')
expect(regions.length).toBe 2
it "removes highlights when a decoration is removed", ->
editor.removeDecorationForMarker(marker, decoration)
waitsFor -> not component.decorationChangedImmediate?
runs ->
regions = node.querySelectorAll('.test-highlight .region')
expect(regions.length).toBe 0
it "does not render a highlight that is within a fold", ->
editor.foldBufferRow(1)
waitsFor -> not component.decorationChangedImmediate?
runs ->
expect(node.querySelectorAll('.test-highlight').length).toBe 0
it "moves rendered highlights when the marker moves", ->
regionStyle = node.querySelector('.test-highlight .region').style
originalTop = parseInt(regionStyle.top)
editor.getBuffer().insert([0, 0], '\n')
regionStyle = node.querySelector('.test-highlight .region').style
newTop = parseInt(regionStyle.top)
expect(newTop).toBe originalTop + lineHeightInPixels
it "removes highlights when a decoration's marker is destroyed", ->
marker.destroy()
waitsFor -> not component.decorationChangedImmediate?
runs ->
regions = node.querySelectorAll('.test-highlight .region')
expect(regions.length).toBe 0
it "only renders highlights when a decoration's marker is valid", ->
editor.getBuffer().insert([3, 2], 'n')
waitsFor -> not component.decorationChangedImmediate?
runs ->
expect(marker.isValid()).toBe false
regions = node.querySelectorAll('.test-highlight .region')
expect(regions.length).toBe 0
editor.getBuffer().undo()
waitsFor -> not component.decorationChangedImmediate?
runs ->
expect(marker.isValid()).toBe true
regions = node.querySelectorAll('.test-highlight .region')
expect(regions.length).toBe 2
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)
@@ -815,7 +1006,7 @@ describe "EditorComponent", ->
expect(inputNode.offsetTop).toBe 0
expect(inputNode.offsetLeft).toBe 0
describe "mouse interactions", ->
describe "mouse interactions on the scrollView", ->
linesNode = null
beforeEach ->
@@ -906,12 +1097,110 @@ describe "EditorComponent", ->
nextAnimationFrame()
expect(editor.getSelectedScreenRange()).toEqual [[2, 4], [6, 8]]
clientCoordinatesForScreenPosition = (screenPosition) ->
positionOffset = editor.pixelPositionForScreenPosition(screenPosition)
scrollViewClientRect = node.querySelector('.scroll-view').getBoundingClientRect()
clientX = scrollViewClientRect.left + positionOffset.left - editor.getScrollLeft()
clientY = scrollViewClientRect.top + positionOffset.top - editor.getScrollTop()
{clientX, clientY}
describe "when a line is folded", ->
beforeEach ->
editor.foldBufferRow 4
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
describe "mouse interactions on the gutter", ->
gutterNode = null
beforeEach ->
gutterNode = node.querySelector('.gutter')
describe "when the gutter is clicked", ->
it "moves the cursor to the beginning of the clicked row", ->
gutterNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenRowInGutter(4)))
expect(editor.getCursorScreenPosition()).toEqual [4, 0]
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]]
describe "when the gutter is clicked and dragged", ->
beforeEach ->
delayAnimationFrames = true
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)))
expect(editor.getSelectedScreenRange()).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)))
gutterNode.dispatchEvent(buildMouseEvent('mousemove', clientCoordinatesForScreenRowInGutter(2)))
nextAnimationFrame()
gutterNode.dispatchEvent(buildMouseEvent('mouseup', clientCoordinatesForScreenRowInGutter(2)))
expect(editor.getSelectedScreenRange()).toEqual [[2, 0], [7, 0]]
describe "when the gutter is shift-clicked and dragged", ->
beforeEach ->
delayAnimationFrames = true
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()
expect(editor.getSelectedScreenRange()).toEqual [[3, 4], [9, 0]]
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()
expect(editor.getSelectedScreenRange()).toEqual [[4, 4], [6, 0]]
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()
expect(editor.getSelectedScreenRange()).toEqual [[3, 4], [9, 0]]
describe "focus handling", ->
inputNode = null
@@ -1226,6 +1515,64 @@ describe "EditorComponent", ->
node.dispatchEvent(buildTextInputEvent(data: 'x', target: inputNode))
expect(editor.lineForBufferRow(0)).toBe 'var quicksort = function () {'
describe "when IME composition is used to insert international characters", ->
buildIMECompositionEvent = (event, {data}={}) ->
event = new Event(event)
event.data = data
Object.defineProperty(event, 'target', get: -> inputNpde)
event
describe "when nothing is selected", ->
it "inserts the chosen completion", ->
node.dispatchEvent(buildIMECompositionEvent('compositionstart'))
node.dispatchEvent(buildIMECompositionEvent('compositionupdate', data: 's'))
expect(editor.lineForBufferRow(0)).toBe 'svar quicksort = function () {'
node.dispatchEvent(buildIMECompositionEvent('compositionupdate', data: 'sd'))
expect(editor.lineForBufferRow(0)).toBe 'sdvar quicksort = function () {'
node.dispatchEvent(buildIMECompositionEvent('compositionend'))
node.dispatchEvent(buildTextInputEvent(data: '速度', target: inputNode))
expect(editor.lineForBufferRow(0)).toBe '速度var quicksort = function () {'
it "reverts back to the original text when the completion helper is dismissed", ->
node.dispatchEvent(buildIMECompositionEvent('compositionstart'))
node.dispatchEvent(buildIMECompositionEvent('compositionupdate', data: 's'))
expect(editor.lineForBufferRow(0)).toBe 'svar quicksort = function () {'
node.dispatchEvent(buildIMECompositionEvent('compositionupdate', data: 'sd'))
expect(editor.lineForBufferRow(0)).toBe 'sdvar quicksort = function () {'
node.dispatchEvent(buildIMECompositionEvent('compositionend'))
expect(editor.lineForBufferRow(0)).toBe 'var quicksort = function () {'
describe "when a string is selected", ->
beforeEach ->
editor.setSelectedBufferRange [[0, 4], [0, 9]] # select 'quick'
it "inserts the chosen completion", ->
node.dispatchEvent(buildIMECompositionEvent('compositionstart'))
node.dispatchEvent(buildIMECompositionEvent('compositionupdate', data: 's'))
expect(editor.lineForBufferRow(0)).toBe 'var ssort = function () {'
node.dispatchEvent(buildIMECompositionEvent('compositionupdate', data: 'sd'))
expect(editor.lineForBufferRow(0)).toBe 'var sdsort = function () {'
node.dispatchEvent(buildIMECompositionEvent('compositionend'))
node.dispatchEvent(buildTextInputEvent(data: '速度', target: inputNode))
expect(editor.lineForBufferRow(0)).toBe 'var 速度sort = function () {'
it "reverts back to the original text when the completion helper is dismissed", ->
node.dispatchEvent(buildIMECompositionEvent('compositionstart'))
node.dispatchEvent(buildIMECompositionEvent('compositionupdate', data: 's'))
expect(editor.lineForBufferRow(0)).toBe 'var ssort = function () {'
node.dispatchEvent(buildIMECompositionEvent('compositionupdate', data: 'sd'))
expect(editor.lineForBufferRow(0)).toBe 'var sdsort = function () {'
node.dispatchEvent(buildIMECompositionEvent('compositionend'))
expect(editor.lineForBufferRow(0)).toBe 'var quicksort = function () {'
describe "commands", ->
describe "editor:consolidate-selections", ->
it "consolidates selections on the editor model, aborting the key binding if there is only one selection", ->
@@ -1239,26 +1586,67 @@ describe "EditorComponent", ->
expect(event.abortKeyBinding).toHaveBeenCalled()
describe "hiding and showing the editor", ->
describe "when fontSize, fontFamily, or lineHeight changes while the editor is hidden", ->
it "does not attempt to measure the lineHeight and defaultCharWidth until the editor becomes visible again", ->
describe "when the lineHeight changes while the editor is hidden", ->
it "does not attempt to measure the lineHeightInPixels until the editor becomes visible again", ->
wrapperView.hide()
initialLineHeightInPixels = editor.getLineHeightInPixels()
component.setLineHeight(2)
expect(editor.getLineHeightInPixels()).toBe initialLineHeightInPixels
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()
component.setLineHeight(2)
expect(editor.getLineHeightInPixels()).toBe initialLineHeightInPixels
expect(editor.getDefaultCharWidth()).toBe initialCharWidth
component.setFontSize(22)
expect(editor.getLineHeightInPixels()).toBe initialLineHeightInPixels
expect(editor.getDefaultCharWidth()).toBe initialCharWidth
component.setFontFamily('monospace')
expect(editor.getLineHeightInPixels()).toBe initialLineHeightInPixels
expect(editor.getDefaultCharWidth()).toBe initialCharWidth
wrapperView.show()
expect(editor.getLineHeightInPixels()).not.toBe initialLineHeightInPixels
expect(editor.getDefaultCharWidth()).not.toBe initialCharWidth
it "does not re-measure character widths until the editor is shown again", ->
wrapperView.hide()
component.setFontSize(22)
wrapperView.show()
editor.setCursorBufferPosition([0, Infinity])
cursorLeft = node.querySelector('.cursor').getBoundingClientRect().left
line0Right = node.querySelector('.line').getBoundingClientRect().right
expect(cursorLeft).toBe line0Right
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
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])
cursorLeft = node.querySelector('.cursor').getBoundingClientRect().left
line0Right = node.querySelector('.line').getBoundingClientRect().right
expect(cursorLeft).toBe line0Right
describe "when lines are changed while the editor is hidden", ->
it "does not measure new characters until the editor is shown again", ->
editor.setText('')
@@ -1268,6 +1656,21 @@ describe "EditorComponent", ->
wrapperView.show()
expect(node.querySelector('.cursor').style['-webkit-transform']).toBe "translate3d(#{9 * charWidth}px, 0px, 0px)"
describe "when the editor component is resized", ->
it "updates the component based on a new size", ->
editor.setSoftWrap(true)
newHeight = 4 * editor.getLineHeightInPixels() + "px"
expect(newHeight).toBeLessThan node.style.height
node.style.height = newHeight
advanceClock(component.scrollViewMeasurementInterval)
expect(node.querySelectorAll('.line')).toHaveLength(4 + lineOverdrawMargin + 1)
gutterWidth = node.querySelector('.gutter').offsetWidth
node.style.width = gutterWidth + 14 * charWidth + 'px'
advanceClock(component.scrollViewMeasurementInterval)
expect(node.querySelector('.line').textContent).toBe "var quicksort "
buildMouseEvent = (type, properties...) ->
properties = extend({bubbles: true, cancelable: true}, properties...)
event = new MouseEvent(type, properties)
@@ -1276,3 +1679,17 @@ describe "EditorComponent", ->
Object.defineProperty(event, 'target', get: -> properties.target)
Object.defineProperty(event, 'srcObject', get: -> properties.target)
event
clientCoordinatesForScreenPosition = (screenPosition) ->
positionOffset = editor.pixelPositionForScreenPosition(screenPosition)
scrollViewClientRect = node.querySelector('.scroll-view').getBoundingClientRect()
clientX = scrollViewClientRect.left + positionOffset.left - editor.getScrollLeft()
clientY = scrollViewClientRect.top + positionOffset.top - editor.getScrollTop()
{clientX, clientY}
clientCoordinatesForScreenRowInGutter = (screenRow) ->
positionOffset = editor.pixelPositionForScreenPosition([screenRow, 1])
gutterClientRect = node.querySelector('.gutter').getBoundingClientRect()
clientX = gutterClientRect.left + positionOffset.left - editor.getScrollLeft()
clientY = gutterClientRect.top + positionOffset.top - editor.getScrollTop()
{clientX, clientY}
+52 -103
Ver Arquivo
@@ -1831,6 +1831,32 @@ describe "Editor", ->
expect(buffer.lineForRow(1)).toBe ' var sort = function(it) {'
expect(buffer.lineForRow(2)).toBe 'if (items.length <= 1) return items;'
describe '.deleteToEndOfLine()', ->
describe 'when no text is selected', ->
it 'deletes all text between the cursor and the end of the line', ->
editor.setCursorBufferPosition([1, 24])
editor.addCursorAtBufferPosition([2, 5])
[cursor1, cursor2] = editor.getCursors()
editor.deleteToEndOfLine()
expect(buffer.lineForRow(1)).toBe ' var sort = function(it'
expect(buffer.lineForRow(2)).toBe ' i'
expect(cursor1.getBufferPosition()).toEqual [1, 24]
expect(cursor2.getBufferPosition()).toEqual [2, 5]
describe 'when at the end of the line', ->
it 'deletes the next newline', ->
editor.setCursorBufferPosition([1, 30])
editor.deleteToEndOfLine()
expect(buffer.lineForRow(1)).toBe ' var sort = function(items) { if (items.length <= 1) return items;'
describe 'when text is selected', ->
it 'deletes only the text in the selection', ->
editor.setSelectedBufferRanges([[[1, 24], [1, 27]], [[2, 0], [2, 4]]])
editor.deleteToEndOfLine()
expect(buffer.lineForRow(1)).toBe ' var sort = function(it) {'
expect(buffer.lineForRow(2)).toBe 'if (items.length <= 1) return items;'
describe ".deleteToBeginningOfLine()", ->
describe "when no text is selected", ->
it "deletes all text between the cursor and the beginning of the line", ->
@@ -3244,113 +3270,36 @@ describe "Editor", ->
expect(editor.getScrollTop()).toBe 0
expect(editor.getCursorBufferPosition().row).toBe 0
describe "decorations", ->
decoration = null
beforeEach ->
decoration = {type: 'gutter', class: 'one'}
describe ".selectPageUp/Down()", ->
it "selects one screen height of text up or down", ->
editor.manageScrollPosition = true
it "can add decorations to buffer rows and remove them", ->
editor.addDecorationToBufferRow(2, decoration)
editor.addDecorationToBufferRow(2, decoration)
editor.setLineHeightInPixels(10)
editor.setHeight(50)
expect(editor.getScrollHeight()).toBe 130
expect(editor.getCursorBufferPosition().row).toBe 0
decorations = editor.decorationsForBufferRow(2)
expect(decorations).toHaveLength 1
expect(decorations).toContain decoration
editor.selectPageDown()
expect(editor.getScrollTop()).toBe 30
expect(editor.getSelectedBufferRanges()).toEqual [[[0,0], [5,0]]]
editor.removeDecorationFromBufferRow(2, decoration)
decorations = editor.decorationsForBufferRow(2)
expect(decorations).toHaveLength 0
editor.selectPageDown()
expect(editor.getScrollTop()).toBe 80
expect(editor.getSelectedBufferRanges()).toEqual [[[0,0], [10,0]]]
it "can add decorations to buffer row ranges and remove them", ->
editor.addDecorationToBufferRowRange(2, 4, decoration)
expect(editor.decorationsForBufferRow 2).toContain decoration
expect(editor.decorationsForBufferRow 3).toContain decoration
expect(editor.decorationsForBufferRow 4).toContain decoration
editor.selectPageDown()
expect(editor.getScrollTop()).toBe 80
expect(editor.getSelectedBufferRanges()).toEqual [[[0,0], [12,2]]]
editor.removeDecorationFromBufferRowRange(3, 5, decoration)
expect(editor.decorationsForBufferRow 2).toContain decoration
expect(editor.decorationsForBufferRow 3).not.toContain decoration
expect(editor.decorationsForBufferRow 4).not.toContain decoration
editor.moveCursorToBottom()
editor.selectPageUp()
expect(editor.getScrollTop()).toBe 50
expect(editor.getSelectedBufferRanges()).toEqual [[[7,0], [12,2]]]
it "can add decorations associated with markers and remove them", ->
marker = editor.displayBuffer.markBufferRange([[2, 13], [3, 15]], class: 'my-marker', invalidate: 'inside')
editor.selectPageUp()
expect(editor.getScrollTop()).toBe 0
expect(editor.getSelectedBufferRanges()).toEqual [[[2,0], [12,2]]]
editor.addDecorationForMarker(marker, decoration)
expect(editor.decorationsForBufferRow 1).not.toContain decoration
expect(editor.decorationsForBufferRow 2).toContain decoration
expect(editor.decorationsForBufferRow 3).toContain decoration
expect(editor.decorationsForBufferRow 4).not.toContain decoration
editor.getBuffer().insert([0, 0], '\n')
expect(editor.decorationsForBufferRow 2).not.toContain decoration
expect(editor.decorationsForBufferRow 3).toContain decoration
expect(editor.decorationsForBufferRow 4).toContain decoration
expect(editor.decorationsForBufferRow 5).not.toContain decoration
editor.getBuffer().insert([4, 2], 'n')
expect(editor.decorationsForBufferRow 2).not.toContain decoration
expect(editor.decorationsForBufferRow 3).not.toContain decoration
expect(editor.decorationsForBufferRow 4).not.toContain decoration
expect(editor.decorationsForBufferRow 5).not.toContain decoration
editor.getBuffer().undo()
expect(editor.decorationsForBufferRow 2).not.toContain decoration
expect(editor.decorationsForBufferRow 3).toContain decoration
expect(editor.decorationsForBufferRow 4).toContain decoration
expect(editor.decorationsForBufferRow 5).not.toContain decoration
editor.removeDecorationForMarker(marker, decoration)
expect(editor.decorationsForBufferRow 2).not.toContain decoration
expect(editor.decorationsForBufferRow 3).not.toContain decoration
expect(editor.decorationsForBufferRow 4).not.toContain decoration
expect(editor.decorationsForBufferRow 5).not.toContain decoration
describe "decorationsForBufferRow", ->
one = {type: 'one', class: 'one'}
two = {type: 'two', class: 'two'}
typeless = {class: 'typeless'}
beforeEach ->
editor.addDecorationToBufferRow(2, one)
editor.addDecorationToBufferRow(2, two)
editor.addDecorationToBufferRow(2, typeless)
it "returns all decorations with no decorationType specified", ->
decorations = editor.decorationsForBufferRow(2)
expect(decorations).toContain one
expect(decorations).toContain two
expect(decorations).toContain typeless
it "returns typeless decorations with all decorationTypes", ->
decorations = editor.decorationsForBufferRow(2, 'one')
expect(decorations).toContain one
expect(decorations).not.toContain two
expect(decorations).toContain typeless
describe "decorationsForBufferRowRange", ->
one = {type: 'one', class: 'one'}
two = {type: 'two', class: 'two'}
typeless = {class: 'typeless'}
it "returns an object of decorations based on the decorationType", ->
editor.addDecorationToBufferRow(2, one)
editor.addDecorationToBufferRow(3, one)
editor.addDecorationToBufferRow(5, one)
editor.addDecorationToBufferRow(3, two)
editor.addDecorationToBufferRow(4, two)
editor.addDecorationToBufferRow(3, typeless)
editor.addDecorationToBufferRow(5, typeless)
decorations = editor.decorationsForBufferRowRange(2, 5, 'one')
expect(decorations[2]).toContain one
expect(decorations[3]).toContain one
expect(decorations[3]).not.toContain two
expect(decorations[3]).toContain typeless
expect(decorations[4]).toHaveLength 0
expect(decorations[5]).toContain one
expect(decorations[5]).toContain typeless
editor.selectPageUp()
expect(editor.getScrollTop()).toBe 0
expect(editor.getSelectedBufferRanges()).toEqual [[[0,0], [12,2]]]
+5 -5
Ver Arquivo
@@ -1611,7 +1611,7 @@ describe "EditorView", ->
editorView.attachToDom()
expect(atom.config.get("editor.showInvisibles")).toBeFalsy()
expect(editorView.renderedLines.find('.line').text()).toBe " a line with tabs and spaces "
expect(editorView.renderedLines.find('.line').text()).toBe " a line with tabs and spaces "
atom.config.set("editor.showInvisibles", true)
space = editorView.invisibles?.space
@@ -1620,10 +1620,10 @@ describe "EditorView", ->
expect(tab).toBeTruthy()
eol = editorView.invisibles?.eol
expect(eol).toBeTruthy()
expect(editorView.renderedLines.find('.line').text()).toBe "#{space}a line with tabs#{tab} and spaces#{space}#{eol}"
expect(editorView.renderedLines.find('.line').text()).toBe "#{space}a line with tabs#{tab}and spaces#{space}#{eol}"
atom.config.set("editor.showInvisibles", false)
expect(editorView.renderedLines.find('.line').text()).toBe " a line with tabs and spaces "
expect(editorView.renderedLines.find('.line').text()).toBe " a line with tabs and spaces "
it "displays newlines as their own token outside of the other tokens scope", ->
editorView.setShowInvisibles(true)
@@ -1636,7 +1636,7 @@ describe "EditorView", ->
editorView.attachToDom()
atom.config.set("editor.showInvisibles", true)
atom.config.set("editor.invisibles", eol: ";", space: "_", tab: "tab")
expect(editorView.find(".line:first").text()).toBe "_tab _;"
expect(editorView.find(".line:first").text()).toBe "_tab_;"
it "displays trailing carriage return using a visible non-empty value", ->
editor.setText "a line that ends with a carriage return\r\n"
@@ -2082,7 +2082,7 @@ describe "EditorView", ->
tab = miniEditor.invisibles?.tab
expect(tab).toBeTruthy()
miniEditor.getEditor().setText(" a line with tabs\tand spaces ")
expect(miniEditor.renderedLines.find('.line').text()).toBe "#{space}a line with tabs#{tab} and spaces#{space}"
expect(miniEditor.renderedLines.find('.line').text()).toBe "#{space}a line with tabs#{tab}and spaces#{space}"
it "doesn't show the indent guide", ->
atom.config.set "editor.showIndentGuide", true
+6 -1
Ver Arquivo
@@ -33,7 +33,12 @@ $(window).on 'unload', ->
$('html,body').css('overflow', 'auto')
jasmine.getEnv().addEqualityTester(_.isEqual) # Use underscore's definition of equality for toEqual assertions
jasmine.getEnv().defaultTimeoutInterval = 5000
if process.platform is 'win32' and process.env.JANKY_SHA1
# Use longer timeout on Windows CI
jasmine.getEnv().defaultTimeoutInterval = 30000
else
jasmine.getEnv().defaultTimeoutInterval = 5000
specPackageName = null
specPackagePath = null
+67
Ver Arquivo
@@ -344,6 +344,73 @@ describe "TokenizedBuffer", ->
expect(tokenizedBuffer.lineForScreenRow(2).text).toBe "#{tabAsSpaces} buy()#{tabAsSpaces}while supply > demand"
it "aligns the hard tabs to the correct tab stop column", ->
buffer.setText """
1\t2 \t3\t4
12\t3 \t4\t5
123\t4 \t5\t6
"""
tokenizedBuffer.setTabLength(4)
fullyTokenize(tokenizedBuffer)
expect(tokenizedBuffer.lineForScreenRow(0).text).toBe "1 2 3 4"
expect(tokenizedBuffer.lineForScreenRow(0).tokens[1].bufferDelta).toBe 1
expect(tokenizedBuffer.lineForScreenRow(0).tokens[1].screenDelta).toBe 3
expect(tokenizedBuffer.lineForScreenRow(1).text).toBe "12 3 4 5"
expect(tokenizedBuffer.lineForScreenRow(1).tokens[1].bufferDelta).toBe 1
expect(tokenizedBuffer.lineForScreenRow(1).tokens[1].screenDelta).toBe 2
expect(tokenizedBuffer.lineForScreenRow(2).text).toBe "123 4 5 6"
expect(tokenizedBuffer.lineForScreenRow(2).tokens[1].bufferDelta).toBe 1
expect(tokenizedBuffer.lineForScreenRow(2).tokens[1].screenDelta).toBe 1
tokenizedBuffer.setTabLength(3)
fullyTokenize(tokenizedBuffer)
expect(tokenizedBuffer.lineForScreenRow(0).text).toBe "1 2 3 4"
expect(tokenizedBuffer.lineForScreenRow(0).tokens[1].bufferDelta).toBe 1
expect(tokenizedBuffer.lineForScreenRow(0).tokens[1].screenDelta).toBe 2
expect(tokenizedBuffer.lineForScreenRow(1).text).toBe "12 3 4 5"
expect(tokenizedBuffer.lineForScreenRow(1).tokens[1].bufferDelta).toBe 1
expect(tokenizedBuffer.lineForScreenRow(1).tokens[1].screenDelta).toBe 1
expect(tokenizedBuffer.lineForScreenRow(2).text).toBe "123 4 5 6"
expect(tokenizedBuffer.lineForScreenRow(2).tokens[1].bufferDelta).toBe 1
expect(tokenizedBuffer.lineForScreenRow(2).tokens[1].screenDelta).toBe 3
tokenizedBuffer.setTabLength(2)
fullyTokenize(tokenizedBuffer)
expect(tokenizedBuffer.lineForScreenRow(0).text).toBe "1 2 3 4"
expect(tokenizedBuffer.lineForScreenRow(0).tokens[1].bufferDelta).toBe 1
expect(tokenizedBuffer.lineForScreenRow(0).tokens[1].screenDelta).toBe 1
expect(tokenizedBuffer.lineForScreenRow(1).text).toBe "12 3 4 5"
expect(tokenizedBuffer.lineForScreenRow(1).tokens[1].bufferDelta).toBe 1
expect(tokenizedBuffer.lineForScreenRow(1).tokens[1].screenDelta).toBe 2
expect(tokenizedBuffer.lineForScreenRow(2).text).toBe "123 4 5 6"
expect(tokenizedBuffer.lineForScreenRow(2).tokens[1].bufferDelta).toBe 1
expect(tokenizedBuffer.lineForScreenRow(2).tokens[1].screenDelta).toBe 1
tokenizedBuffer.setTabLength(1)
fullyTokenize(tokenizedBuffer)
expect(tokenizedBuffer.lineForScreenRow(0).text).toBe "1 2 3 4"
expect(tokenizedBuffer.lineForScreenRow(0).tokens[1].bufferDelta).toBe 1
expect(tokenizedBuffer.lineForScreenRow(0).tokens[1].screenDelta).toBe 1
expect(tokenizedBuffer.lineForScreenRow(1).text).toBe "12 3 4 5"
expect(tokenizedBuffer.lineForScreenRow(1).tokens[1].bufferDelta).toBe 1
expect(tokenizedBuffer.lineForScreenRow(1).tokens[1].screenDelta).toBe 1
expect(tokenizedBuffer.lineForScreenRow(2).text).toBe "123 4 5 6"
expect(tokenizedBuffer.lineForScreenRow(2).tokens[1].bufferDelta).toBe 1
expect(tokenizedBuffer.lineForScreenRow(2).tokens[1].screenDelta).toBe 1
describe "when the buffer contains surrogate pairs", ->
beforeEach ->
waitsForPromise ->
+2 -2
Ver Arquivo
@@ -196,14 +196,14 @@ describe "WorkspaceView", ->
atom.workspaceView.height(200)
atom.workspaceView.attachToDom()
rightEditorView = atom.workspaceView.getActiveView()
rightEditorView.getEditor().setText(" \t ")
rightEditorView.getEditor().setText("\t ")
leftEditorView = rightEditorView.splitLeft()
expect(rightEditorView.find(".line:first").text()).toBe " "
expect(leftEditorView.find(".line:first").text()).toBe " "
{invisibles} = rightEditorView.component.state
{space, tab, eol} = invisibles
withInvisiblesShowing = "#{space}#{tab} #{space}#{eol}"
withInvisiblesShowing = "#{tab} #{space}#{space}#{eol}"
atom.workspaceView.trigger "window:toggle-invisibles"
expect(rightEditorView.find(".line:first").text()).toBe withInvisiblesShowing
+4 -1
Ver Arquivo
@@ -34,7 +34,10 @@ CursorsComponent = React.createClass
shouldComponentUpdate: (newProps, newState) ->
not newState.blinkOff is @state.blinkOff or
not isEqualForProperties(newProps, @props, 'cursorScreenRanges', 'scrollTop', 'scrollLeft', 'lineHeightInPixels', 'defaultCharWidth')
not isEqualForProperties(newProps, @props,
'cursorScreenRanges', 'scrollTop', 'scrollLeft', 'lineHeightInPixels',
'defaultCharWidth', 'scopedCharacterWidthsChangeCount'
)
componentWillUpdate: (newProps) ->
@pauseCursorBlinking() if @props.cursorScreenRanges and not isEqual(newProps.cursorScreenRanges, @props.cursorScreenRanges)
+81 -82
Ver Arquivo
@@ -34,6 +34,7 @@ class DisplayBuffer extends Model
horizontalScrollMargin: 6
horizontalScrollbarHeight: 15
verticalScrollbarWidth: 15
scopedCharacterWidthsChangeCount: 0
constructor: ({tabLength, @editorWidthInChars, @tokenizedBuffer, buffer}={}) ->
super
@@ -43,7 +44,7 @@ class DisplayBuffer extends Model
@charWidthsByScope = {}
@markers = {}
@foldsByMarkerId = {}
@decorations = {}
@decorationsByMarkerId = {}
@decorationMarkerSubscriptions = {}
@updateAllScreenLines()
@createFoldForMarker(marker) for marker in @buffer.findMarkers(@getFoldMarkerAttributes())
@@ -222,9 +223,11 @@ class DisplayBuffer extends Model
setScopedCharWidth: (scopeNames, char, width) ->
@getScopedCharWidths(scopeNames)[char] = width
@emit 'character-widths-changed', @scopedCharacterWidthsChangeCount++
setScopedCharWidths: (scopeNames, charWidths) ->
_.extend(@getScopedCharWidths(scopeNames), charWidths)
@emit 'character-widths-changed', @scopedCharacterWidthsChangeCount++
clearScopedCharWidths: ->
@charWidthsByScope = {}
@@ -254,15 +257,23 @@ class DisplayBuffer extends Model
{start, end} = selection.getScreenRange()
@intersectsVisibleRowRange(start.row, end.row + 1)
scrollToScreenRange: (screenRange) ->
scrollToScreenRange: (screenRange, options) ->
verticalScrollMarginInPixels = @getVerticalScrollMargin() * @getLineHeightInPixels()
horizontalScrollMarginInPixels = @getHorizontalScrollMargin() * @getDefaultCharWidth()
{top, left, height, width} = @pixelRectForScreenRange(screenRange)
bottom = top + height
right = left + width
desiredScrollTop = top - verticalScrollMarginInPixels
desiredScrollBottom = bottom + verticalScrollMarginInPixels
if options?.center
desiredScrollCenter = top + height / 2
unless @getScrollTop() < desiredScrollCenter < @getScrollBottom()
desiredScrollTop = desiredScrollCenter - @getHeight() / 2
desiredScrollBottom = desiredScrollCenter + @getHeight() / 2
else
desiredScrollTop = top - verticalScrollMarginInPixels
desiredScrollBottom = bottom + verticalScrollMarginInPixels
desiredScrollLeft = left - horizontalScrollMarginInPixels
desiredScrollRight = right + horizontalScrollMarginInPixels
@@ -276,11 +287,11 @@ class DisplayBuffer extends Model
else if desiredScrollRight > @getScrollRight()
@setScrollRight(desiredScrollRight)
scrollToScreenPosition: (screenPosition) ->
@scrollToScreenRange(new Range(screenPosition, screenPosition))
scrollToScreenPosition: (screenPosition, options) ->
@scrollToScreenRange(new Range(screenPosition, screenPosition), options)
scrollToBufferPosition: (bufferPosition) ->
@scrollToScreenPosition(@screenPositionForBufferPosition(bufferPosition))
scrollToBufferPosition: (bufferPosition, options) ->
@scrollToScreenPosition(@screenPositionForBufferPosition(bufferPosition), options)
pixelRectForScreenRange: (screenRange) ->
if screenRange.end.row > screenRange.start.row
@@ -718,51 +729,19 @@ class DisplayBuffer extends Model
rangeForAllLines: ->
new Range([0, 0], @clipScreenPosition([Infinity, Infinity]))
decorationsForBufferRow: (bufferRow, decorationType) ->
decorations = @decorations[bufferRow] ? []
decorations = (dec for dec in decorations when not dec.type? or dec.type is decorationType) if decorationType?
decorations
decorationsForScreenRowRange: (startScreenRow, endScreenRow) ->
decorationsByMarkerId = {}
decorationsForBufferRowRange: (startBufferRow, endBufferRow, decorationType) ->
decorations = {}
for bufferRow in [startBufferRow..endBufferRow]
decorations[bufferRow] = @decorationsForBufferRow(bufferRow, decorationType)
decorations
for marker in @findMarkers(intersectsScreenRowRange: [startScreenRow, endScreenRow])
if decorations = @decorationsByMarkerId[marker.id]
decorationsByMarkerId[marker.id] = decorations
decorationsByMarkerId
addDecorationToBufferRow: (bufferRow, decoration) ->
@decorations[bufferRow] ?= []
for current in @decorations[bufferRow]
return if _.isEqual(current, decoration)
@decorations[bufferRow].push(decoration)
@emit 'decoration-changed', {bufferRow, decoration, action: 'add'}
removeDecorationFromBufferRow: (bufferRow, decorationPattern) ->
return unless decorations = @decorations[bufferRow]
removed = []
i = decorations.length - 1
while i >= 0
if @decorationMatchesPattern(decorations[i], decorationPattern)
removed.push decorations[i]
decorations.splice(i, 1)
i--
delete @decorations[bufferRow] unless @decorations[bufferRow]?
for decoration in removed
@emit 'decoration-changed', {bufferRow, decoration, action: 'remove'}
removed
addDecorationToBufferRowRange: (startBufferRow, endBufferRow, decoration) ->
for bufferRow in [startBufferRow..endBufferRow]
@addDecorationToBufferRow(bufferRow, decoration)
return
removeDecorationFromBufferRowRange: (startBufferRow, endBufferRow, decoration) ->
for bufferRow in [startBufferRow..endBufferRow]
@removeDecorationFromBufferRow(bufferRow, decoration)
return
decorationMatchesType: (decoration, type) ->
if _.isArray(decoration.type)
type in decoration.type
else
type is decoration.type
decorationMatchesPattern: (decoration, decorationPattern) ->
return false unless decoration? and decorationPattern?
@@ -771,43 +750,43 @@ class DisplayBuffer extends Model
true
addDecorationForMarker: (marker, decoration) ->
startRow = marker.getStartBufferPosition().row
endRow = marker.getEndBufferPosition().row
@addDecorationToBufferRowRange(startRow, endRow, decoration)
unless marker?
console.warn 'A null marker cannot be decorated'
return
changedSubscription = @subscribe marker, 'changed', (e) =>
oldStartRow = e.oldHeadBufferPosition.row
oldEndRow = e.oldTailBufferPosition.row
newStartRow = e.newHeadBufferPosition.row
newEndRow = e.newTailBufferPosition.row
marker = @getMarker(marker.id)
@decorationMarkerSubscriptions[marker.id] ?= @subscribe marker, 'destroyed', => @removeAllDecorationsForMarker(marker)
# swap so head is always <= than tail
[oldEndRow, oldStartRow] = [oldStartRow, oldEndRow] if oldStartRow > oldEndRow
[newEndRow, newStartRow] = [newStartRow, newEndRow] if newStartRow > newEndRow
@removeDecorationFromBufferRowRange(oldStartRow, oldEndRow, decoration)
@addDecorationToBufferRowRange(newStartRow, newEndRow, decoration) if e.isValid
destroyedSubscription = @subscribe marker, 'destroyed', (e) =>
@removeDecorationForMarker(marker, decoration)
@decorationMarkerSubscriptions[marker.id] ?= []
@decorationMarkerSubscriptions[marker.id].push {decoration, changedSubscription, destroyedSubscription}
@decorationsByMarkerId[marker.id] ?= []
@decorationsByMarkerId[marker.id].push(decoration)
@emit 'decoration-added', marker, decoration
removeDecorationForMarker: (marker, decorationPattern) ->
unless marker?
console.warn 'A decoration cannot be removed from a null marker'
return
return unless @decorationMarkerSubscriptions[marker.id]?
startRow = marker.getStartBufferPosition().row
endRow = marker.getEndBufferPosition().row
@removeDecorationFromBufferRowRange(startRow, endRow, decorationPattern)
decorations = @decorationsByMarkerId[marker.id]
for i in [decorations.length - 1..0]
decoration = decorations[i]
if @decorationMatchesPattern(decoration, decorationPattern)
decorations.splice(i, 1)
@emit 'decoration-removed', marker, decoration
for subscription in _.clone(@decorationMarkerSubscriptions[marker.id])
if @decorationMatchesPattern(subscription.decoration, decorationPattern)
subscription.changedSubscription.off()
subscription.destroyedSubscription.off()
@decorationMarkerSubscriptions[marker.id] = _.without(@decorationMarkerSubscriptions[marker.id], subscription)
@removedAllMarkerDecorations(marker) if decorations.length is 0
return
removeAllDecorationsForMarker: (marker) ->
decorations = @decorationsByMarkerId[marker.id].slice()
for decoration in decorations
@emit 'decoration-removed', marker, decoration
@removedAllMarkerDecorations(marker)
removedAllMarkerDecorations: (marker) ->
@decorationMarkerSubscriptions[marker.id].off()
delete @decorationsByMarkerId[marker.id]
delete @decorationMarkerSubscriptions[marker.id]
# Retrieves a {DisplayBufferMarker} based on its id.
#
@@ -912,13 +891,34 @@ class DisplayBuffer extends Model
key = 'startRow'
when 'endBufferRow'
key = 'endRow'
when 'startScreenRow'
key = 'startRow'
value = @bufferRowForScreenRow(value)
when 'endScreenRow'
key = 'endRow'
value = @bufferRowForScreenRow(value)
when 'intersectsBufferRowRange'
key = 'intersectsRowRange'
when 'intersectsScreenRowRange'
key = 'intersectsRowRange'
[startRow, endRow] = value
value = [@bufferRowForScreenRow(startRow), @bufferRowForScreenRow(endRow)]
when 'containsBufferRange'
key = 'containsRange'
when 'containsBufferPosition'
key = 'containsPosition'
when 'containedInBufferRange'
key = 'containedInRange'
when 'containedInScreenRange'
key = 'containedInRange'
value = @bufferRangeForScreenRange(value)
when 'intersectsBufferRange'
key = 'intersectsRange'
when 'intersectsScreenRange'
key = 'intersectsRange'
value = @bufferRangeForScreenRange(value)
bufferMarkerParams[key] = value
bufferMarkerParams
findFoldMarker: (attributes) ->
@@ -1052,8 +1052,7 @@ class DisplayBuffer extends Model
@emit 'marker-created', @getMarker(marker.id)
createFoldForMarker: (marker) ->
bufferMarker = new DisplayBufferMarker({bufferMarker: marker, displayBuffer: this})
@addDecorationForMarker(bufferMarker, type: 'gutter', class: 'folded')
@addDecorationForMarker(marker, type: 'gutter', class: 'folded')
new Fold(this, marker)
foldForMarker: (marker) ->
+167 -80
Ver Arquivo
@@ -36,7 +36,10 @@ EditorComponent = React.createClass
scrollSensitivity: 0.4
scrollViewMeasurementRequested: false
measureLineHeightAndDefaultCharWidthWhenShown: false
remeasureCharacterWidthsWhenShown: false
inputEnabled: true
scrollViewMeasurementInterval: 100
scopedCharacterWidthsChangeCount: null
render: ->
{focused, fontSize, lineHeight, fontFamily, showIndentGuide, showInvisibles, visible} = @state
@@ -49,8 +52,11 @@ EditorComponent = React.createClass
renderedRowRange = @getRenderedRowRange()
[renderedStartRow, renderedEndRow] = renderedRowRange
cursorScreenRanges = @getCursorScreenRanges(renderedRowRange)
selectionScreenRanges = @getSelectionScreenRanges(renderedRowRange)
decorations = @getGutterDecorations(renderedRowRange)
decorations = editor.decorationsForScreenRowRange(renderedStartRow, renderedEndRow)
highlightDecorations = @getHighlightDecorations(decorations)
lineDecorations = @getLineDecorations(decorations)
scrollHeight = editor.getScrollHeight()
scrollWidth = editor.getScrollWidth()
scrollTop = editor.getScrollTop()
@@ -73,9 +79,9 @@ EditorComponent = React.createClass
div className: className, style: {fontSize, lineHeight, fontFamily}, tabIndex: -1,
GutterComponent {
ref: 'gutter', editor, renderedRowRange, maxLineNumberDigits, scrollTop,
scrollHeight, lineHeightInPixels, @pendingChanges, mouseWheelScreenRow,
decorations
ref: 'gutter', onMouseDown: @onGutterMouseDown, onWidthChanged: @onGutterWidthChanged,
lineDecorations, defaultCharWidth, editor, renderedRowRange, maxLineNumberDigits, scrollViewHeight,
scrollTop, scrollHeight, lineHeightInPixels, @pendingChanges, mouseWheelScreenRow
}
div ref: 'scrollView', className: 'scroll-view', onMouseDown: @onMouseDown,
@@ -88,13 +94,14 @@ EditorComponent = React.createClass
CursorsComponent {
editor, scrollTop, scrollLeft, cursorScreenRanges, cursorBlinkPeriod, cursorBlinkResumeDelay,
lineHeightInPixels, defaultCharWidth
lineHeightInPixels, defaultCharWidth, @scopedCharacterWidthsChangeCount
}
LinesComponent {
ref: 'lines', editor, lineHeightInPixels, defaultCharWidth,
showIndentGuide, renderedRowRange, @pendingChanges, scrollTop, scrollLeft, @scrollingVertically,
selectionScreenRanges, scrollHeight, scrollWidth, mouseWheelScreenRow, invisibles,
visible, scrollViewHeight
ref: 'lines',
editor, lineHeightInPixels, defaultCharWidth, lineDecorations, highlightDecorations,
showIndentGuide, renderedRowRange, @pendingChanges, scrollTop, scrollLeft,
@scrollingVertically, scrollHeight, scrollWidth, mouseWheelScreenRow, invisibles,
visible, scrollViewHeight, @scopedCharacterWidthsChangeCount
}
ScrollbarComponent
@@ -129,6 +136,10 @@ EditorComponent = React.createClass
height: horizontalScrollbarHeight
width: verticalScrollbarWidth
getPageRows: ->
{editor} = @props
Math.max(1, Math.ceil(editor.getHeight() / editor.getLineHeightInPixels()))
getInitialState: ->
visible: true
@@ -146,6 +157,8 @@ EditorComponent = React.createClass
componentDidMount: ->
{editor} = @props
@scrollViewMeasurementIntervalId = setInterval(@requestScrollViewMeasurement, @scrollViewMeasurementInterval)
@observeEditor()
@listenForDOMEvents()
@listenForCommands()
@@ -162,7 +175,8 @@ EditorComponent = React.createClass
componentWillUnmount: ->
@unsubscribe()
window.removeEventListener('resize', @onWindowResize)
clearInterval(@scrollViewMeasurementIntervalId)
@scrollViewMeasurementIntervalId = null
componentWillUpdate: ->
if @props.editor.isAlive()
@@ -175,6 +189,7 @@ EditorComponent = React.createClass
@updateParentViewFocusedClassIfNeeded(prevState)
@measureScrollbars() if @measuringScrollbars
@measureLineHeightAndCharWidthsIfNeeded(prevState)
@remeasureCharacterWidthsIfNeeded(prevState)
@props.parentView.trigger 'editor:display-updated'
requestUpdate: ->
@@ -215,33 +230,36 @@ EditorComponent = React.createClass
cursorScreenRanges[cursor.id] = screenRange
cursorScreenRanges
getSelectionScreenRanges: (renderedRowRange) ->
getLineDecorations: (decorationsByMarkerId) ->
{editor} = @props
[renderedStartRow, renderedEndRow] = renderedRowRange
decorationsByScreenRow = {}
for markerId, decorations of decorationsByMarkerId
marker = editor.getMarker(markerId)
screenRange = null
if marker.isValid()
for decoration in decorations
if editor.decorationMatchesType(decoration, 'gutter') or editor.decorationMatchesType(decoration, 'line')
screenRange ?= marker.getScreenRange()
startRow = screenRange.start.row
endRow = screenRange.end.row
endRow-- if not screenRange.isEmpty() and screenRange.end.column == 0
for screenRow in [startRow..endRow]
decorationsByScreenRow[screenRow] ?= []
decorationsByScreenRow[screenRow].push decoration
selectionScreenRanges = {}
for selection, index in editor.getSelections()
screenRange = selection.getScreenRange()
decorationsByScreenRow
if not screenRange.isEmpty() and screenRange.intersectsRowRange(renderedStartRow, renderedEndRow)
selectionScreenRanges[selection.id] = screenRange
else if index is 0 # Rendering artifacts occur on the lines GPU layer if we remove the last selection
selectionScreenRanges[selection.id] = new Range(new Point(renderedStartRow, 0), new Point(renderedStartRow, 0))
selectionScreenRanges
getGutterDecorations: (renderedRowRange) ->
getHighlightDecorations: (decorationsByMarkerId) ->
{editor} = @props
[renderedStartRow, renderedEndRow] = renderedRowRange
bufferRows = editor.bufferRowsForScreenRows(renderedStartRow, renderedEndRow - 1)
decorations = {}
for bufferRow in bufferRows
decorations[bufferRow] = editor.decorationsForBufferRow(bufferRow, 'gutter')
decorations[bufferRow].push {class: 'foldable'} if editor.isFoldableAtBufferRow(bufferRow)
decorations
filteredDecorations = {}
for markerId, decorations of decorationsByMarkerId
marker = editor.getMarker(markerId)
if marker.isValid() and not marker.getScreenRange().isEmpty()
for decoration in decorations
if editor.decorationMatchesType(decoration, 'highlight')
filteredDecorations[markerId] ?= {id: markerId, screenRange: marker.getScreenRange(), decorations: []}
filteredDecorations[markerId].decorations.push decoration
filteredDecorations
observeEditor: ->
{editor} = @props
@@ -251,7 +269,9 @@ EditorComponent = React.createClass
@subscribe editor, 'cursors-moved', @onCursorsMoved
@subscribe editor, 'selection-removed selection-screen-range-changed', @onSelectionChanged
@subscribe editor, 'selection-added', @onSelectionAdded
@subscribe editor, 'decoration-changed', @onDecorationChanged
@subscribe editor, 'decoration-added', @onDecorationChanged
@subscribe editor, 'decoration-removed', @onDecorationChanged
@subscribe editor, 'character-widths-changed', @onCharacterWidthsChanged
@subscribe editor.$scrollTop.changes, @onScrollTopChanged
@subscribe editor.$scrollLeft.changes, @requestUpdate
@subscribe editor.$height.changes, @requestUpdate
@@ -266,9 +286,35 @@ EditorComponent = React.createClass
node.addEventListener 'textInput', @onTextInput
scrollViewNode = @refs.scrollView.getDOMNode()
scrollViewNode.addEventListener 'overflowchanged', @onScrollViewOverflowChanged
scrollViewNode.addEventListener 'scroll', @onScrollViewScroll
window.addEventListener('resize', @onWindowResize)
window.addEventListener 'resize', @requestScrollViewMeasurement
@listenForIMEEvents()
listenForIMEEvents: ->
node = @getDOMNode()
{editor} = @props
# The IME composition events work like this:
#
# User types 's', chromium pops up the completion helper
# 1. compositionstart fired
# 2. compositionupdate fired; event.data == 's'
# User hits arrow keys to move around in completion helper
# 3. compositionupdate fired; event.data == 's' for each arry key press
# User escape to cancel
# 4. compositionend fired
# OR User chooses a completion
# 4. compositionend fired
# 5. textInput fired; event.data == the completion string
selectedText = null
node.addEventListener 'compositionstart', =>
selectedText = editor.getSelectedText()
node.addEventListener 'compositionupdate', (event) =>
editor.insertText(event.data, select: true, undo: 'skip')
node.addEventListener 'compositionend', =>
editor.insertText(selectedText, select: true, undo: 'skip')
listenForCommands: ->
{parentView, editor, mini} = @props
@@ -291,6 +337,7 @@ EditorComponent = React.createClass
'editor:consolidate-selections': @consolidateSelections
'editor:delete-to-beginning-of-word': => editor.deleteToBeginningOfWord()
'editor:delete-to-beginning-of-line': => editor.deleteToBeginningOfLine()
'editor:delete-to-end-of-line': => editor.deleteToEndOfLine()
'editor:delete-to-end-of-word': => editor.deleteToEndOfWord()
'editor:delete-line': => editor.deleteLine()
'editor:cut-to-end-of-line': => editor.cutToEndOfLine()
@@ -327,10 +374,14 @@ EditorComponent = React.createClass
'core:move-down': => editor.moveCursorDown()
'core:move-to-top': => editor.moveCursorToTop()
'core:move-to-bottom': => editor.moveCursorToBottom()
'core:page-up': => editor.pageUp()
'core:page-down': => editor.pageDown()
'core:select-up': => editor.selectUp()
'core:select-down': => editor.selectDown()
'core:select-to-top': => editor.selectToTop()
'core:select-to-bottom': => editor.selectToBottom()
'core:select-page-up': => editor.selectPageUp()
'core:select-page-down': => editor.selectPageDown()
'editor:indent': => editor.indent()
'editor:auto-indent': => editor.autoIndentSelectedRows()
'editor:indent-selected-rows': => editor.indentSelectedRows()
@@ -368,8 +419,6 @@ EditorComponent = React.createClass
'editor:toggle-indent-guide': => atom.config.toggle('editor.showIndentGuide')
'editor:toggle-line-numbers': => atom.config.toggle('editor.showLineNumbers')
'editor:scroll-to-cursor': => editor.scrollToCursorPosition()
'core:page-up': => editor.pageUp()
'core:page-down': => editor.pageDown()
'benchmark:scroll': @runScrollBenchmark
addCommandListeners: (listenersByCommandName) ->
@@ -464,17 +513,12 @@ EditorComponent = React.createClass
@pendingVerticalScrollDelta = 0
@pendingHorizontalScrollDelta = 0
onScrollViewOverflowChanged: ->
@requestScrollViewMeasurement()
onWindowResize: ->
@requestScrollViewMeasurement()
onScrollViewScroll: ->
console.warn "EditorScrollView scroll position changed, and it shouldn't have. If you can reproduce this, please report it."
scrollViewNode = @refs.scrollView.getDOMNode()
scrollViewNode.scrollTop = 0
scrollViewNode.scrollLeft = 0
if @isMounted()
console.warn "EditorScrollView scrolled when it shouldn't have."
scrollViewNode = @refs.scrollView.getDOMNode()
scrollViewNode.scrollTop = 0
scrollViewNode.scrollLeft = 0
onMouseDown: (event) ->
return unless event.button is 0 # only handle the left mouse button
@@ -483,6 +527,11 @@ EditorComponent = React.createClass
{detail, shiftKey, metaKey} = event
screenPosition = @screenPositionForMouseEvent(event)
if event.target?.classList.contains('fold-marker')
bufferRow = editor.bufferRowForScreenRow(screenPosition.row)
editor.unfoldBufferRow(bufferRow)
return
if shiftKey
editor.selectToScreenPosition(screenPosition)
else if metaKey
@@ -493,7 +542,46 @@ EditorComponent = React.createClass
when 2 then editor.selectWord()
when 3 then editor.selectLine()
@selectToMousePositionUntilMouseUp(event)
@handleDragUntilMouseUp event, (screenPosition) ->
editor.selectToScreenPosition(screenPosition)
onGutterMouseDown: (event) ->
return unless event.button is 0 # only handle the left mouse button
if event.shiftKey
@onGutterShiftClick(event)
else
@onGutterClick(event)
onGutterClick: (event) ->
{editor} = @props
clickedRow = @screenPositionForMouseEvent(event).row
editor.setCursorScreenPosition([clickedRow, 0])
@handleDragUntilMouseUp event, (screenPosition) ->
dragRow = screenPosition.row
if dragRow < clickedRow # dragging up
editor.setSelectedScreenRange([[dragRow, 0], [clickedRow + 1, 0]])
else
editor.setSelectedScreenRange([[clickedRow, 0], [dragRow + 1, 0]])
onGutterShiftClick: (event) ->
{editor} = @props
clickedRow = @screenPositionForMouseEvent(event).row
tailPosition = editor.getSelection().getTailScreenPosition()
if clickedRow < tailPosition.row
editor.selectToScreenPosition([clickedRow, 0])
else
editor.selectToScreenPosition([clickedRow + 1, 0])
@handleDragUntilMouseUp event, (screenPosition) ->
dragRow = screenPosition.row
if dragRow < tailPosition.row # dragging up
editor.setSelectedScreenRange([[dragRow, 0], tailPosition])
else
editor.setSelectedScreenRange([tailPosition, [dragRow + 1, 0]])
onStylesheetsChanged: (stylesheet) ->
@refreshScrollbars() if @containsScrollbarSelector(stylesheet)
@@ -550,15 +638,18 @@ EditorComponent = React.createClass
@requestUpdate() if @isMounted()
@decorationChangedImmediate = null
selectToMousePositionUntilMouseUp: (event) ->
onCharacterWidthsChanged: (@scopedCharacterWidthsChangeCount) ->
@requestUpdate()
handleDragUntilMouseUp: (event, dragHandler) ->
{editor} = @props
dragging = false
lastMousePosition = {}
animationLoop = =>
requestAnimationFrame =>
if dragging
@selectToMousePosition(lastMousePosition)
screenPosition = @screenPositionForMouseEvent(lastMousePosition)
dragHandler(screenPosition)
animationLoop()
onMouseMove = (event) ->
@@ -582,9 +673,6 @@ EditorComponent = React.createClass
window.addEventListener('mousemove', onMouseMove)
window.addEventListener('mouseup', onMouseUp)
selectToMousePosition: (event) ->
@props.editor.selectToScreenPosition(@screenPositionForMouseEvent(event))
requestScrollViewMeasurement: ->
return if @measurementPending
@@ -606,30 +694,21 @@ EditorComponent = React.createClass
{position} = getComputedStyle(editorNode)
{width, height} = editorNode.style
if position is 'absolute' or height
clientHeight = scrollViewNode.clientHeight
editor.setHeight(clientHeight) if clientHeight > 0
editor.batchUpdates ->
if position is 'absolute' or height
clientHeight = scrollViewNode.clientHeight
editor.setHeight(clientHeight) if clientHeight > 0
if position is 'absolute' or width
clientWidth = scrollViewNode.clientWidth
editor.setWidth(clientWidth) if clientWidth > 0
if position is 'absolute' or width
clientWidth = scrollViewNode.clientWidth
editor.setWidth(clientWidth) if clientWidth > 0
measureLineHeightAndCharWidthsIfNeeded: (prevState) ->
if not isEqualForProperties(prevState, @state, 'lineHeight', 'fontSize', 'fontFamily')
{editor} = @props
editor.batchUpdates =>
oldDefaultCharWidth = editor.getDefaultCharWidth()
if @state.visible
@measureLineHeightAndDefaultCharWidth()
else
@measureLineHeightAndDefaultCharWidthWhenShown = true
unless oldDefaultCharWidth is editor.getDefaultCharWidth()
@remeasureCharacterWidths()
@measureGutter()
if @state.visible
@measureLineHeightAndDefaultCharWidth()
else
@measureLineHeightAndDefaultCharWidthWhenShown = true
else if @measureLineHeightAndDefaultCharWidthWhenShown and @state.visible and not prevState.visible
@measureLineHeightAndDefaultCharWidth()
@@ -637,13 +716,21 @@ EditorComponent = React.createClass
@measureLineHeightAndDefaultCharWidthWhenShown = false
@refs.lines.measureLineHeightAndDefaultCharWidth()
remeasureCharacterWidthsIfNeeded: (prevState) ->
if not isEqualForProperties(prevState, @state, 'fontSize', 'fontFamily')
if @state.visible
@remeasureCharacterWidths()
else
@remeasureCharacterWidthsWhenShown = true
else if @remeasureCharacterWidthsWhenShown and @state.visible and not prevState.visible
@remeasureCharacterWidths()
remeasureCharacterWidths: ->
@remeasureCharacterWidthsWhenShown = false
@refs.lines.remeasureCharacterWidths()
measureGutter: ->
oldGutterWidth = @gutterWidth
@gutterWidth = @refs.gutter.getDOMNode().offsetWidth
@requestUpdate() if @gutterWidth isnt oldGutterWidth
onGutterWidthChanged: (@gutterWidth) ->
@requestUpdate()
measureScrollbars: ->
@measuringScrollbars = false
+4 -1
Ver Arquivo
@@ -157,6 +157,7 @@ class EditorView extends View
'editor:consolidate-selections': (event) => @consolidateSelections(event)
'editor:delete-to-beginning-of-word': => @editor.deleteToBeginningOfWord()
'editor:delete-to-beginning-of-line': => @editor.deleteToBeginningOfLine()
'editor:delete-to-end-of-line': => @editor.deleteToEndOfLine()
'editor:delete-to-end-of-word': => @editor.deleteToEndOfWord()
'editor:delete-line': => @editor.deleteLine()
'editor:cut-to-end-of-line': => @editor.cutToEndOfLine()
@@ -193,12 +194,14 @@ class EditorView extends View
'core:move-down': => @editor.moveCursorDown()
'core:move-to-top': => @editor.moveCursorToTop()
'core:move-to-bottom': => @editor.moveCursorToBottom()
'core:page-down': => @pageDown()
'core:page-up': => @pageUp()
'core:page-down': => @pageDown()
'core:select-up': => @editor.selectUp()
'core:select-down': => @editor.selectDown()
'core:select-to-top': => @editor.selectToTop()
'core:select-to-bottom': => @editor.selectToBottom()
'core:select-page-up': => @editor.selectUp(@getPageRows())
'core:select-page-down': => @editor.selectDown(@getPageRows())
'editor:indent': => @editor.indent()
'editor:auto-indent': => @editor.autoIndentSelectedRows()
'editor:indent-selected-rows': => @editor.indentSelectedRows()
+52 -71
Ver Arquivo
@@ -112,6 +112,7 @@ TextMateScopeSelector = require('first-mate').ScopeSelector
# - {::deleteToBeginningOfWord}
# - {::deleteToBeginningOfLine}
# - {::delete}
# - {::deleteToEndOfLine}
# - {::deleteToEndOfWord}
# - {::deleteLine}
# - {::cutSelectedText}
@@ -214,7 +215,9 @@ class Editor extends Model
@subscribe @displayBuffer, 'grammar-changed', => @handleGrammarChange()
@subscribe @displayBuffer, 'tokenized', => @handleTokenization()
@subscribe @displayBuffer, 'soft-wrap-changed', (args...) => @emit 'soft-wrap-changed', args...
@subscribe @displayBuffer, "decoration-changed", (e) => @emit 'decoration-changed', e
@subscribe @displayBuffer, "decoration-added", (args...) => @emit 'decoration-added', args...
@subscribe @displayBuffer, "decoration-removed", (args...) => @emit 'decoration-removed', args...
@subscribe @displayBuffer, "character-widths-changed", (changeCount) => @emit 'character-widths-changed', changeCount
getViewClass: ->
if atom.config.get('core.useReactEditor')
@@ -689,6 +692,13 @@ class Editor extends Model
delete: ->
@mutateSelectedText (selection) -> selection.delete()
# Public: For each selection, if the selection is not empty, deletes the
# selection; otherwise, deletes all characters of the containing line
# following the cursor. If the cursor is already at the end of the line,
# deletes the following newline.
deleteToEndOfLine: ->
@mutateSelectedText (selection) -> selection.deleteToEndOfLine()
# Public: For each selection, if the selection is empty, delete all characters
# of the containing word following the cursor. Otherwise delete the selected
# text.
@@ -843,6 +853,10 @@ class Editor extends Model
isFoldableAtBufferRow: (bufferRow) ->
@languageMode.isFoldableAtBufferRow(bufferRow)
isFoldableAtScreenRow: (screenRow) ->
bufferRow = @displayBuffer.bufferRowForScreenRow(screenRow)
@isFoldableAtBufferRow(bufferRow)
# TODO: Rename to foldRowRange?
createFold: (startRow, endRow) ->
@displayBuffer.createFold(startRow, endRow)
@@ -1058,41 +1072,38 @@ class Editor extends Model
selection.insertText(fn(text))
selection.setBufferRange(range)
# Public: Get all the decorations for a buffer row.
# Public: Get all the decorations within a screen row range.
#
# bufferRow - the {int} buffer row
# decorationType - the {String} decoration type to filter by eg. 'gutter'
# startScreenRow - the {int} beginning screen row
# endScreenRow - the {int} end screen row (inclusive)
#
# Returns an {Array} of decorations in the form `[{type: 'gutter', class: 'someclass'}, ...]`
# Returns an empty array when no decorations are found
decorationsForBufferRow: (bufferRow, decorationType) ->
@displayBuffer.decorationsForBufferRow(bufferRow, decorationType)
# Returns an {Object} of decorations in the form `{1: [{type: 'gutter', class: 'someclass'}], 2: ...}`
# where the keys are markerIds, and the values are an array of {Decoration} objects attached to the marker.
# Returns an empty object when no decorations are found
decorationsForScreenRowRange: (startScreenRow, endScreenRow) ->
@displayBuffer.decorationsForScreenRowRange(startScreenRow, endScreenRow)
# Public: Get all the decorations for a range of buffer rows (inclusive)
# Public: Adds a decoration that tracks a {Marker}. When the marker moves,
# is invalidated, or is destroyed, the decoration will be updated to reflect the marker's state.
#
# startBufferRow - the {int} start of the buffer row range
# endBufferRow - the {int} end of the buffer row range (inclusive)
# decorationType - the {String} decoration type to filter by eg. 'gutter'
# There are a few supported decoration types:
# * `gutter`: `{type: 'gutter', class: 'linter-error'}` Will add a class to the gutter rows associated with the marker.
# * `line`: `{type: 'line', class: 'linter-error'}` Will add a class to the editor lines associated with the marker.
# * `highlight`: `{type: 'highlight', class: 'linter-error'}` Will highlight the region of the buffer associated with the marker. Your specified class will be added to the highlight.
#
# Returns an {Object} of decorations in the form `{23: [{type: 'gutter', class: 'someclass'}, ...], 24: [...]}`
# Returns an {Object} with keyed with all buffer rows in the range containing empty {Array}s when no decorations are found
decorationsForBufferRowRange: (startBufferRow, endBufferRow, decorationType) ->
@displayBuffer.decorationsForBufferRowRange(startBufferRow, endBufferRow, decorationType)
# Public: Adds a decoration to a buffer row. For example, use to mark a gutter
# line number with a class by using the form `{type: 'gutter', class: 'linter-error'}`
#
# bufferRow - the {int} buffer row
# decoration - the {Object} decoration type to filter by eg. `{type: 'gutter', class: 'linter-error'}`
# marker - the {Marker} you want this decoration to follow
# decoration - the {Object} decoration eg. `{type: 'gutter', class: 'linter-error'}`
#
# Returns nothing
addDecorationToBufferRow: (bufferRow, decoration) ->
@displayBuffer.addDecorationToBufferRow(bufferRow, decoration)
addDecorationForMarker: (marker, decoration) ->
@displayBuffer.addDecorationForMarker(marker, decoration)
# Public: Removes a decoration from a buffer row.
# Public: Removes all decorations associated with a {Marker} that match a
# `decorationPattern` and stop tracking the {Marker}.
#
# ```coffee
# editor.removeDecorationFromBufferRow(2, {type: 'gutter', class: 'linter-error'})
# marker = editor.markBufferRange([[4, 13], [5, 17]])
# editor.removeDecorationForMarker(marker, {type: 'gutter', class: 'linter-error'})
# ```
#
# All decorations matching a pattern will be removed. For example, you might
@@ -1108,49 +1119,9 @@ class Editor extends Model
# You can remove both with:
#
# ```coffee
# editor.removeDecorationFromBufferRow(2, {namespace: 'myns'})
# editor.removeDecorationForMarker(marker, {namespace: 'myns'})
# ```
#
# bufferRow - the {int} buffer row
# decorationPattern - the {Object} decoration type to filter by eg. `{type: 'gutter', class: 'linter-error'}`
#
# Returns an {Array} of the removed decorations
removeDecorationFromBufferRow: (bufferRow, decorationPattern) ->
@displayBuffer.removeDecorationFromBufferRow(bufferRow, decorationPattern)
# Public: Adds a decoration to line numbers in a buffer row range
#
# startBufferRow - the {int} start of the buffer row range
# endBufferRow - the {int} end of the buffer row range (inclusive)
# decoration - the {Object} decoration type to filter by eg. `{type: 'gutter', class: 'linter-error'}`
#
# Returns nothing
addDecorationToBufferRowRange: (startBufferRow, endBufferRow, decoration) ->
@displayBuffer.addDecorationToBufferRowRange(startBufferRow, endBufferRow, decoration)
# Public: Removes a decoration from line numbers in a buffer row range
#
# startBufferRow - the {int} start of the buffer row range
# endBufferRow - the {int} end of the buffer row range (inclusive)
# decoration - the {Object} decoration type to filter by eg. `{type: 'gutter', class: 'linter-error'}`
#
# Returns nothing
removeDecorationFromBufferRowRange: (startBufferRow, endBufferRow, decoration) ->
@displayBuffer.removeDecorationFromBufferRowRange(startBufferRow, endBufferRow, decoration)
# Public: Adds a decoration that tracks a {Marker}. When the marker moves,
# is invalidated, or is destroyed, the decoration will be updated to reflect the marker's state.
#
# marker - the {Marker} you want this decoration to follow
# decoration - the {Object} decoration type to filter by eg. `{type: 'gutter', class: 'linter-error'}`
#
# Returns nothing
addDecorationForMarker: (marker, decoration) ->
@displayBuffer.addDecorationForMarker(marker, decoration)
# Public: Removes all decorations associated with a {Marker} that match a
# `decorationPattern` and stop tracking the {Marker}.
#
# marker - the {Marker} to detach from
# decorationPattern - the {Object} decoration type to filter by eg. `{type: 'gutter', class: 'linter-error'}`
#
@@ -1158,6 +1129,9 @@ class Editor extends Model
removeDecorationForMarker: (marker, decorationPattern) ->
@displayBuffer.removeDecorationForMarker(marker, decorationPattern)
decorationMatchesType: (decoration, type) ->
@displayBuffer.decorationMatchesType(decoration, type)
# Public: Get the {DisplayBufferMarker} for the given marker id.
getMarker: (id) ->
@displayBuffer.getMarker(id)
@@ -1263,7 +1237,7 @@ class Editor extends Model
addCursor: (marker) ->
cursor = new Cursor(editor: this, marker: marker)
@cursors.push(cursor)
@addDecorationForMarker(marker, {class: 'cursor-line'})
@addDecorationForMarker(marker, type: ['gutter', 'line'], class: 'cursor-line')
@emit 'cursor-added', cursor
cursor
@@ -1290,6 +1264,7 @@ class Editor extends Model
if selection.intersectsBufferRange(selectionBufferRange)
return selection
else
@addDecorationForMarker(marker, type: 'highlight', class: 'selection')
@emit 'selection-added', selection
selection
@@ -1614,6 +1589,12 @@ class Editor extends Model
@moveCursorDown(@getRowsPerPage())
@setScrollTop(newScrollTop)
selectPageUp: ->
@selectUp(@getRowsPerPage())
selectPageDown: ->
@selectDown(@getRowsPerPage())
# Returns the number of rows per page
getRowsPerPage: ->
Math.max(1, Math.ceil(@getHeight() / @getLineHeightInPixels()))
@@ -2049,11 +2030,11 @@ class Editor extends Model
pixelRectForScreenRange: (screenRange) -> @displayBuffer.pixelRectForScreenRange(screenRange)
scrollToScreenRange: (screenRange) -> @displayBuffer.scrollToScreenRange(screenRange)
scrollToScreenRange: (screenRange, options) -> @displayBuffer.scrollToScreenRange(screenRange, options)
scrollToScreenPosition: (screenPosition) -> @displayBuffer.scrollToScreenPosition(screenPosition)
scrollToScreenPosition: (screenPosition, options) -> @displayBuffer.scrollToScreenPosition(screenPosition, options)
scrollToBufferPosition: (bufferPosition) -> @displayBuffer.scrollToBufferPosition(bufferPosition)
scrollToBufferPosition: (bufferPosition, options) -> @displayBuffer.scrollToBufferPosition(bufferPosition, options)
horizontallyScrollable: -> @displayBuffer.horizontallyScrollable()
+45 -28
Ver Arquivo
@@ -12,20 +12,23 @@ GutterComponent = React.createClass
mixins: [SubscriberMixin]
dummyLineNumberNode: null
measuredWidth: null
render: ->
{scrollHeight, scrollTop} = @props
{scrollHeight, scrollViewHeight, scrollTop, onMouseDown} = @props
div className: 'gutter', onClick: @onClick,
div className: 'line-numbers editor-colors', ref: 'lineNumbers', style:
height: scrollHeight
div className: 'gutter', onClick: @onClick, onMouseDown: onMouseDown,
# The line-numbers div must have the 'editor-colors' class so it has an
# opaque background to avoid sub-pixel anti-aliasing problems on the GPU
div className: 'gutter line-numbers editor-colors', ref: 'lineNumbers', style:
height: Math.max(scrollHeight, scrollViewHeight)
WebkitTransform: "translate3d(0px, #{-scrollTop}px, 0px)"
componentWillMount: ->
@lineNumberNodesById = {}
@lineNumberIdsByScreenRow = {}
@screenRowsByLineNumberId = {}
@previousDecorations = {}
@renderedDecorationsByLineNumberId = {}
componentDidMount: ->
@appendDummyLineNumber()
@@ -35,22 +38,24 @@ GutterComponent = React.createClass
# visible row range.
shouldComponentUpdate: (newProps) ->
return true unless isEqualForProperties(newProps, @props,
'renderedRowRange', 'scrollTop', 'lineHeightInPixels', 'mouseWheelScreenRow'
'renderedRowRange', 'scrollTop', 'lineHeightInPixels', 'mouseWheelScreenRow', 'lineDecorations',
'scrollViewHeight'
)
{renderedRowRange, pendingChanges, decorations} = newProps
{renderedRowRange, pendingChanges, lineDecorations} = newProps
for change in pendingChanges when Math.abs(change.screenDelta) > 0 or Math.abs(change.bufferDelta) > 0
return true unless change.end <= renderedRowRange.start or renderedRowRange.end <= change.start
return true unless _.isEqual(@previousDecorations, decorations)
false
componentDidUpdate: (oldProps) ->
unless oldProps.maxLineNumberDigits is @props.maxLineNumberDigits
unless isEqualForProperties(oldProps, @props, 'maxLineNumberDigits')
@updateDummyLineNumber()
@removeLineNumberNodes()
unless isEqualForProperties(oldProps, @props, 'maxLineNumberDigits', 'defaultCharWidth')
@measureWidth()
@clearScreenRowCaches() unless oldProps.lineHeightInPixels is @props.lineHeightInPixels
@updateLineNumbers()
@@ -74,7 +79,7 @@ GutterComponent = React.createClass
@removeLineNumberNodes(lineNumberIdsToPreserve)
appendOrUpdateVisibleLineNumberNodes: ->
{editor, renderedRowRange, scrollTop, maxLineNumberDigits, decorations} = @props
{editor, renderedRowRange, scrollTop, maxLineNumberDigits, lineDecorations} = @props
[startRow, endRow] = renderedRowRange
newLineNumberIds = null
@@ -95,15 +100,17 @@ GutterComponent = React.createClass
visibleLineNumberIds.add(id)
if @hasLineNumberNode(id)
@updateLineNumberNode(id, bufferRow, screenRow, wrapCount > 0, decorations[bufferRow])
@updateLineNumberNode(id, bufferRow, screenRow, wrapCount > 0)
else
newLineNumberIds ?= []
newLineNumbersHTML ?= ""
newLineNumberIds.push(id)
newLineNumbersHTML += @buildLineNumberHTML(bufferRow, wrapCount > 0, maxLineNumberDigits, screenRow, decorations[bufferRow])
newLineNumbersHTML += @buildLineNumberHTML(bufferRow, wrapCount > 0, maxLineNumberDigits, screenRow)
@screenRowsByLineNumberId[id] = screenRow
@lineNumberIdsByScreenRow[screenRow] = id
@renderedDecorationsByLineNumberId[id] = lineDecorations[screenRow]
if newLineNumberIds?
WrapperDiv.innerHTML = newLineNumbersHTML
newLineNumberNodes = toArray(WrapperDiv.children)
@@ -114,7 +121,6 @@ GutterComponent = React.createClass
@lineNumberNodesById[lineNumberId] = lineNumberNode
node.appendChild(lineNumberNode)
@previousDecorations = decorations
visibleLineNumberIds
removeLineNumberNodes: (lineNumberIdsToPreserve) ->
@@ -126,20 +132,24 @@ GutterComponent = React.createClass
delete @lineNumberNodesById[lineNumberId]
delete @lineNumberIdsByScreenRow[screenRow] if @lineNumberIdsByScreenRow[screenRow] is lineNumberId
delete @screenRowsByLineNumberId[lineNumberId]
delete @renderedDecorationsByLineNumberId[lineNumberId]
node.removeChild(lineNumberNode)
buildLineNumberHTML: (bufferRow, softWrapped, maxLineNumberDigits, screenRow, decorations) ->
buildLineNumberHTML: (bufferRow, softWrapped, maxLineNumberDigits, screenRow) ->
{editor, lineHeightInPixels, lineDecorations} = @props
if screenRow?
{lineHeightInPixels} = @props
style = "position: absolute; top: #{screenRow * lineHeightInPixels}px;"
else
style = "visibility: hidden;"
innerHTML = @buildLineNumberInnerHTML(bufferRow, softWrapped, maxLineNumberDigits)
classes = ''
if decorations?
if lineDecorations? and decorations = lineDecorations[screenRow]
for decoration in decorations
classes += decoration.class + ' ' if not softWrapped or softWrapped and decoration.softWrap
if editor.decorationMatchesType(decoration, 'gutter')
classes += decoration.class + ' '
classes += "foldable " if bufferRow >= 0 and editor.isFoldableAtBufferRow(bufferRow)
classes += "line-number line-number-#{bufferRow}"
"<div class=\"#{classes}\" style=\"#{style}\" data-buffer-row=\"#{bufferRow}\" data-screen-row=\"#{screenRow}\">#{innerHTML}</div>"
@@ -154,17 +164,25 @@ GutterComponent = React.createClass
iconHTML = '<div class="icon-right"></div>'
padding + lineNumber + iconHTML
updateLineNumberNode: (lineNumberId, bufferRow, screenRow, softWrapped, decorations) ->
updateLineNumberNode: (lineNumberId, bufferRow, screenRow, softWrapped) ->
{editor, lineDecorations} = @props
node = @lineNumberNodesById[lineNumberId]
previousDecorations = @previousDecorations[bufferRow]
if editor.isFoldableAtBufferRow(bufferRow)
node.classList.add('foldable')
else
node.classList.remove('foldable')
decorations = lineDecorations[screenRow]
previousDecorations = @renderedDecorationsByLineNumberId[lineNumberId]
if previousDecorations?
for decoration in previousDecorations
node.classList.remove(decoration.class) if not contains(decorations, decoration)
node.classList.remove(decoration.class) if editor.decorationMatchesType(decoration, 'gutter') and not _.deepContains(decorations, decoration)
if decorations?
for decoration in decorations
if not contains(previousDecorations, decoration) and (not softWrapped or softWrapped and decoration.softWrap)
if editor.decorationMatchesType(decoration, 'gutter') and not _.deepContains(previousDecorations, decoration)
node.classList.add(decoration.class)
unless @screenRowsByLineNumberId[lineNumberId] is screenRow
@@ -192,9 +210,8 @@ GutterComponent = React.createClass
else
editor.foldBufferRow(bufferRow)
# Created because underscore uses === not _.isEqual, which we need
contains = (array, target) ->
return false unless array?
for object in array
return true if _.isEqual(object, target)
false
measureWidth: ->
width = @getDOMNode().offsetWidth
unless width is @measuredWidth
@measuredWidth = width
@props.onWidthChanged?(width)
@@ -2,17 +2,19 @@ React = require 'react-atom-fork'
{div} = require 'reactionary-atom-fork'
module.exports =
SelectionComponent = React.createClass
displayName: 'SelectionComponent'
HighlightComponent = React.createClass
displayName: 'HighlightComponent'
render: ->
{editor, screenRange, lineHeightInPixels} = @props
{editor, screenRange, decoration} = @props
{start, end} = screenRange
rowCount = end.row - start.row + 1
startPixelPosition = editor.pixelPositionForScreenPosition(start)
endPixelPosition = editor.pixelPositionForScreenPosition(end)
div className: 'selection',
className = 'highlight'
className += " #{decoration.class}" if decoration.class?
div {className},
if rowCount is 1
@renderSingleLineRegions(startPixelPosition, endPixelPosition)
else
+24
Ver Arquivo
@@ -0,0 +1,24 @@
React = require 'react-atom-fork'
{div} = require 'reactionary-atom-fork'
{isEqualForProperties} = require 'underscore-plus'
HighlightComponent = require './highlight-component'
module.exports =
HighlightsComponent = React.createClass
displayName: 'HighlightsComponent'
render: ->
div className: 'highlights', @renderHighlights()
renderHighlights: ->
{editor, highlightDecorations, lineHeightInPixels} = @props
highlightComponents = []
for markerId, {screenRange, decorations} of highlightDecorations
for decoration in decorations
highlightComponents.push(HighlightComponent({key: "#{markerId}-#{decoration.class}", screenRange, decoration, editor, lineHeightInPixels}))
highlightComponents
shouldComponentUpdate: (newProps) ->
not isEqualForProperties(newProps, @props, 'highlightDecorations', 'lineHeightInPixels', 'defaultCharWidth', 'scopedCharacterWidthsChangeCount')
+47 -14
Ver Arquivo
@@ -1,9 +1,10 @@
_ = require 'underscore-plus'
React = require 'react-atom-fork'
{div, span} = require 'reactionary-atom-fork'
{debounce, isEqual, isEqualForProperties, multiplyString, toArray} = require 'underscore-plus'
{$$} = require 'space-pen'
SelectionsComponent = require './selections-component'
HighlightsComponent = require './highlights-component'
DummyLineNode = $$(-> @div className: 'line', style: 'position: absolute; visibility: hidden;', => @span 'x')[0]
AcceptFilter = {acceptNode: -> NodeFilter.FILTER_ACCEPT}
@@ -15,26 +16,30 @@ LinesComponent = React.createClass
render: ->
if @isMounted()
{editor, selectionScreenRanges, scrollTop, scrollLeft, scrollHeight, scrollWidth, lineHeightInPixels, defaultCharWidth, scrollViewHeight} = @props
{editor, highlightDecorations, scrollTop, scrollLeft, scrollHeight, scrollWidth} = @props
{lineHeightInPixels, defaultCharWidth, scrollViewHeight, scopedCharacterWidthsChangeCount} = @props
style =
height: Math.max(scrollHeight, scrollViewHeight)
width: scrollWidth
WebkitTransform: "translate3d(#{-scrollLeft}px, #{-scrollTop}px, 0px)"
# The lines div must have the 'editor-colors' class so it has an opaque
# background to avoid sub-pixel anti-aliasing problems on the GPU
div {className: 'lines editor-colors', style},
SelectionsComponent({editor, selectionScreenRanges, lineHeightInPixels, defaultCharWidth}) if @isMounted()
HighlightsComponent({editor, highlightDecorations, lineHeightInPixels, defaultCharWidth, scopedCharacterWidthsChangeCount}) if @isMounted()
componentWillMount: ->
@measuredLines = new WeakSet
@lineNodesByLineId = {}
@screenRowsByLineId = {}
@lineIdsByScreenRow = {}
@renderedDecorationsByLineId = {}
shouldComponentUpdate: (newProps) ->
return true unless isEqualForProperties(newProps, @props,
'renderedRowRange', 'selectionScreenRanges', 'lineHeightInPixels', 'defaultCharWidth',
'renderedRowRange', 'lineDecorations', 'highlightDecorations', 'lineHeightInPixels', 'defaultCharWidth',
'scrollTop', 'scrollLeft', 'showIndentGuide', 'scrollingVertically', 'invisibles', 'visible',
'scrollViewHeight', 'mouseWheelScreenRow'
'scrollViewHeight', 'mouseWheelScreenRow', 'scopedCharacterWidthsChangeCount'
)
{renderedRowRange, pendingChanges} = newProps
@@ -57,7 +62,7 @@ LinesComponent = React.createClass
@lineIdsByScreenRow = {}
updateLines: ->
{editor, renderedRowRange, showIndentGuide, selectionChanged} = @props
{editor, renderedRowRange, showIndentGuide, selectionChanged, lineDecorations} = @props
[startRow, endRow] = renderedRowRange
visibleLines = editor.linesForScreenRows(startRow, endRow - 1)
@@ -75,9 +80,12 @@ LinesComponent = React.createClass
delete @lineNodesByLineId[lineId]
delete @lineIdsByScreenRow[screenRow] if @lineIdsByScreenRow[screenRow] is lineId
delete @screenRowsByLineId[lineId]
delete @renderedDecorationsByLineId[lineId]
node.removeChild(lineNode)
appendOrUpdateVisibleLineNodes: (visibleLines, startRow) ->
{lineDecorations} = @props
newLines = null
newLinesHTML = null
@@ -94,6 +102,8 @@ LinesComponent = React.createClass
@screenRowsByLineId[line.id] = screenRow
@lineIdsByScreenRow[screenRow] = line.id
@renderedDecorationsByLineId[line.id] = lineDecorations[screenRow]
return unless newLines?
WrapperDiv.innerHTML = newLinesHTML
@@ -108,17 +118,25 @@ LinesComponent = React.createClass
@lineNodesByLineId.hasOwnProperty(lineId)
buildLineHTML: (line, screenRow) ->
{editor, mini, showIndentGuide, lineHeightInPixels} = @props
{editor, mini, showIndentGuide, lineHeightInPixels, lineDecorations} = @props
{tokens, text, lineEnding, fold, isSoftWrapped, indentLevel} = line
classes = ''
if decorations = lineDecorations[screenRow]
for decoration in decorations
if editor.decorationMatchesType(decoration, 'line')
classes += decoration.class + ' '
classes += 'line'
top = screenRow * lineHeightInPixels
lineHTML = "<div class=\"line\" style=\"position: absolute; top: #{top}px;\" data-screen-row=\"#{screenRow}\">"
lineHTML = "<div class=\"#{classes}\" style=\"position: absolute; top: #{top}px;\" data-screen-row=\"#{screenRow}\">"
if text is ""
lineHTML += @buildEmptyLineInnerHTML(line)
else
lineHTML += @buildLineInnerHTML(line)
lineHTML += '<span class="fold-marker"></span>' if fold
lineHTML += "</div>"
lineHTML
@@ -186,9 +204,22 @@ LinesComponent = React.createClass
"<span class=\"#{scope.replace(/\.+/g, ' ')}\">"
updateLineNode: (line, screenRow) ->
{editor, lineHeightInPixels, lineDecorations} = @props
lineNode = @lineNodesByLineId[line.id]
decorations = lineDecorations[screenRow]
previousDecorations = @renderedDecorationsByLineId[line.id]
if previousDecorations?
for decoration in previousDecorations
lineNode.classList.remove(decoration.class) if editor.decorationMatchesType(decoration, 'line') and not _.deepContains(decorations, decoration)
if decorations?
for decoration in decorations
if editor.decorationMatchesType(decoration, 'line') and not _.deepContains(previousDecorations, decoration)
lineNode.classList.add(decoration.class)
unless @screenRowsByLineId[line.id] is screenRow
{lineHeightInPixels} = @props
lineNode = @lineNodesByLineId[line.id]
lineNode.style.top = screenRow * lineHeightInPixels + 'px'
lineNode.dataset.screenRow = screenRow
@screenRowsByLineId[line.id] = screenRow
@@ -214,13 +245,15 @@ LinesComponent = React.createClass
@measureCharactersInNewLines()
measureCharactersInNewLines: ->
{editor} = @props
[visibleStartRow, visibleEndRow] = @props.renderedRowRange
node = @getDOMNode()
for tokenizedLine in @props.editor.linesForScreenRows(visibleStartRow, visibleEndRow - 1)
unless @measuredLines.has(tokenizedLine)
lineNode = @lineNodesByLineId[tokenizedLine.id]
@measureCharactersInLine(tokenizedLine, lineNode)
editor.batchUpdates =>
for tokenizedLine in editor.linesForScreenRows(visibleStartRow, visibleEndRow - 1)
unless @measuredLines.has(tokenizedLine)
lineNode = @lineNodesByLineId[tokenizedLine.id]
@measureCharactersInLine(tokenizedLine, lineNode)
measureCharactersInLine: (tokenizedLine, lineNode) ->
{editor} = @props
+6 -8
Ver Arquivo
@@ -5,9 +5,7 @@ EditorComponent = require './editor-component'
module.exports =
class ReactEditorView extends View
# The `overlayer` class is included for backwards compatibility with
# context menus. It should be removed in v1.0.0
@content: -> @div class: 'editor react overlayer'
@content: -> @div class: 'editor react'
focusOnAttach: false
@@ -36,7 +34,7 @@ class ReactEditorView extends View
node = @component.getDOMNode()
@scrollView = $(node).find('.scroll-view')
@underlayer = $(node).find('.selections').addClass('underlayer')
@underlayer = $(node).find('.highlights').addClass('underlayer')
@overlayer = $(node).find('.lines').addClass('overlayer')
@hiddenInput = $(node).find('.hidden-input')
@@ -71,11 +69,11 @@ class ReactEditorView extends View
scrollToBottom: ->
@editor.setScrollBottom(Infinity)
scrollToScreenPosition: (screenPosition) ->
@editor.scrollToScreenPosition(screenPosition)
scrollToScreenPosition: (screenPosition, options) ->
@editor.scrollToScreenPosition(screenPosition, options)
scrollToBufferPosition: (bufferPosition) ->
@editor.scrollToBufferPosition(bufferPosition)
scrollToBufferPosition: (bufferPosition, options) ->
@editor.scrollToBufferPosition(bufferPosition, options)
scrollToCursorPosition: ->
@editor.scrollToCursorPosition()
+23 -1
Ver Arquivo
@@ -91,6 +91,18 @@ class Selection extends Model
end = Math.max(start, end - 1) if range.end.column == 0
[start, end]
getTailScreenPosition: ->
@marker.getTailScreenPosition()
getTailBufferPosition: ->
@marker.getTailBufferPosition()
getHeadScreenPosition: ->
@marker.getHeadScreenPosition()
getHeadBufferPosition: ->
@marker.getHeadBufferPosition()
autoscroll: ->
@editor.scrollToScreenRange(@getScreenRange())
@@ -266,7 +278,8 @@ class Selection extends Model
# FIXME: I have no idea what this does.
getGoalBufferRange: ->
@marker.getAttributes().goalBufferRange
if goalBufferRange = @marker.getAttributes().goalBufferRange
Range.fromObject(goalBufferRange)
# Public: Moves the selection up one row.
addSelectionAbove: ->
@@ -430,6 +443,15 @@ class Selection extends Model
@selectRight()
@deleteSelectedText()
# Public: If the selection is empty, removes all text from the cursor to the
# end of the line. If the cursor is already at the end of the line, it
# removes the following newline. If the selection isn't empty, only deletes
# the contents of the selection.
deleteToEndOfLine: ->
return @delete() if @isEmpty() and @cursor.isAtEndOfLine()
@selectToEndOfLine() if @isEmpty()
@deleteSelectedText()
# Public: Removes the selection or all characters from the start of the
# selection to the end of the current word if nothing is selected.
deleteToEndOfWord: ->
-25
Ver Arquivo
@@ -1,25 +0,0 @@
React = require 'react-atom-fork'
{div} = require 'reactionary-atom-fork'
{isEqualForProperties} = require 'underscore-plus'
SelectionComponent = require './selection-component'
module.exports =
SelectionsComponent = React.createClass
displayName: 'SelectionsComponent'
render: ->
div className: 'selections', @renderSelections()
renderSelections: ->
{editor, selectionScreenRanges, lineHeightInPixels} = @props
selectionComponents = []
for selectionId, screenRange of selectionScreenRanges
selectionComponents.push(SelectionComponent({key: selectionId, screenRange, editor, lineHeightInPixels}))
selectionComponents
componentWillMount: ->
@selectionRanges = {}
shouldComponentUpdate: (newProps) ->
not isEqualForProperties(newProps, @props, 'selectionScreenRanges', 'lineHeightInPixels', 'defaultCharWidth')
+18 -11
Ver Arquivo
@@ -42,16 +42,18 @@ class Token
whitespaceRegexForTabLength: (tabLength) ->
WhitespaceRegexesByTabLength[tabLength] ?= new RegExp("([ ]{#{tabLength}})|(\t)|([^\t]+)", "g")
breakOutAtomicTokens: (tabLength, breakOutLeadingSoftTabs) ->
breakOutAtomicTokens: (tabLength, breakOutLeadingSoftTabs, startColumn) ->
if @hasSurrogatePair
outputTokens = []
column = startColumn
for token in @breakOutSurrogatePairs()
if token.isAtomic
outputTokens.push(token)
else
outputTokens.push(token.breakOutAtomicTokens(tabLength, breakOutLeadingSoftTabs)...)
outputTokens.push(token.breakOutAtomicTokens(tabLength, breakOutLeadingSoftTabs, column)...)
breakOutLeadingSoftTabs = token.isOnlyWhitespace() if breakOutLeadingSoftTabs
column += token.value.length
outputTokens
else
@@ -64,17 +66,21 @@ class Token
outputTokens = []
regex = @whitespaceRegexForTabLength(tabLength)
column = startColumn
while match = regex.exec(@value)
[fullMatch, softTab, hardTab] = match
token = null
if softTab and breakOutLeadingSoftTabs
outputTokens.push(@buildSoftTabToken(tabLength))
token = @buildSoftTabToken(tabLength)
else if hardTab
breakOutLeadingSoftTabs = false
outputTokens.push(@buildHardTabToken(tabLength))
token = @buildHardTabToken(tabLength, column)
else
breakOutLeadingSoftTabs = false
value = match[0]
outputTokens.push(new Token({value, @scopes}))
token = new Token({value, @scopes})
column += token.value.length
outputTokens.push(token)
outputTokens
@@ -105,17 +111,18 @@ class Token
isAtomic: true
)
buildHardTabToken: (tabLength) ->
@buildTabToken(tabLength, true)
buildHardTabToken: (tabLength, column) ->
@buildTabToken(tabLength, true, column)
buildSoftTabToken: (tabLength) ->
@buildTabToken(tabLength, false)
@buildTabToken(tabLength, false, 0)
buildTabToken: (tabLength, isHardTab) ->
buildTabToken: (tabLength, isHardTab, column=0) ->
tabStop = tabLength - (column % tabLength)
new Token(
value: _.multiplyString(" ", tabLength)
value: _.multiplyString(" ", tabStop)
scopes: @scopes
bufferDelta: if isHardTab then 1 else tabLength
bufferDelta: if isHardTab then 1 else tabStop
isAtomic: true
isHardTab: isHardTab
)
+5 -2
Ver Arquivo
@@ -5,8 +5,8 @@ idCounter = 1
module.exports =
class TokenizedLine
constructor: ({tokens, @lineEnding, @ruleStack, @startBufferColumn, @fold, @tabLength, @indentLevel}) ->
@tokens = @breakOutAtomicTokens(tokens)
@startBufferColumn ?= 0
@tokens = @breakOutAtomicTokens(tokens)
@text = @buildText()
@bufferDelta = @buildBufferDelta()
@@ -124,8 +124,11 @@ class TokenizedLine
breakOutAtomicTokens: (inputTokens) ->
outputTokens = []
breakOutLeadingSoftTabs = true
column = @startBufferColumn
for token in inputTokens
outputTokens.push(token.breakOutAtomicTokens(@tabLength, breakOutLeadingSoftTabs)...)
newTokens = token.breakOutAtomicTokens(@tabLength, breakOutLeadingSoftTabs, column)
column += newToken.value.length for newToken in newTokens
outputTokens.push(newTokens...)
breakOutLeadingSoftTabs = token.isOnlyWhitespace() if breakOutLeadingSoftTabs
outputTokens
+10
Ver Arquivo
@@ -151,6 +151,10 @@
}
}
.editor .fold-marker {
cursor: default;
}
.editor .fold-marker:after {
.icon(0.8em, inline);
content: @ellipsis;
@@ -251,6 +255,12 @@
width: 1px;
}
.editor .highlight {
background: none;
padding: 0;
}
.editor .highlight .region,
.editor .selection .region {
position: absolute;
pointer-events: none;