Comparar commits

...

572 Commits

Autor SHA1 Mensagem Data
Kevin Sawicki 883f7e1f5a Use cross-platform path in specs 2014-09-10 10:22:48 -07:00
Kevin Sawicki d9b1b7b399 Don't dump symbols on Windows CI for now
Currently errors and isn't uploaded yet anyway as a release asset
2014-09-10 10:08:26 -07:00
Kevin Sawicki d1bdda5b3e Upgrade to language-html@0.26 2014-09-10 09:40:55 -07:00
Nathan Sobo a3f3b7e032 Upgrade text-buffer to fix null exception 2014-09-10 09:45:06 -06:00
Kevin Sawicki 3228de7ead Merge pull request #3508 from envygeeks/patch-2
Proc requires super user permissions on Linux.
2014-09-09 18:45:50 -07:00
Jordon Bedwell 5a64b09924 Proc requires super user permissions on Linux. 2014-09-09 20:36:40 -05:00
Kevin Sawicki b0c17aa98f 📝 Correct misspellings 2014-09-09 18:32:37 -07:00
Kevin Sawicki 9f5f4f2c10 Merge pull request #3499 from envygeeks/patch-1
Add alternatives alternative to symlinks.
2014-09-09 18:31:22 -07:00
Jordon Bedwell 335339ef61 Change language and make the command one line. 2014-09-09 20:20:39 -05:00
Kevin Sawicki 291bf7fe08 Upgrade to pathwatcher 2.0.12 2014-09-09 16:31:00 -07:00
Kevin Sawicki 40eaf69ab0 Upgrade to metrics@0.34 2014-09-09 16:25:44 -07:00
Kevin Sawicki 55ac855de1 Upgrade to pathwatcher 2.0.11 2014-09-09 16:12:20 -07:00
Kevin Sawicki 66b27ad52a Upgrade to snippets@0.52 2014-09-09 14:30:52 -07:00
Ben Ogle f357f694b3 Merge pull request #3456 from atom/ns-editor-event-methods
Add event subscription methods for Editor and associated classes
2014-09-09 13:40:45 -07:00
Ben Ogle 2a02375c4d Move the Decoration events to an events section 2014-09-09 12:31:01 -07:00
Ben Ogle e452b88fec Add sections for events 2014-09-09 12:30:43 -07:00
Ben Ogle 0f83fe54f9 Add and use Editor::observeSelections() and ::observeCursors() 2014-09-09 12:08:43 -07:00
Ben Ogle 3e5666f183 use getPaneView rather than getPane 2014-09-09 12:08:12 -07:00
Nathan Sobo 260f72d2b5 Merge pull request #3495 from atom/atom-shell-v0.16.0
Upgrade to Chrome 37
2014-09-09 12:43:42 -06:00
Nathan Sobo 84deefb6b1 Upgrade atom-shell to incorporate changes in 92.2 and 92.3 2014-09-09 12:31:48 -06:00
Nathan Sobo 5afceb3951 Merge remote-tracking branch 'origin/master' into atom-shell-v0.16.0
Conflicts:
	apm/package.json
2014-09-09 12:05:54 -06:00
Ben Ogle 72be16736c isDestroyed -> destroyed 2014-09-09 10:58:12 -07:00
Ben Ogle 789d9c8eff 📝 Selection event methods 2014-09-09 10:56:44 -07:00
Ben Ogle 858ac5bf79 onDidChangeSelectionScreenRange -> onDidChangeSelectionRange
for consistency
2014-09-09 10:54:57 -07:00
Ben Ogle 9db804b413 Doc events in Cursor 2014-09-09 10:51:46 -07:00
Ben Ogle c7525e8cff 💄 doc strings 2014-09-09 10:51:35 -07:00
Ben Ogle 7198a919bf onDidMoveCursor -> onDidChangeCursorPosition 2014-09-09 10:43:25 -07:00
Ben Ogle aebdfb4cf6 Event docs 2014-09-09 10:39:41 -07:00
Kevin Sawicki 7b1f8cc7da Set project path for window:open-path files
The project path will now be set to the parent directory if it isn't
already set when a file path is specified via the window:open-path
event.

Closes atom/tree-view#217
2014-09-09 09:55:31 -07:00
Nathan Sobo 17aa6f958c Upgrade autocomplete to fix specs 2014-09-09 10:18:19 -06:00
Cheng Zhao 3e10f491b4 Disable subpixel font scaling. 2014-09-09 21:54:32 +08:00
Cheng Zhao 3487ab9b11 Upgrade to atom-shell@0.16.2 2014-09-09 20:44:49 +08:00
Kevin Sawicki f6545d4002 Check if name matches before resolving path
Path resolution hits the filesystem so check for a name
match first.
2014-09-08 17:27:34 -07:00
Kevin Sawicki 408cac4632 🐎 Use DOM APIs to find editor views 2014-09-08 17:10:30 -07:00
Kevin Sawicki bb5a440651 :racehorse Upgrade to atom-keymap@2.0.6 2014-09-08 16:56:10 -07:00
Ben Ogle 5883e27c60 Make Editor::onDidChangeGrammar emit from Editor
Seems like the initial implementation was to handle the event, unfold 
all then, re-emit the grammar-changed event.
2014-09-08 16:32:43 -07:00
Ben Ogle 1d073173d4 Add Editor::onDidChangeSelectedScreenRange 2014-09-08 16:25:35 -07:00
Ben Ogle 601b311496 Add Editor::onDidMoveCursor 2014-09-08 16:20:54 -07:00
Ben Ogle d4835e1d8e Add Editor:onScrollTopChanged and Editor:onScrollLeftChanged 2014-09-08 16:17:06 -07:00
Ben Ogle 6f9f087e11 💄 consistency 2014-09-08 16:05:31 -07:00
Ben Ogle b6a9e1b576 Fix deprecation warning 2014-09-08 16:05:21 -07:00
Ben Ogle efea16848a Add deprecation warnings for all the other converted events 2014-09-08 16:05:07 -07:00
Ben Ogle 8aa1784c4a Add Editor::onDidChangeScreenLines 2014-09-08 16:04:43 -07:00
Nathan Sobo 2d3ae1b44d Upgrade event-kit for api docs 2014-09-08 17:00:46 -06:00
Jordon Bedwell efc730dc2f Add alternatives alternative to symlinks. 2014-09-08 14:39:47 -05:00
Kevin Sawicki 70dd3675e6 Upgrade to minidump 0.8 2014-09-08 12:05:25 -07:00
Kevin Sawicki 30ae93b9d9 Upgrade to apm 0.92.3
Refs #3492
2014-09-08 10:12:31 -07:00
Kevin Sawicki aa5a094cbe Merge pull request #3470 from atom/ks-no-jquery-to-apply-stylesheets
Use DOM APIs to apply stylesheets
2014-09-08 10:09:06 -07:00
Kevin Sawicki ab75f3122f 💄 Use proper Less 2014-09-08 09:45:24 -07:00
Kevin Sawicki 359491fc3f Remove nulled variable 2014-09-08 09:40:06 -07:00
Kevin Sawicki 444eb0e5e5 💄 e -> error 2014-09-08 09:37:39 -07:00
Kevin Sawicki 522d446366 📝 Emit -> Emitted 2014-09-08 09:35:05 -07:00
Kevin Sawicki f8949adf38 💄 Sort requires 2014-09-08 09:34:31 -07:00
Kevin Sawicki 01d62653f2 Remove unused require 2014-09-08 09:33:51 -07:00
Kevin Sawicki 35a48f0cfb Update specs to expect an element 2014-09-08 09:33:51 -07:00
Kevin Sawicki f7103bbed6 Store sheet before it goes away 2014-09-08 09:33:51 -07:00
Kevin Sawicki 7e6b7ada54 Preserve ordering within type class 2014-09-08 09:33:51 -07:00
Kevin Sawicki 5e2f8a3ae3 🐎 Use DOM APIs to apply stylesheets
Previously jQuery was used, but using the DOM APIs directly
takes 1/3 of the time.
2014-09-08 09:33:51 -07:00
Kevin Sawicki 4bda13ec74 Merge pull request #3453 from atom/ks-lower-browser-process-startup-time
Lower browser process startup time
2014-09-08 09:32:48 -07:00
Kevin Sawicki 42f3605465 finish-launching -> ready 2014-09-08 09:22:46 -07:00
Kevin Sawicki e7e4196fa6 Use fs.statSyncNoException 2014-09-08 09:22:45 -07:00
Kevin Sawicki 68801aacdb Don't require shell until it is used 2014-09-08 09:22:45 -07:00
Kevin Sawicki 13956edfb5 Always assign to autoUpdater 2014-09-08 09:22:45 -07:00
Kevin Sawicki 3a7564b59e Start auto updater on the next tick
This prevents it from adding time to first window startup
2014-09-08 09:22:45 -07:00
Kevin Sawicki 4e0928600f 💄 2014-09-08 09:22:45 -07:00
Kevin Sawicki 417134c799 Require dialog when used 2014-09-08 09:22:45 -07:00
Kevin Sawicki b2638c8bad 💄 2014-09-08 09:22:45 -07:00
Kevin Sawicki 27bf096fbc Remove require that is now inlined where used 2014-09-08 09:22:44 -07:00
Kevin Sawicki 4fa6f631a9 Defer ContextMenu require until requested 2014-09-08 09:22:44 -07:00
Kevin Sawicki 6e7968861d Use fs instead of fs-plus
Since only an is file check is performed fs is sufficient
2014-09-08 09:22:44 -07:00
Kevin Sawicki fc59d9c503 Require https on a next tick
This require is heavy in node for some reason so only
require it on Windows since it is only used there and only
after a next tick so that it doesn't affect startup
2014-09-08 09:22:44 -07:00
Kevin Sawicki 163b52efb0 CoffeeScript.compile returns a string 2014-09-08 09:22:44 -07:00
Kevin Sawicki d4ed8a0b73 Defer requiring CoffeeScript
Requiring it up front adds about 30-40ms to startup.
2014-09-08 09:22:44 -07:00
Kevin Sawicki d7106a6b4c Make browser process benchmark executable 2014-09-08 09:22:43 -07:00
Kevin Sawicki 4b867ddc7a Properly delete socket file in benchmark 2014-09-08 09:22:43 -07:00
Kevin Sawicki b5dfaff426 Remove unused requires from main.coffee 2014-09-08 09:22:43 -07:00
Kevin Sawicki 7eba55d009 Add initial browser process startup benchmark 2014-09-08 09:22:43 -07:00
alandarev abbb21bf47 🐧 Add Category 'Development' to linux desktop file
Closes #2827
Closes #3482
Closes #3488
2014-09-08 08:37:50 -07:00
Kevin Sawicki ff4d4f047f Merge pull request #3472 from pinak222/master
🐧 Add MimeType text/plain to .desktop file
2014-09-08 08:34:52 -07:00
Kevin Sawicki 9e21ea39bd Upgrade to language-xml@0.20 2014-09-08 08:33:48 -07:00
Ivan Žužak 11dbee0290 Merge pull request #3468 from atom/iz-update-keymap-instructions
Explain capital letters in keystrokes in default keymap.cson comments
2014-09-08 15:21:46 +02:00
Cheng Zhao de4936efc8 Upgrade to apm@0.93.1 2014-09-08 18:27:24 +08:00
Cheng Zhao 846d81abf5 Upgrade to atom-shell@0.16.1 2014-09-08 18:19:59 +08:00
Cheng Zhao a8e4638612 🍎 Add "Services" menu, fixes #3204 2014-09-08 14:49:25 +08:00
Cheng Zhao 4134f5efe4 Upgrade to apm@0.93.0 2014-09-07 10:26:44 +08:00
Cheng Zhao 1c5acc059e Upgrade to atom-shell@0.16.0 2014-09-07 10:24:35 +08:00
pinak222 c01a24f293 Add MimeType text/plain to .desktop file
This adds Atom in open with menu in KDE (possibly other DE's) for text files therefore providing better integration.
2014-09-06 10:59:01 +05:30
Kevin Sawicki 571ce5bddd Upgrade to tree-view@0.115 2014-09-05 18:33:09 -07:00
Kevin Sawicki 5237e687ee 🐧 Add separator after preference items
Closes #3471
2014-09-05 18:16:12 -07:00
Kevin Sawicki fe74dfdf4e Upgrade to apm 0.92.1 2014-09-05 17:57:46 -07:00
Ben Ogle 250c21f00a Deprecate all events from ::on 2014-09-05 17:26:41 -07:00
Ben Ogle e6dbea09fe Add onDidCreateMarker and onDidUpdateMarkers 2014-09-05 17:26:14 -07:00
Ben Ogle 47ef54a072 Subscribe directly to the decoration destroy for removal 2014-09-05 16:53:35 -07:00
Ben Ogle 86e9778adb Remove the decoration changed event 2014-09-05 16:51:26 -07:00
Ben Ogle aeffef30c6 Directly subscribe to the decorations in EditorComponent 2014-09-05 16:49:30 -07:00
Ben Ogle 7ae25d34e7 Decoration Params -> Properties 2014-09-05 16:20:42 -07:00
Ben Ogle e6e6028683 Deprecate decoration events 2014-09-05 15:14:27 -07:00
Ben Ogle f3e8f11d07 Use DisplayBuffer::onDidChangeSoftWrapped in Editor 2014-09-05 13:42:11 -07:00
Ben Ogle 6e8a626de2 Add ::onDidChangeSoftWrapped to DisplayBuffer and Editor 2014-09-05 13:41:46 -07:00
Ben Ogle 21e4d8a064 Add DisplayBuffer::onDidChange 2014-09-05 13:30:59 -07:00
Ivan Žužak 733e06fa8c Use shift in the example rather than explaining the difference 2014-09-05 22:24:42 +02:00
Ben Ogle db84b7952c fix another linter error 2014-09-05 12:44:56 -07:00
Ben Ogle 557562d8c8 Fix linter error 2014-09-05 12:42:33 -07:00
Ivan Zuzak b1fdb48e9e Link to relevant docs in keymap.cson file 2014-09-05 21:40:54 +02:00
Ben Ogle 0a920b18d7 Update TokenizedBUffer::on deprecations 2014-09-05 12:40:33 -07:00
Ben Ogle 289b22c782 Use event methods in TokenizedBuffer spec 2014-09-05 12:40:14 -07:00
Ivan Zuzak 6d498aad3b Explain capital letters in keystrokes 2014-09-05 21:40:11 +02:00
Ben Ogle 53fbfb8b27 Merge decoration-changed and decoration-updated
Only emit the decorations from the events
2014-09-05 12:35:52 -07:00
Ben Ogle a2adbff3e9 Add TokenizedBuffer::onDidTokenize 2014-09-05 12:33:28 -07:00
Ben Ogle 48a68d87f5 Add TextBuffer::onDidChange 2014-09-05 12:26:54 -07:00
Ben Ogle bcc6adff4f No longer need this as the event method passes it through 2014-09-05 12:26:06 -07:00
Ben Ogle 9435f852dd Use new decoration updated event method 2014-09-05 12:25:14 -07:00
Ben Ogle 569c403d56 Update the events on decorations 2014-09-05 12:08:35 -07:00
Nathan Sobo 6ad9531e5c Add Selection::onDidChangeRange and ::onDidDestroy and deprecate ::on 2014-09-05 11:39:34 -07:00
Nathan Sobo 56687027b6 Add Cursor::onDidChangePosition and ::onDidDestroy and deprecate ::on 2014-09-05 11:39:34 -07:00
Nathan Sobo 6bd8702421 Deprecate DisplayBufferMarker::on 2014-09-05 11:39:34 -07:00
Nathan Sobo c69b5fc0a0 Remove specs for ::onDidMoveCursor, which was removed 2014-09-05 11:39:33 -07:00
Nathan Sobo 1131b33a83 Add DisplayBufferMarker::onDidChange and ::onDidDestroy 2014-09-05 11:39:33 -07:00
Nathan Sobo 651eb78315 Add Editor::onDidAdd/RemoveSelection
Also update EditorComponent to use the new ::onDidAddSelection method.
2014-09-05 11:39:33 -07:00
Nathan Sobo 3e77b9b7c0 Eliminate Editor::onDidMoveCursor
If you want to know if a cursor moved, subscribe to the cursor.
2014-09-05 11:39:33 -07:00
Nathan Sobo e77b4a54dd Add Editor::onDidRemoveCursor 2014-09-05 11:39:33 -07:00
Nathan Sobo 3b6e40fbd8 Add Editor::onDidAddCursor 2014-09-05 11:39:33 -07:00
Nathan Sobo 161edfd15a Remove ‘cursors-moved’ event
It event was not document only being used in EditorComponent. Due to
our batching strategy, it’s fine to respond to individual
::onDidMoveCursor events.
2014-09-05 11:39:33 -07:00
Nathan Sobo 2a81687d38 Add Editor::onDidMoveCursor 2014-09-05 11:39:33 -07:00
Nathan Sobo 05ea381c0a Add Editor::onWill/DidInsertText 2014-09-05 11:39:33 -07:00
Nathan Sobo 63c9da02f6 Add Editor::onDidConflict 2014-09-05 11:39:33 -07:00
Nathan Sobo 5a02303b58 Add Editor::onDidStopChanging which delegates to TextBuffer 2014-09-05 11:39:33 -07:00
Nathan Sobo 1c95a55740 Add ::onDidChangeGrammar to Editor, DisplayBuffer, and TokenizedBuffer 2014-09-05 11:39:33 -07:00
Nathan Sobo 3e260eea56 Add Editor::onDidChangeSoftWrapped 2014-09-05 11:39:33 -07:00
Nathan Sobo 965afc2c37 Rename softWrap to softWrapped
Our new pattern for booleans is to have ::set and ::is, and that means
the property name needs to be in passive voice for ::is to make sense.
This is in preparation for adding a new change observation method.

* ::setSoftWrapped
* ::isSoftWrapped
* ::toggleSoftWrapped
2014-09-05 11:39:33 -07:00
Nathan Sobo d15d1572ef Add Editor::onDidChangeModified 2014-09-05 11:36:40 -07:00
Nathan Sobo 133f3f45ab Add Editor::onDidChangePath 2014-09-05 11:36:40 -07:00
Nathan Sobo b731f7cbdc Add Editor::onDidChangeTitle 2014-09-05 11:36:40 -07:00
Ben Ogle 8f6053c53f Upgrade bracket matcher to use new apis
It was really noisy with deprecations
2014-09-05 11:31:40 -07:00
Kevin Sawicki 0c749537a1 Prepare 0.126 2014-09-05 09:39:06 -07:00
Kevin Sawicki f6bb7fc089 Merge pull request #3458 from atom/ks-store-injection-grammars
Include injection grammars in active list
2014-09-05 09:14:41 -07:00
Ben Ogle 805c621ac6 Merge pull request #3459 from atom/bo-remove-editor-view-split-methods
Deprecate EditorView::split* methods
2014-09-04 17:44:22 -07:00
Ben Ogle 7fe567521e Use the model methods in the commands 2014-09-04 17:09:52 -07:00
Ben Ogle e48748122f Suggest better alternative in deprecation warnings 2014-09-04 16:49:33 -07:00
Ben Ogle 1291cf19fb Rename EditorView::getPane -> ::getPaneView() 2014-09-04 16:49:02 -07:00
Ben Ogle 336afc32c2 Add PaneView::getModel() 2014-09-04 16:48:04 -07:00
Ben Ogle a81393320c Support a copyActiveItem param in Pane::split* methods 2014-09-04 16:41:20 -07:00
Kevin Sawicki 3e407296ad 🐎 Upgrade to less-cache@0.14 2014-09-04 16:31:21 -07:00
Kevin Sawicki 7c8451e178 Use getter on GrammarRegistry 2014-09-04 16:09:46 -07:00
Kevin Sawicki 90592a70be Include injection grammars in active list
Prevents an immediate flicker when restarting Atom with editors
open that have injection matches.
2014-09-04 16:04:32 -07:00
Ben Ogle 914e4e6342 Deprecate EditorView::split* methods 2014-09-04 16:00:34 -07:00
Ben Ogle 86e623e9db Merge pull request #3457 from atom/bo-deprecate-editor-view-methods
Deprecate obvious EditorView methods
2014-09-04 15:42:28 -07:00
Ben Ogle 70df5a5c0a Remove unnecessary fixme 2014-09-04 15:30:06 -07:00
Ben Ogle dba1e22ded Deprecate EditorView::setSoftWrap 2014-09-04 15:29:54 -07:00
Ben Ogle dc21e8707f Deprecate EditorView::getFirstVisibleScreenRow 2014-09-04 15:25:55 -07:00
Ben Ogle 52c19a5dcd Deprecate pixelPositionFor*Position 2014-09-04 15:20:20 -07:00
Ben Ogle 5ae040f688 Add Editor::scrollToTop to compliment scrollToBottom() 2014-09-04 15:17:17 -07:00
Ben Ogle c511a71488 Deprecate EditorView::scrollToBottom 2014-09-04 15:17:01 -07:00
Ben Ogle 04b0ed9704 Deprecate EditorView::scrollTo*Position() functions 2014-09-04 15:14:38 -07:00
Ben Ogle 2c9241506b Fix doc strings 2014-09-04 14:53:33 -07:00
Kevin Sawicki 326f5fc646 Upgrade to settings-view@0.142 2014-09-04 14:29:15 -07:00
Nathan Sobo 705c57158d Merge pull request #3454 from atom/ns-text-buffer-event-methods
Add event subscription methods to TextBuffer and Marker
2014-09-04 15:17:55 -06:00
Nathan Sobo a50f62dc93 Merge remote-tracking branch 'origin/master' into ns-text-buffer-event-methods
Conflicts:
	package.json
2014-09-04 14:40:52 -06:00
Kevin Sawicki eb9d4ba816 📝 Use ### for example sections 2014-09-04 13:40:04 -07:00
Nathan Sobo 6cb085b341 Upgrade text-buffer again to fix specs 2014-09-04 14:15:42 -06:00
Ivan Žužak 1ffe459b1e Merge pull request #3455 from atom/iz-remove-deprecated-calls-check
Remove check for deprecated function calls
2014-09-04 22:02:51 +02:00
Nathan Sobo effe7e8070 Upgrade find-and-replace for spec fixes 2014-09-04 14:00:14 -06:00
Ivan Žužak b5676adf8a Remove check for deprecated function calls 2014-09-04 21:52:00 +02:00
Ivan Žužak 1909f24d5c Merge pull request #3433 from atom/iz-gutter-click-select-line
Support selecting rows by clicking and meta-clicking the gutter
2014-09-04 21:35:31 +02:00
Nathan Sobo a947a357f4 Upgrade text-buffer for event subscription methods 2014-09-04 12:44:53 -06:00
Kevin Sawicki d8be03850b Merge pull request #3443 from atom/ks-upgrade-fuzzaldrin
Upgrade fuzzaldrin
2014-09-04 09:20:39 -07:00
Kevin Sawicki 641698330f Upgrade to fuzzy-finder@0.58 2014-09-04 09:02:38 -07:00
Kevin Sawicki 55913626cc Upgrade to fuzzaldrin 2.1 2014-09-04 09:02:38 -07:00
Ivan Zuzak af2f155082 Merge branch 'master' into iz-gutter-click-select-line 2014-09-04 16:22:51 +02:00
Nathan Sobo b9e90f2fdd Fix editor observation docs 2014-09-04 08:06:19 -06:00
Nathan Sobo 621c247a75 Merge pull request #3418 from atom/ns-simplify-events
Clean Up Workspace Event Subscription API
2014-09-04 07:47:55 -06:00
Nathan Sobo 2f6f374cc7 Upgrade event-kit to stop throwing when emitting on disposed emitters 2014-09-04 07:02:35 -06:00
Nathan Sobo aac64e3a9b Document Workspace::onDidOpen 2014-09-04 07:01:32 -06:00
Nathan Sobo 9de0ba17b2 Add Workspace::onDidOpen event subscription method 2014-09-04 06:57:59 -06:00
Nathan Sobo 57699e6245 Use Workspace::getActivePane instead of property in spec 2014-09-04 06:57:46 -06:00
Ivan Zuzak 8a6e72f21f Respect preserveFolds attribute when creating selections from markers 2014-09-04 13:57:41 +02:00
Nathan Sobo 0f912c97b9 Upgrade emissary to call prefer calling ::dispose in Subscriber mixin 2014-09-03 19:37:42 -06:00
Nathan Sobo 4946eec367 Merge remote-tracking branch 'origin/master' into ns-simplify-events
Conflicts:
	docs/your-first-package.md
2014-09-03 19:31:37 -06:00
Nathan Sobo db571a2fbf Avoid deprecation by calling ::getActivePaneItem 2014-09-03 19:26:19 -06:00
Nathan Sobo 22c62b3107 Replace ‘editor-created’ event with ::onDidAddTextEditor 2014-09-03 19:26:07 -06:00
Nathan Sobo 83327eeabb Don’t test for editor-created events when editor is copied
We can handle this through ::onDidAddTextEditor when the copy is added
back to the pane.
2014-09-03 19:25:40 -06:00
Ben Ogle 8e286194f1 Merge pull request #3446 from atom/bo-move-var
Add columnCount variables to cursor::moveLeft and moveRight and related methods
2014-09-03 17:36:31 -07:00
Ben Ogle 691c7ee585 Considerably more elegant (and correct) moveLeft and moveRight 2014-09-03 17:19:06 -07:00
Ben Ogle 48b693c1c1 Add columnCounts to selection methods 2014-09-03 16:20:25 -07:00
Ben Ogle 7a3893c7bb Update doc strings 2014-09-03 16:10:33 -07:00
Ben Ogle 06165b2167 Remove the max call 2014-09-03 16:10:33 -07:00
Ben Ogle f82c59d865 Add columnCount to moveRight 2014-09-03 16:10:33 -07:00
Ben Ogle 29f15d0f20 Make moveLeft() with huge values span multiple rows 2014-09-03 16:10:33 -07:00
Ben Ogle 99c07decf2 Add columnCount var to Editor::moveLeft and Cursor::moveLeft 2014-09-03 16:10:33 -07:00
Ben Ogle 851034c8d3 Failing test 2014-09-03 16:10:33 -07:00
Ben Ogle 3a85148f69 Fix doc strings 2014-09-03 16:10:33 -07:00
Nathan Sobo f87f7c358a Kill a couple deprecation errors 2014-09-03 17:08:13 -06:00
Ben Ogle df579a9295 Merge pull request #3445 from atom/bo-line-for-row
lineForRow changes
2014-09-03 16:07:42 -07:00
Ben Ogle b90670ff2d Gracefully handle the case when there is no screenline 2014-09-03 16:00:57 -07:00
Nathan Sobo 67dc703c18 💄 docs 2014-09-03 16:46:53 -06:00
Nathan Sobo c508f76af0 Upgrade event-kit for deprecated .off on subscriptions 2014-09-03 16:40:18 -06:00
Nathan Sobo a8c1f2d0a5 Deprecate Workspace methods 2014-09-03 16:37:36 -06:00
Nathan Sobo 7ad992e52f 💄 2014-09-03 16:35:46 -06:00
Nathan Sobo 87fb0b46f7 Deprecate theorist-provided behaviors in Pane 2014-09-03 16:31:14 -06:00
Nathan Sobo 873818ee52 Deprecate string-based event subscriptions 2014-09-03 16:21:42 -06:00
Ben Ogle 523a255e48 Add Editor::lineTextForScreenRow()
Closes #3055
2014-09-03 14:42:46 -07:00
Ben Ogle 5e21d1ca5b Deprecate Editor::lineLengthForBufferRow 2014-09-03 14:42:46 -07:00
Ben Ogle 0703788209 Editor::linesForScreenRows -> ::tokenizedLinesForScreenRows 2014-09-03 14:42:46 -07:00
Ben Ogle c0c941b8db lineForBufferRow -> lineTextForBufferRow 2014-09-03 14:42:46 -07:00
Ben Ogle e3a0339fe3 Editor::lineForScreenRow -> ::tokenizedLineForScreenRow 2014-09-03 14:42:46 -07:00
Ben Ogle c4265776b3 Rename unused method 2014-09-03 14:42:46 -07:00
Ben Ogle ae49fd50b7 DisplayBuffer::linesForRows -> ::tokenizedLinesForScreenRows
Clarity!
2014-09-03 14:42:46 -07:00
Ben Ogle cdbbec91f0 row -> bufferRow for clarity 2014-09-03 14:42:45 -07:00
Ben Ogle dbb0ff9830 Remove unused method 2014-09-03 14:42:45 -07:00
Ben Ogle 99f899dc4a lineForScreenRow -> tokenizedLineForRow
The method was severely mislabeled
2014-09-03 14:42:45 -07:00
Ben Ogle 2b9b4a48ef tokenizedLineForRow -> tokenizedLineForScreenRow 2014-09-03 14:42:45 -07:00
Ben Ogle b516f5a74d lineForBufferRow -> lineTextForBufferRow 2014-09-03 14:42:45 -07:00
Ben Ogle 05bbc480b0 displayBuffer::lineForRow -> tokenizedLineForRow 2014-09-03 14:42:45 -07:00
Ben Ogle c0e2ed4282 Fix comments 2014-09-03 12:04:21 -07:00
Ben Ogle d3c2cd756a Fix comment 2014-09-03 12:01:36 -07:00
Kevin Sawicki 1ff9da5f7a Upgrade to markdown-preview@0.101 2014-09-03 11:31:41 -07:00
Nathan Sobo d3422786c3 Unmount component when EditorView is detached
Fixes #3431

When a pane item is moved to another pane, we *detach* the associated
view but we don’t fully remove it. This was to prevent removing a view
when it was used as the pane item directly. However, this was causing
the editor component not to be unmounted, which caused leaks and
incorrect measurements.

We need to unmount the component, but we don’t want to destroy the
editor. So I’ve moved editor destruction to the wrapper view for now,
and I only do it when the view is actually removed.

Ultimately, we need to have a 1:1 relationship with pane items and their
views and only allow a pane item to appear once in the pane tree. Then
we can recycle the same view and avoid this confusing situation where
the old view is detached and a new view is created.
2014-09-03 11:42:56 -06:00
Ivan Zuzak ebbfaa23ce Preserve folds when selecting rows by clicking the gutter 2014-09-03 18:54:46 +02:00
Ivan Zuzak 29ad748aa4 Dont propagate fold icon clicks to editor component 2014-09-03 18:52:50 +02:00
Nathan Sobo d753a7f38d Merge pull request #3440 from atom/ns-exclusive-selection-intersection
Don’t merge adjacent non-empty selections
2014-09-03 09:24:05 -06:00
Ben Ogle 1e4fb5b4bc nof 2014-09-02 17:44:01 -07:00
Nathan Sobo cfd2722348 Don’t merge adjacent non-empty selections
This improves the behavior of #3433
2014-09-02 18:10:05 -06:00
Ben Ogle ae0bffc4ea Fix some doc strings 2014-09-02 17:01:20 -07:00
Ben Ogle 3d8f3883d1 Upgrade deprecation cop to remove the status bar icon. 2014-09-02 16:52:22 -07:00
Ben Ogle 81532d52f7 Move select all fn so docs read better 2014-09-02 16:45:49 -07:00
Ben Ogle 0b34e46a1a Merge pull request #3420 from atom/bo-update-cursor-selection-apis
Update cursor and selection APIs
2014-09-02 16:41:42 -07:00
Ben Ogle 539e4e0745 Upgrade fnr to fix specs 2014-09-02 15:10:38 -07:00
Ben Ogle 1f925078e9 Remove deprecation 2014-09-02 14:59:06 -07:00
Ben Ogle a564da1eb0 Remove unnecessary deprecated methods 2014-09-02 14:58:20 -07:00
Ben Ogle dca36d1307 Reorder return values of getSelectedBufferRanges
The implementation does not depend on a specific order, just the test
2014-09-02 14:55:02 -07:00
Ben Ogle 3f67252757 Add row count docs 2014-09-02 14:50:15 -07:00
Ben Ogle a7db555030 Order the getting ranges / positions by their insertion order 2014-09-02 14:38:11 -07:00
Kevin Sawicki 3bd7edb94e Upgrade to language-sass@0.21 2014-09-02 14:25:40 -07:00
Kevin Sawicki a9227b5f43 Upgrade to language-less@0.15 2014-09-02 14:22:51 -07:00
Ben Ogle 659c05c825 Add selectToBufferPosition 2014-09-02 14:09:56 -07:00
Ben Ogle 5ea64f8b11 selectWord() -> selectWordsContainingCursors() 2014-09-02 14:03:57 -07:00
Ben Ogle 70af6198bd selectLine -> selectLinesContainingCursors 2014-09-02 14:02:26 -07:00
Kevin Sawicki 1aa391a207 Don't load ~/.atom/init.coffee in safe mode
This ensures the code from the init script isn't causing issues
when trying to reproduce something in safe mode.

Refs #2772
2014-09-02 13:51:26 -07:00
Kevin Sawicki 03a6d10703 Upgrade to tree-view@0.114 2014-09-02 13:44:13 -07:00
Kevin Sawicki 24add494ae Normalize project path in Project::setPath
This will remove consecutive slashes as well as . and ..
characters and make path comparisons accurate.

Closes atom/tree-view#191
2014-09-02 10:49:02 -07:00
Nathan Sobo c5aa446bc2 Prevent focusout events on hidden input when clicking focused editors
Fixes #3384

This is achieved by calling .preventDefault() on mousedown events when
the editor is already focused, which prevents the moving of focus to
the editor itself.
2014-09-02 11:37:46 -06:00
Kevin Sawicki fbcef99aac Upgrade to language-todo@0.11 2014-09-02 09:04:49 -07:00
Kevin Sawicki 8fa8519924 Upgrade to language-python@0.19 2014-09-02 08:56:04 -07:00
Kevin Sawicki 5a36988930 Upgrade to language-xml@0.19 2014-09-02 08:49:06 -07:00
Ivan Zuzak 65cae0f68d Fix indentation to make coffeelint happy 2014-09-01 18:24:04 +02:00
Ivan Zuzak 7f14965ca8 Support selecting multiple rows with meta-click 2014-09-01 17:42:55 +02:00
Ivan Zuzak 8c36d2673b Select row when clicking the gutter 2014-08-30 20:20:09 +02:00
Kevin Sawicki 7f2d9984b8 Upgrade to language-go@0.17 2014-08-29 11:30:06 -07:00
Kevin Sawicki d43702f017 🐎 Use direct comparison instead of regex 2014-08-29 09:55:50 -07:00
Kevin Sawicki 690d32cca9 Document undefined case 2014-08-29 09:42:51 -07:00
Kevin Sawicki d870cb8f36 Return undefined to ? fallback works 2014-08-29 09:42:01 -07:00
Kevin Sawicki 7cd080786c Return a boolean like the comment says 2014-08-29 09:40:55 -07:00
Kevin Sawicki a8a251c457 💄 2014-08-29 09:36:55 -07:00
Kevin Sawicki 9574458feb 📝 💄 2014-08-29 09:34:59 -07:00
Kevin Sawicki 692bf534b6 Merge pull request #3369 from khakionion/master
Better autodetection of Soft Tabs.
2014-08-29 09:34:01 -07:00
Kevin Sawicki f35346c507 Upgrade to tree-view@0.113 2014-08-29 09:15:34 -07:00
Kevin Sawicki 78f7ff6ee7 Focus existing window after opening path 2014-08-29 09:10:12 -07:00
Kevin Sawicki 71470f88ad Merge pull request #3409 from syndbg/master
Added linux open-folder keybind.
2014-08-29 08:32:07 -07:00
Ben Ogle 4b4af946cf Fix specs 2014-08-28 18:30:08 -07:00
Ben Ogle 5163d0f810 Fix dupe functions 2014-08-28 18:19:12 -07:00
Ben Ogle 08388d87c0 Remove getSelectionAtIndex
We’re going to eventually get rid of the clone in 
getSelections / getCursors freeze those properties 
and return them directly. Then they can operate 
on the list garbage free.
2014-08-28 18:16:33 -07:00
Ben Ogle 9c53d6f014 Deprecate getCursor() 2014-08-28 18:10:18 -07:00
Nathan Sobo c9e5ff6606 Organize workspace API into sections. Add ::getActiveTextEditor 2014-08-28 19:06:57 -06:00
Ben Ogle 2fced0c1bc Deprecate getSelection 2014-08-28 17:51:16 -07:00
Ben Ogle 271af5dd99 Add Editor::setSelectedScreenRanges() 2014-08-28 17:28:24 -07:00
Nathan Sobo 6c2bb26e77 Add Workspace::observeTextEditors and ::onDidAddTextEditor 2014-08-28 18:28:10 -06:00
Nathan Sobo 70a23b0107 Add Workspace::getPanes and ::getPaneItems 2014-08-28 18:27:13 -06:00
Ben Ogle c6a76e6c62 De public a non-used method 2014-08-28 16:59:10 -07:00
Ben Ogle 535724fa84 Nothing uses this function, not internal nor packages. 2014-08-28 16:58:59 -07:00
Ben Ogle 14dcf50683 De-public a couple unused methods 2014-08-28 16:53:44 -07:00
Ben Ogle 882261e782 Break into Essential and extended 2014-08-28 16:53:21 -07:00
Nathan Sobo acb9bdaf33 Add pane and pane item observer methods to workspace. Also document. 2014-08-28 17:52:30 -06:00
Ben Ogle 713d82a895 Reorder selections section 2014-08-28 16:51:31 -07:00
Nathan Sobo a33706ddbc Add PaneContainer::onDidAddPaneItem and ::observePaneItems 2014-08-28 17:42:12 -06:00
Ben Ogle 2551313b58 Deprecate methods on cursor 2014-08-28 16:31:10 -07:00
Nathan Sobo 877fa40a49 Activate next pane on before invoking onDidDestroy observers 2014-08-28 17:23:49 -06:00
Nathan Sobo cee7539e35 Add PaneContainer::observePanes and ::onDidAddPane 2014-08-28 17:22:28 -06:00
Ben Ogle 7ca5ece68a Add methods to be symmetrical with selections
* getCursorsOrderedByBufferPosition
* getCursorBufferPositions
* getCursorScreenPositions
2014-08-28 16:06:44 -07:00
Nathan Sobo 5471e9bccc Add PaneContainer::onDidDestroyPaneItem 2014-08-28 16:58:38 -06:00
Nathan Sobo b60b9f3e3a Add Pane::observeItems 2014-08-28 16:40:54 -06:00
Nathan Sobo 12f78dd957 💄 2014-08-28 16:27:20 -06:00
Ben Ogle e74244fc25 Move less used function down in the order 2014-08-28 15:25:35 -07:00
Ben Ogle 65ba95a449 Add missing docs 2014-08-28 15:25:23 -07:00
Ben Ogle 370ad23f7c Remove Cursor from ::moveCursor* methods 2014-08-28 15:25:12 -07:00
Nathan Sobo cea4db5381 Break pane API into sections 2014-08-28 16:24:53 -06:00
Nathan Sobo 345617e0f3 Clean up existing pane API docs 2014-08-28 15:57:30 -06:00
Nathan Sobo 78c24fb737 Remove legacy event documentation 2014-08-28 15:54:16 -06:00
Ben Ogle 01b48d2a0a Move adding cursor functions under more heavily used functions 2014-08-28 14:44:23 -07:00
Ben Ogle 45a9bd21fc Deprecate getCursorScreenRow
It’s out of place, and unnecessary.
2014-08-28 14:43:36 -07:00
Ben Ogle db83375293 Buffer before screen, get before set 2014-08-28 14:38:58 -07:00
Ben Ogle a4a25576a2 Split cursor methods into essential and extended 2014-08-28 14:35:39 -07:00
Nathan Sobo e88eb3012e Document Pane event subscription methods 2014-08-28 15:32:02 -06:00
Nathan Sobo d7063c0932 Add Pane::onDidDestroy 2014-08-28 15:21:35 -06:00
Nathan Sobo 34cb5d6012 Don’t implement ::isActive in terms of the theorist model behavior 2014-08-28 15:21:10 -06:00
Nathan Sobo 95e9686b37 Emit legacy item-added DOM event correctly 2014-08-28 14:42:13 -06:00
Nathan Sobo b1916069de Delegate ::getActivePane[Item] directly to PaneContainer 2014-08-28 14:42:13 -06:00
Nathan Sobo 9223361c22 Add PaneContainer::onDidChange/observeRoot
Also, don’t use a behavior to monitor root changes
2014-08-28 14:42:13 -06:00
Nathan Sobo 7556b85806 Remove/add the *subscription*, not the child 2014-08-28 14:42:12 -06:00
Nathan Sobo b74554ad4c Actually subscribe 2014-08-28 14:42:12 -06:00
Nathan Sobo b34367ad44 💄 pane-container-spec 2014-08-28 14:42:12 -06:00
Nathan Sobo cf3e1177ab Upgrade event-kit to fix stupid bug 2014-08-28 14:42:12 -06:00
Nathan Sobo 74b2f26540 Add PaneContainer::onDidChangeActivePaneItem 2014-08-28 14:41:56 -06:00
Nathan Sobo b89202f82c Dispose of PaneAxis subscriptions when destroyed 2014-08-28 14:41:56 -06:00
Nathan Sobo 9487609f0c 💄 pane-container-spec 2014-08-28 14:41:56 -06:00
Nathan Sobo da63c6bab2 Add ::onDidDestroy to Pane and PaneAxis 2014-08-28 14:41:56 -06:00
Ivan Žužak a00b3b2cc9 Merge pull request #3394 from atom/iz-builtin-context-menu-commands
Add cut/copy/paste built-in context menu items
2014-08-28 20:41:21 +02:00
Ben Ogle 0888d8ac60 Upgrade atom-dark-ui 2014-08-28 11:27:45 -07:00
Ben Ogle b895f7074d Upgrade find and replace to remove deprecation 2014-08-28 11:19:40 -07:00
Ivan Zuzak 7b73d6749d Add undo, redo, delete and select all commands to context menu 2014-08-28 20:13:16 +02:00
Nathan Sobo 67c3a41a60 Upgrade event-kit to fix bug and get CompositeDisposable::remove 2014-08-28 11:43:33 -06:00
Nathan Sobo c25ab0db43 Rename ::disposables to ::subscriptions in PaneView
Now that emissary’s Subscriber no longer conflicts with the name
2014-08-28 11:43:09 -06:00
Nathan Sobo 9979ae4b09 Add child event methods to PaneAxis
This eliminates our reliance on the Sequence object for informing us
of changes
2014-08-28 11:43:09 -06:00
Nathan Sobo a9294aebc3 Don’t use Sequence in PaneAxis 2014-08-28 11:43:09 -06:00
Nathan Sobo f161f5352e Don’t rely on Sequences to reparent the last child 2014-08-28 11:43:09 -06:00
Nathan Sobo 4f826a70f8 Use Array instead of Sequence for Pane::items 2014-08-28 11:43:08 -06:00
Nathan Sobo 1d365db9df Use getActiveItem getter in pane-view-spec 2014-08-28 11:43:08 -06:00
Nathan Sobo b9feed8eb4 Handle objects in subscriptions instead of multiple args 2014-08-28 11:43:08 -06:00
Nathan Sobo 475dc6074c 💄 2014-08-28 11:43:08 -06:00
Nathan Sobo 40d93cd0cf Use Pane::setActiveItem internally so observers are invoked 2014-08-28 11:43:08 -06:00
Nathan Sobo 99d70b4a4e Don’t emit ::onDidChangeActiveItem events unless it really changes 2014-08-28 11:43:08 -06:00
Nathan Sobo 87cdc1a45d Don’t invoke ::onDidChangeActivePane events unless it really changes 2014-08-28 11:43:08 -06:00
Nathan Sobo e7a7e86dea Add Pane::observeActive and ::observeActiveItem
These have behavior semantics, invoking the observer immediately with
the current value of the observed property.
2014-08-28 11:43:07 -06:00
Nathan Sobo b6c669a292 Subscribe with new event methods in PaneView 2014-08-28 11:43:07 -06:00
Nathan Sobo 44d70aaa5b Add Pane::onDidChangeActive() 2014-08-28 11:43:07 -06:00
Nathan Sobo 2b63f8a4ee Add PaneContainer::onDidChangeActivePane 2014-08-28 11:43:07 -06:00
Nathan Sobo 8225f759bf Add Pane::onWillDestroyItem() 2014-08-28 11:43:07 -06:00
Nathan Sobo 2d58d9c8b5 Add Pane::onDidActivate 2014-08-28 11:43:07 -06:00
Nathan Sobo 548018e9b2 Add spec for onDidRemoveItem observers when moving items to other panes 2014-08-28 11:43:07 -06:00
Nathan Sobo 9bd2eec4bc Add Pane::onDidMoveItem() 2014-08-28 11:43:07 -06:00
Nathan Sobo b8fcbe9451 Start adding event subscription methods to pane
This branch uses EventKit, an ultra-simple library for implementing
events. The object implementing the methods maintains its own emitter
object rather than doing a mixin like Emissary encourages. This will
make it easier for us to deprecate ::on on the object itself. Unlike
emissary, the EventKit Emitter implements a super minimalistic API that
only allows one value to be emitted and always returns a Disposable
from subscriptions.
2014-08-28 11:43:06 -06:00
Ivan Zuzak 861dff107a 💄 simplify assignment using coffeescript magic 2014-08-28 19:42:59 +02:00
Nathan Sobo ebb02bcd37 Use public getters instead of properties in pane-spec 2014-08-28 11:42:42 -06:00
syndg fcb72049e1 Added linux open-folder keybind.
Moved application:open-dev to `ctrl-alt-o`
2014-08-28 14:05:42 +03:00
Michael Herring 039e61e960 updated regex in usesSoftTabs to only look for Tab and Space 2014-08-28 17:44:49 +09:00
Michael Herring 002f9ca2b1 merge latest upstream master 2014-08-28 17:35:09 +09:00
Ben Ogle 726b04ef80 Update find-and-replace 2014-08-27 19:21:49 -07:00
Kevin Sawicki 20b64dd744 Upgrade to tabs@0.50 2014-08-27 15:26:31 -07:00
Kevin Sawicki a3d2924484 Split on whitespace 2014-08-27 14:43:23 -07:00
Kevin Sawicki d225966374 installSize -> installedSize 2014-08-27 14:35:16 -07:00
Kevin Sawicki 6d23fa1620 🐧 Set installedSize value in control file
Closes #2183
2014-08-27 14:23:09 -07:00
Kevin Sawicki 682d84dea0 Log location of created .deb file 2014-08-27 14:09:57 -07:00
Kevin Sawicki 969e5b65df Upgrade to welcome@0.18 2014-08-27 12:36:36 -07:00
Kevin Sawicki 8e5f4ced21 🐧 Move settings items to Edit menu
Closes #2467
2014-08-27 12:34:48 -07:00
Ben Ogle bbdd304834 Merge pull request #3229 from atom/docs-metadata
New API docs
2014-08-27 11:33:04 -07:00
Kevin Sawicki 5360b719f2 Upgrade to oniguruma 3.0.4 2014-08-27 11:29:14 -07:00
Kevin Sawicki 17a9e397f3 🐧 Add ~/.atom open commands to File menu 2014-08-27 11:10:30 -07:00
Kevin Sawicki f01f0eb90b Upgrade to language-sass@0.20 2014-08-27 10:57:33 -07:00
Kevin Sawicki 53d16098ca Merge pull request #3396 from syndbg/master
Added missing keybind in linux.cson.
2014-08-27 10:17:25 -07:00
Ben Ogle cb34539508 Fix syntax highlighting 2014-08-27 09:36:08 -07:00
syndg 1221a140aa Added missing keybind in linux.cson.
`'ctrl-,': 'application:show-settings'` exists in `win32.cson` and `darwin32.cson`
2014-08-27 17:10:08 +03:00
Ivan Zuzak 776a8e935a Add cut/copy/paste built-in context menu items 2014-08-27 13:55:44 +02:00
Ivan Zuzak 096255f283 Support built-in context menu items 2014-08-27 13:54:55 +02:00
Ben Ogle 327749b6c5 Upgrade packages in the public docs.
They now are on the latest grunt-atomdoc and have docstrings that export
well
2014-08-26 19:23:51 -07:00
Kevin Sawicki cd3bd048fd Merge pull request #3389 from atom/ks-spec-task-on-linux
Add spec task on Linux
2014-08-26 18:38:34 -07:00
Ben Ogle d79807fbe5 Document atom.* globals as properties 2014-08-26 16:00:35 -07:00
Ben Ogle 7a67619216 Use donna 1.0 2014-08-26 15:59:53 -07:00
Kevin Sawicki 496e49a5b9 Use correct process name on Linux 2014-08-26 15:10:43 -07:00
Kevin Sawicki 08decbe533 Explicitly set app path perms to 755 on Linux 2014-08-26 14:59:42 -07:00
Kevin Sawicki 37fc8b5945 Add initial spec task support on linux 2014-08-26 14:59:42 -07:00
Kevin Sawicki f88c805466 Upgrade to markdown-preview@0.100 2014-08-26 14:59:28 -07:00
Kevin Sawicki e8cd59eaef Waits for reloaded event from ThemeManager
Previously disabling a theme wouldn't wait for the full reload
to finish since and the spec would intermittently fail because
the promise was fulfilled after the spec completed and the
subscription was leaked.
2014-08-26 14:23:20 -07:00
Kevin Sawicki 600005de15 Upgrade to go-to-line@0.25 2014-08-26 14:21:14 -07:00
Kevin Sawicki aaa788b8da Upgrade to settings-view@0.141 2014-08-26 12:53:44 -07:00
Kevin Sawicki 9a70fdc3d9 Prepare 0.125 2014-08-26 12:04:27 -07:00
Ben Ogle 48f161b63a Merge pull request #3388 from atom/bo-remove-all-old-editor
Remove ancillary old editor views
2014-08-26 11:39:51 -07:00
Kevin Sawicki f032dacebb Upgrade to background-tips@0.16 2014-08-26 11:24:10 -07:00
Ben Ogle a296364e53 Fixup doc strings that don’t parse correctly
We can’t have a huge indent on the second line of a list item. Markdown
parses it as a code block. :(
2014-08-26 10:47:36 -07:00
Kevin Sawicki 5ba86b3dbc Normalize file paths in Project::resolve
This ensures the drive case letter is consistent on Windows
when opening file paths from the command line.
2014-08-26 10:22:20 -07:00
Ben Ogle 790f134d7c Remove ancillary old editor views 2014-08-26 10:07:18 -07:00
Kevin Sawicki 32353a31eb Only restore window when it is minimized
Restoring a maximized windows on Windows unmaximizes it.

Closes #3381
2014-08-26 09:55:22 -07:00
Nathan Sobo 0bae432109 Revert "Merge the react and non-react editor styles"
This reverts commit 01c4fe5340.
2014-08-26 09:24:44 -06:00
Ben Ogle d8a4280df1 Upgrade text-buffer for updated doc strings 2014-08-25 19:05:50 -07:00
Nathan Sobo dccb2c295c Update status bar to fix specs 2014-08-25 18:48:03 -06:00
Nathan Sobo 2a94e4a33c Pass tokenizedLines to LinesComponent so measuring stays in sync
Fixes #3318
2014-08-25 18:16:45 -06:00
Nathan Sobo 3371ceadf3 Add regression coverage for remeasuring characters w/ requested update
The problem here is that the lines in the editor no longer reflect the
lines on screen, but we're looking for line nodes corresponding to the
editor's current contents.
2014-08-25 18:16:38 -06:00
Nathan Sobo 070ca1a4bb Call checkForVisibilityChange when showing editors via EditorView::show/hide 2014-08-25 18:16:29 -06:00
Kevin Sawicki 157a753bfb Upgrade to space-pen 3.4.3 2014-08-25 16:36:53 -07:00
Nathan Sobo 0849f1ea84 Perform a sync update after editor becomes visible
This pauses updates during measurements. If any updates were requested
while measuring, we force an update synchronously to prevent flicker.

This commit also removes the visibility check before updating, because
we can't force an update from this spot in the lifecycle. This shouldn't
be needed anyway because we are polling for visibility changes.
2014-08-25 17:31:29 -06:00
Nathan Sobo c4f5a3516b Assign atom.lastUncaughtError in window.onerror handler 2014-08-25 16:36:14 -06:00
Kevin Sawicki 08f39c6a5a Upgrade to atom-keymap@2.0.4 2014-08-25 15:35:47 -07:00
Kevin Sawicki 69aa34954f Remove call to no-op EditorView method 2014-08-25 15:31:39 -07:00
Ben Ogle d6d7d3942c 📝 Convert EditorView docs 2014-08-25 15:26:38 -07:00
Ben Ogle c3acd8cf0c 📝 Convert workspaceView docs 2014-08-25 15:13:46 -07:00
Ben Ogle 12f58f0478 📝 Convert workspace docs 2014-08-25 15:13:45 -07:00
Ben Ogle 66e6a481a4 📝 Convert ThemeManager docs 2014-08-25 15:13:45 -07:00
Ben Ogle de27dce6bf Convert SelectListView docs 2014-08-25 15:13:45 -07:00
Ben Ogle 6c19a58c7c 📝 Convert Project docs 2014-08-25 15:13:45 -07:00
Ben Ogle c294208f87 📝 Convert PaneView docs 2014-08-25 15:13:45 -07:00
Ben Ogle d7f3add250 📝 Convert Pane docs 2014-08-25 15:13:45 -07:00
Ben Ogle a93ef05e13 📝 Convert Selection docs 2014-08-25 15:13:45 -07:00
Kevin Sawicki 8241918b6e Include classes from node_modules in proper object and order 2014-08-25 15:13:45 -07:00
Ben Ogle 8aae7b983e 📝 Convert Cursor Docs 2014-08-25 15:13:45 -07:00
Ben Ogle ad4f464d7c Fix Decoration events 2014-08-25 15:13:45 -07:00
Ben Ogle 352f1e34ab No mo : 2014-08-25 15:08:46 -07:00
Kevin Sawicki 2cdae3c6c1 Upgrade to pathwatcher 2.0.9 2014-08-25 15:08:46 -07:00
Kevin Sawicki 5df944e804 📝 Update Task docs 2014-08-25 15:08:46 -07:00
Kevin Sawicki 762f8c2e5a 📝 Update Syntax docs 2014-08-25 15:08:46 -07:00
Kevin Sawicki 41c96a82a9 Upgrade to first-mate 2.0.4 2014-08-25 15:08:46 -07:00
Ben Ogle 5a51d3a0c9 Fix indentation 2014-08-25 15:08:46 -07:00
Ben Ogle d79eb8eed3 Update decoration docs 2014-08-25 15:08:46 -07:00
Ben Ogle 9bb6bdf666 Update decorateMarker docs 2014-08-25 15:08:46 -07:00
Ben Ogle 0ab282340f Upgrade to tello@0.2 2014-08-25 15:08:45 -07:00
Ben Ogle 393f6bdd35 Add optional to all optional args 2014-08-25 15:08:45 -07:00
Ben Ogle a4b2f3aa4b Editor event documentation 2014-08-25 15:08:45 -07:00
Kevin Sawicki ee4c7e996f 📝 Convert ScrollView docs 2014-08-25 15:08:45 -07:00
Kevin Sawicki 59bf16fed3 📝 Convert PackageManager docs 2014-08-25 15:08:45 -07:00
Ben Ogle 99917a536e Add a couple events for test purposes 2014-08-25 15:08:45 -07:00
Ben Ogle 61a82fa0e4 Upgrade tello to use new events format 2014-08-25 15:08:45 -07:00
Kevin Sawicki 0bca0810b9 📝 Convert MenuManager docs 2014-08-25 15:08:45 -07:00
Kevin Sawicki 7d910000a2 📝 Convert DeserializerManager docs 2014-08-25 15:08:45 -07:00
Kevin Sawicki af67671f60 📝 Convert ContextMenuManager docs 2014-08-25 15:08:45 -07:00
Kevin Sawicki 0079007bb6 📝 Convert Config docs 2014-08-25 15:08:45 -07:00
Kevin Sawicki c2fa20c543 📝 Convert Clipboard docs 2014-08-25 15:08:45 -07:00
Kevin Sawicki aa5870570f 📝 Convert BufferedProcess docs 2014-08-25 15:08:45 -07:00
Kevin Sawicki 0e0d62b54c 📝 Convert BufferedNodeProcess docs 2014-08-25 15:08:45 -07:00
Kevin Sawicki 37542e3117 📝 Convert Atom docs 2014-08-25 15:08:44 -07:00
Kevin Sawicki 90e89ebaf9 📝 Convert Git docs 2014-08-25 15:07:15 -07:00
Ben Ogle e4939a8d6d Add sections to editor docs 2014-08-25 15:07:15 -07:00
Kevin Sawicki 50ddb8e3cc Upload atom-api.json asset during publish 2014-08-25 15:04:22 -07:00
Kevin Sawicki c61e36c7d3 Remove donnatello output 2014-08-25 15:04:22 -07:00
Kevin Sawicki 3035242daf Task is no longer async 2014-08-25 15:04:22 -07:00
Kevin Sawicki cfc965c135 Write to docs/output/api.json 2014-08-25 15:04:22 -07:00
Kevin Sawicki 1bc879a9b0 Remove copy-docs unused task 2014-08-25 15:04:22 -07:00
Kevin Sawicki 6225acbe8b Genrate docs from local api.json files 2014-08-25 15:04:22 -07:00
Kevin Sawicki bdf68ba15f Upgrade to theorist 1.0.2 2014-08-25 15:04:22 -07:00
Kevin Sawicki bbef8083d5 Upgrade to text-buffer 3.0.2 2014-08-25 15:04:21 -07:00
Kevin Sawicki f2f88cb343 Upgrade to space-pen 3.4.2 2014-08-25 15:04:21 -07:00
Kevin Sawicki a42de1e9be Upgrade to pathwatcher@2.0.8 2014-08-25 15:04:21 -07:00
Kevin Sawicki 1c97eb977e 💄 2014-08-25 15:04:21 -07:00
Kevin Sawicki d13fd495e4 Upgrade to first-mate 2.0.3 2014-08-25 15:04:21 -07:00
Kevin Sawicki c49ef1fd9d Parse all api.json files in node_modules 2014-08-25 15:04:21 -07:00
Ben Ogle e011be3428 Update argument lists in editor.coffee 2014-08-25 15:04:21 -07:00
Kevin Sawicki f993b78479 Remove unused deploy-docs task 2014-08-25 15:04:21 -07:00
Ben Ogle 86ef06ebb1 Update to contain superclasses 2014-08-25 15:04:21 -07:00
Ben Ogle 9f27e53bf1 Remove unnecessary metadata.json 2014-08-25 15:04:21 -07:00
Ben Ogle 720770e2ba Dont dupe classes 2014-08-25 15:04:21 -07:00
Ben Ogle e86b8c1a66 Remove unnecessary things 2014-08-25 15:04:20 -07:00
Ben Ogle 3b9d25ae92 Use donna and tello. Together. 2014-08-25 15:04:20 -07:00
Ben Ogle 9ee6a3d430 Docs task uses metadoc 2014-08-25 15:04:19 -07:00
Ben Ogle 162713e5b5 Sync package.json files for doc imports 2014-08-25 15:04:19 -07:00
Ben Ogle 83659272ac Merge pull request #3132 from atom/bo-remove-oe
Remove old editor
2014-08-25 14:59:30 -07:00
Ben Ogle abadc9805c Add _all_ the defaults in from editorView 2014-08-25 14:46:48 -07:00
Ben Ogle ad269357b7 Ugh. No @mini. @mini is a prop. 2014-08-25 14:34:19 -07:00
Ben Ogle ae323d2083 Use @mini 2014-08-25 12:24:19 -07:00
Ben Ogle cef24a3979 Remove the react editor options 2014-08-25 12:15:34 -07:00
Ben Ogle 7220c61e98 upgrade to find-and-replace to remove old editor stuff 2014-08-25 12:13:41 -07:00
Ben Ogle 6e40f3b2f9 upgrade to bookmarks to remove old editor stuff 2014-08-25 12:11:39 -07:00
Ben Ogle c4811a8fb7 upgrade to git-diff to remove old editor stuff 2014-08-25 12:10:13 -07:00
Kevin Sawicki c7242ec964 Upgrade to language-yaml@0.17 2014-08-25 12:09:39 -07:00
Ben Ogle d37bd14a62 Deprecate getEditor() at a later date… 2014-08-25 12:07:48 -07:00
Ben Ogle 3d2d8c491f rename react-editor-view -> editor-view 2014-08-25 12:07:48 -07:00
Ben Ogle b05a83a6ce move getEditor fn 2014-08-25 12:07:48 -07:00
Ben Ogle 0d68430bb8 Deprecate gutter api 2014-08-25 12:07:48 -07:00
Ben Ogle d75485a2d8 Deprecate no-op’d things 2014-08-25 12:07:48 -07:00
Ben Ogle e54bc0fbaa Remove toggle soft wrap and toggle soft tabs
No one uses them in packages, and we don’t use them internally
2014-08-25 12:07:17 -07:00
Ben Ogle cf9208bc02 Deprecate pageUp / pageDown 2014-08-25 12:07:16 -07:00
Ben Ogle 35fd19f3ef 📝 Add docs for current public methods 2014-08-25 12:07:16 -07:00
Ben Ogle 97bcdcc9b0 Rename ReactEditorView -> EditorView 2014-08-25 12:05:55 -07:00
Ben Ogle fc441ef5e2 📝 fix return doc 2014-08-25 12:05:55 -07:00
Ben Ogle 01c4fe5340 Merge the react and non-react editor styles 2014-08-25 12:05:10 -07:00
Ben Ogle 431fab1a43 Remove the editor-view 2014-08-25 12:05:10 -07:00
Ben Ogle 0e62c98768 Fix the mini-editor test 2014-08-25 12:02:38 -07:00
Ben Ogle f8026bb005 Require the react-editor-view where we require the editor-view 2014-08-25 12:02:38 -07:00
Ben Ogle ef889a50ed Merge pull request #3376 from atom/bo-marker-in-splits
Only emit the marker-created event when the marker exists
2014-08-25 11:55:12 -07:00
Ben Ogle c3c91ae6c3 Only emit the marker-created event when the marker exists
This caused problems in the case of find-and-replace:select-all with 
multiple editors into the same file. 

* a marker is created on the TextBuffer capturing the selection
* DisplayBuffer:create-marker is fired from the first DisplayBuffer. 
The marker is turned into a selection which is merged into the current 
selection, deleting the marker that was created.
* DisplayBuffer::handleBufferMarkerCreated is called on the second
DisplayBuffer. The marker has been destroyed at this point, so it emits 
DisplayBuffer:create-marker with undefined. 
* ERROR>

Closes #3364
2014-08-25 11:42:44 -07:00
Ben Ogle c073d042b8 Merge pull request #3371 from atom/iz-fix-context-menu-again
Dont handle ctrl+click mouse events on OSX
2014-08-25 10:44:11 -07:00
Kevin Sawicki ecf237697b Add react editor view spec for data grammar attrs 2014-08-25 10:22:44 -07:00
Kevin Sawicki 4b8fd222f1 Merge pull request #2996 from thomasjo/editorview-grammarscopes
Add grammar scope to EditorView
2014-08-25 10:14:46 -07:00
Kevin Sawicki 62a1888c6b Upgrade to language-mustache@0.10 2014-08-25 10:06:29 -07:00
Kevin Sawicki 176d73dfcc Upgrade to apm 0.92 2014-08-25 09:58:09 -07:00
Kevin Sawicki c0704edb0d Merge pull request #3355 from atom/ks-open-files-in-current-window
Open file paths in focused window
2014-08-25 09:39:11 -07:00
Kevin Sawicki fb53f85573 Rename topWindow to lastFocusedWindow 2014-08-25 09:21:27 -07:00
Kevin Sawicki 6610447e09 📝 Clarify safeMode description 2014-08-25 09:21:27 -07:00
Kevin Sawicki 9f7560bb89 📝 Doc other atom.open options 2014-08-25 09:21:27 -07:00
Kevin Sawicki a853a27857 Open folder in current untitled window
Untitled windows will now have their project path set when a folder
is opened.
2014-08-25 09:21:27 -07:00
Kevin Sawicki 154fe4006f 📝 Doc file/folder path behavior in usage message 2014-08-25 09:21:27 -07:00
Kevin Sawicki 17feb91876 📝 Move comment above isFile check 2014-08-25 09:21:27 -07:00
Kevin Sawicki 5bdf8a14a7 Use once for closed event 2014-08-25 09:21:27 -07:00
Kevin Sawicki 1196e5a264 📝 Doc dev mode case 2014-08-25 09:21:27 -07:00
Kevin Sawicki a4ae314b00 💄 2014-08-25 09:21:27 -07:00
Kevin Sawicki c9390b61de Track AtomWindow from ipc open event
This ensures the window where the file is dropped is used to open
that file into even when that window does not have focus.
2014-08-25 09:21:27 -07:00
Kevin Sawicki 2811663177 Open file paths in focused window 2014-08-25 09:21:26 -07:00
Thomas Johansen 4dc89f1b1e 🐛 Fix incorrect assertions 2014-08-24 14:14:26 +02:00
Ivan Zuzak b0a9eefa04 Dont handle ctrl+click mouse events on OSX
Ctrl+click on OSX brings up the context menu. In that case, handling the click
event deselects any selection, and that shouldn't happen. See
https://github.com/atom/atom/issues/3308.
2014-08-24 13:33:30 +02:00
Michael Herring aa9728c004 Better autodetection of Soft Tabs.
Check for the presence of a space, instead of the absence of a tab.
This ensures that Soft Tabs will not be activated on a file that (for
example) starts with a BOM or something, but is otherwise full of hard
tabs.
2014-08-24 19:04:46 +09:00
Thomas Johansen dc55d42491 Add test to verify implementation 2014-08-24 10:17:26 +02:00
Thomas Johansen d3b06542a5 Change attribute name to data-grammar 2014-08-24 10:17:25 +02:00
Thomas Johansen 9c154a2f5a Implement support for React Editor 2014-08-24 10:17:25 +02:00
Thomas Johansen 6b1868efd5 Make sure the scope data attribute is always set 2014-08-24 10:17:25 +02:00
Thomas Johansen e50887aab8 Use data attribute instead of CSS classes 2014-08-24 10:17:25 +02:00
Thomas Johansen c6ca03fa49 Add grammar scope to EditorView
Only works when the `grammar-changed` event has been triggered,
and only implemented for Space Pen views.
2014-08-24 10:17:25 +02:00
Ben Ogle a15deaef81 Upgrade to git-diff@0.38.0 to add arrow for deletions. 2014-08-23 10:43:46 -07:00
Kevin Sawicki 461cca2d22 Upgrade to incompatible-packages@0.9 2014-08-22 12:01:41 -07:00
Nathan Sobo d15728321c Merge pull request #3353 from atom/ns-fix-char-width-measurement
Rework DOM measurement to try to prevent measurement errors
2014-08-22 12:07:42 -06:00
Nathan Sobo 9164b0ea3b Don't remeasure char widths until we've performed initial measurement 2014-08-22 11:37:04 -06:00
Nathan Sobo 59a80dcd60 Don't use guard clauses in short measurement methods 2014-08-22 11:36:17 -06:00
Nathan Sobo 72354ebf32 Check for visibility change when mounting instead of polling DOM 2014-08-22 11:35:51 -06:00
Kevin Sawicki a56cea2408 Upgrade to language-ruby-on-rails@0.18 2014-08-22 10:29:05 -07:00
Kevin Sawicki cc28eaf6d2 Upgrade to language-mustache@0.9 2014-08-22 08:56:29 -07:00
Kevin Sawicki 283c76ad3c Upgrade to language-make@0.12 2014-08-22 08:50:53 -07:00
Nathan Sobo a71a524ec7 Rework DOM measurement to try to prevent measurement errors
* Simplify scrollbar refresh and measurement by using imperative DOM
  manipulation instead of React to hide/show scrollbars.
* Rename `::performInitialMeasurement` to `::becameVisible`
* Break `::checkForVisibilityChange` out of `::pollDOM` and use it in
  to check for the element becoming visible in `componentWillUpdate`.
* Don't rely on stored visibility state anywhere. Always check again.
  This could potentially be cached for an update cycle but being wrong
  about this is disastrous so I'm being conservative.
2014-08-21 17:50:46 -06:00
Ben Ogle a2f7ec9d73 Merge pull request #3350 from atom/bo-theme-classes
Add classes to the workspace for themes
2014-08-21 12:01:53 -07:00
Ben Ogle 26d696a93d Add classes to the workspace for themes
Fixes #3097
2014-08-21 11:53:10 -07:00
Ben Ogle 30aa47026d Do not move cursor when interacting with horiz scrollbar
Fix #3284
2014-08-21 10:40:16 -07:00
Kevin Sawicki ec6bfbb9e6 Merge pull request #3349 from aaronshaf/patch-1
Removed unnecessary comma
2014-08-21 10:11:00 -06:00
Aaron Shafovaloff d986ab0293 Removed unnecessary comma 2014-08-21 10:09:32 -06:00
Kevin Sawicki c7f5321d14 Upgrade to language-make@0.11 2014-08-21 09:59:38 -06:00
Kevin Sawicki cb868bab4c Upgrade to language-hyperlink@0.12 2014-08-21 09:57:15 -06:00
Kevin Sawicki 024d7f8d88 Upgrade to language-sass@0.19 2014-08-20 10:13:32 -06:00
Cheng Zhao 1022d8ab4b Upgrade to atom-shell@0.15.9 2014-08-20 19:04:45 +08:00
Kevin Sawicki 760e6c6c29 Upgrade to language-sass@0.18 2014-08-19 16:37:42 -06:00
Nathan Sobo beae15ef7f Update find-and-replace for new config settings 2014-08-19 16:24:14 -06:00
Nathan Sobo 5cb084d568 Fix editor:move-to-first-character-of-line with leading hard tabs
Fixes #3314
2014-08-19 11:50:04 -06:00
Kevin Sawicki f72daffdbb Upgrade to spell-check@0.42 2014-08-19 11:44:09 -06:00
Kevin Sawicki 4aba8fef7e 📝 Mention Task::start/Task::send throw errors
Refs #3326
2014-08-19 11:39:20 -06:00
Kevin Sawicki 75853e2f9c Upgrade to language-ruby-on-rails@0.17 2014-08-19 10:28:46 -06:00
Kevin Sawicki 3baeb3797f Merge pull request #3289 from abahgat/mkdeb-permissions
🐧 Set file mode to 755 to directories created for mkdeb.
2014-08-19 10:03:15 -06:00
Kevin Sawicki 7d0452f093 Upgrade to language-ruby@0.35 2014-08-19 09:45:50 -06:00
Kevin Sawicki 7edcadb50a Upgrade to language-ruby-on-rails@0.16 2014-08-18 19:11:17 -06:00
Kevin Sawicki fc198b6e17 Upgrade to language-gfm@0.48 2014-08-18 18:57:03 -06:00
Kevin Sawicki 86f8944aaf Remove default Window menu on Windows/Linux
This is really only a thing on Mac OS X

Closes #3307
2014-08-18 09:31:10 -06:00
Kevin Sawicki dda0e7f4ce Upgrade to language-c@0.28 2014-08-18 08:35:07 -06:00
Kevin Sawicki e1ff6ab327 Upgrade to setings-view@0.140 2014-08-18 08:22:29 -06:00
Kevin Sawicki 2b87f4bcb5 Merge pull request #3315 from dmnd/fix-checkout-head
Fix error when running `checkout-head-revision`
2014-08-18 08:07:07 -06:00
Cheng Zhao 3190c0c517 Upgrade to atom-shell@0.15.8 2014-08-18 20:02:15 +08:00
Desmond Brand a1d8ee86f9 Fix error when running checkout-head-revision
I got the following error when running `checkout-head-revision`:

```
Uncaught TypeError: undefined is not a function /Applications/Atom.app/Contents/Resources/app/src/editor-component.js:804
addCommandListeners.editor:checkout-head-revision /Applications/Atom.app/Contents/Resources/app/src/editor-component.js:804
(anonymous function) /Applications/Atom.app/Contents/Resources/app/src/editor-component.js:840
jQuery.event.dispatch /Applications/Atom.app/Contents/Resources/app/node_modules/space-pen/vendor/jquery.js:4676
elemData.handle /Applications/Atom.app/Contents/Resources/app/node_modules/space-pen/vendor/jquery.js:4360
module.exports.KeymapManager.dispatchCommandEvent /Applications/Atom.app/Contents/Resources/app/node_modules/atom-keymap/lib/keymap-manager.js:400
module.exports.KeymapManager.handleKeyboardEvent /Applications/Atom.app/Contents/Resources/app/node_modules/atom-keymap/lib/keymap-manager.js:181
module.exports.WindowEventHandler.onKeydown
```

It looks like it was caused by a bad merge conflict resolution in 0eaec57.

Test Plan:

Built new Atom and pressed `cmd-alt-z` and didn't get an error.

Looked at a side-by-side diff to check for any other merge issues:

```
git difftool -t opendiff 32e59ce..32e59ce^ src/editor-component.coffee
```
2014-08-17 17:57:08 -07:00
Kevin Sawicki ebbea64b3d Merge pull request #3295 from dmnd/tighten-lint-rules
Tighten lint rules
2014-08-15 17:18:34 -07:00
Desmond Brand a20f04149c remove line_endings rule 2014-08-15 17:06:43 -07:00
Kevin Sawicki b46e03437c Upgrade to styleguide@0.30 2014-08-15 16:58:24 -07:00
Desmond Brand ccd32cd084 💄 arrow spacing warn -> error 2014-08-15 15:57:50 -07:00
Desmond Brand 6b5d16173b 💄 more lint rules 2014-08-15 15:55:22 -07:00
Kevin Sawicki 237d71417d Upgrade to language-coffee-script@0.30 2014-08-15 15:54:59 -07:00
Desmond Brand 590bfa0c86 💄 Tighten lint rules
coffeelint.json had some rules set to ignore, but since #3116 was merged there
are no violations of these rules in the code anymore. Tighten up the rules to
prevent those kinds of errors from creeping back in.

Test Plan:

Lint executes without finding any errors

```
$ ./script/grunt coffeelint
Running "coffeelint:src" (coffeelint) task
>> 80 files lint free.

Running "coffeelint:build" (coffeelint) task
>> 23 files lint free.

Running "coffeelint:test" (coffeelint) task
>> 40 files lint free.
Done, without errors.
```
2014-08-15 15:34:33 -07:00
Ben Ogle 881c21829b Fix benchmark requires 2014-08-15 14:13:03 -07:00
Ben Ogle 34f96b2ea2 Correctly bootstrap the benchmarks 2014-08-15 14:13:03 -07:00
Ben Ogle c989557b5e Run benchmarks passes specDir 2014-08-15 14:13:03 -07:00
Kevin Sawicki 0a07e862c2 Upgrade to language-html@0.25 2014-08-15 14:02:37 -07:00
Kevin Sawicki 0eaec57f7b Merge pull request #3116 from maschs/ms-srcLint
coffeelint!
2014-08-15 13:52:15 -07:00
Kevin Sawicki 5873a03145 Prepare 0.124 2014-08-15 12:43:52 -07:00
Kevin Sawicki 3ff8f35863 Swap order of ctrl-w and ctrl-f4
This allows `Ctrl-W` to appear in the system menu again until
the atom-shell issue with function keys is fixed.

Refs atom/atom-shell#485
Closes #3283
2014-08-15 12:17:09 -07:00
Nathan Sobo 6c52bcf20c Assign ::firstNonWhitespace/TrailingWhitespaceIndex in Token::split
Fixes #3277
2014-08-15 12:58:27 -06:00
Kevin Sawicki 5975548cec Upgrade to space-pen@3.4.1 2014-08-15 11:56:42 -07:00
Kevin Sawicki 3742dadbb9 Upgrade to language-gfm@0.47 2014-08-15 11:39:28 -07:00
Ben Ogle 919f541685 Merge pull request #3261 from atom/bo-default-react-mini
Default to the react mini editors
2014-08-15 11:16:28 -07:00
Kevin Sawicki 8bebfdb871 Start shipping language-mustache 2014-08-15 11:00:30 -07:00
Alessandro Bahgat cda8382902 🐧 Set file mode to 755 to directories created for mkdeb. 2014-08-15 13:41:42 -04:00
Kevin Sawicki dfbf0de961 Upgrade to language-yaml@0.16 2014-08-15 09:48:38 -07:00
Kevin Sawicki 4a9e397be0 Upgrade to language-less@0.14 2014-08-15 09:00:09 -07:00
Kevin Sawicki e10a578c04 Upgrade to open-on-github@0.30 2014-08-15 08:45:21 -07:00
Nathan Sobo 2bd8456923 Preserve invisibles in editor model across serialization
Fixes #3281
2014-08-15 08:18:53 -06:00
Cheng Zhao c5f0126078 Merge pull request #3286 from atom/atom-shell-v0.15.7
Upgrade to atom-shell@0.15.7
2014-08-15 22:18:49 +08:00
Cheng Zhao 28500e189b Upgrade to atom-shell@0.15.7 2014-08-15 18:55:34 +08:00
Kevin Sawicki a62e90820d Upgrade to autoflow@0.18 2014-08-14 16:45:43 -07:00
Kevin Sawicki 853e8e8f17 Upgrade to status-bar@0.43 2014-08-14 16:27:29 -07:00
Kevin Sawicki 1da47dbbbd Upgrade to grammar-selectoro@0.29 2014-08-14 16:18:52 -07:00
Kevin Sawicki f96ac09c36 Merge pull request #3276 from atom/ks-restore-maximized-state
Restore maximized state of window
2014-08-14 15:55:53 -07:00
Kevin Sawicki d6852cab15 Don't maximize on Mac OS X
Just setting size, position, and full screen state is sufficient since
there is no explicit maximized state.
2014-08-14 15:46:44 -07:00
Kevin Sawicki 75f01f87da Maximize window after it is shown 2014-08-14 15:30:04 -07:00
Kevin Sawicki f901007892 Document darwin check 2014-08-14 14:37:42 -07:00
Kevin Sawicki a7a6236b26 Add maximized parameter 2014-08-14 14:37:15 -07:00
Kevin Sawicki ef47bdab3f Remove unused method 2014-08-14 14:34:14 -07:00
Kevin Sawicki 8cf999f73b Store maximized state in window dimensions 2014-08-14 14:33:02 -07:00
Kevin Sawicki 20b44500d1 Restore maximized state of window
On Linux and Windows there is a maximized state that isn't the
same as simply restoring the window's size on Mac OS X.
2014-08-14 14:22:20 -07:00
Kevin Sawicki e0ebc661f2 Upgrade to language-sass@0.17 2014-08-14 14:14:19 -07:00
Kevin Sawicki d38711e2bf Uprade to atom-keymap@2.0.2 2014-08-14 13:11:51 -07:00
Kevin Sawicki 753f47ef21 Upgrade to pathwatcher@2.0.7 2014-08-14 13:11:08 -07:00
Kevin Sawicki e202ecd1b6 📝 Bool -> Boolean 2014-08-14 13:07:05 -07:00
Kevin Sawicki 02557d36c2 Prepare 0.123 2014-08-14 13:05:19 -07:00
Maximilian Schüßler cbe5593381 Do not use "-> return" to mark empty function 2014-08-14 19:42:50 +02:00
Maximilian Schüßler 32e59ce238 Cleanup code according to coffeelint 2014-08-14 19:41:49 +02:00
Ben Ogle e725a8ffeb Default to on in the specs 2014-08-13 14:51:05 -07:00
Ben Ogle 23c67c53dc Default the react mini editors to on 2014-08-13 14:43:18 -07:00
103 arquivos alterados com 6520 adições e 9265 exclusões
+1 -1
Ver Arquivo
@@ -6,6 +6,6 @@
"url": "https://github.com/atom/atom.git"
},
"dependencies": {
"atom-package-manager": "0.91.0"
"atom-package-manager": "0.93.2"
}
}
+2 -3
Ver Arquivo
@@ -1,6 +1,6 @@
require '../src/window'
Atom = require '../src/atom'
atom = new Atom()
window.atom = Atom.loadOrCreate('spec')
atom.show() unless atom.getLoadSettings().exitWhenDone
window.atom = atom
@@ -9,5 +9,4 @@ window.atom = atom
atom.openDevTools()
document.title = "Benchmark Suite"
benchmarkSuite = require.resolve('./benchmark-suite')
runSpecSuite(benchmarkSuite, true)
runSpecSuite('../benchmark/benchmark-suite', atom.getLoadSettings().logFile)
+4 -2
Ver Arquivo
@@ -1,7 +1,9 @@
require '../spec/spec-helper'
path = require 'path'
{$, _, Point, fs} = require 'atom'
{$, Point} = require 'atom'
_ = require 'underscore-plus'
fs = require 'fs-plus'
Project = require '../src/project'
TokenizedBuffer = require '../src/tokenized-buffer'
@@ -101,7 +103,7 @@ $.fn.resultOfTrigger = (type) ->
event.result
$.fn.enableKeymap = ->
@on 'keydown', (e) => window.keymap.handleKeyEvent(e)
@on 'keydown', (e) -> window.keymap.handleKeyEvent(e)
$.fn.attachToDom = ->
$('#jasmine-content').append(this)
+9 -9
Ver Arquivo
@@ -53,7 +53,7 @@ describe "editorView.", ->
describe "at-end.", ->
beforeEach ->
editorView.moveCursorToBottom()
editorView.moveToBottom()
benchmark "insert-delete", ->
editorView.insertText('"')
@@ -62,8 +62,8 @@ describe "editorView.", ->
describe "empty-vs-set-innerHTML.", ->
[firstRow, lastRow] = []
beforeEach ->
firstRow = editorView.getFirstVisibleScreenRow()
lastRow = editorView.getLastVisibleScreenRow()
firstRow = editorView.getModel().getFirstVisibleScreenRow()
lastRow = editorView.getModel().getLastVisibleScreenRow()
benchmark "build-gutter-html.", 1000, ->
editorView.gutter.renderLineNumbers(null, firstRow, lastRow)
@@ -97,13 +97,13 @@ describe "editorView.", ->
describe "multiple-lines.", ->
[firstRow, lastRow] = []
beforeEach ->
firstRow = editorView.getFirstVisibleScreenRow()
lastRow = editorView.getLastVisibleScreenRow()
firstRow = editorView.getModel().getFirstVisibleScreenRow()
lastRow = editorView.getModel().getLastVisibleScreenRow()
benchmark "cache-entire-visible-area", 100, ->
for i in [firstRow..lastRow]
line = editorView.lineElementForScreenRow(i)[0]
editorView.positionLeftForLineAndColumn(line, i, Math.max(0, editorView.lineLengthForBufferRow(i)))
editorView.positionLeftForLineAndColumn(line, i, Math.max(0, editorView.getModel().lineTextForBufferRow(i).length))
describe "text-rendering.", ->
beforeEach ->
@@ -178,7 +178,7 @@ describe "editorView.", ->
atom.workspaceView.openSync('huge.js')
benchmark "moving-to-eof.", 1, ->
editorView.moveCursorToBottom()
editorView.moveToBottom()
describe "on-first-line.", ->
benchmark "inserting-newline", 5, ->
@@ -195,11 +195,11 @@ describe "editorView.", ->
endPosition = null
beforeEach ->
editorView.moveCursorToBottom()
editorView.moveToBottom()
endPosition = editorView.getCursorScreenPosition()
benchmark "move-to-beginning-of-word", ->
editorView.moveCursorToBeginningOfWord()
editorView.moveToBeginningOfWord()
editorView.setCursorScreenPosition(endPosition)
benchmark "insert", ->
+56
Ver Arquivo
@@ -0,0 +1,56 @@
#!/usr/bin/env coffee
{spawn, exec} = require 'child_process'
fs = require 'fs'
os = require 'os'
path = require 'path'
_ = require 'underscore-plus'
temp = require 'temp'
directoryToOpen = temp.mkdirSync('browser-process-startup-')
socketPath = path.join(os.tmpdir(), 'atom.sock')
numberOfRuns = 10
deleteSocketFile = ->
try
fs.unlinkSync(socketPath) if fs.existsSync(socketPath)
catch error
console.error(error)
launchAtom = (callback) ->
deleteSocketFile()
cmd = 'atom'
args = ['--safe', '--new-window', '--foreground', directoryToOpen]
atomProcess = spawn(cmd, args)
output = ''
startupTimes = []
dataListener = (data) ->
output += data
if match = /App load time: (\d+)/.exec(output)
startupTime = parseInt(match[1])
atomProcess.stderr.removeListener 'data', dataListener
atomProcess.kill()
exec 'pkill -9 Atom', (error) ->
console.error(error) if error?
callback(startupTime)
atomProcess.stderr.on 'data', dataListener
startupTimes = []
collector = (startupTime) ->
startupTimes.push(startupTime)
if startupTimes.length < numberOfRuns
launchAtom(collector)
else
maxTime = _.max(startupTimes)
minTime = _.min(startupTimes)
totalTime = startupTimes.reduce (previousValue=0, currentValue) -> previousValue + currentValue
console.log "Startup Runs: #{startupTimes.length}"
console.log "First run time: #{startupTimes[0]}ms"
console.log "Max time: #{maxTime}ms"
console.log "Min time: #{minTime}ms"
console.log "Average time: #{Math.round(totalTime/startupTimes.length)}ms"
launchAtom(collector)
+8 -3
Ver Arquivo
@@ -60,7 +60,7 @@ module.exports = (grunt) ->
contentsDir = shellAppDir
appDir = path.join(shellAppDir, 'resources', 'app')
installDir ?= process.env.INSTALL_PREFIX ? '/usr/local'
killCommand ='pkill -9 Atom'
killCommand ='pkill -9 atom'
coffeeConfig =
glob_to_multiple:
@@ -130,7 +130,7 @@ module.exports = (grunt) ->
atom: {appDir, appName, symbolsDir, buildDir, contentsDir, installDir, shellAppDir}
docsOutputDir: 'docs/output/api'
docsOutputDir: 'docs/output'
coffee: coffeeConfig
@@ -225,9 +225,14 @@ module.exports = (grunt) ->
grunt.registerTask('compile', ['coffee', 'prebuild-less', 'cson', 'peg'])
grunt.registerTask('lint', ['coffeelint', 'csslint', 'lesslint'])
grunt.registerTask('test', ['shell:kill-atom', 'run-specs'])
grunt.registerTask('ci', ['output-disk-space', 'download-atom-shell', 'build', 'dump-symbols', 'set-version', 'check-licenses', 'lint', 'test', 'codesign', 'publish-build'])
grunt.registerTask('docs', ['markdown:guides', 'build-docs'])
ciTasks = ['output-disk-space', 'download-atom-shell', 'build']
ciTasks.push('dump-symbols') unless process.platform is 'win32'
ciTasks.push('set-version', 'check-licenses', 'lint', 'test', 'codesign', 'publish-build')
grunt.registerTask('ci', ciTasks)
defaultTasks = ['download-atom-shell', 'build', 'set-version']
defaultTasks.push 'install' unless process.platform is 'linux'
grunt.registerTask('default', defaultTasks)
+3 -2
Ver Arquivo
@@ -7,7 +7,8 @@
},
"dependencies": {
"async": "~0.2.9",
"biscotto": ">=2.1.1 <3.0",
"donna": "~1.0",
"tello": "~0.2",
"formidable": "~1.0.14",
"fs-plus": "2.x",
"github-releases": "~0.2.0",
@@ -26,7 +27,7 @@
"harmony-collections": "~0.3.8",
"json-front-matter": "~0.1.3",
"legal-eagle": "~0.4.0",
"minidump": "~0.7",
"minidump": "~0.8",
"normalize-package-data": "0.2.12",
"npm": "~1.4.5",
"rcedit": "~0.1.2",
+28 -151
Ver Arquivo
@@ -1,162 +1,39 @@
path = require 'path'
async = require 'async'
fs = require 'fs-plus'
request = require 'request'
_ = require 'underscore-plus'
donna = require 'donna'
tello = require 'tello'
module.exports = (grunt) ->
{rm} = require('./task-helpers')(grunt)
getClassesToInclude = ->
modulesPath = path.resolve(__dirname, '..', '..', 'node_modules')
classes = {}
fs.traverseTreeSync modulesPath, (modulePath) ->
return true unless path.basename(modulePath) is 'package.json'
return true unless fs.isFileSync(modulePath)
cmd = path.join('node_modules', '.bin', 'coffee')
commonArgs = [path.join('build', 'node_modules', '.bin', 'biscotto'), '--']
opts =
stdio: 'inherit'
apiPath = path.join(path.dirname(modulePath), 'api.json')
if fs.isFileSync(apiPath)
_.extend(classes, grunt.file.readJSON(apiPath).classes)
true
classes
sortClasses = (classes) ->
sortedClasses = {}
for className in Object.keys(classes).sort()
sortedClasses[className] = classes[className]
sortedClasses
grunt.registerTask 'build-docs', 'Builds the API docs in src', ->
done = @async()
docsOutputDir = grunt.config.get('docsOutputDir')
downloadIncludes (error, includePaths) ->
if error?
done(error)
else
rm(docsOutputDir)
args = [
commonArgs...
'--title', 'Atom API Documentation'
'-o', docsOutputDir
'-r', 'docs/README.md'
'--stability', '1'
'src/'
includePaths...
]
grunt.util.spawn({cmd, args, opts}, done)
metadata = donna.generateMetadata(['.'])
api = tello.digest(metadata)
_.extend(api.classes, getClassesToInclude())
api.classes = sortClasses(api.classes)
grunt.registerTask 'lint-docs', 'Generate stats about the doc coverage', ->
done = @async()
downloadIncludes (error, includePaths) ->
if error?
done(error)
else
args = [
commonArgs...
'--noOutput'
'src/'
includePaths...
]
grunt.util.spawn({cmd, args, opts}, done)
grunt.registerTask 'missing-docs', 'Generate stats about the doc coverage', ->
done = @async()
downloadIncludes (error, includePaths) ->
if error?
done(error)
else
args = [
commonArgs...
'--noOutput'
'--missing'
'src/'
includePaths...
]
grunt.util.spawn({cmd, args, opts}, done)
grunt.registerTask 'copy-docs', 'Copies over latest API docs to atom-docs', ->
done = @async()
fetchTag = (args..., callback) ->
cmd = 'git'
args = ['describe', '--abbrev=0', '--tags']
grunt.util.spawn {cmd, args}, (error, result) ->
if error?
callback(error)
else
callback(null, String(result).trim())
copyDocs = (tag, callback) ->
cmd = 'cp'
args = ['-r', 'docs/output/', "../atom.io/public/docs/api/#{tag}/"]
fs.exists "../atom.io/public/docs/api/", (exists) ->
if exists
grunt.util.spawn {cmd, args}, (error, result) ->
if error?
callback(error)
else
callback(null, tag)
else
grunt.log.error "../atom.io/public/docs/api/ doesn't exist"
return false
grunt.util.async.waterfall [fetchTag, copyDocs], done
grunt.registerTask 'deploy-docs', 'Publishes latest API docs to atom-docs.githubapp.com', ->
done = @async()
docsRepoArgs = ['--work-tree=../atom-docs/', '--git-dir=../atom-docs/.git/']
fetchTag = (args..., callback) ->
cmd = 'git'
args = ['describe', '--abbrev=0', '--tags']
grunt.util.spawn {cmd, args}, (error, result) ->
if error?
callback(error)
else
callback(null, String(result).trim().split('.')[0..1].join('.'))
stageDocs = (tag, callback) ->
cmd = 'git'
args = [docsRepoArgs..., 'add', "public/#{tag}"]
grunt.util.spawn({cmd, args, opts}, callback)
fetchSha = (args..., callback) ->
cmd = 'git'
args = ['rev-parse', 'HEAD']
grunt.util.spawn {cmd, args}, (error, result) ->
if error?
callback(error)
else
callback(null, String(result).trim())
commitChanges = (sha, callback) ->
cmd = 'git'
args = [docsRepoArgs..., 'commit', "-m Update API docs to #{sha}"]
grunt.util.spawn({cmd, args, opts}, callback)
pushOrigin = (args..., callback) ->
cmd = 'git'
args = [docsRepoArgs..., 'push', 'origin', 'master']
grunt.util.spawn({cmd, args, opts}, callback)
pushHeroku = (args..., callback) ->
cmd = 'git'
args = [docsRepoArgs..., 'push', 'heroku', 'master']
grunt.util.spawn({cmd, args, opts}, callback)
grunt.util.async.waterfall [fetchTag, stageDocs, fetchSha, commitChanges, pushOrigin, pushHeroku], done
downloadFileFromRepo = ({repo, file}, callback) ->
uri = "https://raw.github.com/atom/#{repo}/master/#{file}"
request uri, (error, response, contents) ->
return callback(error) if error?
downloadPath = path.join('docs', 'includes', repo, file)
fs.writeFile downloadPath, contents, (error) ->
callback(error, downloadPath)
downloadIncludes = (callback) ->
includes = [
{repo: 'atom-keymap', file: 'src/keymap-manager.coffee'}
{repo: 'atom-keymap', file: 'src/key-binding.coffee'}
{repo: 'first-mate', file: 'src/grammar.coffee'}
{repo: 'first-mate', file: 'src/grammar-registry.coffee'}
{repo: 'node-pathwatcher', file: 'src/directory.coffee'}
{repo: 'node-pathwatcher', file: 'src/file.coffee'}
{repo: 'space-pen', file: 'src/space-pen.coffee'}
{repo: 'text-buffer', file: 'src/marker.coffee'}
{repo: 'text-buffer', file: 'src/point.coffee'}
{repo: 'text-buffer', file: 'src/range.coffee'}
{repo: 'text-buffer', file: 'src/text-buffer.coffee'}
{repo: 'theorist', file: 'src/model.coffee'}
]
async.map(includes, downloadFileFromRepo, callback)
apiJson = JSON.stringify(api, null, 2)
apiJsonPath = path.join(docsOutputDir, 'api.json')
grunt.file.write(apiJsonPath, apiJson)
+21 -9
Ver Arquivo
@@ -13,8 +13,16 @@ module.exports = (grunt) ->
grunt.file.write(outputPath, filled)
outputPath
getInstalledSize = (buildDir, callback) ->
cmd = 'du'
args = ['-sk', path.join(buildDir, 'Atom')]
spawn {cmd, args}, (error, {stdout}) ->
installedSize = stdout.split(/\s+/)?[0] or '200000' # default to 200MB
callback(null, installedSize)
grunt.registerTask 'mkdeb', 'Create debian package', ->
done = @async()
buildDir = grunt.config.get('atom.buildDir')
if process.arch is 'ia32'
arch = 'i386'
@@ -28,13 +36,17 @@ module.exports = (grunt) ->
maintainer = 'GitHub <atom@github.com>'
installDir = '/usr'
iconName = 'atom'
data = {name, version, description, section, arch, maintainer, installDir, iconName}
getInstalledSize buildDir, (error, installedSize) ->
data = {name, version, description, section, arch, maintainer, installDir, iconName, installedSize}
controlFilePath = fillTemplate(path.join('resources', 'linux', 'debian', 'control'), data)
desktopFilePath = fillTemplate(path.join('resources', 'linux', 'Atom.desktop'), data)
icon = path.join('resources', 'atom.png')
controlFilePath = fillTemplate(path.join('resources', 'linux', 'debian', 'control'), data)
desktopFilePath = fillTemplate(path.join('resources', 'linux', 'Atom.desktop'), data)
icon = path.join('resources', 'atom.png')
buildDir = grunt.config.get('atom.buildDir')
cmd = path.join('script', 'mkdeb')
args = [version, arch, controlFilePath, desktopFilePath, icon, buildDir]
spawn({cmd, args}, done)
cmd = path.join('script', 'mkdeb')
args = [version, arch, controlFilePath, desktopFilePath, icon, buildDir]
spawn {cmd, args}, (error) ->
if error?
done(error)
else
grunt.log.ok "Created #{buildDir}/atom-#{version}-#{arch}.deb"
done()
+7 -4
Ver Arquivo
@@ -17,6 +17,7 @@ defaultHeaders =
module.exports = (gruntObject) ->
grunt = gruntObject
{cp} = require('./task-helpers')(grunt)
grunt.registerTask 'publish-build', 'Publish the built app', ->
return if process.env.JANKY_SHA1 and process.env.JANKY_BRANCH isnt 'master'
@@ -24,8 +25,10 @@ module.exports = (gruntObject) ->
tasks.unshift('build-docs', 'prepare-docs') if process.platform is 'darwin'
grunt.task.run(tasks)
grunt.registerTask 'prepare-docs', 'Move the build docs to the build dir', ->
fs.copySync(grunt.config.get('docsOutputDir'), path.join(grunt.config.get('atom.buildDir'), 'atom-docs'))
grunt.registerTask 'prepare-docs', 'Move api.json to atom-api.json', ->
docsOutputDir = grunt.config.get('docsOutputDir')
buildDir = grunt.config.get('atom.buildDir')
cp path.join(docsOutputDir, 'api.json'), path.join(buildDir, 'atom-api.json')
grunt.registerTask 'upload-assets', 'Upload the assets to a GitHub release', ->
done = @async()
@@ -46,7 +49,7 @@ getAssets = ->
[
{assetName: 'atom-mac.zip', sourcePath: 'Atom.app'}
{assetName: 'atom-mac-symbols.zip', sourcePath: 'Atom.breakpad.syms'}
{assetName: 'atom-docs.zip', sourcePath: 'atom-docs'}
{assetName: 'atom-api.json', sourcePath: 'atom-api.json'}
]
else
[
@@ -70,7 +73,7 @@ zipAssets = (buildDir, assets, callback) ->
callback(error)
tasks = []
for {assetName, sourcePath} in assets
for {assetName, sourcePath} in assets when path.extname(assetName) is '.zip'
fs.removeSync(path.join(buildDir, assetName))
tasks.push(zip.bind(this, buildDir, sourcePath, assetName))
async.parallel(tasks, callback)
+18 -14
Ver Arquivo
@@ -2,7 +2,6 @@ fs = require 'fs'
path = require 'path'
_ = require 'underscore-plus'
async = require 'async'
module.exports = (grunt) ->
@@ -10,18 +9,27 @@ module.exports = (grunt) ->
packageSpecQueue = null
getAppPath = ->
contentsDir = grunt.config.get('atom.contentsDir')
switch process.platform
when 'darwin'
path.join(contentsDir, 'MacOS', 'Atom')
when 'linux'
path.join(contentsDir, 'atom')
when 'win32'
path.join(contentsDir, 'atom.exe')
runPackageSpecs = (callback) ->
failedPackages = []
rootDir = grunt.config.get('atom.shellAppDir')
contentsDir = grunt.config.get('atom.contentsDir')
resourcePath = process.cwd()
if process.platform is 'darwin'
appPath = path.join(contentsDir, 'MacOS', 'Atom')
else if process.platform is 'win32'
appPath = path.join(contentsDir, 'atom.exe')
appPath = getAppPath()
# Ensure application is executable on Linux
fs.chmodSync(appPath, '755') if process.platform is 'linux'
packageSpecQueue = async.queue (packagePath, callback) ->
if process.platform is 'darwin'
if process.platform in ['darwin', 'linux']
options =
cmd: appPath
args: ['--test', "--resource-path=#{resourcePath}", "--spec-directory=#{path.join(packagePath, 'spec')}"]
@@ -57,15 +65,11 @@ module.exports = (grunt) ->
packageSpecQueue.drain = -> callback(null, failedPackages)
runCoreSpecs = (callback) ->
contentsDir = grunt.config.get('atom.contentsDir')
if process.platform is 'darwin'
appPath = path.join(contentsDir, 'MacOS', 'Atom')
else if process.platform is 'win32'
appPath = path.join(contentsDir, 'atom.exe')
appPath = getAppPath()
resourcePath = process.cwd()
coreSpecsPath = path.resolve('spec')
if process.platform is 'darwin'
if process.platform in ['darwin', 'linux']
options =
cmd: appPath
args: ['--test', "--resource-path=#{resourcePath}", "--spec-directory=#{coreSpecsPath}"]
@@ -90,7 +94,7 @@ module.exports = (grunt) ->
# TODO: This should really be parallel on both platforms, however our
# fixtures step on each others toes currently.
if process.platform is 'darwin'
if process.platform in ['darwin', 'linux']
method = async.parallel
else if process.platform is 'win32'
method = async.series
+5 -5
Ver Arquivo
@@ -1,14 +1,14 @@
{
"indentation": {
"level": "ignore"
},
"max_line_length": {
"level": "ignore"
},
"no_empty_param_list": {
"level": "error"
},
"no_unnecessary_fat_arrows": {
"level": "ignore"
"arrow_spacing": {
"level": "error"
},
"no_interpolation_in_single_quotes": {
"level": "error"
}
}
+1 -1
Ver Arquivo
@@ -56,7 +56,7 @@ character of the current line:
class EditorView
listenForEvents: ->
@command 'editor:move-to-first-character-of-line', =>
@editor.moveCursorToFirstCharacterOfLine()
@editor.moveToFirstCharacterOfLine()
```
The `::command` method is basically an enhanced version of jQuery's `::on`
+10 -1
Ver Arquivo
@@ -88,7 +88,7 @@ this is the reason for this error you can issue
and restart Atom. If Atom now works fine, you can make this setting permanent:
```sh
echo 32768 > /proc/sys/fs/inotify/max_user_watches
echo 32768 | sudo tee -a /proc/sys/fs/inotify/max_user_watches
```
See also https://github.com/atom/atom/issues/2082.
@@ -100,6 +100,15 @@ have Node.js installed, or node isn't identified as Node.js on your machine.
If it's the latter, entering `sudo ln -s /usr/bin/nodejs /usr/bin/node` into
your terminal may fix the issue.
#### You can also use Alternatives
On some variants (mostly Debian based distros) it's preferable for you to use
Alternatives so that changes to the binary paths can be fixed or altered easily:
```sh
sudo update-alternatives --install /usr/bin/node node /usr/bin/nodejs 1 --slave /usr/bin/js js /usr/bin/nodejs
```
### Linux build error reports in atom/atom
* Use [this search](https://github.com/atom/atom/search?q=label%3Abuild-error+label%3Alinux&type=Issues)
to get a list of reports about build errors on Linux.
+1 -1
Ver Arquivo
@@ -123,7 +123,7 @@ You can open this file in an editor from the _Atom > Open Your Config_ menu.
- `showInvisibles`: Whether to render placeholders for invisible characters (defaults to `false`)
- `showIndentGuide`: Show/hide indent indicators within the editor
- `showLineNumbers`: Show/hide line numbers within the gutter
- `softWrap`: Enable/disable soft wrapping of text within the editor
- `softWrapped`: Enable/disable soft wrapping of text within the editor
- `softWrapAtPreferredLineLength`: Enable/disable soft line wrapping at `preferredLineLength`
- `tabLength`: Number of spaces within a tab (defaults to `2`)
- `fuzzyFinder`
+3 -3
Ver Arquivo
@@ -53,7 +53,7 @@ module.exports =
convert: ->
# This assumes the active pane item is an editor
editor = atom.workspace.activePaneItem
editor = atom.workspace.getActivePaneItem()
editor.insertText('Hello, World!')
```
@@ -131,8 +131,8 @@ inserting 'Hello, World!' convert the selected text to ASCII art.
```coffeescript
convert: ->
# This assumes the active pane item is an editor
editor = atom.workspace.activePaneItem
selection = editor.getSelection()
editor = atom.workspace.getActivePaneItem()
selection = editor.getLastSelection()
figlet = require 'figlet'
figlet selection.getText(), {font: "Larry 3D 2"}, (error, asciiArt) ->
+1 -1
Ver Arquivo
@@ -11,4 +11,4 @@
# atom.workspaceView.eachEditorView (editorView) ->
# editor = editorView.getEditor()
# if path.extname(editor.getPath()) is '.md'
# editor.setSoftWrap(true)
# editor.setSoftWrapped(true)
+4 -1
Ver Arquivo
@@ -13,6 +13,9 @@
# 'enter': 'editor:newline'
#
# '.workspace':
# 'ctrl-P': 'core:move-up'
# 'ctrl-shift-p': 'core:move-up'
# 'ctrl-p': 'core:move-down'
#
# You can find more information about keymaps in these guides:
# * https://atom.io/docs/latest/customizing-atom#customizing-key-bindings
# * https://atom.io/docs/latest/advanced/keymaps
+1 -4
Ver Arquivo
@@ -15,10 +15,7 @@ unless process.env.ATOM_SHELL_INTERNAL_RUN_AS_NODE
module.exports.$ = $
module.exports.$$ = $$
module.exports.$$$ = $$$
if atom.config.get('core.useReactMiniEditors')
module.exports.EditorView = require '../src/react-editor-view'
else
module.exports.EditorView = require '../src/editor-view'
module.exports.EditorView = require '../src/editor-view'
module.exports.ScrollView = require '../src/scroll-view'
module.exports.SelectListView = require '../src/select-list-view'
module.exports.Task = require '../src/task'
+3 -1
Ver Arquivo
@@ -10,10 +10,12 @@
'ctrl-shift-i': 'window:toggle-dev-tools'
'ctrl-alt-p': 'window:run-package-specs'
'ctrl-alt-s': 'application:run-all-specs'
'ctrl-shift-o': 'application:open-dev'
'ctrl-alt-o': 'application:open-dev'
'ctrl-shift-o': 'application:open-folder'
'F11': 'window:toggle-full-screen'
# Sublime Parity
'ctrl-,': 'application:show-settings'
'ctrl-N': 'application:new-window'
'ctrl-W': 'window:close'
'ctrl-o': 'application:open-file'
+1 -1
Ver Arquivo
@@ -25,8 +25,8 @@
'ctrl-n': 'application:new-file'
'ctrl-s': 'core:save'
'ctrl-S': 'core:save-as'
'ctrl-w': 'core:close'
'ctrl-f4': 'core:close'
'ctrl-w': 'core:close'
'ctrl-z': 'core:undo'
'ctrl-shift-z': 'core:redo'
'ctrl-y': 'core:redo'
+13 -1
Ver Arquivo
@@ -18,6 +18,8 @@
{ type: 'separator' }
{ label: 'Install Shell Commands', command: 'window:install-shell-commands' }
{ type: 'separator' }
{ label: 'Services', submenu: [] }
{ type: 'separator' }
{ label: 'Hide Atom', command: 'application:hide' }
{ label: 'Hide Others', command: 'application:hide-other-applications' }
{ label: 'Show All', command: 'application:unhide-all-applications' }
@@ -164,7 +166,7 @@
]
}
{ type: 'separator' }
{ label: 'Toggle Soft Wrap', command: 'editor:toggle-soft-wrap' }
{ label: 'Toggle Soft Wrap', command: 'editor:toggle-soft-wrapped' }
]
}
@@ -192,3 +194,13 @@
]
}
]
'context-menu':
'.overlayer':
'Undo': 'core:undo'
'Redo': 'core:redo'
'Cut': 'core:cut'
'Copy': 'core:copy'
'Paste': 'core:paste'
'Delete': 'core:delete'
'Select All': 'core:select-all'
+19 -13
Ver Arquivo
@@ -8,8 +8,6 @@
{ label: 'Open Folder...', command: 'application:open-folder' }
{ label: 'Reopen Last &Item', command: 'pane:reopen-closed-item' }
{ type: 'separator' }
{ label: '&Preferences...', command: 'application:show-settings' }
{ type: 'separator' }
{ label: '&Save', command: 'core:save' }
{ label: 'Save &As...', command: 'core:save-as' }
{ label: 'Save A&ll', command: 'window:save-all' }
@@ -80,6 +78,14 @@
{ label: 'Fold Level 9', command: 'editor:fold-at-indent-level-9' }
]
}
{ type: 'separator' }
{ label: '&Preferences', command: 'application:show-settings' }
{ label: 'Open Your Config', command: 'application:open-your-config' }
{ label: 'Open Your Init Script', command: 'application:open-your-init-script' }
{ label: 'Open Your Keymap', command: 'application:open-your-keymap' }
{ label: 'Open Your Snippets', command: 'application:open-your-snippets' }
{ label: 'Open Your Stylesheet', command: 'application:open-your-stylesheet' }
{ type: 'separator' }
]
}
@@ -98,7 +104,7 @@
]
}
{ type: 'separator' }
{ label: 'Toggle Soft &Wrap', command: 'editor:toggle-soft-wrap' }
{ label: 'Toggle Soft &Wrap', command: 'editor:toggle-soft-wrapped' }
]
}
@@ -133,16 +139,6 @@
submenu: []
}
{
label: '&Window'
submenu: [
{ label: 'Mi&nimize', command: 'application:minimize' }
{ label: 'Ma&ximize', command: 'application:zoom' }
{ type: 'separator' }
{ label: 'Bring &All to Front', command: 'application:bring-all-windows-to-front' }
]
}
{
label: '&Help'
submenu: [
@@ -155,3 +151,13 @@
]
}
]
'context-menu':
'.overlayer':
'Undo': 'core:undo'
'Redo': 'core:redo'
'Cut': 'core:cut'
'Copy': 'core:copy'
'Paste': 'core:paste'
'Delete': 'core:delete'
'Select All': 'core:select-all'
+11 -11
Ver Arquivo
@@ -122,7 +122,7 @@
]
}
{ type: 'separator' }
{ label: 'Toggle Soft &Wrap', command: 'editor:toggle-soft-wrap' }
{ label: 'Toggle Soft &Wrap', command: 'editor:toggle-soft-wrapped' }
]
}
@@ -157,16 +157,6 @@
submenu: []
}
{
label: '&Window'
submenu: [
{ label: 'Mi&nimize', command: 'application:minimize' }
{ label: 'Ma&ximize', command: 'application:zoom' }
{ type: 'separator' }
{ label: 'Bring &All to Front', command: 'application:bring-all-windows-to-front' }
]
}
{
label: '&Help'
submenu: [
@@ -179,3 +169,13 @@
]
}
]
'context-menu':
'.overlayer':
'Undo': 'core:undo'
'Redo': 'core:redo'
'Cut': 'core:cut'
'Copy': 'core:copy'
'Paste': 'core:paste'
'Delete': 'core:delete'
'Select All': 'core:select-all'
+53 -52
Ver Arquivo
@@ -1,7 +1,7 @@
{
"name": "atom",
"productName": "Atom",
"version": "0.122.0",
"version": "0.126.0",
"description": "A hackable text editor for the 21st Century.",
"main": "./src/browser/main.js",
"repository": {
@@ -17,31 +17,32 @@
"url": "http://github.com/atom/atom/raw/master/LICENSE.md"
}
],
"atomShellVersion": "0.15.6",
"atomShellVersion": "0.16.2",
"dependencies": {
"async": "0.2.6",
"atom-keymap": "^2.0.1",
"atom-keymap": "^2.0.6",
"bootstrap": "git+https://github.com/atom/bootstrap.git#6af81906189f1747fd6c93479e3d998ebe041372",
"clear-cut": "0.4.0",
"coffee-script": "1.7.0",
"coffeestack": "0.7.0",
"delegato": "^1",
"emissary": "^1.2.2",
"first-mate": "^2.0.1",
"emissary": "^1.3.1",
"event-kit": "0.6.0",
"first-mate": "^2.0.5",
"fs-plus": "^2.2.6",
"fstream": "0.1.24",
"fuzzaldrin": "^1.1",
"fuzzaldrin": "^2.1",
"git-utils": "^2.1.4",
"grim": "0.12.0",
"guid": "0.0.10",
"jasmine-tagged": "^1.1.2",
"less-cache": "0.13.0",
"less-cache": "0.14.0",
"mixto": "^1",
"mkdirp": "0.3.5",
"nslog": "^1.0.1",
"oniguruma": "^3.0.3",
"oniguruma": "^3.0.4",
"optimist": "0.4.0",
"pathwatcher": "^2.0.6",
"pathwatcher": "^2.0.12",
"property-accessors": "^1",
"q": "^1.0.1",
"random-words": "0.0.1",
@@ -54,16 +55,16 @@
"season": "^1.0.2",
"semver": "1.1.4",
"serializable": "^1",
"space-pen": "3.2.0",
"space-pen": "3.4.6",
"temp": "0.7.0",
"text-buffer": "^3.0.1",
"theorist": "^1",
"text-buffer": "^3.2.2",
"theorist": "^1.0.2",
"underscore-plus": "^1.5.1",
"vm-compatibility-layer": "0.1.0"
},
"packageDependencies": {
"atom-dark-syntax": "0.19.0",
"atom-dark-ui": "0.33.0",
"atom-dark-ui": "0.34.0",
"atom-light-syntax": "0.20.0",
"atom-light-ui": "0.29.0",
"base16-tomorrow-dark-theme": "0.21.0",
@@ -71,74 +72,74 @@
"solarized-dark-syntax": "0.22.0",
"solarized-light-syntax": "0.12.0",
"archive-view": "0.36.0",
"autocomplete": "0.31.0",
"autoflow": "0.17.0",
"autocomplete": "0.32.0",
"autoflow": "0.18.0",
"autosave": "0.15.0",
"background-tips": "0.15.0",
"bookmarks": "0.27.0",
"bracket-matcher": "0.54.0",
"background-tips": "0.16.0",
"bookmarks": "0.28.0",
"bracket-matcher": "0.55.0",
"command-palette": "0.24.0",
"deprecation-cop": "0.9.0",
"deprecation-cop": "0.10.0",
"dev-live-reload": "0.34.0",
"exception-reporting": "0.20.0",
"feedback": "0.33.0",
"find-and-replace": "0.128.0",
"fuzzy-finder": "0.57.0",
"git-diff": "0.37.0",
"go-to-line": "0.24.0",
"grammar-selector": "0.27.0",
"find-and-replace": "0.134.0",
"fuzzy-finder": "0.58.0",
"git-diff": "0.39.0",
"go-to-line": "0.25.0",
"grammar-selector": "0.29.0",
"image-view": "0.36.0",
"incompatible-packages": "0.8.0",
"incompatible-packages": "0.9.0",
"keybinding-resolver": "0.19.0",
"link": "0.25.0",
"markdown-preview": "0.99.0",
"metrics": "0.33.0",
"open-on-github": "0.29.0",
"markdown-preview": "0.101.0",
"metrics": "0.34.0",
"open-on-github": "0.30.0",
"package-generator": "0.31.0",
"release-notes": "0.36.0",
"settings-view": "0.139.0",
"snippets": "0.51.0",
"spell-check": "0.41.0",
"status-bar": "0.42.0",
"styleguide": "0.29.0",
"settings-view": "0.142.0",
"snippets": "0.52.0",
"spell-check": "0.42.0",
"status-bar": "0.44.0",
"styleguide": "0.30.0",
"symbols-view": "0.63.0",
"tabs": "0.49.0",
"tabs": "0.50.0",
"timecop": "0.22.0",
"tree-view": "0.112.0",
"tree-view": "0.115.0",
"update-package-dependencies": "0.6.0",
"welcome": "0.17.0",
"welcome": "0.18.0",
"whitespace": "0.25.0",
"wrap-guide": "0.21.0",
"language-c": "0.27.0",
"language-coffee-script": "0.29.0",
"language-c": "0.28.0",
"language-coffee-script": "0.30.0",
"language-css": "0.17.0",
"language-gfm": "0.46.0",
"language-gfm": "0.48.0",
"language-git": "0.9.0",
"language-go": "0.16.0",
"language-html": "0.24.0",
"language-hyperlink": "0.10.0",
"language-go": "0.17.0",
"language-html": "0.26.0",
"language-hyperlink": "0.12.0",
"language-java": "0.11.0",
"language-javascript": "0.39.0",
"language-json": "0.8.0",
"language-less": "0.13.0",
"language-make": "0.10.0",
"language-less": "0.15.0",
"language-make": "0.12.0",
"language-mustache": "0.10.0",
"language-objective-c": "0.11.0",
"language-perl": "0.9.0",
"language-php": "0.15.0",
"language-property-list": "0.7.0",
"language-python": "0.18.0",
"language-ruby": "0.34.0",
"language-ruby-on-rails": "0.15.0",
"language-sass": "0.16.0",
"language-python": "0.19.0",
"language-ruby": "0.35.0",
"language-ruby-on-rails": "0.18.0",
"language-sass": "0.21.0",
"language-shellscript": "0.8.0",
"language-source": "0.8.0",
"language-sql": "0.10.0",
"language-text": "0.6.0",
"language-todo": "0.10.0",
"language-todo": "0.11.0",
"language-toml": "0.12.0",
"language-xml": "0.18.0",
"language-yaml": "0.15.0"
"language-xml": "0.20.0",
"language-yaml": "0.17.0"
},
"private": true,
"scripts": {
+2 -1
Ver Arquivo
@@ -5,4 +5,5 @@ Exec=<%= installDir %>/share/atom/atom %U
Icon=<%= iconName %>
Type=Application
StartupNotify=true
Categories=GNOME;GTK;Utility;TextEditor;
Categories=GNOME;GTK;Utility;TextEditor;Development;
MimeType=text/plain;
+1 -1
Ver Arquivo
@@ -3,6 +3,6 @@ Version: <%= version %>
Section: <%= section %>
Priority: optional
Architecture: <%= arch %>
Installed-Size: `du -ks usr|cut -f 1`
Installed-Size: <%= installedSize %>
Maintainer: <%= maintainer %>
Description: <%= description %>
+5 -4
Ver Arquivo
@@ -13,21 +13,22 @@ CONTROL_FILE="$3"
DESKTOP_FILE="$4"
ICON_FILE="$5"
DEB_PATH="$6"
FILE_MODE=755
TARGET_ROOT="`mktemp -d`"
chmod 755 "$TARGET_ROOT"
TARGET="$TARGET_ROOT/atom-$VERSION-$ARCH"
mkdir -p "$TARGET/usr"
mkdir -m $FILE_MODE -p "$TARGET/usr"
env INSTALL_PREFIX="$TARGET/usr" script/grunt install
mkdir -p "$TARGET/DEBIAN"
mkdir -m $FILE_MODE -p "$TARGET/DEBIAN"
cp "$CONTROL_FILE" "$TARGET/DEBIAN/control"
mkdir -p "$TARGET/usr/share/applications"
mkdir -m $FILE_MODE -p "$TARGET/usr/share/applications"
cp "$DESKTOP_FILE" "$TARGET/usr/share/applications"
mkdir -p "$TARGET/usr/share/pixmaps"
mkdir -m $FILE_MODE -p "$TARGET/usr/share/pixmaps"
cp "$ICON_FILE" "$TARGET/usr/share/pixmaps"
dpkg-deb -b "$TARGET"
+2 -2
Ver Arquivo
@@ -119,14 +119,14 @@ class AtomReporter extends View
grim.clearDeprecations()
handleEvents: ->
$(document).on "click", ".spec-toggle", ({currentTarget}) =>
$(document).on "click", ".spec-toggle", ({currentTarget}) ->
element = $(currentTarget)
specFailures = element.parent().find('.spec-failures')
specFailures.toggle()
element.toggleClass('folded')
false
$(document).on "click", ".deprecation-toggle", ({currentTarget}) =>
$(document).on "click", ".deprecation-toggle", ({currentTarget}) ->
element = $(currentTarget)
deprecationList = $(document).find('.deprecation-list')
deprecationList.toggle()
+20 -15
Ver Arquivo
@@ -241,15 +241,15 @@ describe "the `atom` global", ->
two = atom.themes.stringToId(two)
three = atom.themes.stringToId(three)
expect(atom.themes.stylesheetElementForId(one)).not.toExist()
expect(atom.themes.stylesheetElementForId(two)).not.toExist()
expect(atom.themes.stylesheetElementForId(three)).not.toExist()
expect(atom.themes.stylesheetElementForId(one)).toBeNull()
expect(atom.themes.stylesheetElementForId(two)).toBeNull()
expect(atom.themes.stylesheetElementForId(three)).toBeNull()
atom.packages.activatePackage("package-with-stylesheets-manifest")
expect(atom.themes.stylesheetElementForId(one)).toExist()
expect(atom.themes.stylesheetElementForId(two)).toExist()
expect(atom.themes.stylesheetElementForId(three)).not.toExist()
expect(atom.themes.stylesheetElementForId(one)).not.toBeNull()
expect(atom.themes.stylesheetElementForId(two)).not.toBeNull()
expect(atom.themes.stylesheetElementForId(three)).toBeNull()
expect($('#jasmine-content').css('font-size')).toBe '1px'
describe "when the metadata does not contain a 'stylesheets' manifest", ->
@@ -263,14 +263,14 @@ describe "the `atom` global", ->
two = atom.themes.stringToId(two)
three = atom.themes.stringToId(three)
expect(atom.themes.stylesheetElementForId(one)).not.toExist()
expect(atom.themes.stylesheetElementForId(two)).not.toExist()
expect(atom.themes.stylesheetElementForId(three)).not.toExist()
expect(atom.themes.stylesheetElementForId(one)).toBeNull()
expect(atom.themes.stylesheetElementForId(two)).toBeNull()
expect(atom.themes.stylesheetElementForId(three)).toBeNull()
atom.packages.activatePackage("package-with-stylesheets")
expect(atom.themes.stylesheetElementForId(one)).toExist()
expect(atom.themes.stylesheetElementForId(two)).toExist()
expect(atom.themes.stylesheetElementForId(three)).toExist()
expect(atom.themes.stylesheetElementForId(one)).not.toBeNull()
expect(atom.themes.stylesheetElementForId(two)).not.toBeNull()
expect(atom.themes.stylesheetElementForId(three)).not.toBeNull()
expect($('#jasmine-content').css('font-size')).toBe '3px'
describe "grammar loading", ->
@@ -493,13 +493,14 @@ describe "the `atom` global", ->
expect(atom.config.get('core.disabledPackages')).toContain packageName
describe "with themes", ->
reloadedHandler = null
beforeEach ->
waitsForPromise ->
atom.themes.activateThemes()
afterEach ->
atom.themes.deactivateThemes()
atom.config.unobserve('core.themes')
it ".enablePackage() and .disablePackage() enables and disables a theme", ->
packageName = 'theme-with-package-file'
@@ -517,13 +518,17 @@ describe "the `atom` global", ->
expect(atom.config.get('core.themes')).toContain packageName
expect(atom.config.get('core.disabledPackages')).not.toContain packageName
# disabling of theme
reloadedHandler = jasmine.createSpy('reloadedHandler')
reloadedHandler.reset()
atom.themes.on('reloaded', reloadedHandler)
pack = atom.packages.disablePackage(packageName)
waitsFor ->
not (pack in atom.packages.getActivePackages())
reloadedHandler.callCount is 1
runs ->
expect(atom.packages.getActivePackages()).not.toContain pack
expect(atom.config.get('core.themes')).not.toContain packageName
expect(atom.config.get('core.themes')).not.toContain packageName
expect(atom.config.get('core.disabledPackages')).not.toContain packageName
+2 -1
Ver Arquivo
@@ -6,7 +6,8 @@ describe "ContextMenuManager", ->
[contextMenu] = []
beforeEach ->
contextMenu = new ContextMenuManager
{resourcePath} = atom.getLoadSettings()
contextMenu = new ContextMenuManager({resourcePath})
describe "adding definitions", ->
it 'loads', ->
+144 -132
Ver Arquivo
@@ -9,7 +9,7 @@ describe "DisplayBuffer", ->
buffer = atom.project.bufferForPathSync('sample.js')
displayBuffer = new DisplayBuffer({buffer, tabLength})
changeHandler = jasmine.createSpy 'changeHandler'
displayBuffer.on 'changed', changeHandler
displayBuffer.onDidChange changeHandler
waitsForPromise ->
atom.packages.activatePackage('language-javascript')
@@ -58,7 +58,7 @@ describe "DisplayBuffer", ->
describe "soft wrapping", ->
beforeEach ->
displayBuffer.setSoftWrap(true)
displayBuffer.setSoftWrapped(true)
displayBuffer.setEditorWidthInChars(50)
changeHandler.reset()
@@ -67,48 +67,48 @@ describe "DisplayBuffer", ->
it "uses the preferred line length as the soft wrap column when it is less than the configured soft wrap column", ->
atom.config.set('editor.preferredLineLength', 100)
atom.config.set('editor.softWrapAtPreferredLineLength', true)
expect(displayBuffer.lineForRow(10).text).toBe ' return '
expect(displayBuffer.tokenizedLineForScreenRow(10).text).toBe ' return '
atom.config.set('editor.preferredLineLength', 5)
expect(displayBuffer.lineForRow(10).text).toBe 'funct'
expect(displayBuffer.tokenizedLineForScreenRow(10).text).toBe 'funct'
atom.config.set('editor.softWrapAtPreferredLineLength', false)
expect(displayBuffer.lineForRow(10).text).toBe ' return '
expect(displayBuffer.tokenizedLineForScreenRow(10).text).toBe ' return '
describe "when the line is shorter than the max line length", ->
it "renders the line unchanged", ->
expect(displayBuffer.lineForRow(0).text).toBe buffer.lineForRow(0)
expect(displayBuffer.tokenizedLineForScreenRow(0).text).toBe buffer.lineForRow(0)
describe "when the line is empty", ->
it "renders the empty line", ->
expect(displayBuffer.lineForRow(13).text).toBe ''
expect(displayBuffer.tokenizedLineForScreenRow(13).text).toBe ''
describe "when there is a non-whitespace character at the max length boundary", ->
describe "when there is whitespace before the boundary", ->
it "wraps the line at the end of the first whitespace preceding the boundary", ->
expect(displayBuffer.lineForRow(10).text).toBe ' return '
expect(displayBuffer.lineForRow(11).text).toBe 'sort(left).concat(pivot).concat(sort(right));'
expect(displayBuffer.tokenizedLineForScreenRow(10).text).toBe ' return '
expect(displayBuffer.tokenizedLineForScreenRow(11).text).toBe 'sort(left).concat(pivot).concat(sort(right));'
describe "when there is no whitespace before the boundary", ->
it "wraps the line exactly at the boundary since there's no more graceful place to wrap it", ->
buffer.setTextInRange([[0, 0], [1, 0]], 'abcdefghijklmnopqrstuvwxyz\n')
displayBuffer.setEditorWidthInChars(10)
expect(displayBuffer.lineForRow(0).text).toBe 'abcdefghij'
expect(displayBuffer.lineForRow(1).text).toBe 'klmnopqrst'
expect(displayBuffer.lineForRow(2).text).toBe 'uvwxyz'
expect(displayBuffer.tokenizedLineForScreenRow(0).text).toBe 'abcdefghij'
expect(displayBuffer.tokenizedLineForScreenRow(1).text).toBe 'klmnopqrst'
expect(displayBuffer.tokenizedLineForScreenRow(2).text).toBe 'uvwxyz'
describe "when there is a whitespace character at the max length boundary", ->
it "wraps the line at the first non-whitespace character following the boundary", ->
expect(displayBuffer.lineForRow(3).text).toBe ' var pivot = items.shift(), current, left = [], '
expect(displayBuffer.lineForRow(4).text).toBe 'right = [];'
expect(displayBuffer.tokenizedLineForScreenRow(3).text).toBe ' var pivot = items.shift(), current, left = [], '
expect(displayBuffer.tokenizedLineForScreenRow(4).text).toBe 'right = [];'
describe "when there are hard tabs", ->
beforeEach ->
buffer.setText(buffer.getText().replace(new RegExp(' ', 'g'), '\t'))
it "correctly tokenizes the hard tabs", ->
expect(displayBuffer.lineForRow(3).tokens[0].isHardTab).toBeTruthy()
expect(displayBuffer.lineForRow(3).tokens[1].isHardTab).toBeTruthy()
expect(displayBuffer.tokenizedLineForScreenRow(3).tokens[0].isHardTab).toBeTruthy()
expect(displayBuffer.tokenizedLineForScreenRow(3).tokens[1].isHardTab).toBeTruthy()
describe "when the buffer changes", ->
describe "when buffer lines are updated", ->
@@ -121,57 +121,57 @@ describe "DisplayBuffer", ->
describe "when the update makes a soft-wrapped line shorter than the max line length", ->
it "rewraps the line and emits a change event", ->
buffer.delete([[6, 24], [6, 42]])
expect(displayBuffer.lineForRow(7).text).toBe ' current < pivot ? : right.push(current);'
expect(displayBuffer.lineForRow(8).text).toBe ' }'
expect(displayBuffer.tokenizedLineForScreenRow(7).text).toBe ' current < pivot ? : right.push(current);'
expect(displayBuffer.tokenizedLineForScreenRow(8).text).toBe ' }'
expect(changeHandler).toHaveBeenCalled()
[[event]]= changeHandler.argsForCall
expect(event).toEqual(start: 7, end: 8, screenDelta: -1, bufferDelta: 0)
describe "when the update causes a line to softwrap an additional time", ->
describe "when the update causes a line to soft wrap an additional time", ->
it "rewraps the line and emits a change event", ->
buffer.insert([6, 28], '1234567890')
expect(displayBuffer.lineForRow(7).text).toBe ' current < pivot ? '
expect(displayBuffer.lineForRow(8).text).toBe 'left1234567890.push(current) : '
expect(displayBuffer.lineForRow(9).text).toBe 'right.push(current);'
expect(displayBuffer.lineForRow(10).text).toBe ' }'
expect(displayBuffer.tokenizedLineForScreenRow(7).text).toBe ' current < pivot ? '
expect(displayBuffer.tokenizedLineForScreenRow(8).text).toBe 'left1234567890.push(current) : '
expect(displayBuffer.tokenizedLineForScreenRow(9).text).toBe 'right.push(current);'
expect(displayBuffer.tokenizedLineForScreenRow(10).text).toBe ' }'
expect(changeHandler).toHaveBeenCalledWith(start: 7, end: 8, screenDelta: 1, bufferDelta: 0)
describe "when buffer lines are inserted", ->
it "inserts / updates wrapped lines and emits a change event", ->
buffer.insert([6, 21], '1234567890 abcdefghij 1234567890\nabcdefghij')
expect(displayBuffer.lineForRow(7).text).toBe ' current < pivot1234567890 abcdefghij '
expect(displayBuffer.lineForRow(8).text).toBe '1234567890'
expect(displayBuffer.lineForRow(9).text).toBe 'abcdefghij ? left.push(current) : '
expect(displayBuffer.lineForRow(10).text).toBe 'right.push(current);'
expect(displayBuffer.tokenizedLineForScreenRow(7).text).toBe ' current < pivot1234567890 abcdefghij '
expect(displayBuffer.tokenizedLineForScreenRow(8).text).toBe '1234567890'
expect(displayBuffer.tokenizedLineForScreenRow(9).text).toBe 'abcdefghij ? left.push(current) : '
expect(displayBuffer.tokenizedLineForScreenRow(10).text).toBe 'right.push(current);'
expect(changeHandler).toHaveBeenCalledWith(start: 7, end: 8, screenDelta: 2, bufferDelta: 1)
describe "when buffer lines are removed", ->
it "removes lines and emits a change event", ->
buffer.setTextInRange([[3, 21], [7, 5]], ';')
expect(displayBuffer.lineForRow(3).text).toBe ' var pivot = items;'
expect(displayBuffer.lineForRow(4).text).toBe ' return '
expect(displayBuffer.lineForRow(5).text).toBe 'sort(left).concat(pivot).concat(sort(right));'
expect(displayBuffer.lineForRow(6).text).toBe ' };'
expect(displayBuffer.tokenizedLineForScreenRow(3).text).toBe ' var pivot = items;'
expect(displayBuffer.tokenizedLineForScreenRow(4).text).toBe ' return '
expect(displayBuffer.tokenizedLineForScreenRow(5).text).toBe 'sort(left).concat(pivot).concat(sort(right));'
expect(displayBuffer.tokenizedLineForScreenRow(6).text).toBe ' };'
expect(changeHandler).toHaveBeenCalledWith(start: 3, end: 9, screenDelta: -6, bufferDelta: -4)
describe "when a newline is inserted, deleted, and re-inserted at the end of a wrapped line (regression)", ->
it "correctly renders the original wrapped line", ->
buffer = atom.project.buildBufferSync(null, '')
displayBuffer = new DisplayBuffer({buffer, tabLength, editorWidthInChars: 30, softWrap: true})
displayBuffer = new DisplayBuffer({buffer, tabLength, editorWidthInChars: 30, softWrapped: true})
buffer.insert([0, 0], "the quick brown fox jumps over the lazy dog.")
buffer.insert([0, Infinity], '\n')
buffer.delete([[0, Infinity], [1, 0]])
buffer.insert([0, Infinity], '\n')
expect(displayBuffer.lineForRow(0).text).toBe "the quick brown fox jumps over "
expect(displayBuffer.lineForRow(1).text).toBe "the lazy dog."
expect(displayBuffer.lineForRow(2).text).toBe ""
expect(displayBuffer.tokenizedLineForScreenRow(0).text).toBe "the quick brown fox jumps over "
expect(displayBuffer.tokenizedLineForScreenRow(1).text).toBe "the lazy dog."
expect(displayBuffer.tokenizedLineForScreenRow(2).text).toBe ""
describe "position translation", ->
it "translates positions accounting for wrapped lines", ->
@@ -204,9 +204,9 @@ describe "DisplayBuffer", ->
describe ".setEditorWidthInChars(length)", ->
it "changes the length at which lines are wrapped and emits a change event for all screen lines", ->
displayBuffer.setEditorWidthInChars(40)
expect(tokensText displayBuffer.lineForRow(4).tokens).toBe 'left = [], right = [];'
expect(tokensText displayBuffer.lineForRow(5).tokens).toBe ' while(items.length > 0) {'
expect(tokensText displayBuffer.lineForRow(12).tokens).toBe 'sort(left).concat(pivot).concat(sort(rig'
expect(tokensText displayBuffer.tokenizedLineForScreenRow(4).tokens).toBe 'left = [], right = [];'
expect(tokensText displayBuffer.tokenizedLineForScreenRow(5).tokens).toBe ' while(items.length > 0) {'
expect(tokensText displayBuffer.tokenizedLineForScreenRow(12).tokens).toBe 'sort(left).concat(pivot).concat(sort(rig'
expect(changeHandler).toHaveBeenCalledWith(start: 0, end: 15, screenDelta: 3, bufferDelta: 0)
it "only allows positive widths to be assigned", ->
@@ -220,10 +220,10 @@ describe "DisplayBuffer", ->
displayBuffer.setWidth(50)
displayBuffer.manageScrollPosition = true
displayBuffer.setSoftWrap(false)
displayBuffer.setSoftWrapped(false)
displayBuffer.setScrollLeft(Infinity)
expect(displayBuffer.getScrollLeft()).toBeGreaterThan 0
displayBuffer.setSoftWrap(true)
displayBuffer.setSoftWrapped(true)
expect(displayBuffer.getScrollLeft()).toBe 0
displayBuffer.setScrollLeft(10)
expect(displayBuffer.getScrollLeft()).toBe 0
@@ -234,7 +234,7 @@ describe "DisplayBuffer", ->
buffer.release()
buffer = atom.project.bufferForPathSync('two-hundred.txt')
displayBuffer = new DisplayBuffer({buffer, tabLength})
displayBuffer.on 'changed', changeHandler
displayBuffer.onDidChange changeHandler
describe "when folds are created and destroyed", ->
describe "when a fold spans multiple lines", ->
@@ -242,7 +242,7 @@ describe "DisplayBuffer", ->
fold = displayBuffer.createFold(4, 7)
expect(fold).toBeDefined()
[line4, line5] = displayBuffer.linesForRows(4, 5)
[line4, line5] = displayBuffer.tokenizedLinesForScreenRows(4, 5)
expect(line4.fold).toBe fold
expect(line4.text).toMatch /^4-+/
expect(line5.text).toBe '8'
@@ -251,7 +251,7 @@ describe "DisplayBuffer", ->
changeHandler.reset()
fold.destroy()
[line4, line5] = displayBuffer.linesForRows(4, 5)
[line4, line5] = displayBuffer.tokenizedLinesForScreenRows(4, 5)
expect(line4.fold).toBeUndefined()
expect(line4.text).toMatch /^4-+/
expect(line5.text).toBe '5'
@@ -262,7 +262,7 @@ describe "DisplayBuffer", ->
it "renders a fold placeholder for the folded line but does not skip any lines", ->
fold = displayBuffer.createFold(4, 4)
[line4, line5] = displayBuffer.linesForRows(4, 5)
[line4, line5] = displayBuffer.tokenizedLinesForScreenRows(4, 5)
expect(line4.fold).toBe fold
expect(line4.text).toMatch /^4-+/
expect(line5.text).toBe '5'
@@ -275,7 +275,7 @@ describe "DisplayBuffer", ->
fold.destroy()
[line4, line5] = displayBuffer.linesForRows(4, 5)
[line4, line5] = displayBuffer.tokenizedLinesForScreenRows(4, 5)
expect(line4.fold).toBeUndefined()
expect(line4.text).toMatch /^4-+/
expect(line5.text).toBe '5'
@@ -287,13 +287,13 @@ describe "DisplayBuffer", ->
innerFold = displayBuffer.createFold(6, 7)
outerFold = displayBuffer.createFold(4, 8)
[line4, line5] = displayBuffer.linesForRows(4, 5)
[line4, line5] = displayBuffer.tokenizedLinesForScreenRows(4, 5)
expect(line4.fold).toBe outerFold
expect(line4.text).toMatch /4-+/
expect(line5.text).toMatch /9-+/
outerFold.destroy()
[line4, line5, line6, line7] = displayBuffer.linesForRows(4, 7)
[line4, line5, line6, line7] = displayBuffer.tokenizedLinesForScreenRows(4, 7)
expect(line4.fold).toBeUndefined()
expect(line4.text).toMatch /^4-+/
expect(line5.text).toBe '5'
@@ -305,7 +305,7 @@ describe "DisplayBuffer", ->
innerFold = displayBuffer.createFold(4, 6)
outerFold = displayBuffer.createFold(4, 8)
[line4, line5] = displayBuffer.linesForRows(4, 5)
[line4, line5] = displayBuffer.tokenizedLinesForScreenRows(4, 5)
expect(line4.fold).toBe outerFold
expect(line4.text).toMatch /4-+/
expect(line5.text).toMatch /9-+/
@@ -326,14 +326,14 @@ describe "DisplayBuffer", ->
innerFold = displayBuffer.createFold(2, 5)
expect(changeHandler).not.toHaveBeenCalled()
[line0, line1] = displayBuffer.linesForRows(0, 1)
[line0, line1] = displayBuffer.tokenizedLinesForScreenRows(0, 1)
expect(line0.fold).toBe outerFold
expect(line1.fold).toBeUndefined()
changeHandler.reset()
innerFold.destroy()
expect(changeHandler).not.toHaveBeenCalled()
[line0, line1] = displayBuffer.linesForRows(0, 1)
[line0, line1] = displayBuffer.tokenizedLinesForScreenRows(0, 1)
expect(line0.fold).toBe outerFold
expect(line1.fold).toBeUndefined()
@@ -342,8 +342,8 @@ describe "DisplayBuffer", ->
fold2 = displayBuffer.createFold(4, 9)
fold1 = displayBuffer.createFold(0, 4)
expect(displayBuffer.lineForRow(0).text).toMatch /^0/
expect(displayBuffer.lineForRow(1).text).toMatch /^10/
expect(displayBuffer.tokenizedLineForScreenRow(0).text).toMatch /^0/
expect(displayBuffer.tokenizedLineForScreenRow(1).text).toMatch /^10/
describe "when there is another display buffer pointing to the same buffer", ->
it "does not create folds in the other display buffer", ->
@@ -363,19 +363,19 @@ describe "DisplayBuffer", ->
buffer.setTextInRange([[1, 0], [5, 1]], 'party!')
it "removes the fold and replaces the selection with the new text", ->
expect(displayBuffer.lineForRow(0).text).toBe "0"
expect(displayBuffer.lineForRow(1).text).toBe "party!"
expect(displayBuffer.lineForRow(2).fold).toBe fold2
expect(displayBuffer.lineForRow(3).text).toMatch /^9-+/
expect(displayBuffer.tokenizedLineForScreenRow(0).text).toBe "0"
expect(displayBuffer.tokenizedLineForScreenRow(1).text).toBe "party!"
expect(displayBuffer.tokenizedLineForScreenRow(2).fold).toBe fold2
expect(displayBuffer.tokenizedLineForScreenRow(3).text).toMatch /^9-+/
expect(changeHandler).toHaveBeenCalledWith(start: 1, end: 3, screenDelta: -2, bufferDelta: -4)
describe "when the changes is subsequently undone", ->
xit "restores destroyed folds", ->
buffer.undo()
expect(displayBuffer.lineForRow(2).text).toBe '2'
expect(displayBuffer.lineForRow(2).fold).toBe fold1
expect(displayBuffer.lineForRow(3).text).toBe '5'
expect(displayBuffer.tokenizedLineForScreenRow(2).text).toBe '2'
expect(displayBuffer.tokenizedLineForScreenRow(2).fold).toBe fold1
expect(displayBuffer.tokenizedLineForScreenRow(3).text).toBe '5'
describe "when the old range surrounds two nested folds", ->
it "removes both folds and replaces the selection with the new text", ->
@@ -384,9 +384,9 @@ describe "DisplayBuffer", ->
buffer.setTextInRange([[1, 0], [10, 0]], 'goodbye')
expect(displayBuffer.lineForRow(0).text).toBe "0"
expect(displayBuffer.lineForRow(1).text).toBe "goodbye10"
expect(displayBuffer.lineForRow(2).text).toBe "11"
expect(displayBuffer.tokenizedLineForScreenRow(0).text).toBe "0"
expect(displayBuffer.tokenizedLineForScreenRow(1).text).toBe "goodbye10"
expect(displayBuffer.tokenizedLineForScreenRow(2).text).toBe "11"
expect(changeHandler).toHaveBeenCalledWith(start: 1, end: 3, screenDelta: -2, bufferDelta: -9)
@@ -403,42 +403,42 @@ describe "DisplayBuffer", ->
it "updates the buffer and re-positions subsequent folds", ->
buffer.setTextInRange([[0, 0], [1, 1]], 'abc')
expect(displayBuffer.lineForRow(0).text).toBe "abc"
expect(displayBuffer.lineForRow(1).fold).toBe fold1
expect(displayBuffer.lineForRow(2).text).toBe "5"
expect(displayBuffer.lineForRow(3).fold).toBe fold2
expect(displayBuffer.lineForRow(4).text).toMatch /^9-+/
expect(displayBuffer.tokenizedLineForScreenRow(0).text).toBe "abc"
expect(displayBuffer.tokenizedLineForScreenRow(1).fold).toBe fold1
expect(displayBuffer.tokenizedLineForScreenRow(2).text).toBe "5"
expect(displayBuffer.tokenizedLineForScreenRow(3).fold).toBe fold2
expect(displayBuffer.tokenizedLineForScreenRow(4).text).toMatch /^9-+/
expect(changeHandler).toHaveBeenCalledWith(start: 0, end: 1, screenDelta: -1, bufferDelta: -1)
changeHandler.reset()
fold1.destroy()
expect(displayBuffer.lineForRow(0).text).toBe "abc"
expect(displayBuffer.lineForRow(1).text).toBe "2"
expect(displayBuffer.lineForRow(3).text).toMatch /^4-+/
expect(displayBuffer.lineForRow(4).text).toBe "5"
expect(displayBuffer.lineForRow(5).fold).toBe fold2
expect(displayBuffer.lineForRow(6).text).toMatch /^9-+/
expect(displayBuffer.tokenizedLineForScreenRow(0).text).toBe "abc"
expect(displayBuffer.tokenizedLineForScreenRow(1).text).toBe "2"
expect(displayBuffer.tokenizedLineForScreenRow(3).text).toMatch /^4-+/
expect(displayBuffer.tokenizedLineForScreenRow(4).text).toBe "5"
expect(displayBuffer.tokenizedLineForScreenRow(5).fold).toBe fold2
expect(displayBuffer.tokenizedLineForScreenRow(6).text).toMatch /^9-+/
expect(changeHandler).toHaveBeenCalledWith(start: 1, end: 1, screenDelta: 2, bufferDelta: 0)
describe "when the old range straddles the beginning of a fold", ->
it "destroys the fold", ->
buffer.setTextInRange([[1, 1], [3, 0]], "a\nb\nc\nd\n")
expect(displayBuffer.lineForRow(1).text).toBe '1a'
expect(displayBuffer.lineForRow(2).text).toBe 'b'
expect(displayBuffer.lineForRow(2).fold).toBeUndefined()
expect(displayBuffer.lineForRow(3).text).toBe 'c'
expect(displayBuffer.tokenizedLineForScreenRow(1).text).toBe '1a'
expect(displayBuffer.tokenizedLineForScreenRow(2).text).toBe 'b'
expect(displayBuffer.tokenizedLineForScreenRow(2).fold).toBeUndefined()
expect(displayBuffer.tokenizedLineForScreenRow(3).text).toBe 'c'
describe "when the old range follows a fold", ->
it "re-positions the screen ranges for the change event based on the preceding fold", ->
buffer.setTextInRange([[10, 0], [11, 0]], 'abc')
expect(displayBuffer.lineForRow(1).text).toBe "1"
expect(displayBuffer.lineForRow(2).fold).toBe fold1
expect(displayBuffer.lineForRow(3).text).toBe "5"
expect(displayBuffer.lineForRow(4).fold).toBe fold2
expect(displayBuffer.lineForRow(5).text).toMatch /^9-+/
expect(displayBuffer.tokenizedLineForScreenRow(1).text).toBe "1"
expect(displayBuffer.tokenizedLineForScreenRow(2).fold).toBe fold1
expect(displayBuffer.tokenizedLineForScreenRow(3).text).toBe "5"
expect(displayBuffer.tokenizedLineForScreenRow(4).fold).toBe fold2
expect(displayBuffer.tokenizedLineForScreenRow(5).text).toMatch /^9-+/
expect(changeHandler).toHaveBeenCalledWith(start: 6, end: 7, screenDelta: -1, bufferDelta: -1)
@@ -449,12 +449,12 @@ describe "DisplayBuffer", ->
expect(fold1.getStartRow()).toBe 2
expect(fold1.getEndRow()).toBe 5
expect(displayBuffer.lineForRow(1).text).toBe "1"
expect(displayBuffer.lineForRow(2).text).toBe "2"
expect(displayBuffer.lineForRow(2).fold).toBe fold1
expect(displayBuffer.lineForRow(3).text).toMatch "5"
expect(displayBuffer.lineForRow(4).fold).toBe fold2
expect(displayBuffer.lineForRow(5).text).toMatch /^9-+/
expect(displayBuffer.tokenizedLineForScreenRow(1).text).toBe "1"
expect(displayBuffer.tokenizedLineForScreenRow(2).text).toBe "2"
expect(displayBuffer.tokenizedLineForScreenRow(2).fold).toBe fold1
expect(displayBuffer.tokenizedLineForScreenRow(3).text).toMatch "5"
expect(displayBuffer.tokenizedLineForScreenRow(4).fold).toBe fold2
expect(displayBuffer.tokenizedLineForScreenRow(5).text).toMatch /^9-+/
expect(changeHandler).toHaveBeenCalledWith(start: 2, end: 2, screenDelta: 0, bufferDelta: 1)
@@ -464,12 +464,12 @@ describe "DisplayBuffer", ->
expect(fold1.getStartRow()).toBe 2
expect(fold1.getEndRow()).toBe 7
expect(displayBuffer.lineForRow(1).text).toBe "1"
expect(displayBuffer.lineForRow(2).text).toBe "2"
expect(displayBuffer.lineForRow(2).fold).toBe fold1
expect(displayBuffer.lineForRow(3).text).toMatch "5"
expect(displayBuffer.lineForRow(4).fold).toBe fold2
expect(displayBuffer.lineForRow(5).text).toMatch /^9-+/
expect(displayBuffer.tokenizedLineForScreenRow(1).text).toBe "1"
expect(displayBuffer.tokenizedLineForScreenRow(2).text).toBe "2"
expect(displayBuffer.tokenizedLineForScreenRow(2).fold).toBe fold1
expect(displayBuffer.tokenizedLineForScreenRow(3).text).toMatch "5"
expect(displayBuffer.tokenizedLineForScreenRow(4).fold).toBe fold2
expect(displayBuffer.tokenizedLineForScreenRow(5).text).toMatch /^9-+/
expect(changeHandler).toHaveBeenCalledWith(start: 2, end: 2, screenDelta: 0, bufferDelta: 3)
@@ -478,21 +478,21 @@ describe "DisplayBuffer", ->
it "destroys the fold", ->
fold2.destroy()
buffer.setTextInRange([[3, 0], [6, 0]], 'a\n')
expect(displayBuffer.lineForRow(2).text).toBe '2'
expect(displayBuffer.lineForRow(2).fold).toBeUndefined()
expect(displayBuffer.lineForRow(3).text).toBe 'a'
expect(displayBuffer.lineForRow(4).text).toBe '6'
expect(displayBuffer.tokenizedLineForScreenRow(2).text).toBe '2'
expect(displayBuffer.tokenizedLineForScreenRow(2).fold).toBeUndefined()
expect(displayBuffer.tokenizedLineForScreenRow(3).text).toBe 'a'
expect(displayBuffer.tokenizedLineForScreenRow(4).text).toBe '6'
describe "when the old range is contained to a single line in-between two folds", ->
it "re-renders the line with the placeholder and re-positions the second fold", ->
buffer.insert([5, 0], 'abc\n')
expect(displayBuffer.lineForRow(1).text).toBe "1"
expect(displayBuffer.lineForRow(2).fold).toBe fold1
expect(displayBuffer.lineForRow(3).text).toMatch "abc"
expect(displayBuffer.lineForRow(4).text).toBe "5"
expect(displayBuffer.lineForRow(5).fold).toBe fold2
expect(displayBuffer.lineForRow(6).text).toMatch /^9-+/
expect(displayBuffer.tokenizedLineForScreenRow(1).text).toBe "1"
expect(displayBuffer.tokenizedLineForScreenRow(2).fold).toBe fold1
expect(displayBuffer.tokenizedLineForScreenRow(3).text).toMatch "abc"
expect(displayBuffer.tokenizedLineForScreenRow(4).text).toBe "5"
expect(displayBuffer.tokenizedLineForScreenRow(5).fold).toBe fold2
expect(displayBuffer.tokenizedLineForScreenRow(6).text).toMatch /^9-+/
expect(changeHandler).toHaveBeenCalledWith(start: 3, end: 3, screenDelta: 1, bufferDelta: 1)
@@ -545,15 +545,15 @@ describe "DisplayBuffer", ->
displayBuffer.createFold(1, 9)
displayBuffer.createFold(11, 12)
expect(displayBuffer.lineForRow(1).text).toBe '1'
expect(displayBuffer.lineForRow(2).text).toBe '10'
expect(displayBuffer.tokenizedLineForScreenRow(1).text).toBe '1'
expect(displayBuffer.tokenizedLineForScreenRow(2).text).toBe '10'
displayBuffer.unfoldBufferRow(2)
expect(displayBuffer.lineForRow(1).text).toBe '1'
expect(displayBuffer.lineForRow(2).text).toBe '2'
expect(displayBuffer.lineForRow(7).fold).toBeDefined()
expect(displayBuffer.lineForRow(8).text).toMatch /^9-+/
expect(displayBuffer.lineForRow(10).fold).toBeDefined()
expect(displayBuffer.tokenizedLineForScreenRow(1).text).toBe '1'
expect(displayBuffer.tokenizedLineForScreenRow(2).text).toBe '2'
expect(displayBuffer.tokenizedLineForScreenRow(7).fold).toBeDefined()
expect(displayBuffer.tokenizedLineForScreenRow(8).text).toMatch /^9-+/
expect(displayBuffer.tokenizedLineForScreenRow(10).fold).toBeDefined()
describe ".outermostFoldsInBufferRowRange(startRow, endRow)", ->
it "returns the outermost folds entirely contained in the given row range, exclusive of end row", ->
@@ -568,7 +568,7 @@ describe "DisplayBuffer", ->
describe "::clipScreenPosition(screenPosition, wrapBeyondNewlines: false, wrapAtSoftNewlines: false, skipAtomicTokens: false)", ->
beforeEach ->
displayBuffer.setSoftWrap(true)
displayBuffer.setSoftWrapped(true)
displayBuffer.setEditorWidthInChars(50)
it "allows valid positions", ->
@@ -643,7 +643,7 @@ describe "DisplayBuffer", ->
it "correctly translates positions on soft wrapped lines containing tabs", ->
buffer.setText('\t\taa bb cc dd ee ff gg')
displayBuffer.setSoftWrap(true)
displayBuffer.setSoftWrapped(true)
displayBuffer.setEditorWidthInChars(10)
expect(displayBuffer.screenPositionForBufferPosition([0, 10], wrapAtSoftNewlines: true)).toEqual [1, 0]
expect(displayBuffer.bufferPositionForScreenPosition([1, 0])).toEqual [0, 10]
@@ -686,7 +686,7 @@ describe "DisplayBuffer", ->
expect(marker2.getScreenRange()).toEqual [[5, 4], [5, 10]]
it "emits a 'marker-created' event on the DisplayBuffer whenever a marker is created", ->
displayBuffer.on 'marker-created', markerCreatedHandler = jasmine.createSpy("markerCreatedHandler")
displayBuffer.onDidCreateMarker markerCreatedHandler = jasmine.createSpy("markerCreatedHandler")
marker1 = displayBuffer.markScreenRange([[5, 4], [5, 10]])
expect(markerCreatedHandler).toHaveBeenCalledWith(marker1)
@@ -722,7 +722,7 @@ describe "DisplayBuffer", ->
beforeEach ->
marker = displayBuffer.markScreenRange([[5, 4], [5, 10]])
marker.on 'changed', markerChangedHandler = jasmine.createSpy("markerChangedHandler")
marker.onDidChange markerChangedHandler = jasmine.createSpy("markerChangedHandler")
it "triggers the 'changed' event whenever the markers head's screen position changes in the buffer or on screen", ->
marker.setHeadScreenPosition([8, 20])
@@ -859,8 +859,8 @@ describe "DisplayBuffer", ->
it "updates markers before emitting buffer change events, but does not notify their observers until the change event", ->
marker2 = displayBuffer.markBufferRange([[8, 1], [8, 1]])
marker2.on 'changed', marker2ChangedHandler = jasmine.createSpy("marker2ChangedHandler")
displayBuffer.on 'changed', changeHandler = jasmine.createSpy("changeHandler").andCallFake -> onDisplayBufferChange()
marker2.onDidChange marker2ChangedHandler = jasmine.createSpy("marker2ChangedHandler")
displayBuffer.onDidChange changeHandler = jasmine.createSpy("changeHandler").andCallFake -> onDisplayBufferChange()
# New change ----
@@ -886,7 +886,7 @@ describe "DisplayBuffer", ->
marker2ChangedHandler.reset()
marker3 = displayBuffer.markBufferRange([[8, 1], [8, 2]])
marker3.on 'changed', marker3ChangedHandler = jasmine.createSpy("marker3ChangedHandler")
marker3.onDidChange marker3ChangedHandler = jasmine.createSpy("marker3ChangedHandler")
onDisplayBufferChange = ->
# calls change handler first
@@ -932,7 +932,7 @@ describe "DisplayBuffer", ->
expect(marker3ChangedHandler).toHaveBeenCalled()
it "updates the position of markers before emitting change events that aren't caused by a buffer change", ->
displayBuffer.on 'changed', changeHandler = jasmine.createSpy("changeHandler").andCallFake ->
displayBuffer.onDidChange changeHandler = jasmine.createSpy("changeHandler").andCallFake ->
# calls change handler first
expect(markerChangedHandler).not.toHaveBeenCalled()
# but still updates the markers
@@ -998,16 +998,16 @@ describe "DisplayBuffer", ->
expect(marker.isValid()).toBeFalsy()
expect(displayBuffer.getMarker(marker.id)).toBeUndefined()
it "emits 'destroyed' events when markers are destroyed", ->
it "notifies ::onDidDestroy observers when markers are destroyed", ->
destroyedHandler = jasmine.createSpy("destroyedHandler")
marker = displayBuffer.markScreenRange([[5, 4], [5, 10]])
marker.on 'destroyed', destroyedHandler
marker.onDidDestroy destroyedHandler
marker.destroy()
expect(destroyedHandler).toHaveBeenCalled()
destroyedHandler.reset()
marker2 = displayBuffer.markScreenRange([[5, 4], [5, 10]])
marker2.on 'destroyed', destroyedHandler
marker2.onDidDestroy destroyedHandler
buffer.getMarker(marker2.id).destroy()
expect(destroyedHandler).toHaveBeenCalled()
@@ -1037,16 +1037,28 @@ describe "DisplayBuffer", ->
expect(start.top).toBe 5 * 20
expect(start.left).toBe (4 * 10) + (6 * 11)
describe 'when there are multiple DisplayBuffers for a buffer', ->
describe 'when a marker is created', ->
it 'the second display buffer will not emit a marker-created event when the marker has been deleted in the first marker-created event', ->
displayBuffer2 = new DisplayBuffer({buffer, tabLength})
displayBuffer.onDidCreateMarker markerCreated1 = jasmine.createSpy().andCallFake (marker) -> marker.destroy()
displayBuffer2.onDidCreateMarker markerCreated2 = jasmine.createSpy()
displayBuffer.markBufferRange([[0, 0], [1, 5]], {})
expect(markerCreated1).toHaveBeenCalled()
expect(markerCreated2).not.toHaveBeenCalled()
describe "decorations", ->
[marker, decoration, decorationParams] = []
[marker, decoration, decorationProperties] = []
beforeEach ->
marker = displayBuffer.markBufferRange([[2, 13], [3, 15]])
decorationParams = {type: 'gutter', class: 'one'}
decoration = displayBuffer.decorateMarker(marker, decorationParams)
decorationProperties = {type: 'gutter', class: 'one'}
decoration = displayBuffer.decorateMarker(marker, decorationProperties)
it "can add decorations associated with markers and remove them", ->
expect(decoration).toBeDefined()
expect(decoration.getParams()).toBe decorationParams
expect(decoration.getProperties()).toBe decorationProperties
expect(displayBuffer.decorationForId(decoration.id)).toBe decoration
expect(displayBuffer.decorationsForScreenRowRange(2, 3)[marker.id][0]).toBe decoration
@@ -1061,12 +1073,12 @@ describe "DisplayBuffer", ->
describe "when a decoration is updated via Decoration::update()", ->
it "emits an 'updated' event containing the new and old params", ->
decoration.on 'updated', updatedSpy = jasmine.createSpy()
decoration.update type: 'gutter', class: 'two'
decoration.onDidChangeProperties updatedSpy = jasmine.createSpy()
decoration.setProperties type: 'gutter', class: 'two'
{oldParams, newParams} = updatedSpy.mostRecentCall.args[0]
expect(oldParams).toEqual decorationParams
expect(newParams).toEqual type: 'gutter', class: 'two', id: decoration.id
{oldProperties, newProperties} = updatedSpy.mostRecentCall.args[0]
expect(oldProperties).toEqual decorationProperties
expect(newProperties).toEqual type: 'gutter', class: 'two', id: decoration.id
describe "::setScrollTop", ->
beforeEach ->
@@ -1159,7 +1171,7 @@ describe "DisplayBuffer", ->
it "recomputes the scroll width when the scoped character widths change in a batch", ->
operatorWidth = 20
displayBuffer.on 'character-widths-changed', changedSpy = jasmine.createSpy()
displayBuffer.onDidChangeCharacterWidths changedSpy = jasmine.createSpy()
displayBuffer.batchCharacterMeasurement ->
displayBuffer.setScopedCharWidth(['source.js', 'keyword.operator.js'], '<', operatorWidth)
+102 -44
Ver Arquivo
@@ -1,7 +1,7 @@
_ = require 'underscore-plus'
{extend, flatten, toArray, last} = _
ReactEditorView = require '../src/react-editor-view'
EditorView = require '../src/editor-view'
EditorComponent = require '../src/editor-component'
nbsp = String.fromCharCode(160)
@@ -34,7 +34,7 @@ describe "EditorComponent", ->
contentNode = document.querySelector('#jasmine-content')
contentNode.style.width = '1000px'
wrapperView = new ReactEditorView(editor, {lineOverdrawMargin})
wrapperView = new EditorView(editor, {lineOverdrawMargin})
wrapperView.attachToDom()
wrapperNode = wrapperView.element
@@ -65,9 +65,9 @@ describe "EditorComponent", ->
linesNode = componentNode.querySelector('.lines')
expect(linesNode.style['-webkit-transform']).toBe "translate3d(0px, 0px, 0px)"
expect(componentNode.querySelectorAll('.line').length).toBe 6 + 2 # no margin above
expect(component.lineNodeForScreenRow(0).textContent).toBe editor.lineForScreenRow(0).text
expect(component.lineNodeForScreenRow(0).textContent).toBe editor.tokenizedLineForScreenRow(0).text
expect(component.lineNodeForScreenRow(0).offsetTop).toBe 0
expect(component.lineNodeForScreenRow(5).textContent).toBe editor.lineForScreenRow(5).text
expect(component.lineNodeForScreenRow(5).textContent).toBe editor.tokenizedLineForScreenRow(5).text
expect(component.lineNodeForScreenRow(5).offsetTop).toBe 5 * lineHeightInPixels
verticalScrollbarNode.scrollTop = 4.5 * lineHeightInPixels
@@ -77,9 +77,9 @@ describe "EditorComponent", ->
expect(linesNode.style['-webkit-transform']).toBe "translate3d(0px, #{-4.5 * lineHeightInPixels}px, 0px)"
expect(componentNode.querySelectorAll('.line').length).toBe 6 + 4 # margin above and below
expect(component.lineNodeForScreenRow(2).offsetTop).toBe 2 * lineHeightInPixels
expect(component.lineNodeForScreenRow(2).textContent).toBe editor.lineForScreenRow(2).text
expect(component.lineNodeForScreenRow(2).textContent).toBe editor.tokenizedLineForScreenRow(2).text
expect(component.lineNodeForScreenRow(9).offsetTop).toBe 9 * lineHeightInPixels
expect(component.lineNodeForScreenRow(9).textContent).toBe editor.lineForScreenRow(9).text
expect(component.lineNodeForScreenRow(9).textContent).toBe editor.tokenizedLineForScreenRow(9).text
it "updates the top position of subsequent lines when lines are inserted or removed", ->
editor.getBuffer().deleteRows(0, 1)
@@ -111,11 +111,11 @@ describe "EditorComponent", ->
buffer.insert([0, 0], '\n\n')
nextAnimationFrame()
expect(component.lineNodeForScreenRow(3).textContent).toBe editor.lineForScreenRow(3).text
expect(component.lineNodeForScreenRow(3).textContent).toBe editor.tokenizedLineForScreenRow(3).text
buffer.delete([[0, 0], [3, 0]])
nextAnimationFrame()
expect(component.lineNodeForScreenRow(3).textContent).toBe editor.lineForScreenRow(3).text
expect(component.lineNodeForScreenRow(3).textContent).toBe editor.tokenizedLineForScreenRow(3).text
it "updates the top position of lines when the line height changes", ->
initialLineHeightInPixels = editor.getLineHeightInPixels()
@@ -278,7 +278,7 @@ describe "EditorComponent", ->
describe "when soft wrapping is enabled", ->
beforeEach ->
editor.setText "a line that wraps \n"
editor.setSoftWrap(true)
editor.setSoftWrapped(true)
nextAnimationFrame()
componentNode.style.width = 16 * charWidth + editor.getVerticalScrollbarWidth() + 'px'
component.measureHeightAndWidth()
@@ -457,7 +457,7 @@ describe "EditorComponent", ->
expect(component.lineNumberNodeForScreenRow(6).offsetTop).toBe 6 * lineHeightInPixels
it "renders • characters for soft-wrapped lines", ->
editor.setSoftWrap(true)
editor.setSoftWrapped(true)
wrapperNode.style.height = 4.5 * lineHeightInPixels + 'px'
wrapperNode.style.width = 30 * charWidth + 'px'
component.measureHeightAndWidth()
@@ -601,7 +601,7 @@ describe "EditorComponent", ->
describe "cursor rendering", ->
it "renders the currently visible cursors, translated relative to the scroll position", ->
cursor1 = editor.getCursor()
cursor1 = editor.getLastCursor()
cursor1.setScreenPosition([0, 5])
wrapperNode.style.height = 4.5 * lineHeightInPixels + 'px'
@@ -710,7 +710,7 @@ describe "EditorComponent", ->
expect(cursorsNode.classList.contains('blink-off')).toBe false
# Stop blinking after moving the cursor
editor.moveCursorRight()
editor.moveRight()
expect(cursorsNode.classList.contains('blink-off')).toBe false
advanceClock(component.props.cursorBlinkResumeDelay)
@@ -814,8 +814,8 @@ describe "EditorComponent", ->
it "does not render empty selections", ->
editor.addSelectionForBufferRange([[2, 2], [2, 2]])
nextAnimationFrame()
expect(editor.getSelection(0).isEmpty()).toBe true
expect(editor.getSelection(1).isEmpty()).toBe true
expect(editor.getSelections()[0].isEmpty()).toBe true
expect(editor.getSelections()[1].isEmpty()).toBe true
expect(componentNode.querySelectorAll('.selection').length).toBe 0
@@ -893,7 +893,7 @@ describe "EditorComponent", ->
it "only applies decorations to screen rows that are spanned by their marker when lines are soft-wrapped", ->
editor.setText("a line that wraps, ok")
editor.setSoftWrap(true)
editor.setSoftWrapped(true)
componentNode.style.width = 16 * charWidth + 'px'
component.measureHeightAndWidth()
nextAnimationFrame()
@@ -1134,7 +1134,7 @@ describe "EditorComponent", ->
it "renders the decoration's new params", ->
expect(componentNode.querySelector('.test-highlight')).toBeTruthy()
decoration.update(type: 'highlight', class: 'new-test-highlight')
decoration.setProperties(type: 'highlight', class: 'new-test-highlight')
nextAnimationFrame()
expect(componentNode.querySelector('.test-highlight')).toBeFalsy()
@@ -1318,6 +1318,12 @@ describe "EditorComponent", ->
linesNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenPosition([4, 8]), {target}))
expect(editor.isFoldedAtBufferRow 4).toBe false
describe "when the horizontal scrollbar is interacted with", ->
it "clicking on the scrollbar does not move the cursor", ->
target = horizontalScrollbarNode
linesNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenPosition([4, 8]), {target}))
expect(editor.getCursorScreenPosition()).toEqual [0, 0]
describe "mouse interactions on the gutter", ->
gutterNode = null
@@ -1325,9 +1331,19 @@ describe "EditorComponent", ->
gutterNode = componentNode.querySelector('.gutter')
describe "when the gutter is clicked", ->
it "moves the cursor to the beginning of the clicked row", ->
it "selects the clicked row", ->
gutterNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenRowInGutter(4)))
expect(editor.getCursorScreenPosition()).toEqual [4, 0]
expect(editor.getSelectedScreenRange()).toEqual [[4, 0], [5, 0]]
describe "when the gutter is meta-clicked", ->
it "creates a new selection for the clicked row", ->
editor.setSelectedScreenRange([[3, 0], [3, 2]])
gutterNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenRowInGutter(4), metaKey: true))
expect(editor.getSelectedScreenRanges()).toEqual [[[3, 0], [3, 2]], [[4, 0], [5, 0]]]
gutterNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenRowInGutter(6), metaKey: true))
expect(editor.getSelectedScreenRanges()).toEqual [[[3, 0], [3, 2]], [[4, 0], [5, 0]], [[6, 0], [7, 0]]]
describe "when the gutter is shift-clicked", ->
beforeEach ->
@@ -1360,6 +1376,40 @@ describe "EditorComponent", ->
gutterNode.dispatchEvent(buildMouseEvent('mouseup', clientCoordinatesForScreenRowInGutter(2)))
expect(editor.getSelectedScreenRange()).toEqual [[2, 0], [7, 0]]
describe "when the gutter is meta-clicked and dragged", ->
beforeEach ->
editor.setSelectedScreenRange([[3, 0], [3, 2]])
describe "when dragging downward", ->
it "selects the rows between the start and end of the drag", ->
gutterNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenRowInGutter(4), metaKey: true))
gutterNode.dispatchEvent(buildMouseEvent('mousemove', clientCoordinatesForScreenRowInGutter(6), metaKey: true))
nextAnimationFrame()
gutterNode.dispatchEvent(buildMouseEvent('mouseup', clientCoordinatesForScreenRowInGutter(6), metaKey: true))
expect(editor.getSelectedScreenRanges()).toEqual [[[3, 0], [3, 2]], [[4, 0], [7, 0]]]
it "merges overlapping selections", ->
gutterNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenRowInGutter(2), metaKey: true))
gutterNode.dispatchEvent(buildMouseEvent('mousemove', clientCoordinatesForScreenRowInGutter(6), metaKey: true))
nextAnimationFrame()
gutterNode.dispatchEvent(buildMouseEvent('mouseup', clientCoordinatesForScreenRowInGutter(6), metaKey: true))
expect(editor.getSelectedScreenRanges()).toEqual [[[2, 0], [7, 0]]]
describe "when dragging upward", ->
it "selects the rows between the start and end of the drag", ->
gutterNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenRowInGutter(6), metaKey: true))
gutterNode.dispatchEvent(buildMouseEvent('mousemove', clientCoordinatesForScreenRowInGutter(4), metaKey: true))
nextAnimationFrame()
gutterNode.dispatchEvent(buildMouseEvent('mouseup', clientCoordinatesForScreenRowInGutter(4), metaKey: true))
expect(editor.getSelectedScreenRanges()).toEqual [[[3, 0], [3, 2]], [[4, 0], [7, 0]]]
it "merges overlapping selections", ->
gutterNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenRowInGutter(6), metaKey: true))
gutterNode.dispatchEvent(buildMouseEvent('mousemove', clientCoordinatesForScreenRowInGutter(2), metaKey: true))
nextAnimationFrame()
gutterNode.dispatchEvent(buildMouseEvent('mouseup', clientCoordinatesForScreenRowInGutter(2), metaKey: true))
expect(editor.getSelectedScreenRanges()).toEqual [[[2, 0], [7, 0]]]
describe "when the gutter is shift-clicked and dragged", ->
describe "when the shift-click is below the existing selection's tail", ->
describe "when dragging downward", ->
@@ -1431,7 +1481,7 @@ describe "EditorComponent", ->
cursor = null
beforeEach ->
cursor = editor.getCursor()
cursor = editor.getLastCursor()
cursor.setScreenPosition([0, 0])
it "adds the 'has-selection' class to the editor when there is a selection", ->
@@ -1551,6 +1601,7 @@ describe "EditorComponent", ->
height: 8px;
}
"""
nextAnimationFrame()
scrollbarCornerNode = componentNode.querySelector('.scrollbar-corner')
expect(verticalScrollbarNode.offsetWidth).toBe 8
@@ -1776,28 +1827,28 @@ describe "EditorComponent", ->
it "inserts the newest character in the input's value into the buffer", ->
componentNode.dispatchEvent(buildTextInputEvent(data: 'x', target: inputNode))
nextAnimationFrame()
expect(editor.lineForBufferRow(0)).toBe 'xvar quicksort = function () {'
expect(editor.lineTextForBufferRow(0)).toBe 'xvar quicksort = function () {'
componentNode.dispatchEvent(buildTextInputEvent(data: 'y', target: inputNode))
nextAnimationFrame()
expect(editor.lineForBufferRow(0)).toBe 'xyvar quicksort = function () {'
expect(editor.lineTextForBufferRow(0)).toBe 'xyvar quicksort = function () {'
it "replaces the last character if the length of the input's value doesn't increase, as occurs with the accented character menu", ->
componentNode.dispatchEvent(buildTextInputEvent(data: 'u', target: inputNode))
nextAnimationFrame()
expect(editor.lineForBufferRow(0)).toBe 'uvar quicksort = function () {'
expect(editor.lineTextForBufferRow(0)).toBe 'uvar quicksort = function () {'
# simulate the accented character suggestion's selection of the previous character
inputNode.setSelectionRange(0, 1)
componentNode.dispatchEvent(buildTextInputEvent(data: 'ü', target: inputNode))
nextAnimationFrame()
expect(editor.lineForBufferRow(0)).toBe 'üvar quicksort = function () {'
expect(editor.lineTextForBufferRow(0)).toBe 'üvar quicksort = function () {'
it "does not handle input events when input is disabled", ->
component.setInputEnabled(false)
componentNode.dispatchEvent(buildTextInputEvent(data: 'x', target: inputNode))
expect(nextAnimationFrame).toBe noAnimationFrame
expect(editor.lineForBufferRow(0)).toBe 'var quicksort = function () {'
expect(editor.lineTextForBufferRow(0)).toBe 'var quicksort = function () {'
describe "when IME composition is used to insert international characters", ->
inputNode = null
@@ -1815,46 +1866,46 @@ describe "EditorComponent", ->
it "inserts the chosen completion", ->
componentNode.dispatchEvent(buildIMECompositionEvent('compositionstart', target: inputNode))
componentNode.dispatchEvent(buildIMECompositionEvent('compositionupdate', data: 's', target: inputNode))
expect(editor.lineForBufferRow(0)).toBe 'svar quicksort = function () {'
expect(editor.lineTextForBufferRow(0)).toBe 'svar quicksort = function () {'
componentNode.dispatchEvent(buildIMECompositionEvent('compositionupdate', data: 'sd', target: inputNode))
expect(editor.lineForBufferRow(0)).toBe 'sdvar quicksort = function () {'
expect(editor.lineTextForBufferRow(0)).toBe 'sdvar quicksort = function () {'
componentNode.dispatchEvent(buildIMECompositionEvent('compositionend', target: inputNode))
componentNode.dispatchEvent(buildTextInputEvent(data: '速度', target: inputNode))
expect(editor.lineForBufferRow(0)).toBe '速度var quicksort = function () {'
expect(editor.lineTextForBufferRow(0)).toBe '速度var quicksort = function () {'
it "reverts back to the original text when the completion helper is dismissed", ->
componentNode.dispatchEvent(buildIMECompositionEvent('compositionstart', target: inputNode))
componentNode.dispatchEvent(buildIMECompositionEvent('compositionupdate', data: 's', target: inputNode))
expect(editor.lineForBufferRow(0)).toBe 'svar quicksort = function () {'
expect(editor.lineTextForBufferRow(0)).toBe 'svar quicksort = function () {'
componentNode.dispatchEvent(buildIMECompositionEvent('compositionupdate', data: 'sd', target: inputNode))
expect(editor.lineForBufferRow(0)).toBe 'sdvar quicksort = function () {'
expect(editor.lineTextForBufferRow(0)).toBe 'sdvar quicksort = function () {'
componentNode.dispatchEvent(buildIMECompositionEvent('compositionend', target: inputNode))
expect(editor.lineForBufferRow(0)).toBe 'var quicksort = function () {'
expect(editor.lineTextForBufferRow(0)).toBe 'var quicksort = function () {'
it "allows multiple accented character to be inserted with the ' on a US international layout", ->
inputNode.value = "'"
inputNode.setSelectionRange(0, 1)
componentNode.dispatchEvent(buildIMECompositionEvent('compositionstart', target: inputNode))
componentNode.dispatchEvent(buildIMECompositionEvent('compositionupdate', data: "'", target: inputNode))
expect(editor.lineForBufferRow(0)).toBe "'var quicksort = function () {"
expect(editor.lineTextForBufferRow(0)).toBe "'var quicksort = function () {"
componentNode.dispatchEvent(buildIMECompositionEvent('compositionend', target: inputNode))
componentNode.dispatchEvent(buildTextInputEvent(data: 'á', target: inputNode))
expect(editor.lineForBufferRow(0)).toBe "ávar quicksort = function () {"
expect(editor.lineTextForBufferRow(0)).toBe "ávar quicksort = function () {"
inputNode.value = "'"
inputNode.setSelectionRange(0, 1)
componentNode.dispatchEvent(buildIMECompositionEvent('compositionstart', target: inputNode))
componentNode.dispatchEvent(buildIMECompositionEvent('compositionupdate', data: "'", target: inputNode))
expect(editor.lineForBufferRow(0)).toBe "á'var quicksort = function () {"
expect(editor.lineTextForBufferRow(0)).toBe "á'var quicksort = function () {"
componentNode.dispatchEvent(buildIMECompositionEvent('compositionend', target: inputNode))
componentNode.dispatchEvent(buildTextInputEvent(data: 'á', target: inputNode))
expect(editor.lineForBufferRow(0)).toBe "áávar quicksort = function () {"
expect(editor.lineTextForBufferRow(0)).toBe "áávar quicksort = function () {"
describe "when a string is selected", ->
beforeEach ->
@@ -1863,25 +1914,25 @@ describe "EditorComponent", ->
it "inserts the chosen completion", ->
componentNode.dispatchEvent(buildIMECompositionEvent('compositionstart', target: inputNode))
componentNode.dispatchEvent(buildIMECompositionEvent('compositionupdate', data: 's', target: inputNode))
expect(editor.lineForBufferRow(0)).toBe 'var ssort = function () {'
expect(editor.lineTextForBufferRow(0)).toBe 'var ssort = function () {'
componentNode.dispatchEvent(buildIMECompositionEvent('compositionupdate', data: 'sd', target: inputNode))
expect(editor.lineForBufferRow(0)).toBe 'var sdsort = function () {'
expect(editor.lineTextForBufferRow(0)).toBe 'var sdsort = function () {'
componentNode.dispatchEvent(buildIMECompositionEvent('compositionend', target: inputNode))
componentNode.dispatchEvent(buildTextInputEvent(data: '速度', target: inputNode))
expect(editor.lineForBufferRow(0)).toBe 'var 速度sort = function () {'
expect(editor.lineTextForBufferRow(0)).toBe 'var 速度sort = function () {'
it "reverts back to the original text when the completion helper is dismissed", ->
componentNode.dispatchEvent(buildIMECompositionEvent('compositionstart', target: inputNode))
componentNode.dispatchEvent(buildIMECompositionEvent('compositionupdate', data: 's', target: inputNode))
expect(editor.lineForBufferRow(0)).toBe 'var ssort = function () {'
expect(editor.lineTextForBufferRow(0)).toBe 'var ssort = function () {'
componentNode.dispatchEvent(buildIMECompositionEvent('compositionupdate', data: 'sd', target: inputNode))
expect(editor.lineForBufferRow(0)).toBe 'var sdsort = function () {'
expect(editor.lineTextForBufferRow(0)).toBe 'var sdsort = function () {'
componentNode.dispatchEvent(buildIMECompositionEvent('compositionend', target: inputNode))
expect(editor.lineForBufferRow(0)).toBe 'var quicksort = function () {'
expect(editor.lineTextForBufferRow(0)).toBe 'var quicksort = function () {'
describe "commands", ->
describe "editor:consolidate-selections", ->
@@ -1904,7 +1955,7 @@ describe "EditorComponent", ->
hiddenParent.style.display = 'none'
contentNode.appendChild(hiddenParent)
wrapperView = new ReactEditorView(editor, {lineOverdrawMargin})
wrapperView = new EditorView(editor, {lineOverdrawMargin})
wrapperNode = wrapperView.element
wrapperView.appendTo(hiddenParent)
@@ -1946,6 +1997,7 @@ describe "EditorComponent", ->
wrapperView.hide()
component.setFontSize(22)
editor.getBuffer().insert([0, 0], 'a') # regression test against atom/atom#3318
wrapperView.show()
editor.setCursorBufferPosition([0, Infinity])
@@ -2014,7 +2066,7 @@ describe "EditorComponent", ->
describe "soft wrapping", ->
beforeEach ->
editor.setSoftWrap(true)
editor.setSoftWrapped(true)
nextAnimationFrame()
it "updates the wrap location when the editor is resized", ->
@@ -2140,10 +2192,10 @@ describe "EditorComponent", ->
describe "legacy editor compatibility", ->
it "triggers the screen-lines-changed event before the editor:display-update event", ->
editor.setSoftWrap(true)
editor.setSoftWrapped(true)
callingOrder = []
editor.on 'screen-lines-changed', -> callingOrder.push 'screen-lines-changed'
editor.onDidChangeScreenLines -> callingOrder.push 'screen-lines-changed'
wrapperView.on 'editor:display-updated', -> callingOrder.push 'editor:display-updated'
editor.insertText("HELLO! HELLO!\n HELLO! HELLO! HELLO! HELLO! HELLO! HELLO! HELLO! HELLO! HELLO! HELLO! HELLO! HELLO! HELLO! HELLO! HELLO! HELLO! HELLO! HELLO! ")
nextAnimationFrame()
@@ -2157,6 +2209,12 @@ describe "EditorComponent", ->
setEditorWidthInChars(wrapperView, 10)
expect(componentNode.querySelector('.scroll-view').offsetWidth).toBe charWidth * 10
describe "grammar data attributes", ->
it "adds and updates the grammar data attribute based on the current grammar", ->
expect(wrapperNode.dataset.grammar).toBe 'source js'
editor.setGrammar(atom.syntax.nullGrammar)
expect(wrapperNode.dataset.grammar).toBe 'text plain null-grammar'
buildMouseEvent = (type, properties...) ->
properties = extend({bubbles: true, cancelable: true}, properties...)
properties.detail ?= 1
+496 -357
Ver Arquivo
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
+2 -2
Ver Arquivo
@@ -253,10 +253,10 @@ describe "Git", ->
statusHandler = jasmine.createSpy('statusHandler')
atom.project.getRepo().on 'status-changed', statusHandler
editor.getBuffer().emit 'path-changed'
editor.getBuffer().emitter.emit 'did-change-path'
expect(statusHandler.callCount).toBe 1
expect(statusHandler).toHaveBeenCalledWith editor.getPath(), 256
editor.getBuffer().emit 'path-changed'
editor.getBuffer().emitter.emit 'did-change-path'
expect(statusHandler.callCount).toBe 1
describe "when a project is deserialized", ->
+18 -18
Ver Arquivo
@@ -275,46 +275,46 @@ describe "LanguageMode", ->
it "folds every foldable line", ->
languageMode.foldAll()
fold1 = editor.lineForScreenRow(0).fold
fold1 = editor.tokenizedLineForScreenRow(0).fold
expect([fold1.getStartRow(), fold1.getEndRow()]).toEqual [0, 12]
fold1.destroy()
fold2 = editor.lineForScreenRow(1).fold
fold2 = editor.tokenizedLineForScreenRow(1).fold
expect([fold2.getStartRow(), fold2.getEndRow()]).toEqual [1, 9]
fold2.destroy()
fold3 = editor.lineForScreenRow(4).fold
fold3 = editor.tokenizedLineForScreenRow(4).fold
expect([fold3.getStartRow(), fold3.getEndRow()]).toEqual [4, 7]
describe ".foldBufferRow(bufferRow)", ->
describe "when bufferRow can be folded", ->
it "creates a fold based on the syntactic region starting at the given row", ->
languageMode.foldBufferRow(1)
fold = editor.lineForScreenRow(1).fold
fold = editor.tokenizedLineForScreenRow(1).fold
expect(fold.getStartRow()).toBe 1
expect(fold.getEndRow()).toBe 9
describe "when bufferRow can't be folded", ->
it "searches upward for the first row that begins a syntatic region containing the given buffer row (and folds it)", ->
languageMode.foldBufferRow(8)
fold = editor.lineForScreenRow(1).fold
fold = editor.tokenizedLineForScreenRow(1).fold
expect(fold.getStartRow()).toBe 1
expect(fold.getEndRow()).toBe 9
describe "when the bufferRow is already folded", ->
it "searches upward for the first row that begins a syntatic region containing the folded row (and folds it)", ->
languageMode.foldBufferRow(2)
expect(editor.lineForScreenRow(1).fold).toBeDefined()
expect(editor.lineForScreenRow(0).fold).not.toBeDefined()
expect(editor.tokenizedLineForScreenRow(1).fold).toBeDefined()
expect(editor.tokenizedLineForScreenRow(0).fold).not.toBeDefined()
languageMode.foldBufferRow(1)
expect(editor.lineForScreenRow(0).fold).toBeDefined()
expect(editor.tokenizedLineForScreenRow(0).fold).toBeDefined()
describe "when the bufferRow is in a multi-line comment", ->
it "searches upward and downward for surrounding comment lines and folds them as a single fold", ->
buffer.insert([1,0], " //this is a comment\n // and\n //more docs\n\n//second comment")
languageMode.foldBufferRow(1)
fold = editor.lineForScreenRow(1).fold
fold = editor.tokenizedLineForScreenRow(1).fold
expect(fold.getStartRow()).toBe 1
expect(fold.getEndRow()).toBe 3
@@ -322,7 +322,7 @@ describe "LanguageMode", ->
it "searches upward for the first row that begins a syntatic region containing the folded row (and folds it)", ->
buffer.insert([1,0], " //this is a single line comment\n")
languageMode.foldBufferRow(1)
fold = editor.lineForScreenRow(0).fold
fold = editor.tokenizedLineForScreenRow(0).fold
expect(fold.getStartRow()).toBe 0
expect(fold.getEndRow()).toBe 13
@@ -357,38 +357,38 @@ describe "LanguageMode", ->
it "folds every foldable line", ->
languageMode.foldAll()
fold1 = editor.lineForScreenRow(0).fold
fold1 = editor.tokenizedLineForScreenRow(0).fold
expect([fold1.getStartRow(), fold1.getEndRow()]).toEqual [0, 19]
fold1.destroy()
fold2 = editor.lineForScreenRow(1).fold
fold2 = editor.tokenizedLineForScreenRow(1).fold
expect([fold2.getStartRow(), fold2.getEndRow()]).toEqual [1, 4]
fold3 = editor.lineForScreenRow(2).fold.destroy()
fold3 = editor.tokenizedLineForScreenRow(2).fold.destroy()
fold4 = editor.lineForScreenRow(3).fold
fold4 = editor.tokenizedLineForScreenRow(3).fold
expect([fold4.getStartRow(), fold4.getEndRow()]).toEqual [6, 8]
describe ".foldAllAtIndentLevel()", ->
it "folds every foldable range at a given indentLevel", ->
languageMode.foldAllAtIndentLevel(2)
fold1 = editor.lineForScreenRow(6).fold
fold1 = editor.tokenizedLineForScreenRow(6).fold
expect([fold1.getStartRow(), fold1.getEndRow()]).toEqual [6, 8]
fold1.destroy()
fold2 = editor.lineForScreenRow(11).fold
fold2 = editor.tokenizedLineForScreenRow(11).fold
expect([fold2.getStartRow(), fold2.getEndRow()]).toEqual [11, 14]
fold2.destroy()
it "does not fold anything but the indentLevel", ->
languageMode.foldAllAtIndentLevel(0)
fold1 = editor.lineForScreenRow(0).fold
fold1 = editor.tokenizedLineForScreenRow(0).fold
expect([fold1.getStartRow(), fold1.getEndRow()]).toEqual [0, 19]
fold1.destroy()
fold2 = editor.lineForScreenRow(5).fold
fold2 = editor.tokenizedLineForScreenRow(5).fold
expect(fold2).toBeFalsy()
describe ".isFoldableAtBufferRow(bufferRow)", ->
+70 -21
Ver Arquivo
@@ -27,42 +27,91 @@ describe "PaneContainer", ->
it "preserves the active pane across serialization, independent of focus", ->
pane3A.activate()
expect(containerA.activePane).toBe pane3A
expect(containerA.getActivePane()).toBe pane3A
containerB = containerA.testSerialization()
[pane1B, pane2B, pane3B] = containerB.getPanes()
expect(containerB.activePane).toBe pane3B
expect(containerB.getActivePane()).toBe pane3B
describe "::activePane", ->
it "does not allow the root pane to be destroyed", ->
container = new PaneContainer
container.getRoot().destroy()
expect(container.getRoot()).toBeDefined()
expect(container.getRoot().isDestroyed()).toBe false
describe "::getActivePane()", ->
[container, pane1, pane2] = []
beforeEach ->
container = new PaneContainer
pane1 = container.root
pane1 = container.getRoot()
it "references the first pane if no pane has been made active", ->
expect(container.activePane).toBe pane1
expect(pane1.active).toBe true
it "returns the first pane if no pane has been made active", ->
expect(container.getActivePane()).toBe pane1
expect(pane1.isActive()).toBe true
it "references the most pane on which ::activate was most recently called", ->
it "returns the most pane on which ::activate() was most recently called", ->
pane2 = pane1.splitRight()
pane2.activate()
expect(container.activePane).toBe pane2
expect(pane1.active).toBe false
expect(pane2.active).toBe true
expect(container.getActivePane()).toBe pane2
expect(pane1.isActive()).toBe false
expect(pane2.isActive()).toBe true
pane1.activate()
expect(container.activePane).toBe pane1
expect(pane1.active).toBe true
expect(pane2.active).toBe false
expect(container.getActivePane()).toBe pane1
expect(pane1.isActive()).toBe true
expect(pane2.isActive()).toBe false
it "is reassigned to the next pane if the current active pane is destroyed", ->
it "returns the next pane if the current active pane is destroyed", ->
pane2 = pane1.splitRight()
pane2.activate()
pane2.destroy()
expect(container.activePane).toBe pane1
expect(pane1.active).toBe true
expect(container.getActivePane()).toBe pane1
expect(pane1.isActive()).toBe true
it "does not allow the root pane to be destroyed", ->
pane1.destroy()
expect(container.root).toBe pane1
expect(pane1.isDestroyed()).toBe false
describe "::onDidChangeActivePaneItem()", ->
[container, pane1, pane2, observed] = []
beforeEach ->
container = new PaneContainer(root: new Pane(items: [new Object, new Object]))
container.getRoot().splitRight(items: [new Object, new Object])
[pane1, pane2] = container.getPanes()
observed = []
container.onDidChangeActivePaneItem (item) -> observed.push(item)
it "invokes observers when the active item of the active pane changes", ->
pane2.activateNextItem()
pane2.activateNextItem()
expect(observed).toEqual [pane2.itemAtIndex(1), pane2.itemAtIndex(0)]
it "invokes observers when the active pane changes", ->
pane1.activate()
pane2.activate()
expect(observed).toEqual [pane1.itemAtIndex(0), pane2.itemAtIndex(0)]
describe "::observePanes()", ->
it "invokes observers with all current and future panes", ->
container = new PaneContainer
container.getRoot().splitRight()
[pane1, pane2] = container.getPanes()
observed = []
container.observePanes (pane) -> observed.push(pane)
pane3 = pane2.splitDown()
pane4 = pane2.splitRight()
expect(observed).toEqual [pane1, pane2, pane3, pane4]
describe "::observePaneItems()", ->
it "invokes observers with all current and future pane items", ->
container = new PaneContainer(root: new Pane(items: [new Object, new Object]))
container.getRoot().splitRight(items: [new Object])
[pane1, pane2] = container.getPanes()
observed = []
container.observePaneItems (pane) -> observed.push(pane)
pane3 = pane2.splitDown(items: [new Object])
pane3.addItems([new Object, new Object])
expect(observed).toEqual container.getPaneItems()
+218 -107
Ver Arquivo
@@ -21,39 +21,83 @@ describe "Pane", ->
describe "construction", ->
it "sets the active item to the first item", ->
pane = new Pane(items: [new Item("A"), new Item("B")])
expect(pane.activeItem).toBe pane.items[0]
expect(pane.getActiveItem()).toBe pane.itemAtIndex(0)
it "compacts the items array", ->
pane = new Pane(items: [undefined, new Item("A"), null, new Item("B")])
expect(pane.items.length).toBe 2
expect(pane.activeItem).toBe pane.items[0]
expect(pane.getItems().length).toBe 2
expect(pane.getActiveItem()).toBe pane.itemAtIndex(0)
describe "::activate()", ->
[container, pane1, pane2] = []
beforeEach ->
container = new PaneContainer(root: new Pane)
container.getRoot().splitRight()
[pane1, pane2] = container.getPanes()
it "changes the active pane on the container", ->
expect(container.getActivePane()).toBe pane2
pane1.activate()
expect(container.getActivePane()).toBe pane1
pane2.activate()
expect(container.getActivePane()).toBe pane2
it "invokes ::onDidChangeActivePane observers on the container", ->
observed = []
container.onDidChangeActivePane (activePane) -> observed.push(activePane)
pane1.activate()
pane1.activate()
pane2.activate()
pane1.activate()
expect(observed).toEqual [pane1, pane2, pane1]
it "invokes ::onDidChangeActive observers on the relevant panes", ->
observed = []
pane1.onDidChangeActive (active) -> observed.push(active)
pane1.activate()
pane2.activate()
expect(observed).toEqual [true, false]
it "invokes ::onDidActivate() observers", ->
eventCount = 0
pane1.onDidActivate -> eventCount++
pane1.activate()
pane1.activate()
pane2.activate()
expect(eventCount).toBe 2
describe "::addItem(item, index)", ->
it "adds the item at the given index", ->
pane = new Pane(items: [new Item("A"), new Item("B")])
[item1, item2] = pane.items
[item1, item2] = pane.getItems()
item3 = new Item("C")
pane.addItem(item3, 1)
expect(pane.items).toEqual [item1, item3, item2]
expect(pane.getItems()).toEqual [item1, item3, item2]
it "adds the item after the active item ", ->
it "adds the item after the active item if no index is provided", ->
pane = new Pane(items: [new Item("A"), new Item("B"), new Item("C")])
[item1, item2, item3] = pane.items
[item1, item2, item3] = pane.getItems()
pane.activateItem(item2)
item4 = new Item("D")
pane.addItem(item4)
expect(pane.items).toEqual [item1, item2, item4, item3]
expect(pane.getItems()).toEqual [item1, item2, item4, item3]
it "sets the active item after adding the first item", ->
pane = new Pane
item = new Item("A")
events = []
pane.on 'item-added', -> events.push('item-added')
pane.$activeItem.changes.onValue -> events.push('active-item-changed')
pane.addItem(item)
expect(pane.activeItem).toBe item
expect(events).toEqual ['item-added', 'active-item-changed']
expect(pane.getActiveItem()).toBe item
it "invokes ::onDidAddItem() observers", ->
pane = new Pane(items: [new Item("A"), new Item("B")])
events = []
pane.onDidAddItem (event) -> events.push(event)
item = new Item("C")
pane.addItem(item, 1)
expect(events).toEqual [{item, index: 1}]
describe "::activateItem(item)", ->
pane = null
@@ -62,83 +106,102 @@ describe "Pane", ->
pane = new Pane(items: [new Item("A"), new Item("B")])
it "changes the active item to the current item", ->
expect(pane.activeItem).toBe pane.items[0]
pane.activateItem(pane.items[1])
expect(pane.activeItem).toBe pane.items[1]
expect(pane.getActiveItem()).toBe pane.itemAtIndex(0)
pane.activateItem(pane.itemAtIndex(1))
expect(pane.getActiveItem()).toBe pane.itemAtIndex(1)
it "adds the given item if it isn't present in ::items", ->
item = new Item("C")
pane.activateItem(item)
expect(item in pane.items).toBe true
expect(pane.activeItem).toBe item
expect(item in pane.getItems()).toBe true
expect(pane.getActiveItem()).toBe item
it "invokes ::onDidChangeActiveItem() observers", ->
observed = []
pane.onDidChangeActiveItem (item) -> observed.push(item)
pane.activateItem(pane.itemAtIndex(1))
expect(observed).toEqual [pane.itemAtIndex(1)]
describe "::activateNextItem() and ::activatePreviousItem()", ->
it "sets the active item to the next/previous item, looping around at either end", ->
pane = new Pane(items: [new Item("A"), new Item("B"), new Item("C")])
[item1, item2, item3] = pane.items
[item1, item2, item3] = pane.getItems()
expect(pane.activeItem).toBe item1
expect(pane.getActiveItem()).toBe item1
pane.activatePreviousItem()
expect(pane.activeItem).toBe item3
expect(pane.getActiveItem()).toBe item3
pane.activatePreviousItem()
expect(pane.activeItem).toBe item2
expect(pane.getActiveItem()).toBe item2
pane.activateNextItem()
expect(pane.activeItem).toBe item3
expect(pane.getActiveItem()).toBe item3
pane.activateNextItem()
expect(pane.activeItem).toBe item1
expect(pane.getActiveItem()).toBe item1
describe "::activateItemAtIndex(index)", ->
it "activates the item at the given index", ->
pane = new Pane(items: [new Item("A"), new Item("B"), new Item("C")])
[item1, item2, item3] = pane.items
[item1, item2, item3] = pane.getItems()
pane.activateItemAtIndex(2)
expect(pane.activeItem).toBe item3
expect(pane.getActiveItem()).toBe item3
pane.activateItemAtIndex(1)
expect(pane.activeItem).toBe item2
expect(pane.getActiveItem()).toBe item2
pane.activateItemAtIndex(0)
expect(pane.activeItem).toBe item1
expect(pane.getActiveItem()).toBe item1
# Doesn't fail with out-of-bounds indices
pane.activateItemAtIndex(100)
expect(pane.activeItem).toBe item1
expect(pane.getActiveItem()).toBe item1
pane.activateItemAtIndex(-1)
expect(pane.activeItem).toBe item1
expect(pane.getActiveItem()).toBe item1
describe "::destroyItem(item)", ->
[pane, item1, item2, item3] = []
beforeEach ->
pane = new Pane(items: [new Item("A"), new Item("B"), new Item("C")])
[item1, item2, item3] = pane.items
[item1, item2, item3] = pane.getItems()
it "removes the item from the items list", ->
expect(pane.activeItem).toBe item1
it "removes the item from the items list and destroyes it", ->
expect(pane.getActiveItem()).toBe item1
pane.destroyItem(item2)
expect(item2 in pane.items).toBe false
expect(pane.activeItem).toBe item1
expect(item2 in pane.getItems()).toBe false
expect(item2.isDestroyed()).toBe true
expect(pane.getActiveItem()).toBe item1
pane.destroyItem(item1)
expect(item1 in pane.items).toBe false
expect(item1 in pane.getItems()).toBe false
expect(item1.isDestroyed()).toBe true
it "invokes ::onWillDestroyItem() observers before destroying the item", ->
events = []
pane.onWillDestroyItem (event) ->
expect(item2.isDestroyed()).toBe false
events.push(event)
pane.destroyItem(item2)
expect(item2.isDestroyed()).toBe true
expect(events).toEqual [{item: item2, index: 1}]
it "invokes ::onDidRemoveItem() observers", ->
events = []
pane.onDidRemoveItem (event) -> events.push(event)
pane.destroyItem(item2)
expect(events).toEqual [{item: item2, index: 1, destroyed: true}]
describe "when the destroyed item is the active item and is the first item", ->
it "activates the next item", ->
expect(pane.activeItem).toBe item1
expect(pane.getActiveItem()).toBe item1
pane.destroyItem(item1)
expect(pane.activeItem).toBe item2
expect(pane.getActiveItem()).toBe item2
describe "when the destroyed item is the active item and is not the first item", ->
beforeEach ->
pane.activateItem(item2)
it "activates the previous item", ->
expect(pane.activeItem).toBe item2
expect(pane.getActiveItem()).toBe item2
pane.destroyItem(item2)
expect(pane.activeItem).toBe item1
it "emits 'item-removed' with the item, its index, and true indicating the item is being destroyed", ->
pane.on 'item-removed', itemRemovedHandler = jasmine.createSpy("itemRemovedHandler")
pane.destroyItem(item2)
expect(itemRemovedHandler).toHaveBeenCalledWith(item2, 1, true)
expect(pane.getActiveItem()).toBe item1
describe "if the item is modified", ->
itemUri = null
@@ -157,7 +220,7 @@ describe "Pane", ->
pane.destroyItem(item1)
expect(item1.save).toHaveBeenCalled()
expect(item1 in pane.items).toBe false
expect(item1 in pane.getItems()).toBe false
expect(item1.isDestroyed()).toBe true
describe "when the item has no uri", ->
@@ -170,7 +233,7 @@ describe "Pane", ->
expect(atom.showSaveDialogSync).toHaveBeenCalled()
expect(item1.saveAs).toHaveBeenCalledWith("/selected/path")
expect(item1 in pane.items).toBe false
expect(item1 in pane.getItems()).toBe false
expect(item1.isDestroyed()).toBe true
describe "if the [Don't Save] option is selected", ->
@@ -179,7 +242,7 @@ describe "Pane", ->
pane.destroyItem(item1)
expect(item1.save).not.toHaveBeenCalled()
expect(item1 in pane.items).toBe false
expect(item1 in pane.getItems()).toBe false
expect(item1.isDestroyed()).toBe true
describe "if the [Cancel] option is selected", ->
@@ -188,7 +251,7 @@ describe "Pane", ->
pane.destroyItem(item1)
expect(item1.save).not.toHaveBeenCalled()
expect(item1 in pane.items).toBe true
expect(item1 in pane.getItems()).toBe true
expect(item1.isDestroyed()).toBe false
describe "when the last item is destroyed", ->
@@ -197,7 +260,7 @@ describe "Pane", ->
expect(atom.config.get('core.destroyEmptyPanes')).toBe false
pane.destroyItem(item) for item in pane.getItems()
expect(pane.isDestroyed()).toBe false
expect(pane.activeItem).toBeUndefined()
expect(pane.getActiveItem()).toBeUndefined()
expect(-> pane.saveActiveItem()).not.toThrow()
expect(-> pane.saveActiveItemAs()).not.toThrow()
@@ -210,10 +273,10 @@ describe "Pane", ->
describe "::destroyActiveItem()", ->
it "destroys the active item", ->
pane = new Pane(items: [new Item("A"), new Item("B")])
activeItem = pane.activeItem
activeItem = pane.getActiveItem()
pane.destroyActiveItem()
expect(activeItem.isDestroyed()).toBe true
expect(activeItem in pane.items).toBe false
expect(activeItem in pane.getItems()).toBe false
it "does not throw an exception if there are no more items", ->
pane = new Pane
@@ -222,27 +285,40 @@ describe "Pane", ->
describe "::destroyItems()", ->
it "destroys all items", ->
pane = new Pane(items: [new Item("A"), new Item("B"), new Item("C")])
[item1, item2, item3] = pane.items
[item1, item2, item3] = pane.getItems()
pane.destroyItems()
expect(item1.isDestroyed()).toBe true
expect(item2.isDestroyed()).toBe true
expect(item3.isDestroyed()).toBe true
expect(pane.items).toEqual []
expect(pane.getItems()).toEqual []
describe "::observeItems()", ->
it "invokes the observer with all current and future items", ->
pane = new Pane(items: [new Item, new Item])
[item1, item2] = pane.getItems()
observed = []
pane.observeItems (item) -> observed.push(item)
item3 = new Item
pane.addItem(item3)
expect(observed).toEqual [item1, item2, item3]
describe "when an item emits a destroyed event", ->
it "removes it from the list of items", ->
pane = new Pane(items: [new Item("A"), new Item("B"), new Item("C")])
[item1, item2, item3] = pane.items
pane.items[1].destroy()
expect(pane.items).toEqual [item1, item3]
[item1, item2, item3] = pane.getItems()
pane.itemAtIndex(1).destroy()
expect(pane.getItems()).toEqual [item1, item3]
describe "::destroyInactiveItems()", ->
it "destroys all items but the active item", ->
pane = new Pane(items: [new Item("A"), new Item("B"), new Item("C")])
[item1, item2, item3] = pane.items
[item1, item2, item3] = pane.getItems()
pane.activateItem(item2)
pane.destroyInactiveItems()
expect(pane.items).toEqual [item2]
expect(pane.getItems()).toEqual [item2]
describe "::saveActiveItem()", ->
pane = null
@@ -253,30 +329,30 @@ describe "Pane", ->
describe "when the active item has a uri", ->
beforeEach ->
pane.activeItem.uri = "test"
pane.getActiveItem().uri = "test"
describe "when the active item has a save method", ->
it "saves the current item", ->
pane.activeItem.save = jasmine.createSpy("save")
pane.getActiveItem().save = jasmine.createSpy("save")
pane.saveActiveItem()
expect(pane.activeItem.save).toHaveBeenCalled()
expect(pane.getActiveItem().save).toHaveBeenCalled()
describe "when the current item has no save method", ->
it "does nothing", ->
expect(pane.activeItem.save).toBeUndefined()
expect(pane.getActiveItem().save).toBeUndefined()
pane.saveActiveItem()
describe "when the current item has no uri", ->
describe "when the current item has a saveAs method", ->
it "opens a save dialog and saves the current item as the selected path", ->
pane.activeItem.saveAs = jasmine.createSpy("saveAs")
pane.getActiveItem().saveAs = jasmine.createSpy("saveAs")
pane.saveActiveItem()
expect(atom.showSaveDialogSync).toHaveBeenCalled()
expect(pane.activeItem.saveAs).toHaveBeenCalledWith('/selected/path')
expect(pane.getActiveItem().saveAs).toHaveBeenCalledWith('/selected/path')
describe "when the current item has no saveAs method", ->
it "does nothing", ->
expect(pane.activeItem.saveAs).toBeUndefined()
expect(pane.getActiveItem().saveAs).toBeUndefined()
pane.saveActiveItem()
expect(atom.showSaveDialogSync).not.toHaveBeenCalled()
@@ -289,22 +365,22 @@ describe "Pane", ->
describe "when the current item has a saveAs method", ->
it "opens the save dialog and calls saveAs on the item with the selected path", ->
pane.activeItem.path = __filename
pane.activeItem.saveAs = jasmine.createSpy("saveAs")
pane.getActiveItem().path = __filename
pane.getActiveItem().saveAs = jasmine.createSpy("saveAs")
pane.saveActiveItemAs()
expect(atom.showSaveDialogSync).toHaveBeenCalledWith(__filename)
expect(pane.activeItem.saveAs).toHaveBeenCalledWith('/selected/path')
expect(pane.getActiveItem().saveAs).toHaveBeenCalledWith('/selected/path')
describe "when the current item does not have a saveAs method", ->
it "does nothing", ->
expect(pane.activeItem.saveAs).toBeUndefined()
expect(pane.getActiveItem().saveAs).toBeUndefined()
pane.saveActiveItemAs()
expect(atom.showSaveDialogSync).not.toHaveBeenCalled()
describe "::itemForUri(uri)", ->
it "returns the item for which a call to .getUri() returns the given uri", ->
pane = new Pane(items: [new Item("A"), new Item("B"), new Item("C"), new Item("D")])
[item1, item2, item3] = pane.items
[item1, item2, item3] = pane.getItems()
item1.uri = "a"
item2.uri = "b"
expect(pane.itemForUri("a")).toBe item1
@@ -312,24 +388,32 @@ describe "Pane", ->
expect(pane.itemForUri("bogus")).toBeUndefined()
describe "::moveItem(item, index)", ->
it "moves the item to the given index and emits an 'item-moved' event with the item and its new index", ->
pane = new Pane(items: [new Item("A"), new Item("B"), new Item("C"), new Item("D")])
[item1, item2, item3, item4] = pane.items
pane.on 'item-moved', itemMovedHandler = jasmine.createSpy("itemMovedHandler")
[pane, item1, item2, item3, item4] = []
beforeEach ->
pane = new Pane(items: [new Item("A"), new Item("B"), new Item("C"), new Item("D")])
[item1, item2, item3, item4] = pane.getItems()
it "moves the item to the given index and invokes ::onDidMoveItem observers", ->
pane.moveItem(item1, 2)
expect(pane.getItems()).toEqual [item2, item3, item1, item4]
expect(itemMovedHandler).toHaveBeenCalledWith(item1, 2)
itemMovedHandler.reset()
pane.moveItem(item2, 3)
expect(pane.getItems()).toEqual [item3, item1, item4, item2]
expect(itemMovedHandler).toHaveBeenCalledWith(item2, 3)
itemMovedHandler.reset()
pane.moveItem(item2, 1)
expect(pane.getItems()).toEqual [item3, item2, item1, item4]
expect(itemMovedHandler).toHaveBeenCalledWith(item2, 1)
it "invokes ::onDidMoveItem() observers", ->
events = []
pane.onDidMoveItem (event) -> events.push(event)
pane.moveItem(item1, 2)
pane.moveItem(item2, 3)
expect(events).toEqual [
{item: item1, oldIndex: 0, newIndex: 2}
{item: item2, oldIndex: 0, newIndex: 3}
]
describe "::moveItemToPane(item, pane, index)", ->
[container, pane1, pane2] = []
@@ -339,13 +423,20 @@ describe "Pane", ->
pane1 = new Pane(items: [new Item("A"), new Item("B"), new Item("C")])
container = new PaneContainer(root: pane1)
pane2 = pane1.splitRight(items: [new Item("D"), new Item("E")])
[item1, item2, item3] = pane1.items
[item4, item5] = pane2.items
[item1, item2, item3] = pane1.getItems()
[item4, item5] = pane2.getItems()
it "moves the item to the given pane at the given index", ->
pane1.moveItemToPane(item2, pane2, 1)
expect(pane1.items).toEqual [item1, item3]
expect(pane2.items).toEqual [item4, item2, item5]
expect(pane1.getItems()).toEqual [item1, item3]
expect(pane2.getItems()).toEqual [item4, item2, item5]
it "invokes ::onDidRemoveItem() observers", ->
events = []
pane1.onDidRemoveItem (event) -> events.push(event)
pane1.moveItemToPane(item2, pane2, 1)
expect(events).toEqual [{item: item2, index: 1, destroyed: false}]
describe "when the moved item the last item in the source pane", ->
beforeEach ->
@@ -368,22 +459,27 @@ describe "Pane", ->
[pane1, container] = []
beforeEach ->
pane1 = new Pane(items: ["A"])
pane1 = new Pane(items: [new Item("A")])
container = new PaneContainer(root: pane1)
describe "::splitLeft(params)", ->
describe "when the parent is the container root", ->
it "replaces itself with a row and inserts a new pane to the left of itself", ->
pane2 = pane1.splitLeft(items: ["B"])
pane3 = pane1.splitLeft(items: ["C"])
pane2 = pane1.splitLeft(items: [new Item("B")])
pane3 = pane1.splitLeft(items: [new Item("C")])
expect(container.root.orientation).toBe 'horizontal'
expect(container.root.children).toEqual [pane2, pane3, pane1]
describe "when `copyActiveItem: true` is passed in the params", ->
it "duplicates the active item", ->
pane2 = pane1.splitLeft(copyActiveItem: true)
expect(pane2.getActiveItem()).toEqual pane1.getActiveItem()
describe "when the parent is a column", ->
it "replaces itself with a row and inserts a new pane to the left of itself", ->
pane1.splitDown()
pane2 = pane1.splitLeft(items: ["B"])
pane3 = pane1.splitLeft(items: ["C"])
pane2 = pane1.splitLeft(items: [new Item("B")])
pane3 = pane1.splitLeft(items: [new Item("C")])
row = container.root.children[0]
expect(row.orientation).toBe 'horizontal'
expect(row.children).toEqual [pane2, pane3, pane1]
@@ -391,16 +487,21 @@ describe "Pane", ->
describe "::splitRight(params)", ->
describe "when the parent is the container root", ->
it "replaces itself with a row and inserts a new pane to the right of itself", ->
pane2 = pane1.splitRight(items: ["B"])
pane3 = pane1.splitRight(items: ["C"])
pane2 = pane1.splitRight(items: [new Item("B")])
pane3 = pane1.splitRight(items: [new Item("C")])
expect(container.root.orientation).toBe 'horizontal'
expect(container.root.children).toEqual [pane1, pane3, pane2]
describe "when `copyActiveItem: true` is passed in the params", ->
it "duplicates the active item", ->
pane2 = pane1.splitRight(copyActiveItem: true)
expect(pane2.getActiveItem()).toEqual pane1.getActiveItem()
describe "when the parent is a column", ->
it "replaces itself with a row and inserts a new pane to the right of itself", ->
pane1.splitDown()
pane2 = pane1.splitRight(items: ["B"])
pane3 = pane1.splitRight(items: ["C"])
pane2 = pane1.splitRight(items: [new Item("B")])
pane3 = pane1.splitRight(items: [new Item("C")])
row = container.root.children[0]
expect(row.orientation).toBe 'horizontal'
expect(row.children).toEqual [pane1, pane3, pane2]
@@ -408,16 +509,21 @@ describe "Pane", ->
describe "::splitUp(params)", ->
describe "when the parent is the container root", ->
it "replaces itself with a column and inserts a new pane above itself", ->
pane2 = pane1.splitUp(items: ["B"])
pane3 = pane1.splitUp(items: ["C"])
pane2 = pane1.splitUp(items: [new Item("B")])
pane3 = pane1.splitUp(items: [new Item("C")])
expect(container.root.orientation).toBe 'vertical'
expect(container.root.children).toEqual [pane2, pane3, pane1]
describe "when `copyActiveItem: true` is passed in the params", ->
it "duplicates the active item", ->
pane2 = pane1.splitUp(copyActiveItem: true)
expect(pane2.getActiveItem()).toEqual pane1.getActiveItem()
describe "when the parent is a row", ->
it "replaces itself with a column and inserts a new pane above itself", ->
pane1.splitRight()
pane2 = pane1.splitUp(items: ["B"])
pane3 = pane1.splitUp(items: ["C"])
pane2 = pane1.splitUp(items: [new Item("B")])
pane3 = pane1.splitUp(items: [new Item("C")])
column = container.root.children[0]
expect(column.orientation).toBe 'vertical'
expect(column.children).toEqual [pane2, pane3, pane1]
@@ -425,16 +531,21 @@ describe "Pane", ->
describe "::splitDown(params)", ->
describe "when the parent is the container root", ->
it "replaces itself with a column and inserts a new pane below itself", ->
pane2 = pane1.splitDown(items: ["B"])
pane3 = pane1.splitDown(items: ["C"])
pane2 = pane1.splitDown(items: [new Item("B")])
pane3 = pane1.splitDown(items: [new Item("C")])
expect(container.root.orientation).toBe 'vertical'
expect(container.root.children).toEqual [pane1, pane3, pane2]
describe "when `copyActiveItem: true` is passed in the params", ->
it "duplicates the active item", ->
pane2 = pane1.splitDown(copyActiveItem: true)
expect(pane2.getActiveItem()).toEqual pane1.getActiveItem()
describe "when the parent is a row", ->
it "replaces itself with a column and inserts a new pane below itself", ->
pane1.splitRight()
pane2 = pane1.splitDown(items: ["B"])
pane3 = pane1.splitDown(items: ["C"])
pane2 = pane1.splitDown(items: [new Item("B")])
pane3 = pane1.splitDown(items: [new Item("C")])
column = container.root.children[0]
expect(column.orientation).toBe 'vertical'
expect(column.children).toEqual [pane1, pane3, pane2]
@@ -455,7 +566,7 @@ describe "Pane", ->
pane2 = pane1.splitRight()
it "destroys the pane's destroyable items", ->
[item1, item2] = pane1.items
[item1, item2] = pane1.getItems()
pane1.destroy()
expect(item1.isDestroyed()).toBe true
expect(item2.isDestroyed()).toBe true
@@ -493,12 +604,12 @@ describe "Pane", ->
it "can serialize and deserialize the pane and all its items", ->
newPane = pane.testSerialization()
expect(newPane.items).toEqual pane.items
expect(newPane.getItems()).toEqual pane.getItems()
it "restores the active item on deserialization", ->
pane.activateItemAtIndex(1)
newPane = pane.testSerialization()
expect(newPane.activeItem).toEqual newPane.items[1]
expect(newPane.getActiveItem()).toEqual newPane.itemAtIndex(1)
it "does not include items that cannot be deserialized", ->
spyOn(console, 'warn')
@@ -506,8 +617,8 @@ describe "Pane", ->
pane.activateItem(unserializable)
newPane = pane.testSerialization()
expect(newPane.activeItem).toEqual pane.items[0]
expect(newPane.items.length).toBe pane.items.length - 1
expect(newPane.getActiveItem()).toEqual pane.itemAtIndex(0)
expect(newPane.getItems().length).toBe pane.getItems().length - 1
it "includes the pane's focus state in the serialized state", ->
pane.focus()
+6 -6
Ver Arquivo
@@ -29,7 +29,7 @@ describe "PaneView", ->
runs ->
pane = container.getRoot()
paneModel = pane.model
paneModel = pane.getModel()
paneModel.addItems([view1, editor1, view2, editor2])
afterEach ->
@@ -37,7 +37,7 @@ describe "PaneView", ->
describe "when the active pane item changes", ->
it "hides all item views except the active one", ->
expect(pane.activeItem).toBe view1
expect(pane.getActiveItem()).toBe view1
expect(view1.css('display')).not.toBe 'none'
pane.activateItem(view2)
@@ -48,7 +48,7 @@ describe "PaneView", ->
itemChangedHandler = jasmine.createSpy("itemChangedHandler")
container.on 'pane:active-item-changed', itemChangedHandler
expect(pane.activeItem).toBe view1
expect(pane.getActiveItem()).toBe view1
paneModel.activateItem(view2)
paneModel.activateItem(view2)
@@ -149,7 +149,7 @@ describe "PaneView", ->
activeItemTitleChangedHandler = jasmine.createSpy("activeItemTitleChangedHandler")
pane.on 'pane:active-item-title-changed', activeItemTitleChangedHandler
expect(pane.activeItem).toBe view1
expect(pane.getActiveItem()).toBe view1
view2.trigger 'title-changed'
expect(activeItemTitleChangedHandler).not.toHaveBeenCalled()
@@ -246,7 +246,7 @@ describe "PaneView", ->
it "transfers focus to the active view", ->
focusHandler = jasmine.createSpy("focusHandler")
pane.activeItem.on 'focus', focusHandler
pane.getActiveItem().on 'focus', focusHandler
pane.focus()
expect(focusHandler).toHaveBeenCalled()
@@ -259,7 +259,7 @@ describe "PaneView", ->
describe "when a pane is split", ->
it "builds the appropriate pane-row and pane-column views", ->
pane1 = pane
pane1Model = pane.model
pane1Model = pane.getModel()
pane.activateItem(editor1)
pane2Model = pane1Model.splitRight(items: [pane1Model.copyActiveItem()])
+5
Ver Arquivo
@@ -190,6 +190,11 @@ describe "Project", ->
expect(atom.project.getPath()?).toBeFalsy()
expect(atom.project.getRootDirectory()?).toBeFalsy()
it "normalizes the path to remove consecutive slashes, ., and .. segments", ->
atom.project.setPath("#{require.resolve('./fixtures/dir/a')}#{path.sep}b#{path.sep}#{path.sep}..")
expect(atom.project.getPath()).toEqual path.dirname(require.resolve('./fixtures/dir/a'))
expect(atom.project.getRootDirectory().path).toEqual path.dirname(require.resolve('./fixtures/dir/a'))
describe ".replace()", ->
[filePath, commentFilePath, sampleContent, sampleCommentContent] = []
+6 -6
Ver Arquivo
@@ -35,7 +35,7 @@ describe "Editor", ->
logLines()
throw new Error("Invalid buffer row #{actualBufferRow} for screen row #{screenRow}", )
actualScreenLine = editor.lineForScreenRow(screenRow)
actualScreenLine = editor.tokenizedLineForScreenRow(screenRow)
unless actualScreenLine.text is referenceScreenLine.text
logLines()
throw new Error("Invalid line text at screen row #{screenRow}")
@@ -50,9 +50,9 @@ describe "Editor", ->
randomlyMutateEditor = ->
if Math.random() < .2
softWrap = not editor.getSoftWrap()
steps.push(['setSoftWrap', softWrap])
editor.setSoftWrap(softWrap)
softWrapped = not editor.isSoftWrapped()
steps.push(['setSoftWrapped', softWrapped])
editor.setSoftWrapped(softWrapped)
else
range = getRandomRange()
text = getRandomText()
@@ -79,11 +79,11 @@ describe "Editor", ->
text
getReferenceScreenLines = ->
if editor.getSoftWrap()
if editor.isSoftWrapped()
screenLines = []
bufferRows = []
for bufferRow in [0..tokenizedBuffer.getLastRow()]
for screenLine in softWrapLine(tokenizedBuffer.lineForScreenRow(bufferRow))
for screenLine in softWrapLine(tokenizedBuffer.tokenizedLineForRow(bufferRow))
screenLines.push(screenLine)
bufferRows.push(bufferRow)
else
+3 -3
Ver Arquivo
@@ -6,7 +6,7 @@ describe "Selection", ->
beforeEach ->
buffer = atom.project.bufferForPathSync('sample.js')
editor = new Editor(buffer: buffer, tabLength: 2)
selection = editor.getSelection()
selection = editor.getLastSelection()
afterEach ->
buffer.destroy()
@@ -57,10 +57,10 @@ describe "Selection", ->
expect(selection.isReversed()).toBeFalsy()
describe "when only the selection's tail is moved (regression)", ->
it "emits the 'screen-range-changed' event", ->
it "notifies ::onDidChangeRange observers", ->
selection.setBufferRange([[2, 0], [2, 10]], reversed: true)
changeScreenRangeHandler = jasmine.createSpy('changeScreenRangeHandler')
selection.on 'screen-range-changed', changeScreenRangeHandler
selection.onDidChangeRange changeScreenRangeHandler
buffer.insert([2, 5], 'abc')
expect(changeScreenRangeHandler).toHaveBeenCalled()
-2
Ver Arquivo
@@ -95,7 +95,6 @@ beforeEach ->
config.set "editor.autoIndent", false
config.set "core.disabledPackages", ["package-that-throws-an-exception",
"package-with-broken-package-json", "package-with-broken-keymap"]
config.set "core.useReactEditor", true
config.save.reset()
atom.config = config
@@ -139,7 +138,6 @@ afterEach ->
jasmine.unspy(atom, 'saveSync')
ensureNoPathSubscriptions()
atom.syntax.off()
ensureNoDeprecatedFunctionsCalled() if isCoreSpec
waits(0) # yield to ui thread to make screen update more frequently
ensureNoPathSubscriptions = ->
+15
Ver Arquivo
@@ -247,6 +247,21 @@ describe "ThemeManager", ->
# from within the theme itself
expect($(".editor").css("background-color")).toBe "rgb(0, 152, 255)"
describe "theme classes on the workspace", ->
it 'adds theme-* classes to the workspace for each active theme', ->
expect(atom.workspaceView).toHaveClass 'theme-atom-dark-ui'
themeManager.on 'reloaded', reloadHandler = jasmine.createSpy()
atom.config.set('core.themes', ['theme-with-ui-variables'])
waitsFor ->
reloadHandler.callCount > 0
runs ->
# `theme-` twice as it prefixes the name with `theme-`
expect(atom.workspaceView).toHaveClass 'theme-theme-with-ui-variables'
expect(atom.workspaceView).not.toHaveClass 'theme-atom-dark-ui'
describe "when the user stylesheet changes", ->
it "reloads it", ->
[stylesheetRemovedHandler, stylesheetAddedHandler, stylesheetsChangedHandler] = []
+1 -1
Ver Arquivo
@@ -18,7 +18,7 @@ class TimeReporter extends jasmine.Reporter
log ?= (line) -> console.log(line)
log "Longest running suites:"
suites = _.map(window.timedSuites, (key, value) -> [value, key])
for suite in _.sortBy(suites, (suite) => -suite[1])[0...number]
for suite in _.sortBy(suites, (suite) -> -suite[1])[0...number]
time = Math.round(suite[1] / 100) / 10
log " #{suite[0]} (#{time}s)"
undefined
+179 -183
Ver Arquivo
@@ -41,7 +41,7 @@ describe "TokenizedBuffer", ->
buffer = atom.project.bufferForPathSync('sample.js')
tokenizedBuffer = new TokenizedBuffer({buffer})
startTokenizing(tokenizedBuffer)
tokenizedBuffer.on "changed", changeHandler = jasmine.createSpy('changeHandler')
tokenizedBuffer.onDidChange changeHandler = jasmine.createSpy('changeHandler')
afterEach ->
tokenizedBuffer.destroy()
@@ -49,38 +49,38 @@ describe "TokenizedBuffer", ->
describe "on construction", ->
it "initially creates un-tokenized screen lines, then tokenizes lines chunk at a time in the background", ->
line0 = tokenizedBuffer.lineForScreenRow(0)
line0 = tokenizedBuffer.tokenizedLineForRow(0)
expect(line0.tokens.length).toBe 1
expect(line0.tokens[0]).toEqual(value: line0.text, scopes: ['source.js'])
line11 = tokenizedBuffer.lineForScreenRow(11)
line11 = tokenizedBuffer.tokenizedLineForRow(11)
expect(line11.tokens.length).toBe 2
expect(line11.tokens[0]).toEqual(value: " ", scopes: ['source.js'], isAtomic: true)
expect(line11.tokens[1]).toEqual(value: "return sort(Array.apply(this, arguments));", scopes: ['source.js'])
# background tokenization has not begun
expect(tokenizedBuffer.lineForScreenRow(0).ruleStack).toBeUndefined()
expect(tokenizedBuffer.tokenizedLineForRow(0).ruleStack).toBeUndefined()
# tokenize chunk 1
advanceClock()
expect(tokenizedBuffer.lineForScreenRow(0).ruleStack?).toBeTruthy()
expect(tokenizedBuffer.lineForScreenRow(4).ruleStack?).toBeTruthy()
expect(tokenizedBuffer.lineForScreenRow(5).ruleStack?).toBeFalsy()
expect(tokenizedBuffer.tokenizedLineForRow(0).ruleStack?).toBeTruthy()
expect(tokenizedBuffer.tokenizedLineForRow(4).ruleStack?).toBeTruthy()
expect(tokenizedBuffer.tokenizedLineForRow(5).ruleStack?).toBeFalsy()
expect(changeHandler).toHaveBeenCalledWith(start: 0, end: 4, delta: 0)
changeHandler.reset()
# tokenize chunk 2
advanceClock()
expect(tokenizedBuffer.lineForScreenRow(5).ruleStack?).toBeTruthy()
expect(tokenizedBuffer.lineForScreenRow(9).ruleStack?).toBeTruthy()
expect(tokenizedBuffer.lineForScreenRow(10).ruleStack?).toBeFalsy()
expect(tokenizedBuffer.tokenizedLineForRow(5).ruleStack?).toBeTruthy()
expect(tokenizedBuffer.tokenizedLineForRow(9).ruleStack?).toBeTruthy()
expect(tokenizedBuffer.tokenizedLineForRow(10).ruleStack?).toBeFalsy()
expect(changeHandler).toHaveBeenCalledWith(start: 5, end: 9, delta: 0)
changeHandler.reset()
# tokenize last chunk
advanceClock()
expect(tokenizedBuffer.lineForScreenRow(10).ruleStack?).toBeTruthy()
expect(tokenizedBuffer.lineForScreenRow(12).ruleStack?).toBeTruthy()
expect(tokenizedBuffer.tokenizedLineForRow(10).ruleStack?).toBeTruthy()
expect(tokenizedBuffer.tokenizedLineForRow(12).ruleStack?).toBeTruthy()
expect(changeHandler).toHaveBeenCalledWith(start: 10, end: 12, delta: 0)
describe "when the buffer is partially tokenized", ->
@@ -134,8 +134,8 @@ describe "TokenizedBuffer", ->
expect(tokenizedBuffer.firstInvalidRow()).toBe 5
buffer.setTextInRange([[6, 0], [7, 0]], "\n\n\n")
expect(tokenizedBuffer.lineForScreenRow(6).ruleStack?).toBeFalsy()
expect(tokenizedBuffer.lineForScreenRow(7).ruleStack?).toBeFalsy()
expect(tokenizedBuffer.tokenizedLineForRow(6).ruleStack?).toBeFalsy()
expect(tokenizedBuffer.tokenizedLineForRow(7).ruleStack?).toBeFalsy()
changeHandler.reset()
expect(tokenizedBuffer.firstInvalidRow()).toBe 5
@@ -149,10 +149,10 @@ describe "TokenizedBuffer", ->
it "updates tokens to reflect the change", ->
buffer.setTextInRange([[0, 0], [2, 0]], "foo()\n7\n")
expect(tokenizedBuffer.lineForScreenRow(0).tokens[1]).toEqual(value: '(', scopes: ['source.js', 'meta.brace.round.js'])
expect(tokenizedBuffer.lineForScreenRow(1).tokens[0]).toEqual(value: '7', scopes: ['source.js', 'constant.numeric.js'])
expect(tokenizedBuffer.tokenizedLineForRow(0).tokens[1]).toEqual(value: '(', scopes: ['source.js', 'meta.brace.round.js'])
expect(tokenizedBuffer.tokenizedLineForRow(1).tokens[0]).toEqual(value: '7', scopes: ['source.js', 'constant.numeric.js'])
# line 2 is unchanged
expect(tokenizedBuffer.lineForScreenRow(2).tokens[2]).toEqual(value: 'if', scopes: ['source.js', 'keyword.control.js'])
expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[2]).toEqual(value: 'if', scopes: ['source.js', 'keyword.control.js'])
expect(changeHandler).toHaveBeenCalled()
[event] = changeHandler.argsForCall[0]
@@ -164,7 +164,7 @@ describe "TokenizedBuffer", ->
buffer.insert([5, 30], '/* */')
changeHandler.reset()
buffer.insert([2, 0], '/*')
expect(tokenizedBuffer.lineForScreenRow(3).tokens[0].scopes).toEqual ['source.js']
expect(tokenizedBuffer.tokenizedLineForRow(3).tokens[0].scopes).toEqual ['source.js']
expect(changeHandler).toHaveBeenCalled()
[event] = changeHandler.argsForCall[0]
delete event.bufferChange
@@ -172,9 +172,9 @@ describe "TokenizedBuffer", ->
changeHandler.reset()
advanceClock()
expect(tokenizedBuffer.lineForScreenRow(3).tokens[0].scopes).toEqual ['source.js', 'comment.block.js']
expect(tokenizedBuffer.lineForScreenRow(4).tokens[0].scopes).toEqual ['source.js', 'comment.block.js']
expect(tokenizedBuffer.lineForScreenRow(5).tokens[0].scopes).toEqual ['source.js', 'comment.block.js']
expect(tokenizedBuffer.tokenizedLineForRow(3).tokens[0].scopes).toEqual ['source.js', 'comment.block.js']
expect(tokenizedBuffer.tokenizedLineForRow(4).tokens[0].scopes).toEqual ['source.js', 'comment.block.js']
expect(tokenizedBuffer.tokenizedLineForRow(5).tokens[0].scopes).toEqual ['source.js', 'comment.block.js']
expect(changeHandler).toHaveBeenCalled()
[event] = changeHandler.argsForCall[0]
delete event.bufferChange
@@ -185,23 +185,23 @@ describe "TokenizedBuffer", ->
buffer.insert([5, 0], '*/')
buffer.insert([1, 0], 'var ')
expect(tokenizedBuffer.lineForScreenRow(1).tokens[0].scopes).toEqual ['source.js', 'comment.block.js']
expect(tokenizedBuffer.tokenizedLineForRow(1).tokens[0].scopes).toEqual ['source.js', 'comment.block.js']
describe "when lines are both updated and removed", ->
it "updates tokens to reflect the change", ->
buffer.setTextInRange([[1, 0], [3, 0]], "foo()")
# previous line 0 remains
expect(tokenizedBuffer.lineForScreenRow(0).tokens[0]).toEqual(value: 'var', scopes: ['source.js', 'storage.modifier.js'])
expect(tokenizedBuffer.tokenizedLineForRow(0).tokens[0]).toEqual(value: 'var', scopes: ['source.js', 'storage.modifier.js'])
# previous line 3 should be combined with input to form line 1
expect(tokenizedBuffer.lineForScreenRow(1).tokens[0]).toEqual(value: 'foo', scopes: ['source.js'])
expect(tokenizedBuffer.lineForScreenRow(1).tokens[6]).toEqual(value: '=', scopes: ['source.js', 'keyword.operator.js'])
expect(tokenizedBuffer.tokenizedLineForRow(1).tokens[0]).toEqual(value: 'foo', scopes: ['source.js'])
expect(tokenizedBuffer.tokenizedLineForRow(1).tokens[6]).toEqual(value: '=', scopes: ['source.js', 'keyword.operator.js'])
# lines below deleted regions should be shifted upward
expect(tokenizedBuffer.lineForScreenRow(2).tokens[2]).toEqual(value: 'while', scopes: ['source.js', 'keyword.control.js'])
expect(tokenizedBuffer.lineForScreenRow(3).tokens[4]).toEqual(value: '=', scopes: ['source.js', 'keyword.operator.js'])
expect(tokenizedBuffer.lineForScreenRow(4).tokens[4]).toEqual(value: '<', scopes: ['source.js', 'keyword.operator.js'])
expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[2]).toEqual(value: 'while', scopes: ['source.js', 'keyword.control.js'])
expect(tokenizedBuffer.tokenizedLineForRow(3).tokens[4]).toEqual(value: '=', scopes: ['source.js', 'keyword.operator.js'])
expect(tokenizedBuffer.tokenizedLineForRow(4).tokens[4]).toEqual(value: '<', scopes: ['source.js', 'keyword.operator.js'])
expect(changeHandler).toHaveBeenCalled()
[event] = changeHandler.argsForCall[0]
@@ -214,8 +214,8 @@ describe "TokenizedBuffer", ->
changeHandler.reset()
buffer.setTextInRange([[2, 0], [3, 0]], '/*')
expect(tokenizedBuffer.lineForScreenRow(2).tokens[0].scopes).toEqual ['source.js', 'comment.block.js', 'punctuation.definition.comment.js']
expect(tokenizedBuffer.lineForScreenRow(3).tokens[0].scopes).toEqual ['source.js']
expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[0].scopes).toEqual ['source.js', 'comment.block.js', 'punctuation.definition.comment.js']
expect(tokenizedBuffer.tokenizedLineForRow(3).tokens[0].scopes).toEqual ['source.js']
expect(changeHandler).toHaveBeenCalled()
[event] = changeHandler.argsForCall[0]
delete event.bufferChange
@@ -223,8 +223,8 @@ describe "TokenizedBuffer", ->
changeHandler.reset()
advanceClock()
expect(tokenizedBuffer.lineForScreenRow(3).tokens[0].scopes).toEqual ['source.js', 'comment.block.js']
expect(tokenizedBuffer.lineForScreenRow(4).tokens[0].scopes).toEqual ['source.js', 'comment.block.js']
expect(tokenizedBuffer.tokenizedLineForRow(3).tokens[0].scopes).toEqual ['source.js', 'comment.block.js']
expect(tokenizedBuffer.tokenizedLineForRow(4).tokens[0].scopes).toEqual ['source.js', 'comment.block.js']
expect(changeHandler).toHaveBeenCalled()
[event] = changeHandler.argsForCall[0]
delete event.bufferChange
@@ -235,19 +235,19 @@ describe "TokenizedBuffer", ->
buffer.setTextInRange([[1, 0], [2, 0]], "foo()\nbar()\nbaz()\nquux()")
# previous line 0 remains
expect(tokenizedBuffer.lineForScreenRow(0).tokens[0]).toEqual( value: 'var', scopes: ['source.js', 'storage.modifier.js'])
expect(tokenizedBuffer.tokenizedLineForRow(0).tokens[0]).toEqual( value: 'var', scopes: ['source.js', 'storage.modifier.js'])
# 3 new lines inserted
expect(tokenizedBuffer.lineForScreenRow(1).tokens[0]).toEqual(value: 'foo', scopes: ['source.js'])
expect(tokenizedBuffer.lineForScreenRow(2).tokens[0]).toEqual(value: 'bar', scopes: ['source.js'])
expect(tokenizedBuffer.lineForScreenRow(3).tokens[0]).toEqual(value: 'baz', scopes: ['source.js'])
expect(tokenizedBuffer.tokenizedLineForRow(1).tokens[0]).toEqual(value: 'foo', scopes: ['source.js'])
expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[0]).toEqual(value: 'bar', scopes: ['source.js'])
expect(tokenizedBuffer.tokenizedLineForRow(3).tokens[0]).toEqual(value: 'baz', scopes: ['source.js'])
# previous line 2 is joined with quux() on line 4
expect(tokenizedBuffer.lineForScreenRow(4).tokens[0]).toEqual(value: 'quux', scopes: ['source.js'])
expect(tokenizedBuffer.lineForScreenRow(4).tokens[4]).toEqual(value: 'if', scopes: ['source.js', 'keyword.control.js'])
expect(tokenizedBuffer.tokenizedLineForRow(4).tokens[0]).toEqual(value: 'quux', scopes: ['source.js'])
expect(tokenizedBuffer.tokenizedLineForRow(4).tokens[4]).toEqual(value: 'if', scopes: ['source.js', 'keyword.control.js'])
# previous line 3 is pushed down to become line 5
expect(tokenizedBuffer.lineForScreenRow(5).tokens[4]).toEqual(value: '=', scopes: ['source.js', 'keyword.operator.js'])
expect(tokenizedBuffer.tokenizedLineForRow(5).tokens[4]).toEqual(value: '=', scopes: ['source.js', 'keyword.operator.js'])
expect(changeHandler).toHaveBeenCalled()
[event] = changeHandler.argsForCall[0]
@@ -264,17 +264,17 @@ describe "TokenizedBuffer", ->
[event] = changeHandler.argsForCall[0]
delete event.bufferChange
expect(event).toEqual(start: 2, end: 2, delta: 2)
expect(tokenizedBuffer.lineForScreenRow(2).tokens[0].scopes).toEqual ['source.js', 'comment.block.js', 'punctuation.definition.comment.js']
expect(tokenizedBuffer.lineForScreenRow(3).tokens[0].scopes).toEqual ['source.js', 'comment.block.js']
expect(tokenizedBuffer.lineForScreenRow(4).tokens[0].scopes).toEqual ['source.js', 'comment.block.js']
expect(tokenizedBuffer.lineForScreenRow(5).tokens[0].scopes).toEqual ['source.js']
expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[0].scopes).toEqual ['source.js', 'comment.block.js', 'punctuation.definition.comment.js']
expect(tokenizedBuffer.tokenizedLineForRow(3).tokens[0].scopes).toEqual ['source.js', 'comment.block.js']
expect(tokenizedBuffer.tokenizedLineForRow(4).tokens[0].scopes).toEqual ['source.js', 'comment.block.js']
expect(tokenizedBuffer.tokenizedLineForRow(5).tokens[0].scopes).toEqual ['source.js']
changeHandler.reset()
advanceClock() # tokenize invalidated lines in background
expect(tokenizedBuffer.lineForScreenRow(5).tokens[0].scopes).toEqual ['source.js', 'comment.block.js']
expect(tokenizedBuffer.lineForScreenRow(6).tokens[0].scopes).toEqual ['source.js', 'comment.block.js']
expect(tokenizedBuffer.lineForScreenRow(7).tokens[0].scopes).toEqual ['source.js', 'comment.block.js']
expect(tokenizedBuffer.lineForScreenRow(8).tokens[0].scopes).not.toBe ['source.js', 'comment.block.js']
expect(tokenizedBuffer.tokenizedLineForRow(5).tokens[0].scopes).toEqual ['source.js', 'comment.block.js']
expect(tokenizedBuffer.tokenizedLineForRow(6).tokens[0].scopes).toEqual ['source.js', 'comment.block.js']
expect(tokenizedBuffer.tokenizedLineForRow(7).tokens[0].scopes).toEqual ['source.js', 'comment.block.js']
expect(tokenizedBuffer.tokenizedLineForRow(8).tokens[0].scopes).not.toBe ['source.js', 'comment.block.js']
expect(changeHandler).toHaveBeenCalled()
[event] = changeHandler.argsForCall[0]
@@ -285,13 +285,13 @@ describe "TokenizedBuffer", ->
it "tokenizes the initial chunk synchronously, then tokenizes the remaining lines in the background", ->
commentBlock = _.multiplyString("// a comment\n", tokenizedBuffer.chunkSize + 2)
buffer.insert([0,0], commentBlock)
expect(tokenizedBuffer.lineForScreenRow(0).ruleStack?).toBeTruthy()
expect(tokenizedBuffer.lineForScreenRow(4).ruleStack?).toBeTruthy()
expect(tokenizedBuffer.lineForScreenRow(5).ruleStack?).toBeFalsy()
expect(tokenizedBuffer.tokenizedLineForRow(0).ruleStack?).toBeTruthy()
expect(tokenizedBuffer.tokenizedLineForRow(4).ruleStack?).toBeTruthy()
expect(tokenizedBuffer.tokenizedLineForRow(5).ruleStack?).toBeFalsy()
advanceClock()
expect(tokenizedBuffer.lineForScreenRow(5).ruleStack?).toBeTruthy()
expect(tokenizedBuffer.lineForScreenRow(6).ruleStack?).toBeTruthy()
expect(tokenizedBuffer.tokenizedLineForRow(5).ruleStack?).toBeTruthy()
expect(tokenizedBuffer.tokenizedLineForRow(6).ruleStack?).toBeTruthy()
describe ".findOpeningBracket(closingBufferPosition)", ->
it "returns the position of the matching bracket, skipping any nested brackets", ->
@@ -302,18 +302,18 @@ describe "TokenizedBuffer", ->
expect(tokenizedBuffer.findClosingBracket([1, 29])).toEqual [9, 2]
it "tokenizes leading whitespace based on the new tab length", ->
expect(tokenizedBuffer.lineForScreenRow(5).tokens[0].isAtomic).toBeTruthy()
expect(tokenizedBuffer.lineForScreenRow(5).tokens[0].value).toBe " "
expect(tokenizedBuffer.lineForScreenRow(5).tokens[1].isAtomic).toBeTruthy()
expect(tokenizedBuffer.lineForScreenRow(5).tokens[1].value).toBe " "
expect(tokenizedBuffer.tokenizedLineForRow(5).tokens[0].isAtomic).toBeTruthy()
expect(tokenizedBuffer.tokenizedLineForRow(5).tokens[0].value).toBe " "
expect(tokenizedBuffer.tokenizedLineForRow(5).tokens[1].isAtomic).toBeTruthy()
expect(tokenizedBuffer.tokenizedLineForRow(5).tokens[1].value).toBe " "
tokenizedBuffer.setTabLength(4)
fullyTokenize(tokenizedBuffer)
expect(tokenizedBuffer.lineForScreenRow(5).tokens[0].isAtomic).toBeTruthy()
expect(tokenizedBuffer.lineForScreenRow(5).tokens[0].value).toBe " "
expect(tokenizedBuffer.lineForScreenRow(5).tokens[1].isAtomic).toBeFalsy()
expect(tokenizedBuffer.lineForScreenRow(5).tokens[1].value).toBe " current "
expect(tokenizedBuffer.tokenizedLineForRow(5).tokens[0].isAtomic).toBeTruthy()
expect(tokenizedBuffer.tokenizedLineForRow(5).tokens[0].value).toBe " "
expect(tokenizedBuffer.tokenizedLineForRow(5).tokens[1].isAtomic).toBeFalsy()
expect(tokenizedBuffer.tokenizedLineForRow(5).tokens[1].value).toBe " current "
describe "when the buffer contains hard-tabs", ->
beforeEach ->
@@ -335,7 +335,7 @@ describe "TokenizedBuffer", ->
it "renders each tab as its own atomic token with a value of size tabLength", ->
tabAsSpaces = _.multiplyString(' ', tokenizedBuffer.getTabLength())
screenLine0 = tokenizedBuffer.lineForScreenRow(0)
screenLine0 = tokenizedBuffer.tokenizedLineForRow(0)
expect(screenLine0.text).toBe "# Econ 101#{tabAsSpaces}"
{ tokens } = screenLine0
@@ -347,7 +347,7 @@ describe "TokenizedBuffer", ->
expect(tokens[2].isAtomic).toBeTruthy()
expect(tokens[3].value).toBe ""
expect(tokenizedBuffer.lineForScreenRow(2).text).toBe "#{tabAsSpaces} buy()#{tabAsSpaces}while supply > demand"
expect(tokenizedBuffer.tokenizedLineForRow(2).text).toBe "#{tabAsSpaces} buy()#{tabAsSpaces}while supply > demand"
it "aligns the hard tabs to the correct tab stop column", ->
buffer.setText """
@@ -359,62 +359,62 @@ describe "TokenizedBuffer", ->
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.tokenizedLineForRow(0).text).toBe "1 2 3 4"
expect(tokenizedBuffer.tokenizedLineForRow(0).tokens[1].bufferDelta).toBe 1
expect(tokenizedBuffer.tokenizedLineForRow(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.tokenizedLineForRow(1).text).toBe "12 3 4 5"
expect(tokenizedBuffer.tokenizedLineForRow(1).tokens[1].bufferDelta).toBe 1
expect(tokenizedBuffer.tokenizedLineForRow(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
expect(tokenizedBuffer.tokenizedLineForRow(2).text).toBe "123 4 5 6"
expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[1].bufferDelta).toBe 1
expect(tokenizedBuffer.tokenizedLineForRow(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.tokenizedLineForRow(0).text).toBe "1 2 3 4"
expect(tokenizedBuffer.tokenizedLineForRow(0).tokens[1].bufferDelta).toBe 1
expect(tokenizedBuffer.tokenizedLineForRow(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.tokenizedLineForRow(1).text).toBe "12 3 4 5"
expect(tokenizedBuffer.tokenizedLineForRow(1).tokens[1].bufferDelta).toBe 1
expect(tokenizedBuffer.tokenizedLineForRow(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
expect(tokenizedBuffer.tokenizedLineForRow(2).text).toBe "123 4 5 6"
expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[1].bufferDelta).toBe 1
expect(tokenizedBuffer.tokenizedLineForRow(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.tokenizedLineForRow(0).text).toBe "1 2 3 4"
expect(tokenizedBuffer.tokenizedLineForRow(0).tokens[1].bufferDelta).toBe 1
expect(tokenizedBuffer.tokenizedLineForRow(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.tokenizedLineForRow(1).text).toBe "12 3 4 5"
expect(tokenizedBuffer.tokenizedLineForRow(1).tokens[1].bufferDelta).toBe 1
expect(tokenizedBuffer.tokenizedLineForRow(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
expect(tokenizedBuffer.tokenizedLineForRow(2).text).toBe "123 4 5 6"
expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[1].bufferDelta).toBe 1
expect(tokenizedBuffer.tokenizedLineForRow(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.tokenizedLineForRow(0).text).toBe "1 2 3 4"
expect(tokenizedBuffer.tokenizedLineForRow(0).tokens[1].bufferDelta).toBe 1
expect(tokenizedBuffer.tokenizedLineForRow(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.tokenizedLineForRow(1).text).toBe "12 3 4 5"
expect(tokenizedBuffer.tokenizedLineForRow(1).tokens[1].bufferDelta).toBe 1
expect(tokenizedBuffer.tokenizedLineForRow(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
expect(tokenizedBuffer.tokenizedLineForRow(2).text).toBe "123 4 5 6"
expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[1].bufferDelta).toBe 1
expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[1].screenDelta).toBe 1
describe "when the buffer contains UTF-8 surrogate pairs", ->
beforeEach ->
@@ -435,7 +435,7 @@ describe "TokenizedBuffer", ->
buffer.release()
it "renders each UTF-8 surrogate pair as its own atomic token", ->
screenLine0 = tokenizedBuffer.lineForScreenRow(0)
screenLine0 = tokenizedBuffer.tokenizedLineForRow(0)
expect(screenLine0.text).toBe "'abc\uD835\uDF97def'"
{ tokens } = screenLine0
@@ -447,7 +447,7 @@ describe "TokenizedBuffer", ->
expect(tokens[3].value).toBe "def"
expect(tokens[4].value).toBe "'"
screenLine1 = tokenizedBuffer.lineForScreenRow(1)
screenLine1 = tokenizedBuffer.tokenizedLineForRow(1)
expect(screenLine1.text).toBe "//\uD835\uDF97xyz"
{ tokens } = screenLine1
@@ -468,7 +468,7 @@ describe "TokenizedBuffer", ->
runs ->
tokenizedBuffer = editor.displayBuffer.tokenizedBuffer
tokenizedBuffer.on 'tokenized', tokenizedHandler
tokenizedBuffer.onDidTokenize tokenizedHandler
fullyTokenize(tokenizedBuffer)
expect(tokenizedHandler.callCount).toBe(1)
@@ -483,7 +483,7 @@ describe "TokenizedBuffer", ->
tokenizedBuffer = editor.displayBuffer.tokenizedBuffer
fullyTokenize(tokenizedBuffer)
tokenizedBuffer.on 'tokenized', tokenizedHandler
tokenizedBuffer.onDidTokenize tokenizedHandler
editor.getBuffer().insert([0, 0], "'")
fullyTokenize(tokenizedBuffer)
expect(tokenizedHandler).not.toHaveBeenCalled()
@@ -499,7 +499,7 @@ describe "TokenizedBuffer", ->
runs ->
tokenizedBuffer = editor.displayBuffer.tokenizedBuffer
tokenizedBuffer.on 'tokenized', tokenizedHandler
tokenizedBuffer.onDidTokenize tokenizedHandler
fullyTokenize(tokenizedBuffer)
tokenizedHandler.reset()
@@ -525,7 +525,7 @@ describe "TokenizedBuffer", ->
tokenizedBuffer.setGrammar(atom.syntax.selectGrammar('test.erb'))
fullyTokenize(tokenizedBuffer)
{tokens} = tokenizedBuffer.lineForScreenRow(0)
{tokens} = tokenizedBuffer.tokenizedLineForRow(0)
expect(tokens[0]).toEqual value: "<div class='name'>", scopes: ["text.html.ruby"]
waitsForPromise ->
@@ -533,7 +533,7 @@ describe "TokenizedBuffer", ->
runs ->
fullyTokenize(tokenizedBuffer)
{tokens} = tokenizedBuffer.lineForScreenRow(0)
{tokens} = tokenizedBuffer.tokenizedLineForRow(0)
expect(tokens[0]).toEqual value: '<', scopes: ["text.html.ruby","meta.tag.block.any.html","punctuation.definition.tag.begin.html"]
describe ".tokenForPosition(position)", ->
@@ -599,9 +599,9 @@ describe "TokenizedBuffer", ->
tokenizedBuffer.setInvisibles(space: 'S', tab: 'T')
fullyTokenize(tokenizedBuffer)
expect(tokenizedBuffer.lineForScreenRow(0).text).toBe "SST Sa line with tabsTand T spacesSTS"
expect(tokenizedBuffer.tokenizedLineForRow(0).text).toBe "SST Sa line with tabsTand T spacesSTS"
# Also needs to work for copies
expect(tokenizedBuffer.lineForScreenRow(0).copy().text).toBe "SST Sa line with tabsTand T spacesSTS"
expect(tokenizedBuffer.tokenizedLineForRow(0).copy().text).toBe "SST Sa line with tabsTand T spacesSTS"
it "assigns endOfLineInvisibles to tokenized lines", ->
buffer = new TextBuffer(text: "a line that ends in a carriage-return-line-feed \r\na line that ends in just a line-feed\na line with no ending")
@@ -611,17 +611,17 @@ describe "TokenizedBuffer", ->
tokenizedBuffer.setInvisibles(cr: 'R', eol: 'N')
fullyTokenize(tokenizedBuffer)
expect(tokenizedBuffer.lineForScreenRow(0).endOfLineInvisibles).toEqual ['R', 'N']
expect(tokenizedBuffer.lineForScreenRow(1).endOfLineInvisibles).toEqual ['N']
expect(tokenizedBuffer.tokenizedLineForRow(0).endOfLineInvisibles).toEqual ['R', 'N']
expect(tokenizedBuffer.tokenizedLineForRow(1).endOfLineInvisibles).toEqual ['N']
# Lines ending in soft wraps get no invisibles
[left, right] = tokenizedBuffer.lineForScreenRow(0).softWrapAt(20)
[left, right] = tokenizedBuffer.tokenizedLineForRow(0).softWrapAt(20)
expect(left.endOfLineInvisibles).toBe null
expect(right.endOfLineInvisibles).toEqual ['R', 'N']
tokenizedBuffer.setInvisibles(cr: 'R', eol: false)
expect(tokenizedBuffer.lineForScreenRow(0).endOfLineInvisibles).toEqual ['R']
expect(tokenizedBuffer.lineForScreenRow(1).endOfLineInvisibles).toEqual []
expect(tokenizedBuffer.tokenizedLineForRow(0).endOfLineInvisibles).toEqual ['R']
expect(tokenizedBuffer.tokenizedLineForRow(1).endOfLineInvisibles).toEqual []
describe "leading and trailing whitespace", ->
beforeEach ->
@@ -629,64 +629,45 @@ describe "TokenizedBuffer", ->
tokenizedBuffer = new TokenizedBuffer({buffer})
fullyTokenize(tokenizedBuffer)
it "sets ::hasLeadingWhitespace to true and assigns ::firstNonWhitespaceIndex on tokens that have leading whitespace", ->
expect(tokenizedBuffer.lineForScreenRow(0).tokens[0].hasLeadingWhitespace).toBe false
expect(tokenizedBuffer.lineForScreenRow(0).tokens[0].firstNonWhitespaceIndex).toBe null
it "assigns ::firstNonWhitespaceIndex on tokens that have leading whitespace", ->
expect(tokenizedBuffer.tokenizedLineForRow(0).tokens[0].firstNonWhitespaceIndex).toBe null
expect(tokenizedBuffer.tokenizedLineForRow(1).tokens[0].firstNonWhitespaceIndex).toBe 2
expect(tokenizedBuffer.tokenizedLineForRow(1).tokens[1].firstNonWhitespaceIndex).toBe null
expect(tokenizedBuffer.lineForScreenRow(1).tokens[0].hasLeadingWhitespace).toBe true
expect(tokenizedBuffer.lineForScreenRow(1).tokens[0].firstNonWhitespaceIndex).toBe 2
expect(tokenizedBuffer.lineForScreenRow(1).tokens[1].hasLeadingWhitespace).toBe false
expect(tokenizedBuffer.lineForScreenRow(1).tokens[1].firstNonWhitespaceIndex).toBe null
expect(tokenizedBuffer.lineForScreenRow(2).tokens[0].hasLeadingWhitespace).toBe true
expect(tokenizedBuffer.lineForScreenRow(2).tokens[0].firstNonWhitespaceIndex).toBe 2
expect(tokenizedBuffer.lineForScreenRow(2).tokens[1].hasLeadingWhitespace).toBe true
expect(tokenizedBuffer.lineForScreenRow(2).tokens[1].firstNonWhitespaceIndex).toBe 2
expect(tokenizedBuffer.lineForScreenRow(2).tokens[2].hasLeadingWhitespace).toBe false
expect(tokenizedBuffer.lineForScreenRow(2).tokens[2].firstNonWhitespaceIndex).toBe null
expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[0].firstNonWhitespaceIndex).toBe 2
expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[1].firstNonWhitespaceIndex).toBe 2
expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[2].firstNonWhitespaceIndex).toBe null
# The 4th token *has* leading whitespace, but isn't entirely whitespace
buffer.insert([5, 0], ' ')
expect(tokenizedBuffer.lineForScreenRow(5).tokens[3].hasLeadingWhitespace).toBe true
expect(tokenizedBuffer.lineForScreenRow(5).tokens[3].firstNonWhitespaceIndex).toBe 1
expect(tokenizedBuffer.lineForScreenRow(5).tokens[4].hasLeadingWhitespace).toBe false
expect(tokenizedBuffer.lineForScreenRow(5).tokens[4].firstNonWhitespaceIndex).toBe null
expect(tokenizedBuffer.tokenizedLineForRow(5).tokens[3].firstNonWhitespaceIndex).toBe 1
expect(tokenizedBuffer.tokenizedLineForRow(5).tokens[4].firstNonWhitespaceIndex).toBe null
# Lines that are *only* whitespace are not considered to have leading whitespace
buffer.insert([10, 0], ' ')
expect(tokenizedBuffer.lineForScreenRow(10).tokens[0].hasLeadingWhitespace).toBe false
expect(tokenizedBuffer.lineForScreenRow(10).tokens[0].firstNonWhitespaceIndex).toBe null
expect(tokenizedBuffer.tokenizedLineForRow(10).tokens[0].firstNonWhitespaceIndex).toBe null
it "sets ::hasTrailingWhitespace to true and assigns ::firstTrailingWhitespaceIndex on tokens that have trailing whitespace", ->
it "assigns ::firstTrailingWhitespaceIndex on tokens that have trailing whitespace", ->
buffer.insert([0, Infinity], ' ')
expect(tokenizedBuffer.lineForScreenRow(0).tokens[11].hasTrailingWhitespace).toBe false
expect(tokenizedBuffer.lineForScreenRow(0).tokens[11].firstTrailingWhitespaceIndex).toBe null
expect(tokenizedBuffer.lineForScreenRow(0).tokens[12].hasTrailingWhitespace).toBe true
expect(tokenizedBuffer.lineForScreenRow(0).tokens[12].firstTrailingWhitespaceIndex).toBe 0
expect(tokenizedBuffer.tokenizedLineForRow(0).tokens[11].firstTrailingWhitespaceIndex).toBe null
expect(tokenizedBuffer.tokenizedLineForRow(0).tokens[12].firstTrailingWhitespaceIndex).toBe 0
# The last token *has* trailing whitespace, but isn't entirely whitespace
buffer.setTextInRange([[2, 39], [2, 40]], ' ')
expect(tokenizedBuffer.lineForScreenRow(2).tokens[14].hasTrailingWhitespace).toBe false
expect(tokenizedBuffer.lineForScreenRow(2).tokens[14].firstTrailingWhitespaceIndex).toBe null
expect(tokenizedBuffer.lineForScreenRow(2).tokens[15].hasTrailingWhitespace).toBe true
console.log tokenizedBuffer.lineForScreenRow(2).tokens[15]
expect(tokenizedBuffer.lineForScreenRow(2).tokens[15].firstTrailingWhitespaceIndex).toBe 6
expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[14].firstTrailingWhitespaceIndex).toBe null
expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[15].firstTrailingWhitespaceIndex).toBe 6
# Lines that are *only* whitespace are considered to have trailing whitespace
buffer.insert([10, 0], ' ')
expect(tokenizedBuffer.lineForScreenRow(10).tokens[0].hasTrailingWhitespace).toBe true
expect(tokenizedBuffer.lineForScreenRow(10).tokens[0].firstTrailingWhitespaceIndex).toBe 0
expect(tokenizedBuffer.tokenizedLineForRow(10).tokens[0].firstTrailingWhitespaceIndex).toBe 0
it "only marks trailing whitespace on the last segment of a soft-wrapped line", ->
buffer.insert([0, Infinity], ' ')
tokenizedLine = tokenizedBuffer.lineForScreenRow(0)
tokenizedLine = tokenizedBuffer.tokenizedLineForRow(0)
[segment1, segment2] = tokenizedLine.softWrapAt(16)
expect(segment1.tokens[5].value).toBe ' '
expect(segment1.tokens[5].hasTrailingWhitespace).toBe false
expect(segment1.tokens[5].firstTrailingWhitespaceIndex).toBe null
expect(segment2.tokens[6].value).toBe ' '
expect(segment2.tokens[6].hasTrailingWhitespace).toBe true
expect(segment2.tokens[6].firstTrailingWhitespaceIndex).toBe 0
it "sets leading and trailing whitespace correctly on a line with invisible characters that is copied", ->
@@ -695,9 +676,24 @@ describe "TokenizedBuffer", ->
tokenizedBuffer.setInvisibles(space: 'S', tab: 'T')
fullyTokenize(tokenizedBuffer)
line = tokenizedBuffer.lineForScreenRow(0).copy()
expect(line.tokens[0].hasLeadingWhitespace).toBe true
expect(line.tokens[line.tokens.length - 1].hasTrailingWhitespace).toBe true
line = tokenizedBuffer.tokenizedLineForRow(0).copy()
expect(line.tokens[0].firstNonWhitespaceIndex).toBe 2
expect(line.tokens[line.tokens.length - 1].firstTrailingWhitespaceIndex).toBe 0
it "sets the ::firstNonWhitespaceIndex and ::firstTrailingWhitespaceIndex correctly when tokens are split for soft-wrapping", ->
tokenizedBuffer.setInvisibles(space: 'S')
buffer.setText(" token ")
fullyTokenize(tokenizedBuffer)
token = tokenizedBuffer.tokenizedLines[0].tokens[0]
[leftToken, rightToken] = token.splitAt(1)
expect(leftToken.hasInvisibleCharacters).toBe true
expect(leftToken.firstNonWhitespaceIndex).toBe 1
expect(leftToken.firstTrailingWhitespaceIndex).toBe null
expect(leftToken.hasInvisibleCharacters).toBe true
expect(rightToken.firstNonWhitespaceIndex).toBe null
expect(rightToken.firstTrailingWhitespaceIndex).toBe 5
describe "indent level", ->
beforeEach ->
@@ -707,84 +703,84 @@ describe "TokenizedBuffer", ->
describe "when the line is non-empty", ->
it "has an indent level based on the leading whitespace on the line", ->
expect(tokenizedBuffer.lineForScreenRow(0).indentLevel).toBe 0
expect(tokenizedBuffer.lineForScreenRow(1).indentLevel).toBe 1
expect(tokenizedBuffer.lineForScreenRow(2).indentLevel).toBe 2
expect(tokenizedBuffer.tokenizedLineForRow(0).indentLevel).toBe 0
expect(tokenizedBuffer.tokenizedLineForRow(1).indentLevel).toBe 1
expect(tokenizedBuffer.tokenizedLineForRow(2).indentLevel).toBe 2
buffer.insert([2, 0], ' ')
expect(tokenizedBuffer.lineForScreenRow(2).indentLevel).toBe 2.5
expect(tokenizedBuffer.tokenizedLineForRow(2).indentLevel).toBe 2.5
describe "when the line is empty", ->
it "assumes the indentation level of the first non-empty line below or above if one exists", ->
buffer.insert([12, 0], ' ')
buffer.insert([12, Infinity], '\n\n')
expect(tokenizedBuffer.lineForScreenRow(13).indentLevel).toBe 2
expect(tokenizedBuffer.lineForScreenRow(14).indentLevel).toBe 2
expect(tokenizedBuffer.tokenizedLineForRow(13).indentLevel).toBe 2
expect(tokenizedBuffer.tokenizedLineForRow(14).indentLevel).toBe 2
buffer.insert([1, Infinity], '\n\n')
expect(tokenizedBuffer.lineForScreenRow(2).indentLevel).toBe 2
expect(tokenizedBuffer.lineForScreenRow(3).indentLevel).toBe 2
expect(tokenizedBuffer.tokenizedLineForRow(2).indentLevel).toBe 2
expect(tokenizedBuffer.tokenizedLineForRow(3).indentLevel).toBe 2
buffer.setText('\n\n\n')
expect(tokenizedBuffer.lineForScreenRow(1).indentLevel).toBe 0
expect(tokenizedBuffer.tokenizedLineForRow(1).indentLevel).toBe 0
describe "when the changed lines are surrounded by whitespace-only lines", ->
it "updates the indentLevel of empty lines that precede the change", ->
expect(tokenizedBuffer.lineForScreenRow(12).indentLevel).toBe 0
expect(tokenizedBuffer.tokenizedLineForRow(12).indentLevel).toBe 0
buffer.insert([12, 0], '\n')
buffer.insert([13, 0], ' ')
expect(tokenizedBuffer.lineForScreenRow(12).indentLevel).toBe 1
expect(tokenizedBuffer.tokenizedLineForRow(12).indentLevel).toBe 1
it "updates empty line indent guides when the empty line is the last line", ->
buffer.insert([12, 2], '\n')
# The newline and he tab need to be in two different operations to surface the bug
buffer.insert([12, 0], ' ')
expect(tokenizedBuffer.lineForScreenRow(13).indentLevel).toBe 1
expect(tokenizedBuffer.tokenizedLineForRow(13).indentLevel).toBe 1
buffer.insert([12, 0], ' ')
expect(tokenizedBuffer.lineForScreenRow(13).indentLevel).toBe 2
expect(tokenizedBuffer.lineForScreenRow(14)).not.toBeDefined()
expect(tokenizedBuffer.tokenizedLineForRow(13).indentLevel).toBe 2
expect(tokenizedBuffer.tokenizedLineForRow(14)).not.toBeDefined()
it "updates the indentLevel of empty lines surrounding a change that inserts lines", ->
# create some new lines
buffer.insert([7, 0], '\n\n')
buffer.insert([5, 0], '\n\n')
expect(tokenizedBuffer.lineForScreenRow(5).indentLevel).toBe 3
expect(tokenizedBuffer.lineForScreenRow(6).indentLevel).toBe 3
expect(tokenizedBuffer.lineForScreenRow(9).indentLevel).toBe 3
expect(tokenizedBuffer.lineForScreenRow(10).indentLevel).toBe 3
expect(tokenizedBuffer.lineForScreenRow(11).indentLevel).toBe 2
expect(tokenizedBuffer.tokenizedLineForRow(5).indentLevel).toBe 3
expect(tokenizedBuffer.tokenizedLineForRow(6).indentLevel).toBe 3
expect(tokenizedBuffer.tokenizedLineForRow(9).indentLevel).toBe 3
expect(tokenizedBuffer.tokenizedLineForRow(10).indentLevel).toBe 3
expect(tokenizedBuffer.tokenizedLineForRow(11).indentLevel).toBe 2
tokenizedBuffer.on "changed", changeHandler = jasmine.createSpy('changeHandler')
tokenizedBuffer.onDidChange changeHandler = jasmine.createSpy('changeHandler')
buffer.setTextInRange([[7, 0], [8, 65]], ' one\n two\n three\n four')
delete changeHandler.argsForCall[0][0].bufferChange
expect(changeHandler).toHaveBeenCalledWith(start: 5, end: 10, delta: 2)
expect(tokenizedBuffer.lineForScreenRow(5).indentLevel).toBe 4
expect(tokenizedBuffer.lineForScreenRow(6).indentLevel).toBe 4
expect(tokenizedBuffer.lineForScreenRow(11).indentLevel).toBe 4
expect(tokenizedBuffer.lineForScreenRow(12).indentLevel).toBe 4
expect(tokenizedBuffer.lineForScreenRow(13).indentLevel).toBe 2
expect(tokenizedBuffer.tokenizedLineForRow(5).indentLevel).toBe 4
expect(tokenizedBuffer.tokenizedLineForRow(6).indentLevel).toBe 4
expect(tokenizedBuffer.tokenizedLineForRow(11).indentLevel).toBe 4
expect(tokenizedBuffer.tokenizedLineForRow(12).indentLevel).toBe 4
expect(tokenizedBuffer.tokenizedLineForRow(13).indentLevel).toBe 2
it "updates the indentLevel of empty lines surrounding a change that removes lines", ->
# create some new lines
buffer.insert([7, 0], '\n\n')
buffer.insert([5, 0], '\n\n')
tokenizedBuffer.on "changed", changeHandler = jasmine.createSpy('changeHandler')
tokenizedBuffer.onDidChange changeHandler = jasmine.createSpy('changeHandler')
buffer.setTextInRange([[7, 0], [8, 65]], ' ok')
delete changeHandler.argsForCall[0][0].bufferChange
expect(changeHandler).toHaveBeenCalledWith(start: 5, end: 10, delta: -1)
expect(tokenizedBuffer.lineForScreenRow(5).indentLevel).toBe 2
expect(tokenizedBuffer.lineForScreenRow(6).indentLevel).toBe 2
expect(tokenizedBuffer.lineForScreenRow(7).indentLevel).toBe 2 # new text
expect(tokenizedBuffer.lineForScreenRow(8).indentLevel).toBe 2
expect(tokenizedBuffer.lineForScreenRow(9).indentLevel).toBe 2
expect(tokenizedBuffer.lineForScreenRow(10).indentLevel).toBe 2 # }
expect(tokenizedBuffer.tokenizedLineForRow(5).indentLevel).toBe 2
expect(tokenizedBuffer.tokenizedLineForRow(6).indentLevel).toBe 2
expect(tokenizedBuffer.tokenizedLineForRow(7).indentLevel).toBe 2 # new text
expect(tokenizedBuffer.tokenizedLineForRow(8).indentLevel).toBe 2
expect(tokenizedBuffer.tokenizedLineForRow(9).indentLevel).toBe 2
expect(tokenizedBuffer.tokenizedLineForRow(10).indentLevel).toBe 2 # }
+7 -7
Ver Arquivo
@@ -10,13 +10,13 @@ describe "TokenizedLine", ->
atom.project.open('coffee.coffee').then (o) -> editor = o
it "returns true when the line is only whitespace", ->
expect(editor.lineForScreenRow(3).isOnlyWhitespace()).toBe true
expect(editor.lineForScreenRow(7).isOnlyWhitespace()).toBe true
expect(editor.lineForScreenRow(23).isOnlyWhitespace()).toBe true
expect(editor.tokenizedLineForScreenRow(3).isOnlyWhitespace()).toBe true
expect(editor.tokenizedLineForScreenRow(7).isOnlyWhitespace()).toBe true
expect(editor.tokenizedLineForScreenRow(23).isOnlyWhitespace()).toBe true
it "returns false when the line is not only whitespace", ->
expect(editor.lineForScreenRow(0).isOnlyWhitespace()).toBe false
expect(editor.lineForScreenRow(2).isOnlyWhitespace()).toBe false
expect(editor.tokenizedLineForScreenRow(0).isOnlyWhitespace()).toBe false
expect(editor.tokenizedLineForScreenRow(2).isOnlyWhitespace()).toBe false
describe "::getScopeTree()", ->
it "returns a tree whose inner nodes are scopes and whose leaf nodes are tokens in those scopes", ->
@@ -35,6 +35,6 @@ describe "TokenizedLine", ->
runs ->
tokenIndex = 0
tokens = editor.lineForScreenRow(1).tokens
scopeTree = editor.lineForScreenRow(1).getScopeTree()
tokens = editor.tokenizedLineForScreenRow(1).tokens
scopeTree = editor.tokenizedLineForScreenRow(1).getScopeTree()
ensureValidScopeTree(scopeTree)
+32
Ver Arquivo
@@ -256,3 +256,35 @@ describe "Window", ->
elements.trigger "core:focus-previous"
expect(elements.find("[tabindex=1]:focus")).toExist()
describe "the window:open-path event", ->
beforeEach ->
spyOn(atom.workspace, 'open')
describe "when the project does not have a path", ->
beforeEach ->
atom.project.setPath()
describe "when the opened path exists", ->
it "sets the project path to the opened path", ->
$(window).trigger('window:open-path', [{pathToOpen: __filename}])
expect(atom.project.getPath()).toBe __dirname
describe "when the opened path does not exist but its parent directory does", ->
it "sets the project path to the opened path's parent directory", ->
$(window).trigger('window:open-path', [{pathToOpen: path.join(__dirname, 'this-path-does-not-exist.txt')}])
expect(atom.project.getPath()).toBe __dirname
describe "when the opened path is a file", ->
it "opens it in the workspace", ->
$(window).trigger('window:open-path', [{pathToOpen: __filename}])
expect(atom.workspace.open.mostRecentCall.args[0]).toBe __filename
describe "when the opened path is a directory", ->
it "does not open it in the workspace", ->
$(window).trigger('window:open-path', [{pathToOpen: __dirname}])
expect(atom.workspace.open.callCount).toBe 0
+95 -61
Ver Arquivo
@@ -8,8 +8,12 @@ describe "Workspace", ->
atom.workspace = workspace = new Workspace
describe "::open(uri, options)", ->
openEvents = null
beforeEach ->
spyOn(workspace.activePane, 'activate').andCallThrough()
openEvents = []
workspace.onDidOpen (event) -> openEvents.push(event)
spyOn(workspace.getActivePane(), 'activate').andCallThrough()
describe "when the 'searchAllPanes' option is false (default)", ->
describe "when called without a uri", ->
@@ -21,18 +25,21 @@ describe "Workspace", ->
runs ->
expect(editor1.getPath()).toBeUndefined()
expect(workspace.activePane.items).toEqual [editor1]
expect(workspace.activePaneItem).toBe editor1
expect(workspace.activePane.activate).toHaveBeenCalled()
expect(workspace.getActivePane().items).toEqual [editor1]
expect(workspace.getActivePaneItem()).toBe editor1
expect(workspace.getActivePane().activate).toHaveBeenCalled()
expect(openEvents).toEqual [{uri: undefined, pane: workspace.getActivePane(), item: editor1, index: 0}]
openEvents = []
waitsForPromise ->
workspace.open().then (editor) -> editor2 = editor
runs ->
expect(editor2.getPath()).toBeUndefined()
expect(workspace.activePane.items).toEqual [editor1, editor2]
expect(workspace.activePaneItem).toBe editor2
expect(workspace.activePane.activate).toHaveBeenCalled()
expect(workspace.getActivePane().items).toEqual [editor1, editor2]
expect(workspace.getActivePaneItem()).toBe editor2
expect(workspace.getActivePane().activate).toHaveBeenCalled()
expect(openEvents).toEqual [{uri: undefined, pane: workspace.getActivePane(), item: editor2, index: 1}]
describe "when called with a uri", ->
describe "when the active pane already has an editor for the given uri", ->
@@ -51,8 +58,29 @@ describe "Workspace", ->
runs ->
expect(editor).toBe editor1
expect(workspace.activePaneItem).toBe editor
expect(workspace.activePane.activate).toHaveBeenCalled()
expect(workspace.getActivePaneItem()).toBe editor
expect(workspace.getActivePane().activate).toHaveBeenCalled()
expect(openEvents).toEqual [
{
uri: atom.project.resolve('a')
item: editor1
pane: atom.workspace.getActivePane()
index: 0
}
{
uri: atom.project.resolve('b')
item: editor2
pane: atom.workspace.getActivePane()
index: 1
}
{
uri: atom.project.resolve('a')
item: editor1
pane: atom.workspace.getActivePane()
index: 0
}
]
describe "when the active pane does not have an editor for the given uri", ->
it "adds and activates a new editor for the given path on the active pane", ->
@@ -62,9 +90,9 @@ describe "Workspace", ->
runs ->
expect(editor.getUri()).toBe atom.project.resolve('a')
expect(workspace.activePaneItem).toBe editor
expect(workspace.activePane.items).toEqual [editor]
expect(workspace.activePane.activate).toHaveBeenCalled()
expect(workspace.getActivePaneItem()).toBe editor
expect(workspace.getActivePane().items).toEqual [editor]
expect(workspace.getActivePane().activate).toHaveBeenCalled()
describe "when the 'searchAllPanes' option is true", ->
describe "when an editor for the given uri is already open on an inactive pane", ->
@@ -83,14 +111,14 @@ describe "Workspace", ->
workspace.open('b').then (o) -> editor2 = o
runs ->
expect(workspace.activePaneItem).toBe editor2
expect(workspace.getActivePaneItem()).toBe editor2
waitsForPromise ->
workspace.open('a', searchAllPanes: true)
runs ->
expect(workspace.activePane).toBe pane1
expect(workspace.activePaneItem).toBe editor1
expect(workspace.getActivePane()).toBe pane1
expect(workspace.getActivePaneItem()).toBe editor1
describe "when no editor for the given uri is open in any pane", ->
it "opens an editor for the given uri in the active pane", ->
@@ -99,21 +127,21 @@ describe "Workspace", ->
workspace.open('a', searchAllPanes: true).then (o) -> editor = o
runs ->
expect(workspace.activePaneItem).toBe editor
expect(workspace.getActivePaneItem()).toBe editor
describe "when the 'split' option is set", ->
describe "when the 'split' option is 'left'", ->
it "opens the editor in the leftmost pane of the current pane axis", ->
pane1 = workspace.activePane
pane1 = workspace.getActivePane()
pane2 = pane1.splitRight()
expect(workspace.activePane).toBe pane2
expect(workspace.getActivePane()).toBe pane2
editor = null
waitsForPromise ->
workspace.open('a', split: 'left').then (o) -> editor = o
runs ->
expect(workspace.activePane).toBe pane1
expect(workspace.getActivePane()).toBe pane1
expect(pane1.items).toEqual [editor]
expect(pane2.items).toEqual []
@@ -123,37 +151,37 @@ describe "Workspace", ->
workspace.open('a', split: 'left').then (o) -> editor = o
runs ->
expect(workspace.activePane).toBe pane1
expect(workspace.getActivePane()).toBe pane1
expect(pane1.items).toEqual [editor]
expect(pane2.items).toEqual []
describe "when a pane axis is the leftmost sibling of the current pane", ->
it "opens the new item in the current pane", ->
editor = null
pane1 = workspace.activePane
pane1 = workspace.getActivePane()
pane2 = pane1.splitLeft()
pane3 = pane2.splitDown()
pane1.activate()
expect(workspace.activePane).toBe pane1
expect(workspace.getActivePane()).toBe pane1
waitsForPromise ->
workspace.open('a', split: 'left').then (o) -> editor = o
runs ->
expect(workspace.activePane).toBe pane1
expect(workspace.getActivePane()).toBe pane1
expect(pane1.items).toEqual [editor]
describe "when the 'split' option is 'right'", ->
it "opens the editor in the rightmost pane of the current pane axis", ->
editor = null
pane1 = workspace.activePane
pane1 = workspace.getActivePane()
pane2 = null
waitsForPromise ->
workspace.open('a', split: 'right').then (o) -> editor = o
runs ->
pane2 = workspace.getPanes().filter((p) -> p != pane1)[0]
expect(workspace.activePane).toBe pane2
expect(workspace.getActivePane()).toBe pane2
expect(pane1.items).toEqual []
expect(pane2.items).toEqual [editor]
@@ -163,18 +191,18 @@ describe "Workspace", ->
workspace.open('a', split: 'right').then (o) -> editor = o
runs ->
expect(workspace.activePane).toBe pane2
expect(workspace.getActivePane()).toBe pane2
expect(pane1.items).toEqual []
expect(pane2.items).toEqual [editor]
describe "when a pane axis is the rightmost sibling of the current pane", ->
it "opens the new item in a new pane split to the right of the current pane", ->
editor = null
pane1 = workspace.activePane
pane1 = workspace.getActivePane()
pane2 = pane1.splitRight()
pane3 = pane2.splitDown()
pane1.activate()
expect(workspace.activePane).toBe pane1
expect(workspace.getActivePane()).toBe pane1
pane4 = null
waitsForPromise ->
@@ -182,7 +210,7 @@ describe "Workspace", ->
runs ->
pane4 = workspace.getPanes().filter((p) -> p != pane1)[0]
expect(workspace.activePane).toBe pane4
expect(workspace.getActivePane()).toBe pane4
expect(pane4.items).toEqual [editor]
expect(workspace.paneContainer.root.children[0]).toBe pane1
expect(workspace.paneContainer.root.children[1]).toBe pane4
@@ -203,21 +231,21 @@ describe "Workspace", ->
workspace.open("bar://baz").then (item) ->
expect(item).toEqual { bar: "bar://baz" }
it "emits an 'editor-created' event", ->
it "notifies ::onDidAddTextEditor observers", ->
absolutePath = require.resolve('./fixtures/dir/a')
newEditorHandler = jasmine.createSpy('newEditorHandler')
workspace.on 'editor-created', newEditorHandler
workspace.onDidAddTextEditor newEditorHandler
editor = null
waitsForPromise ->
workspace.open(absolutePath).then (e) -> editor = e
runs ->
expect(newEditorHandler).toHaveBeenCalledWith editor
expect(newEditorHandler.argsForCall[0][0].textEditor).toBe editor
describe "::reopenItem()", ->
it "opens the uri associated with the last closed pane that isn't currently open", ->
pane = workspace.activePane
pane = workspace.getActivePane()
waitsForPromise ->
workspace.open('a').then ->
workspace.open('b').then ->
@@ -226,44 +254,44 @@ describe "Workspace", ->
runs ->
# does not reopen items with no uri
expect(workspace.activePaneItem.getUri()).toBeUndefined()
expect(workspace.getActivePaneItem().getUri()).toBeUndefined()
pane.destroyActiveItem()
waitsForPromise ->
workspace.reopenItem()
runs ->
expect(workspace.activePaneItem.getUri()).not.toBeUndefined()
expect(workspace.getActivePaneItem().getUri()).not.toBeUndefined()
# destroy all items
expect(workspace.activePaneItem.getUri()).toBe atom.project.resolve('file1')
expect(workspace.getActivePaneItem().getUri()).toBe atom.project.resolve('file1')
pane.destroyActiveItem()
expect(workspace.activePaneItem.getUri()).toBe atom.project.resolve('b')
expect(workspace.getActivePaneItem().getUri()).toBe atom.project.resolve('b')
pane.destroyActiveItem()
expect(workspace.activePaneItem.getUri()).toBe atom.project.resolve('a')
expect(workspace.getActivePaneItem().getUri()).toBe atom.project.resolve('a')
pane.destroyActiveItem()
# reopens items with uris
expect(workspace.activePaneItem).toBeUndefined()
expect(workspace.getActivePaneItem()).toBeUndefined()
waitsForPromise ->
workspace.reopenItem()
runs ->
expect(workspace.activePaneItem.getUri()).toBe atom.project.resolve('a')
expect(workspace.getActivePaneItem().getUri()).toBe atom.project.resolve('a')
# does not reopen items that are already open
waitsForPromise ->
workspace.open('b')
runs ->
expect(workspace.activePaneItem.getUri()).toBe atom.project.resolve('b')
expect(workspace.getActivePaneItem().getUri()).toBe atom.project.resolve('b')
waitsForPromise ->
workspace.reopenItem()
runs ->
expect(workspace.activePaneItem.getUri()).toBe atom.project.resolve('file1')
expect(workspace.getActivePaneItem().getUri()).toBe atom.project.resolve('file1')
describe "::increase/decreaseFontSize()", ->
it "increases/decreases the font size without going below 1", ->
@@ -282,7 +310,22 @@ describe "Workspace", ->
describe "::openLicense()", ->
it "opens the license as plain-text in a buffer", ->
waitsForPromise -> workspace.openLicense()
runs -> expect(workspace.activePaneItem.getText()).toMatch /Copyright/
runs -> expect(workspace.getActivePaneItem().getText()).toMatch /Copyright/
describe "::observeTextEditors()", ->
it "invokes the observer with current and future text editors", ->
observed = []
waitsForPromise -> workspace.open()
waitsForPromise -> workspace.open()
waitsForPromise -> workspace.openLicense()
runs ->
workspace.observeTextEditors (editor) -> observed.push(editor)
waitsForPromise -> workspace.open()
expect(observed).toEqual workspace.getTextEditors()
describe "when an editor is destroyed", ->
it "removes the editor", ->
@@ -292,23 +335,9 @@ describe "Workspace", ->
workspace.open("a").then (e) -> editor = e
runs ->
expect(workspace.getEditors()).toHaveLength 1
expect(workspace.getTextEditors()).toHaveLength 1
editor.destroy()
expect(workspace.getEditors()).toHaveLength 0
describe "when an editor is copied", ->
it "emits an 'editor-created' event", ->
editor = null
handler = jasmine.createSpy('editorCreatedHandler')
workspace.on 'editor-created', handler
waitsForPromise ->
workspace.open("a").then (o) -> editor = o
runs ->
expect(handler.callCount).toBe 1
editorCopy = editor.copy()
expect(handler.callCount).toBe 2
expect(workspace.getTextEditors()).toHaveLength 0
it "stores the active grammars used by all the open editors", ->
waitsForPromise ->
@@ -317,14 +346,19 @@ describe "Workspace", ->
waitsForPromise ->
atom.packages.activatePackage('language-coffee-script')
waitsForPromise ->
atom.packages.activatePackage('language-todo')
waitsForPromise ->
atom.workspace.open('sample.coffee')
runs ->
atom.workspace.getActiveEditor().setText('i = /test/;')
atom.workspace.getActiveEditor().setText """
i = /test/; #FIXME
"""
state = atom.workspace.serialize()
expect(state.packagesWithActiveGrammars).toEqual ['language-coffee-script', 'language-javascript']
expect(state.packagesWithActiveGrammars).toEqual ['language-coffee-script', 'language-javascript', 'language-todo']
jsPackage = atom.packages.getLoadedPackage('language-javascript')
coffeePackage = atom.packages.getLoadedPackage('language-coffee-script')
+103 -50
Ver Arquivo
@@ -17,29 +17,14 @@ WindowEventHandler = require './window-event-handler'
# Public: Atom global for dealing with packages, themes, menus, and the window.
#
# An instance of this class is always available as the `atom` global.
#
# ## Useful properties available:
#
# * `atom.clipboard` - A {Clipboard} instance
# * `atom.config` - A {Config} instance
# * `atom.contextMenu` - A {ContextMenuManager} instance
# * `atom.deserializers` - A {DeserializerManager} instance
# * `atom.keymaps` - A {KeymapManager} instance
# * `atom.menu` - A {MenuManager} instance
# * `atom.packages` - A {PackageManager} instance
# * `atom.project` - A {Project} instance
# * `atom.syntax` - A {Syntax} instance
# * `atom.themes` - A {ThemeManager} instance
# * `atom.workspace` - A {Workspace} instance
# * `atom.workspaceView` - A {WorkspaceView} instance
module.exports =
class Atom extends Model
@version: 1 # Increment this when the serialization format changes
# Public: Load or create the Atom environment in the given mode.
#
# mode - Pass 'editor' or 'spec' depending on the kind of environment you
# want to build.
# * `mode` A {String} mode that is either 'editor' or 'spec' depending on the
# kind of environment you want to build.
#
# Returns an Atom instance, fully initialized
@loadOrCreate: (mode) ->
@@ -111,6 +96,43 @@ class Atom extends Model
remote.getCurrentWindow()
workspaceViewParentSelector: 'body'
lastUncaughtError: null
# Public: A {Clipboard} instance
clipboard: null
# Public: A {Config} instance
config: null
# Public: A {ContextMenuManager} instance
contextMenu: null
# Public: A {DeserializerManager} instance
deserializers: null
# Public: A {KeymapManager} instance
keymaps: null
# Public: A {MenuManager} instance
menu: null
# Public: A {PackageManager} instance
packages: null
# Public: A {Project} instance
project: null
# Public: A {Syntax} instance
syntax: null
# Public: A {ThemeManager} instance
themes: null
# Public: A {Workspace} instance
workspace: null
# Public: A {WorkspaceView} instance
workspaceView: null
# Call .loadOrCreate instead
constructor: (@state) ->
@@ -119,12 +141,14 @@ class Atom extends Model
@deserializers = new DeserializerManager()
# Public: Sets up the basic services that should be available in all modes
# (both spec and application). Call after this instance has been assigned to
# the `atom` global.
# (both spec and application).
#
# Call after this instance has been assigned to the `atom` global.
initialize: ->
window.onerror = =>
@openDevTools()
@executeJavaScriptInDevTools('InspectorFrontendAPI.showConsole()')
@lastUncaughtError = Array::slice.call(arguments)
@emit 'uncaught-error', arguments...
@unsubscribe()
@@ -157,7 +181,7 @@ class Atom extends Model
@keymap = @keymaps # Deprecated
@packages = new PackageManager({devMode, configDirPath, resourcePath, safeMode})
@themes = new ThemeManager({packageManager: @packages, configDirPath, resourcePath, safeMode})
@contextMenu = new ContextMenuManager(devMode)
@contextMenu = new ContextMenuManager({resourcePath, devMode})
@menu = new MenuManager({resourcePath})
@clipboard = new Clipboard()
@@ -191,12 +215,17 @@ class Atom extends Model
# Public: Get the dimensions of this window.
#
# Returns an object with x, y, width, and height keys.
# Returns an {Object} with the following keys:
# * `x` The window's x-position {Number}.
# * `y` The window's y-position {Number}.
# * `width` The window's width {Number}.
# * `height` The window's height {Number}.
getWindowDimensions: ->
browserWindow = @getCurrentWindow()
[x, y] = browserWindow.getPosition()
[width, height] = browserWindow.getSize()
{x, y, width, height}
maximized = browserWindow.isMaximized()
{x, y, width, height, maximized}
# Public: Set the dimensions of the window.
#
@@ -204,11 +233,11 @@ class Atom extends Model
# in the dimensions parameter. If x or y are omitted the window will be
# centered. If height or width are omitted only the position will be changed.
#
# dimensions - An {Object} with the following keys:
# :x - The new x coordinate.
# :y - The new y coordinate.
# :width - The new width.
# :height - The new height.
# * `dimensions` An {Object} with the following keys:
# * `x` The new x coordinate.
# * `y` The new y coordinate.
# * `width` The new width.
# * `height` The new height.
setWindowDimensions: ({x, y, width, height}) ->
if width? and height?
@setSize(width, height)
@@ -249,6 +278,7 @@ class Atom extends Model
unless @isValidDimensions(dimensions)
dimensions = @getDefaultWindowDimensions()
@setWindowDimensions(dimensions)
dimensions
storeWindowDimensions: ->
dimensions = @getWindowDimensions()
@@ -256,7 +286,7 @@ class Atom extends Model
# Public: Get the load settings for the current window.
#
# Returns an object containing all the load setting key/value pairs.
# Returns an {Object} containing all the load setting key/value pairs.
getLoadSettings: ->
@constructor.getLoadSettings()
@@ -291,14 +321,15 @@ class Atom extends Model
# Call this method when establishing a real application window.
startEditorWindow: ->
{resourcePath, safeMode} = @getLoadSettings()
CommandInstaller = require './command-installer'
resourcePath = atom.getLoadSettings().resourcePath
CommandInstaller.installAtomCommand resourcePath, false, (error) ->
console.warn error.message if error?
CommandInstaller.installApmCommand resourcePath, false, (error) ->
console.warn error.message if error?
@restoreWindowDimensions()
dimensions = @restoreWindowDimensions()
@config.load()
@config.setDefaults('core', require('./workspace-view').configDefaults)
@config.setDefaults('editor', require('./editor-view').configDefaults)
@@ -306,12 +337,16 @@ class Atom extends Model
@themes.loadBaseStylesheets()
@packages.loadPackages()
@deserializeEditorWindow()
@watchProjectPath()
@packages.activate()
@keymaps.loadUserKeymap()
@requireUserInitScript()
@requireUserInitScript() unless safeMode
@menu.update()
@displayWindow()
maximize = dimensions?.maximized and process.platform isnt 'darwin'
@displayWindow({maximize})
unloadEditorWindow: ->
return if not @project and not @workspaceView
@@ -344,19 +379,33 @@ class Atom extends Model
pack.reloadStylesheets?()
null
# Notify the browser project of the window's current project path
watchProjectPath: ->
onProjectPathChanged = =>
ipc.send('window-command', 'project-path-changed', @project.getPath())
@subscribe @project, 'path-changed', onProjectPathChanged
onProjectPathChanged()
# Public: Open a new Atom window using the given options.
#
# Calling this method without an options parameter will open a prompt to pick
# a file/folder to open in the new window.
#
# options - An {Object} with the following keys:
# :pathsToOpen - An {Array} of {String} paths to open.
# * `options` An {Object} with the following keys:
# * `pathsToOpen` An {Array} of {String} paths to open.
# * `newWindow` A {Boolean}, true to always open a new window instead of
# reusing existing windows depending on the paths to open.
# * `devMode` A {Boolean}, true to open the window in development mode.
# Development mode loads the Atom source from the locally cloned
# repository and also loads all the packages in ~/.atom/dev/packages
# * `safeMode` A {Boolean}, true to open the window in safe mode. Safe
# mode prevents all packages installed to ~/.atom/packages from loading.
open: (options) ->
ipc.send('open', options)
# Public: Open a confirm dialog.
#
# ## Example
# ## Examples
#
# ```coffee
# atom.confirm
@@ -367,12 +416,12 @@ class Atom extends Model
# Bad: -> window.alert('bummer')
# ```
#
# options - An {Object} with the following keys:
# :message - The {String} message to display.
# :detailedMessage - The {String} detailed message to display.
# :buttons - Either an array of strings or an object where keys are
# button names and the values are callbacks to invoke when
# clicked.
# * `options` An {Object} with the following keys:
# * `message` The {String} message to display.
# * `detailedMessage` The {String} detailed message to display.
# * `buttons` Either an array of strings or an object where keys are
# button names and the values are callbacks to invoke when
# clicked.
#
# Returns the chosen button index {Number} if the buttons option was an array.
confirm: ({message, detailedMessage, buttons}={}) ->
@@ -435,15 +484,15 @@ class Atom extends Model
# Public: Set the size of current window.
#
# width - The {Number} of pixels.
# height - The {Number} of pixels.
# * `width` The {Number} of pixels.
# * `height` The {Number} of pixels.
setSize: (width, height) ->
@getCurrentWindow().setSize(width, height)
# Public: Set the position of current window.
#
# x - The {Number} of pixels.
# y - The {Number} of pixels.
# * `x` The {Number} of pixels.
# * `y` The {Number} of pixels.
setPosition: (x, y) ->
ipc.send('call-window-method', 'setPosition', x, y)
@@ -456,11 +505,12 @@ class Atom extends Model
#
# This is done in a next tick to prevent a white flicker from occurring
# if called synchronously.
displayWindow: ->
displayWindow: ({maximize}={}) ->
setImmediate =>
@show()
@focus()
@setFullScreen(true) if @workspaceView.fullScreen
@setFullScreen(true) if @workspace.fullScreen
@maximize() if maximize
# Public: Close the current window.
close: ->
@@ -498,6 +548,9 @@ class Atom extends Model
isFullScreen: ->
@getCurrentWindow().isFullScreen()
maximize: ->
ipc.send('call-window-method', 'maximize')
# Public: Get the version of the Atom application.
#
# Returns the version text {String}.
@@ -526,7 +579,7 @@ class Atom extends Model
# This time include things like loading and activating packages, creating
# DOM elements for the editor, and reading the config.
#
# Returns the number of milliseconds taken to load the window or null
# Returns the {Number} of milliseconds taken to load the window or null
# if the window hasn't finished loading yet.
getWindowLoadTime: ->
@loadTime
@@ -558,8 +611,8 @@ class Atom extends Model
# The globals will be set on the `window` object and removed after the
# require completes.
#
# id - The {String} module name or path.
# globals - An {Object} to set as globals during require (default: {})
# * `id` The {String} module name or path.
# * `globals` An optinal {Object} to set as globals during require.
requireWithGlobals: (id, globals={}) ->
existingGlobals = {}
for key, value of globals
+1 -1
Ver Arquivo
@@ -116,7 +116,7 @@ class ApplicationMenu
item.metadata ?= {}
if item.command
item.accelerator = @acceleratorForCommand(item.command, keystrokesByCommand)
item.click = => global.atomApplication.sendCommand(item.command)
item.click = -> global.atomApplication.sendCommand(item.command)
item.metadata['windowSpecific'] = true unless /^application:/.test(item.command)
@translateTemplate(item.submenu, keystrokesByCommand) if item.submenu
template
+54 -19
Ver Arquivo
@@ -5,13 +5,11 @@ AutoUpdateManager = require './auto-update-manager'
BrowserWindow = require 'browser-window'
Menu = require 'menu'
app = require 'app'
dialog = require 'dialog'
fs = require 'fs'
ipc = require 'ipc'
path = require 'path'
os = require 'os'
net = require 'net'
shell = require 'shell'
url = require 'url'
{EventEmitter} = require 'events'
_ = require 'underscore-plus'
@@ -103,6 +101,12 @@ class AtomApplication
window.once 'window:loaded', =>
@autoUpdateManager.emitUpdateAvailableEvent(window)
focusHandler = => @lastFocusedWindow = window
window.browserWindow.on 'focus', focusHandler
window.browserWindow.once 'closed', =>
@lastFocusedWindow = null if window is @lastFocusedWindow
window.browserWindow.removeListener 'focus', focusHandler
# Creates server to listen for additional atom application launches.
#
# You can run the atom command multiple times, but after the first launch
@@ -149,8 +153,8 @@ class AtomApplication
atomWindow ?= @focusedWindow()
atomWindow?.browserWindow.inspectElement(x, y)
@on 'application:open-documentation', -> shell.openExternal('https://atom.io/docs/latest/?app')
@on 'application:open-terms-of-use', -> shell.openExternal('https://atom.io/terms')
@on 'application:open-documentation', -> require('shell').openExternal('https://atom.io/docs/latest/?app')
@on 'application:open-terms-of-use', -> require('shell').openExternal('https://atom.io/terms')
@on 'application:install-update', -> @autoUpdateManager.install()
@on 'application:check-for-update', => @autoUpdateManager.check()
@@ -199,13 +203,15 @@ class AtomApplication
# A request from the associated render process to open a new render process.
ipc.on 'open', (event, options) =>
window = @windowForEvent(event)
if options?
if options.pathsToOpen?.length > 0
options.window = window
@openPaths(options)
else
new AtomWindow(options)
else
@promptForPath()
@promptForPath({window})
ipc.on 'update-application-menu', (event, template, keystrokesByCommand) =>
@applicationMenu.update(template, keystrokesByCommand)
@@ -281,8 +287,12 @@ class AtomApplication
# Returns the {AtomWindow} for the given path.
windowForPath: (pathToOpen) ->
for atomWindow in @windows
return atomWindow if atomWindow.containsPath(pathToOpen)
_.find @windows, (atomWindow) -> atomWindow.containsPath(pathToOpen)
# Returns the {AtomWindow} for the given ipc event.
windowForEvent: ({sender}) ->
window = BrowserWindow.fromWebContents(sender)
_.find @windows, ({browserWindow}) -> window is browserWindow
# Public: Returns the currently focused {AtomWindow} or undefined if none.
focusedWindow: ->
@@ -296,9 +306,10 @@ class AtomApplication
# :newWindow - Boolean of whether this should be opened in a new window.
# :devMode - Boolean to control the opened window's dev mode.
# :safeMode - Boolean to control the opened window's safe mode.
openPaths: ({pathsToOpen, pidToKillWhenClosed, newWindow, devMode, safeMode}) ->
# :window - {AtomWindow} to open file paths in.
openPaths: ({pathsToOpen, pidToKillWhenClosed, newWindow, devMode, safeMode, window}) ->
for pathToOpen in pathsToOpen ? []
@openPath({pathToOpen, pidToKillWhenClosed, newWindow, devMode, safeMode})
@openPath({pathToOpen, pidToKillWhenClosed, newWindow, devMode, safeMode, window})
# Public: Opens a single path, in an existing window if possible.
#
@@ -309,15 +320,33 @@ class AtomApplication
# :devMode - Boolean to control the opened window's dev mode.
# :safeMode - Boolean to control the opened window's safe mode.
# :windowDimensions - Object with height and width keys.
openPath: ({pathToOpen, pidToKillWhenClosed, newWindow, devMode, safeMode, windowDimensions}={}) ->
# :window - {AtomWindow} to open file paths in.
openPath: ({pathToOpen, pidToKillWhenClosed, newWindow, devMode, safeMode, windowDimensions, window}={}) ->
{pathToOpen, initialLine, initialColumn} = @locationForPathToOpen(pathToOpen)
unless devMode
existingWindow = @windowForPath(pathToOpen) unless pidToKillWhenClosed or newWindow
if existingWindow
unless pidToKillWhenClosed or newWindow
pathToOpenStat = fs.statSyncNoException(pathToOpen)
# Default to using the specified window or the last focused window
currentWindow = window ? @lastFocusedWindow
if pathToOpenStat.isFile?()
# Open the file in the current window
existingWindow = currentWindow
else if pathToOpenStat.isDirectory?()
# Open the folder in the current window if it doesn't have a path
existingWindow = currentWindow unless currentWindow?.hasProjectPath()
# Don't reuse windows in dev mode
existingWindow ?= @windowForPath(pathToOpen) unless devMode
if existingWindow?
openedWindow = existingWindow
openedWindow.openPath(pathToOpen, initialLine)
openedWindow.restore()
if openedWindow.isMinimized()
openedWindow.restore()
else
openedWindow.focus()
else
if devMode
try
@@ -331,7 +360,7 @@ class AtomApplication
if pidToKillWhenClosed?
@pidsToOpenWindows[pidToKillWhenClosed] = openedWindow
openedWindow.browserWindow.on 'closed', =>
openedWindow.browserWindow.once 'closed', =>
@killProcessForWindow(openedWindow)
# Kill all processes associated with opened windows.
@@ -407,14 +436,17 @@ class AtomApplication
safeMode ?= false
new AtomWindow({bootstrapScript, resourcePath, exitWhenDone, isSpec, devMode, specDirectory, logFile, safeMode})
runBenchmarks: ->
runBenchmarks: ({exitWhenDone, specDirectory}={}) ->
try
bootstrapScript = require.resolve(path.resolve(global.devResourcePath, 'benchmark', 'benchmark-bootstrap'))
catch error
bootstrapScript = require.resolve(path.resolve(__dirname, '..', '..', 'benchmark', 'benchmark-bootstrap'))
specDirectory ?= path.dirname(bootstrapScript)
isSpec = true
new AtomWindow({bootstrapScript, @resourcePath, isSpec})
devMode = true
new AtomWindow({bootstrapScript, @resourcePath, exitWhenDone, isSpec, specDirectory, devMode})
locationForPathToOpen: (pathToOpen) ->
return {pathToOpen} unless pathToOpen
@@ -441,7 +473,8 @@ class AtomApplication
# should be in dev mode or not.
# :safeMode - A Boolean which controls whether any newly opened windows
# should be in safe mode or not.
promptForPath: ({type, devMode, safeMode}={}) ->
# :window - An {AtomWindow} to use for opening a selected file path.
promptForPath: ({type, devMode, safeMode, window}={}) ->
type ?= 'all'
properties =
switch type
@@ -449,5 +482,7 @@ class AtomApplication
when 'folder' then ['openDirectory']
when 'all' then ['openFile', 'openDirectory']
else throw new Error("#{type} is an invalid type for promptForPath")
dialog = require 'dialog'
dialog.showOpenDialog title: 'Open', properties: properties.concat(['multiSelections', 'createDirectory']), (pathsToOpen) =>
@openPaths({pathsToOpen, devMode, safeMode})
@openPaths({pathsToOpen, devMode, safeMode, window})
+2 -2
Ver Arquivo
@@ -1,5 +1,5 @@
app = require 'app'
fs = require 'fs-plus'
fs = require 'fs'
path = require 'path'
protocol = require 'protocol'
@@ -24,5 +24,5 @@ class AtomProtocolHandler
relativePath = path.normalize(request.url.substr(7))
for loadPath in @loadPaths
filePath = path.join(loadPath, relativePath)
break if fs.isFileSync(filePath)
break if fs.statSyncNoException(filePath).isFile?()
return new protocol.RequestFileJob(filePath)
+22 -5
Ver Arquivo
@@ -1,7 +1,5 @@
BrowserWindow = require 'browser-window'
ContextMenu = require './context-menu'
app = require 'app'
dialog = require 'dialog'
path = require 'path'
fs = require 'fs'
url = require 'url'
@@ -25,9 +23,14 @@ class AtomWindow
# Normalize to make sure drive letter case is consistent on Windows
@resourcePath = path.normalize(@resourcePath) if @resourcePath
@browserWindow = new BrowserWindow
show: false
title: 'Atom'
icon: @constructor.iconPath
'web-preferences':
'subpixel-font-scaling': false
global.atomApplication.addWindow(this)
@browserWindow = new BrowserWindow show: false, title: 'Atom', icon: @constructor.iconPath
@handleEvents()
loadSettings = _.extend({}, settings)
@@ -49,6 +52,8 @@ class AtomWindow
@emit 'window:loaded'
@loaded = true
@browserWindow.on 'project-path-changed', (@projectPath) =>
@browserWindow.loadUrl @getUrl(loadSettings)
@browserWindow.focusOnWebView() if @isSpec
@@ -66,9 +71,18 @@ class AtomWindow
slashes: true
query: {loadSettings: JSON.stringify(loadSettings)}
hasProjectPath: -> @projectPath?.length > 0
getInitialPath: ->
@browserWindow.loadSettings.initialPath
setupContextMenu: ->
ContextMenu = null
@browserWindow.on 'context-menu', (menuTemplate) =>
ContextMenu ?= require './context-menu'
new ContextMenu(menuTemplate, this)
containsPath: (pathToCheck) ->
initialPath = @getInitialPath()
if not initialPath
@@ -91,6 +105,7 @@ class AtomWindow
@browserWindow.on 'unresponsive', =>
return if @isSpec
dialog = require 'dialog'
chosen = dialog.showMessageBox @browserWindow,
type: 'warning'
buttons: ['Close', 'Keep Waiting']
@@ -101,6 +116,7 @@ class AtomWindow
@browserWindow.webContents.on 'crashed', =>
global.atomApplication.exit(100) if @exitWhenDone
dialog = require 'dialog'
chosen = dialog.showMessageBox @browserWindow,
type: 'warning'
buttons: ['Close Window', 'Reload', 'Keep It Open']
@@ -110,8 +126,7 @@ class AtomWindow
when 0 then @browserWindow.destroy()
when 1 then @browserWindow.restart()
@browserWindow.on 'context-menu', (menuTemplate) =>
new ContextMenu(menuTemplate, this)
@setupContextMenu()
if @isSpec
# Workaround for https://github.com/atom/atom-shell/issues/380
@@ -169,6 +184,8 @@ class AtomWindow
isFocused: -> @browserWindow.isFocused()
isMinimized: -> @browserWindow.isMinimized()
isWebViewFocused: -> @browserWindow.isWebViewFocused()
isSpecWindow: -> @isSpec
+24 -17
Ver Arquivo
@@ -1,44 +1,47 @@
https = require 'https'
autoUpdater = require 'auto-updater'
dialog = require 'dialog'
autoUpdater = null
_ = require 'underscore-plus'
{EventEmitter} = require 'events'
IDLE_STATE='idle'
CHECKING_STATE='checking'
DOWNLOADING_STATE='downloading'
UPDATE_AVAILABLE_STATE='update-available'
NO_UPDATE_AVAILABLE_STATE='no-update-available'
ERROR_STATE='error'
IdleState = 'idle'
CheckingState = 'checking'
DownladingState = 'downloading'
UpdateAvailableState = 'update-available'
NoUpdateAvailableState = 'no-update-available'
ErrorState = 'error'
module.exports =
class AutoUpdateManager
_.extend @prototype, EventEmitter.prototype
constructor: (@version) ->
@state = IDLE_STATE
@state = IdleState
@feedUrl = "https://atom.io/api/updates?version=#{@version}"
process.nextTick => @setupAutoUpdater()
setupAutoUpdater: ->
autoUpdater = require 'auto-updater'
if process.platform is 'win32'
autoUpdater.checkForUpdates = => @checkForUpdatesShim()
autoUpdater.setFeedUrl @feedUrl
autoUpdater.on 'checking-for-update', =>
@setState(CHECKING_STATE)
@setState(CheckingState)
autoUpdater.on 'update-not-available', =>
@setState(NO_UPDATE_AVAILABLE_STATE)
@setState(NoUpdateAvailableState)
autoUpdater.on 'update-available', =>
@setState(DOWNLOADING_STATE)
@setState(DownladingState)
autoUpdater.on 'error', (event, message) =>
@setState(ERROR_STATE)
@setState(ErrorState)
console.error "Error Downloading Update: #{message}"
autoUpdater.on 'update-downloaded', (event, @releaseNotes, @releaseVersion) =>
@setState(UPDATE_AVAILABLE_STATE)
@setState(UpdateAvailableState)
@emitUpdateAvailableEvent(@getWindows()...)
# Only released versions should check for updates.
@@ -48,6 +51,8 @@ class AutoUpdateManager
# Windows doesn't have an auto-updater, so use this method to shim the events.
checkForUpdatesShim: ->
autoUpdater.emit 'checking-for-update'
https = require 'https'
request = https.get @feedUrl, (response) ->
if response.statusCode == 200
body = ""
@@ -67,14 +72,14 @@ class AutoUpdateManager
atomWindow.sendCommand('window:update-available', [@releaseVersion, @releaseNotes])
setState: (state) ->
return unless @state != state
return if @state is state
@state = state
@emit 'state-changed', @state
getState: ->
@state
check: ({hidePopups}={})->
check: ({hidePopups}={}) ->
unless hidePopups
autoUpdater.once 'update-not-available', @onUpdateNotAvailable
autoUpdater.once 'error', @onUpdateError
@@ -86,10 +91,12 @@ class AutoUpdateManager
onUpdateNotAvailable: =>
autoUpdater.removeListener 'error', @onUpdateError
dialog = require 'dialog'
dialog.showMessageBox type: 'info', buttons: ['OK'], message: 'No update available.', detail: "Version #{@version} is the latest version."
onUpdateError: (event, message) =>
autoUpdater.removeListener 'update-not-available', @onUpdateNotAvailable
dialog = require 'dialog'
dialog.showMessageBox type: 'warning', buttons: ['OK'], message: 'There was an error checking for updates.', detail: message
getWindows: ->
+20 -8
Ver Arquivo
@@ -3,11 +3,9 @@ global.shellStartTime = Date.now()
crashReporter = require 'crash-reporter'
app = require 'app'
fs = require 'fs'
module = require 'module'
path = require 'path'
optimist = require 'optimist'
nslog = require 'nslog'
dialog = require 'dialog'
console.log = nslog
@@ -33,14 +31,14 @@ start = ->
app.on 'will-finish-launching', ->
setupCrashReporter()
app.on 'finish-launching', ->
app.on 'ready', ->
app.removeListener 'open-file', addPathToOpen
app.removeListener 'open-url', addUrlToOpen
args.pathsToOpen = args.pathsToOpen.map (pathToOpen) ->
path.resolve(args.executedFrom ? process.cwd(), pathToOpen.toString())
require('coffee-script').register()
setupCoffeeScript()
if args.devMode
require(path.join(args.resourcePath, 'src', 'coffee-cache')).register()
AtomApplication = require path.join(args.resourcePath, 'src', 'browser', 'atom-application')
@@ -57,13 +55,29 @@ global.devResourcePath = path.normalize(global.devResourcePath) if global.devRes
setupCrashReporter = ->
crashReporter.start(productName: 'Atom', companyName: 'GitHub')
setupCoffeeScript = ->
CoffeeScript = null
require.extensions['.coffee'] = (module, filePath) ->
CoffeeScript ?= require('coffee-script')
coffee = fs.readFileSync(filePath, 'utf8')
js = CoffeeScript.compile(coffee, filename: filePath)
module._compile(js, filePath)
parseCommandLine = ->
version = app.getVersion()
options = optimist(process.argv[1..])
options.usage """
Atom Editor v#{version}
Usage: atom [options] [file ...]
Usage: atom [options] [path ...]
One or more paths to files or folders to open may be specified.
File paths will open in the current window.
Folder paths will open in an existing window if that folder has already been
opened or a new window if it hasn't.
"""
options.alias('d', 'dev').boolean('d').describe('d', 'Run in development mode.')
options.alias('f', 'foreground').boolean('f').describe('f', 'Keep the browser process in the foreground.')
@@ -102,9 +116,7 @@ parseCommandLine = ->
else if devMode
resourcePath = global.devResourcePath
try
fs.statSync resourcePath
catch
unless fs.statSyncNoException(resourcePath)
resourcePath = path.dirname(path.dirname(__dirname))
{resourcePath, pathsToOpen, executedFrom, test, version, pidToKillWhenClosed, devMode, safeMode, newWindow, specDirectory, logFile}
+22 -21
Ver Arquivo
@@ -6,34 +6,35 @@ path = require 'path'
#
# This is necessary on Windows since it doesn't support shebang `#!` lines.
#
# ## Requiring in packages
# ## Examples
#
# ```coffee
# {BufferedNodeProcess} = require 'atom'
# {BufferedNodeProcess} = require 'atom'
# ```
module.exports =
class BufferedNodeProcess extends BufferedProcess
# Public: Runs the given Node script by spawning a new child process.
#
# options - An {Object} with the following keys:
# :command - The {String} path to the JavaScript script to execute.
# :args - The {Array} of arguments to pass to the script (optional).
# :options - The options {Object} to pass to Node's `ChildProcess.spawn`
# method (optional).
# :stdout - The callback {Function} that receives a single argument which
# contains the standard output from the command. The callback is
# called as data is received but it's buffered to ensure only
# complete lines are passed until the source stream closes. After
# the source stream has closed all remaining data is sent in a
# final call (optional).
# :stderr - The callback {Function} that receives a single argument which
# contains the standard error output from the command. The
# callback is called as data is received but it's buffered to
# ensure only complete lines are passed until the source stream
# closes. After the source stream has closed all remaining data
# is sent in a final call (optional).
# :exit - The callback {Function} which receives a single argument
# containing the exit status (optional).
# * `options` An {Object} with the following keys:
# * `command` The {String} path to the JavaScript script to execute.
# * `args` The {Array} of arguments to pass to the script (optional).
# * `options` The options {Object} to pass to Node's `ChildProcess.spawn`
# method (optional).
# * `stdout` The callback {Function} that receives a single argument which
# contains the standard output from the command. The callback is
# called as data is received but it's buffered to ensure only
# complete lines are passed until the source stream closes. After
# the source stream has closed all remaining data is sent in a
# final call (optional).
# * `stderr` The callback {Function} that receives a single argument which
# contains the standard error output from the command. The
# callback is called as data is received but it's buffered to
# ensure only complete lines are passed until the source stream
# closes. After the source stream has closed all remaining data
# is sent in a final call (optional).
# * `exit` The callback {Function} which receives a single argument
# containing the exit status (optional).
constructor: ({command, args, options, stdout, stderr, exit}) ->
node =
if process.platform is 'darwin'
+23 -23
Ver Arquivo
@@ -4,7 +4,7 @@ ChildProcess = require 'child_process'
# Public: A wrapper which provides standard error/output line buffering for
# Node's ChildProcess.
#
# ## Requiring in packages
# ## Examples
#
# ```coffee
# {BufferedProcess} = require 'atom'
@@ -19,25 +19,25 @@ module.exports =
class BufferedProcess
# Public: Runs the given command by spawning a new child process.
#
# options - An {Object} with the following keys:
# :command - The {String} command to execute.
# :args - The {Array} of arguments to pass to the command (optional).
# :options - The options {Object} to pass to Node's `ChildProcess.spawn`
# method (optional).
# :stdout - The callback {Function} that receives a single argument which
# contains the standard output from the command. The callback is
# called as data is received but it's buffered to ensure only
# complete lines are passed until the source stream closes. After
# the source stream has closed all remaining data is sent in a
# final call (optional).
# :stderr - The callback {Function} that receives a single argument which
# contains the standard error output from the command. The
# callback is called as data is received but it's buffered to
# ensure only complete lines are passed until the source stream
# closes. After the source stream has closed all remaining data
# is sent in a final call (optional).
# :exit - The callback {Function} which receives a single argument
# containing the exit status (optional).
# * `options` An {Object} with the following keys:
# * `command` The {String} command to execute.
# * `args` The {Array} of arguments to pass to the command (optional).
# * `options` The options {Object} to pass to Node's `ChildProcess.spawn`
# method (optional).
# * `stdout` The callback {Function} that receives a single argument which
# contains the standard output from the command. The callback is
# called as data is received but it's buffered to ensure only
# complete lines are passed until the source stream closes. After
# the source stream has closed all remaining data is sent in a
# final call (optional).
# * `stderr` The callback {Function} that receives a single argument which
# contains the standard error output from the command. The
# callback is called as data is received but it's buffered to
# ensure only complete lines are passed until the source stream
# closes. After the source stream has closed all remaining data
# is sent in a final call (optional).
# * `exit` The callback {Function} which receives a single argument
# containing the exit status (optional).
constructor: ({command, args, options, stdout, stderr, exit}={}) ->
options ?= {}
# Related to joyent/node#2318
@@ -95,9 +95,9 @@ class BufferedProcess
# Helper method to pass data line by line.
#
# stream - The Stream to read from.
# onLines - The callback to call with each line of data.
# onDone - The callback to call when the stream has closed.
# * `stream` The Stream to read from.
# * `onLines` The callback to call with each line of data.
# * `onDone` The callback to call when the stream has closed.
bufferStream: (stream, onLines, onDone) ->
stream.setEncoding('utf8')
buffered = ''
+13 -5
Ver Arquivo
@@ -4,6 +4,14 @@ crypto = require 'crypto'
# Public: Represents the clipboard used for copying and pasting in Atom.
#
# An instance of this class is always available as the `atom.clipboard` global.
#
# ## Examples
#
# ```coffee
# atom.clipboard.write('hello')
#
# console.log(atom.clipboard.read()) # 'hello'
# ```
module.exports =
class Clipboard
metadata: null
@@ -11,7 +19,7 @@ class Clipboard
# Creates an `md5` hash of some text.
#
# text - A {String} to hash.
# * `text` A {String} to hash.
#
# Returns a hashed {String}.
md5: (text) ->
@@ -22,8 +30,8 @@ class Clipboard
# The metadata associated with the text is available by calling
# {::readWithMetadata}.
#
# text - The {String} to store.
# metadata - The additional info to associate with the text.
# * `text` The {String} to store.
# * `metadata` The additional info to associate with the text.
write: (text, metadata) ->
@signatureForMetadata = @md5(text)
@metadata = metadata
@@ -39,8 +47,8 @@ class Clipboard
# associated metadata.
#
# Returns an {Object} with the following keys:
# :text - The {String} clipboard text.
# :metadata - The metadata stored by an earlier call to {::write}.
# * `text` The {String} clipboard text.
# * `metadata` The metadata stored by an earlier call to {::write}.
readWithMetadata: ->
text = @read()
if @signatureForMetadata is @md5(text)
+1 -1
Ver Arquivo
@@ -41,7 +41,7 @@ module.exports =
callback()
return
symlinkCommand commandPath, destinationPath, (error) =>
symlinkCommand commandPath, destinationPath, (error) ->
if askForPrivilege and error?.code is 'EACCES'
try
error = null
+41 -44
Ver Arquivo
@@ -15,9 +15,9 @@ pathWatcher = require 'pathwatcher'
# * Create your own root keypath using your package's name.
# * Don't depend on (or write to) configuration keys outside of your keypath.
#
# ## Example
# ## Examples
#
# ```coffeescript
# ```coffee
# atom.config.set('my-package.key', 'value')
# atom.config.observe 'my-package.key', ->
# console.log 'My configuration changed:', atom.config.get('my-package.key')
@@ -39,7 +39,7 @@ class Config
fs.makeTreeSync(@configDirPath)
queue = async.queue ({sourcePath, destinationPath}, callback) =>
queue = async.queue ({sourcePath, destinationPath}, callback) ->
fs.copy(sourcePath, destinationPath, callback)
queue.drain = done
@@ -88,17 +88,17 @@ class Config
_.extend hash, defaults
@emit 'updated'
# Public: Get the {String} path to the config file being used.
# Extended: Get the {String} path to the config file being used.
getUserConfigPath: ->
@configFilePath
# Public: Returns a new {Object} containing all of settings and defaults.
# Extended: Returns a new {Object} containing all of settings and defaults.
getSettings: ->
_.deepExtend(@settings, @defaultSettings)
# Public: Retrieves the setting for the given key.
# Essential: Retrieves the setting for the given key.
#
# keyPath - The {String} name of the key to retrieve.
# * `keyPath` The {String} name of the key to retrieve.
#
# Returns the value from Atom's default settings, the user's configuration
# file, or `null` if the key doesn't exist in either.
@@ -117,19 +117,19 @@ class Config
value
# Public: Retrieves the setting for the given key as an integer.
# Extended: Retrieves the setting for the given key as an integer.
#
# keyPath - The {String} name of the key to retrieve
# * `keyPath` The {String} name of the key to retrieve
#
# Returns the value from Atom's default settings, the user's configuration
# file, or `NaN` if the key doesn't exist in either.
getInt: (keyPath) ->
parseInt(@get(keyPath))
# Public: Retrieves the setting for the given key as a positive integer.
# Extended: Retrieves the setting for the given key as a positive integer.
#
# keyPath - The {String} name of the key to retrieve
# defaultValue - The integer {Number} to fall back to if the value isn't
# * `keyPath` The {String} name of the key to retrieve
# * `defaultValue` The integer {Number} to fall back to if the value isn't
# positive, defaults to 0.
#
# Returns the value from Atom's default settings, the user's configuration
@@ -137,12 +137,12 @@ class Config
getPositiveInt: (keyPath, defaultValue=0) ->
Math.max(@getInt(keyPath), 0) or defaultValue
# Public: Sets the value for a configuration setting.
# Essential: Sets the value for a configuration setting.
#
# This value is stored in Atom's internal configuration file.
#
# keyPath - The {String} name of the key.
# value - The value of the setting.
# * `keyPath` The {String} name of the key.
# * `value` The value of the setting.
#
# Returns the `value`.
set: (keyPath, value) ->
@@ -153,47 +153,47 @@ class Config
@update()
value
# Public: Toggle the value at the key path.
# Extended: Toggle the value at the key path.
#
# The new value will be `true` if the value is currently falsy and will be
# `false` if the value is currently truthy.
#
# keyPath - The {String} name of the key.
# * `keyPath` The {String} name of the key.
#
# Returns the new value.
toggle: (keyPath) ->
@set(keyPath, !@get(keyPath))
# Public: Restore the key path to its default value.
# Extended: Restore the key path to its default value.
#
# keyPath - The {String} name of the key.
# * `keyPath` The {String} name of the key.
#
# Returns the new value.
restoreDefault: (keyPath) ->
@set(keyPath, _.valueForKeyPath(@defaultSettings, keyPath))
# Public: Get the default value of the key path.
# Extended: Get the default value of the key path.
#
# keyPath - The {String} name of the key.
# * `keyPath` The {String} name of the key.
#
# Returns the default value.
getDefault: (keyPath) ->
defaultValue = _.valueForKeyPath(@defaultSettings, keyPath)
_.deepClone(defaultValue)
# Public: Is the key path value its default value?
# Extended: Is the key path value its default value?
#
# keyPath - The {String} name of the key.
# * `keyPath` The {String} name of the key.
#
# Returns a {Boolean}, `true` if the current value is the default, `false`
# otherwise.
isDefault: (keyPath) ->
not _.valueForKeyPath(@settings, keyPath)?
# Public: Push the value to the array at the key path.
# Extended: Push the value to the array at the key path.
#
# keyPath - The {String} key path.
# value - The value to push to the array.
# * `keyPath` The {String} key path.
# * `value` The value to push to the array.
#
# Returns the new array length {Number} of the setting.
pushAtKeyPath: (keyPath, value) ->
@@ -202,10 +202,10 @@ class Config
@set(keyPath, arrayValue)
result
# Public: Add the value to the beginning of the array at the key path.
# Extended: Add the value to the beginning of the array at the key path.
#
# keyPath - The {String} key path.
# value - The value to shift onto the array.
# * `keyPath` The {String} key path.
# * `value` The value to shift onto the array.
#
# Returns the new array length {Number} of the setting.
unshiftAtKeyPath: (keyPath, value) ->
@@ -216,8 +216,8 @@ class Config
# Public: Remove the value from the array at the key path.
#
# keyPath - The {String} key path.
# value - The value to remove from the array.
# * `keyPath` The {String} key path.
# * `value` The value to remove from the array.
#
# Returns the new array value of the setting.
removeAtKeyPath: (keyPath, value) ->
@@ -226,20 +226,17 @@ class Config
@set(keyPath, arrayValue)
result
# Public: Establishes an event listener for a given key.
# Essential: Add a listener for changes to a given key path.
#
# `callback` is fired whenever the value of the key is changed and will
# be fired immediately unless the `callNow` option is `false`.
#
# keyPath - The {String} name of the key to observe
# options - An optional {Object} containing the `callNow` key.
# callback - The {Function} to call when the value of the key changes.
# The first argument will be the new value of the key and the
#   second argument will be an {Object} with a `previous` property
# that is the prior value of the key.
# * `keyPath` The {String} name of the key to observe
# * `options` An optional {Object} containing the `callNow` key.
# * `callback` The {Function} to call when the value of the key changes.
# The first argument will be the new value of the key and the
#   second argument will be an {Object} with a `previous` property
# that is the prior value of the key.
#
# Returns an {Object} with the following keys:
# :off - A {Function} that unobserves the `keyPath` when called.
# * `off` A {Function} that unobserves the `keyPath` when called.
observe: (keyPath, options={}, callback) ->
if _.isFunction(options)
callback = options
@@ -259,9 +256,9 @@ class Config
callback(value) if options.callNow ? true
subscription
# Public: Unobserve all callbacks on a given key.
# Unobserve all callbacks on a given key.
#
# keyPath - The {String} name of the key to unobserve.
# * `keyPath` The {String} name of the key to unobserve.
unobserve: (keyPath) ->
@off("updated.#{keyPath.replace(/\./, '-')}")
+30 -16
Ver Arquivo
@@ -1,6 +1,9 @@
{$} = require './space-pen-extensions'
_ = require 'underscore-plus'
remote = require 'remote'
path = require 'path'
CSON = require 'season'
fs = require 'fs-plus'
# Public: Provides a registry for commands that you'd like to appear in the
# context menu.
@@ -9,7 +12,7 @@ remote = require 'remote'
# global.
module.exports =
class ContextMenuManager
constructor: (@devMode=false) ->
constructor: ({@resourcePath, @devMode}) ->
@definitions = {}
@devModeDefinitions = {}
@activeElement = null
@@ -21,16 +24,22 @@ class ContextMenuManager
@commandOptions = x: e.pageX, y: e.pageY
]
atom.keymaps.on 'bundled-keymaps-loaded', => @loadPlatformItems()
loadPlatformItems: ->
menusDirPath = path.join(@resourcePath, 'menus')
platformMenuPath = fs.resolve(menusDirPath, process.platform, ['cson', 'json'])
map = CSON.readFileSync(platformMenuPath)
atom.contextMenu.add(platformMenuPath, map['context-menu'])
# Public: Creates menu definitions from the object specified by the menu
# cson API.
# JSON API.
#
# name - The path of the file that contains the menu definitions.
# object - The 'context-menu' object specified in the menu cson API.
# options - An {Object} with the following keys:
# :devMode - Determines whether the entries should only be shown when
# the window is in dev mode.
#
# Returns nothing.
# * `name` The path of the file that contains the menu definitions.
# * `object` The 'context-menu' object specified in the menu JSON API.
# * `options` An optional {Object} with the following keys:
# * `devMode` Determines whether the entries should only be shown when
# the window is in dev mode.
add: (name, object, {devMode}={}) ->
for selector, items of object
for label, commandOrSubmenu of items
@@ -43,6 +52,8 @@ class ContextMenuManager
menuItem = @buildMenuItem(label, commandOrSubmenu)
@addBySelector(selector, menuItem, {devMode})
undefined
buildMenuItem: (label, command) ->
if command is '-'
{type: 'separator'}
@@ -52,12 +63,12 @@ class ContextMenuManager
# Registers a command to be displayed when the relevant item is right
# clicked.
#
# selector - The css selector for the active element which should include
# the given command in its context menu.
# definition - The object containing keys which match the menu template API.
# options - An {Object} with the following keys:
# :devMode - Indicates whether this command should only appear while the
# editor is in dev mode.
# * `selector` The css selector for the active element which should include
# the given command in its context menu.
# * `definition` The object containing keys which match the menu template API.
# * `options` An optional {Object} with the following keys:
# * `devMode` Indicates whether this command should only appear while the
# editor is in dev mode.
addBySelector: (selector, definition, {devMode}={}) ->
definitions = if devMode then @devModeDefinitions else @definitions
if not _.findWhere(definitions[selector], definition) or _.isEqual(definition, {type: 'separator'})
@@ -79,7 +90,7 @@ class ContextMenuManager
# active element are listed first. The further down the list you go, the higher
# up the ancestor hierarchy they match.
#
# element - The DOM element to generate the menu template for.
# * `element` The DOM element to generate the menu template for.
menuTemplateForMostSpecificElement: (element, {devMode}={}) ->
menuTemplate = @definitionsForElement(element, {devMode})
if element.parentElement
@@ -109,9 +120,12 @@ class ContextMenuManager
delete template.executeAtBuild
# Public: Request a context menu to be displayed.
#
# * `event` A DOM event.
showForEvent: (event) ->
@activeElement = event.target
menuTemplate = @combinedMenuTemplateForElement(event.target)
return unless menuTemplate?.length > 0
@executeBuildHandlers(event, menuTemplate)
remote.getCurrentWindow().emit('context-menu', menuTemplate)
undefined
-113
Ver Arquivo
@@ -1,113 +0,0 @@
{View} = require './space-pen-extensions'
_ = require 'underscore-plus'
module.exports =
class CursorView extends View
@content: ->
@div class: 'cursor idle', => @raw '&nbsp;'
@blinkPeriod: 800
@blinkCursors: ->
element.classList.toggle('blink-off') for [element] in @cursorViews
@startBlinking: (cursorView) ->
@cursorViews ?= []
@cursorViews.push(cursorView)
if @cursorViews.length is 1
@blinkInterval = setInterval(@blinkCursors.bind(this), @blinkPeriod / 2)
@stopBlinking: (cursorView) ->
cursorView[0].classList.remove('blink-off')
_.remove(@cursorViews, cursorView)
clearInterval(@blinkInterval) if @cursorViews.length is 0
blinking: false
visible: true
needsUpdate: true
needsRemoval: false
shouldPauseBlinking: false
initialize: (@cursor, @editorView) ->
@subscribe @cursor, 'moved', =>
@needsUpdate = true
@shouldPauseBlinking = true
@subscribe @cursor, 'visibility-changed', =>
@needsUpdate = true
@subscribe @cursor, 'autoscrolled', =>
@editorView.requestDisplayUpdate()
@subscribe @cursor, 'destroyed', =>
@needsRemoval = true
beforeRemove: ->
@editorView.removeCursorView(this)
@stopBlinking()
updateDisplay: ->
screenPosition = @getScreenPosition()
pixelPosition = @getPixelPosition()
unless _.isEqual(@lastPixelPosition, pixelPosition)
@lastPixelPosition = pixelPosition
@css(pixelPosition)
@trigger 'cursor:moved'
if @shouldPauseBlinking
@resetBlinking()
else if !@startBlinkingTimeout
@startBlinking()
@setVisible(@cursor.isVisible() and not @editorView.getEditor().isFoldedAtScreenRow(screenPosition.row))
# Override for speed. The base function checks the computedStyle
isHidden: ->
this[0].style.display is 'none' or not @isOnDom()
needsAutoscroll: ->
@cursor.needsAutoscroll
clearAutoscroll: ->
@cursor.clearAutoscroll()
getPixelPosition: ->
@editorView.pixelPositionForScreenPosition(@getScreenPosition())
setVisible: (visible) ->
unless @visible is visible
@visible = visible
hiddenCursor = 'hidden-cursor'
if visible
@removeClass hiddenCursor
else
@addClass hiddenCursor
stopBlinking: ->
@constructor.stopBlinking(this) if @blinking
@blinking = false
startBlinking: ->
@constructor.startBlinking(this) unless @blinking
@blinking = true
resetBlinking: ->
@stopBlinking()
@startBlinking()
getBufferPosition: ->
@cursor.getBufferPosition()
getScreenPosition: ->
@cursor.getScreenPosition()
removeIdleClassTemporarily: ->
@removeClass 'idle'
window.clearTimeout(@idleTimeout) if @idleTimeout
@idleTimeout = window.setTimeout (=> @addClass 'idle'), 200
resetCursorAnimation: ->
window.clearTimeout(@idleTimeout) if @idleTimeout
@removeClass 'idle'
_.defer => @addClass 'idle'
+176 -80
Ver Arquivo
@@ -1,8 +1,10 @@
{Point, Range} = require 'text-buffer'
{Model} = require 'theorist'
{Emitter} = require 'event-kit'
_ = require 'underscore-plus'
Grim = require 'grim'
# Public: The `Cursor` class represents the little blinking line identifying
# Extended: The `Cursor` class represents the little blinking line identifying
# where text can be inserted.
#
# Cursors belong to {Editor}s and have some metadata attached in the form
@@ -17,9 +19,11 @@ class Cursor extends Model
# Instantiated by an {Editor}
constructor: ({@editor, @marker, id}) ->
@emitter = new Emitter
@assignId(id)
@updateVisibility()
@marker.on 'changed', (e) =>
@marker.onDidChange (e) =>
@updateVisibility()
{oldHeadScreenPosition, newHeadScreenPosition} = e
{oldHeadBufferPosition, newHeadBufferPosition} = e
@@ -40,13 +44,61 @@ class Cursor extends Model
textChanged: textChanged
@emit 'moved', movedEvent
@editor.cursorMoved(movedEvent)
@marker.on 'destroyed', =>
@emitter.emit 'did-change-position'
@editor.cursorMoved(this, movedEvent)
@marker.onDidDestroy =>
@destroyed = true
@editor.removeCursor(this)
@emit 'destroyed'
@emitter.emit 'did-destroy'
@emitter.dispose()
@needsAutoscroll = true
###
Section: Events
###
# Essential: Calls your `callback` when the cursor has been moved.
#
# * `callback` {Function}
# * `event` {Object}
# * `oldBufferPosition` {Point}
# * `oldScreenPosition` {Point}
# * `newBufferPosition` {Point}
# * `newScreenPosition` {Point}
# * `textChanged` {Boolean}
onDidChangePosition: (callback) ->
@emitter.on 'did-change-position', callback
# Extended: Calls your `callback` when the cursor is destroyed
#
# * `callback` {Function}
onDidDestroy: (callback) ->
@emitter.on 'did-destroy', callback
# Extended: Calls your `callback` when the cursor's visibility has changed
#
# * `callback` {Function}
# * `visibility` {Boolean}
onDidChangeVisibility: (callback) ->
@emitter.on 'did-change-visibility', callback
on: (eventName) ->
switch eventName
when 'moved'
Grim.deprecate("Use Cursor::onDidChangePosition instead")
when 'destroyed'
Grim.deprecate("Use Cursor::onDidDestroy instead")
when 'destroyed'
Grim.deprecate("Use Cursor::onDidDestroy instead")
else
Grim.deprecate("::on is no longer supported. Use the event subscription methods instead")
super
###
Section: Methods
###
destroy: ->
@marker.destroy()
@@ -63,11 +115,10 @@ class Cursor extends Model
# Public: Moves a cursor to a given screen position.
#
# screenPosition - An {Array} of two numbers: the screen row, and the screen
# column.
# options - An {Object} with the following keys:
# :autoscroll - A Boolean which, if `true`, scrolls the {Editor} to wherever
# the cursor moves to.
# * `screenPosition` {Array} of two numbers: the screen row, and the screen column.
# * `options` (optional) {Object} with the following keys:
# * `autoscroll` A Boolean which, if `true`, scrolls the {Editor} to wherever
# the cursor moves to.
setScreenPosition: (screenPosition, options={}) ->
@changePosition options, =>
@marker.setHeadScreenPosition(screenPosition, options)
@@ -82,11 +133,10 @@ class Cursor extends Model
# Public: Moves a cursor to a given buffer position.
#
# bufferPosition - An {Array} of two numbers: the buffer row, and the buffer
# column.
# options - An {Object} with the following keys:
# :autoscroll - A Boolean which, if `true`, scrolls the {Editor} to wherever
# the cursor moves to.
# * `bufferPosition` {Array} of two numbers: the buffer row, and the buffer column.
# * `options` (optional) {Object} with the following keys:
# * `autoscroll` A Boolean which, if `true`, scrolls the {Editor} to wherever
# the cursor moves to.
setBufferPosition: (bufferPosition, options={}) ->
@changePosition options, =>
@marker.setHeadBufferPosition(bufferPosition, options)
@@ -108,19 +158,19 @@ class Cursor extends Model
@visible = visible
@needsAutoscroll ?= true if @visible and @isLastCursor()
@emit 'visibility-changed', @visible
@emitter.emit 'did-change-visibility', @visible
# Public: Returns the visibility of the cursor.
isVisible: -> @visible
# Public: Get the RegExp used by the cursor to determine what a "word" is.
#
# options: An optional {Object} with the following keys:
# :includeNonWordCharacters - A {Boolean} indicating whether to include
# non-word characters in the regex.
# (default: true)
# * `options` (optional) {Object} with the following keys:
# * `includeNonWordCharacters` A {Boolean} indicating whether to include
# non-word characters in the regex. (default: true)
#
# Returns a {RegExp}.
wordRegExp: ({includeNonWordCharacters}={})->
wordRegExp: ({includeNonWordCharacters}={}) ->
includeNonWordCharacters ?= true
nonWordCharacters = atom.config.get('editor.nonWordCharacters')
segments = ["^[\t ]*$"]
@@ -135,7 +185,7 @@ class Cursor extends Model
#
# Returns a {Boolean}.
isLastCursor: ->
this == @editor.getCursor()
this == @editor.getLastCursor()
# Public: Identifies if the cursor is surrounded by whitespace.
#
@@ -200,10 +250,15 @@ class Cursor extends Model
# Public: Returns the cursor's current buffer row of text excluding its line
# ending.
getCurrentBufferLine: ->
@editor.lineForBufferRow(@getBufferRow())
@editor.lineTextForBufferRow(@getBufferRow())
# Public: Moves the cursor up one screen row.
moveUp: (rowCount = 1, {moveToEndOfSelection}={}) ->
#
# * `rowCount` (optional) {Number} number of rows to move (default: 1)
# * `options` (optional) {Object} with the following keys:
# * `moveToEndOfSelection` if true, move to the left of the selection if a
# selection exists.
moveUp: (rowCount=1, {moveToEndOfSelection}={}) ->
range = @marker.getScreenRange()
if moveToEndOfSelection and not range.isEmpty()
{ row, column } = range.start
@@ -215,7 +270,12 @@ class Cursor extends Model
@goalColumn = column
# Public: Moves the cursor down one screen row.
moveDown: (rowCount = 1, {moveToEndOfSelection}={}) ->
#
# * `rowCount` (optional) {Number} number of rows to move (default: 1)
# * `options` (optional) {Object} with the following keys:
# * `moveToEndOfSelection` if true, move to the left of the selection if a
# selection exists.
moveDown: (rowCount=1, {moveToEndOfSelection}={}) ->
range = @marker.getScreenRange()
if moveToEndOfSelection and not range.isEmpty()
{ row, column } = range.end
@@ -228,30 +288,51 @@ class Cursor extends Model
# Public: Moves the cursor left one screen column.
#
# options - An {Object} with the following keys:
# :moveToEndOfSelection - if true, move to the left of the selection if a
# selection exists.
moveLeft: ({moveToEndOfSelection}={}) ->
# * `columnCount` (optional) {Number} number of columns to move (default: 1)
# * `options` (optional) {Object} with the following keys:
# * `moveToEndOfSelection` if true, move to the left of the selection if a
# selection exists.
moveLeft: (columnCount=1, {moveToEndOfSelection}={}) ->
range = @marker.getScreenRange()
if moveToEndOfSelection and not range.isEmpty()
@setScreenPosition(range.start)
else
{row, column} = @getScreenPosition()
[row, column] = if column > 0 then [row, column - 1] else [row - 1, Infinity]
while columnCount > column and row > 0
columnCount -= column
column = @editor.lineTextForScreenRow(--row).length
columnCount-- # subtract 1 for the row move
column = column - columnCount
@setScreenPosition({row, column})
# Public: Moves the cursor right one screen column.
#
# options - An {Object} with the following keys:
# :moveToEndOfSelection - if true, move to the right of the selection if a
# selection exists.
moveRight: ({moveToEndOfSelection}={}) ->
# * `columnCount` (optional) {Number} number of columns to move (default: 1)
# * `options` (optional) {Object} with the following keys:
# * `moveToEndOfSelection` if true, move to the right of the selection if a
# selection exists.
moveRight: (columnCount=1, {moveToEndOfSelection}={}) ->
range = @marker.getScreenRange()
if moveToEndOfSelection and not range.isEmpty()
@setScreenPosition(range.end)
else
{ row, column } = @getScreenPosition()
@setScreenPosition([row, column + 1], skipAtomicTokens: true, wrapBeyondNewlines: true, wrapAtSoftNewlines: true)
maxLines = @editor.getScreenLineCount()
rowLength = @editor.lineTextForScreenRow(row).length
columnsRemainingInLine = rowLength - column
while columnCount > columnsRemainingInLine and row < maxLines - 1
columnCount -= columnsRemainingInLine
columnCount-- # subtract 1 for the row move
column = 0
rowLength = @editor.lineTextForScreenRow(++row).length
columnsRemainingInLine = rowLength
column = column + columnCount
@setScreenPosition({row, column}, skipAtomicTokens: true, wrapBeyondNewlines: true, wrapAtSoftNewlines: true)
# Public: Moves the cursor to the top of the buffer.
moveToTop: ->
@@ -272,24 +353,20 @@ class Cursor extends Model
# Public: Moves the cursor to the beginning of the first character in the
# line.
moveToFirstCharacterOfLine: ->
{row, column} = @getScreenPosition()
screenRow = @getScreenRow()
lineBufferRange = @editor.bufferRangeForScreenRange([[screenRow, 0], [screenRow, Infinity]])
bufferRange = @editor.bufferRangeForScreenRange([[row, 0], [row, Infinity]])
screenLineText = @editor.getTextInBufferRange(bufferRange)
goalColumn = screenLineText.search(/\S/)
goalColumn = 0 if goalColumn == column or goalColumn == -1
@setScreenPosition([row, goalColumn])
firstCharacterColumn = null
@editor.scanInBufferRange /\S/, lineBufferRange, ({range, stop}) ->
firstCharacterColumn = range.start.column
stop()
# Public: Moves the cursor to the beginning of the buffer line, skipping all
# whitespace.
skipLeadingWhitespace: ->
position = @getBufferPosition()
scanRange = @getCurrentLineBufferRange()
endOfLeadingWhitespace = null
@editor.scanInBufferRange /^[ \t]*/, scanRange, ({range}) =>
endOfLeadingWhitespace = range.end
if firstCharacterColumn? and firstCharacterColumn isnt @getBufferColumn()
targetBufferColumn = firstCharacterColumn
else
targetBufferColumn = lineBufferRange.start.column
@setBufferPosition(endOfLeadingWhitespace) if endOfLeadingWhitespace.isGreaterThan(position)
@setBufferPosition([lineBufferRange.start.row, targetBufferColumn])
# Public: Moves the cursor to the end of the line.
moveToEndOfScreenLine: ->
@@ -323,16 +400,27 @@ class Cursor extends Model
if position = @getMoveNextWordBoundaryBufferPosition()
@setBufferPosition(position)
# Public: Moves the cursor to the beginning of the buffer line, skipping all
# whitespace.
skipLeadingWhitespace: ->
position = @getBufferPosition()
scanRange = @getCurrentLineBufferRange()
endOfLeadingWhitespace = null
@editor.scanInBufferRange /^[ \t]*/, scanRange, ({range}) ->
endOfLeadingWhitespace = range.end
@setBufferPosition(endOfLeadingWhitespace) if endOfLeadingWhitespace.isGreaterThan(position)
# Public: Retrieves the buffer position of where the current word starts.
#
# options - An {Object} with the following keys:
# :wordRegex - A {RegExp} indicating what constitutes a "word"
# (default: {::wordRegExp}).
# :includeNonWordCharacters - A {Boolean} indicating whether to include
# non-word characters in the default word regex.
# Has no effect if wordRegex is set.
# :allowPrevious - A {Boolean} indicating whether the beginning of the
# previous word can be returned.
# * `options` (optional) An {Object} with the following keys:
# * `wordRegex` A {RegExp} indicating what constitutes a "word"
# (default: {::wordRegExp}).
# * `includeNonWordCharacters` A {Boolean} indicating whether to include
# non-word characters in the default word regex.
# Has no effect if wordRegex is set.
# * `allowPrevious` A {Boolean} indicating whether the beginning of the
# previous word can be returned.
#
# Returns a {Range}.
getBeginningOfCurrentWordBufferPosition: (options = {}) ->
@@ -342,7 +430,7 @@ class Cursor extends Model
scanRange = [[previousNonBlankRow, 0], currentBufferPosition]
beginningOfWordPosition = null
@editor.backwardsScanInBufferRange (options.wordRegex ? @wordRegExp(options)), scanRange, ({range, stop}) =>
@editor.backwardsScanInBufferRange (options.wordRegex ? @wordRegExp(options)), scanRange, ({range, stop}) ->
if range.end.isGreaterThanOrEqual(currentBufferPosition) or allowPrevious
beginningOfWordPosition = range.start
if not beginningOfWordPosition?.isEqual(currentBufferPosition)
@@ -363,7 +451,7 @@ class Cursor extends Model
scanRange = [[previousNonBlankRow, 0], currentBufferPosition]
beginningOfWordPosition = null
@editor.backwardsScanInBufferRange (options.wordRegex ? @wordRegExp()), scanRange, ({range, stop}) =>
@editor.backwardsScanInBufferRange (options.wordRegex ? @wordRegExp()), scanRange, ({range, stop}) ->
if range.start.row < currentBufferPosition.row and currentBufferPosition.column > 0
# force it to stop at the beginning of each line
beginningOfWordPosition = new Point(currentBufferPosition.row, 0)
@@ -384,7 +472,7 @@ class Cursor extends Model
scanRange = [currentBufferPosition, @editor.getEofBufferPosition()]
endOfWordPosition = null
@editor.scanInBufferRange (options.wordRegex ? @wordRegExp()), scanRange, ({range, stop}) =>
@editor.scanInBufferRange (options.wordRegex ? @wordRegExp()), scanRange, ({range, stop}) ->
if range.start.row > currentBufferPosition.row
# force it to stop at the beginning of each line
endOfWordPosition = new Point(range.start.row, 0)
@@ -400,12 +488,12 @@ class Cursor extends Model
# Public: Retrieves the buffer position of where the current word ends.
#
# options - An {Object} with the following keys:
# :wordRegex - A {RegExp} indicating what constitutes a "word"
# (default: {::wordRegExp})
# :includeNonWordCharacters - A Boolean indicating whether to include
# non-word characters in the default word regex.
# Has no effect if wordRegex is set.
# * `options` (optional) {Object} with the following keys:
# * `wordRegex` A {RegExp} indicating what constitutes a "word"
# (default: {::wordRegExp})
# * `includeNonWordCharacters` A Boolean indicating whether to include
# non-word characters in the default word regex. Has no effect if
# wordRegex is set.
#
# Returns a {Range}.
getEndOfCurrentWordBufferPosition: (options = {}) ->
@@ -414,7 +502,7 @@ class Cursor extends Model
scanRange = [currentBufferPosition, @editor.getEofBufferPosition()]
endOfWordPosition = null
@editor.scanInBufferRange (options.wordRegex ? @wordRegExp(options)), scanRange, ({range, stop}) =>
@editor.scanInBufferRange (options.wordRegex ? @wordRegExp(options)), scanRange, ({range, stop}) ->
if range.start.isLessThanOrEqual(currentBufferPosition) or allowNext
endOfWordPosition = range.end
if not endOfWordPosition?.isEqual(currentBufferPosition)
@@ -424,18 +512,18 @@ class Cursor extends Model
# Public: Retrieves the buffer position of where the next word starts.
#
# options -
# :wordRegex - A {RegExp} indicating what constitutes a "word"
# (default: {::wordRegExp}).
# * `options` (optional) {Object}
# * `wordRegex` A {RegExp} indicating what constitutes a "word"
# (default: {::wordRegExp}).
#
# Returns a {Range}.
# Returns a {Range}
getBeginningOfNextWordBufferPosition: (options = {}) ->
currentBufferPosition = @getBufferPosition()
start = if @isInsideWord() then @getEndOfCurrentWordBufferPosition() else currentBufferPosition
scanRange = [start, @editor.getEofBufferPosition()]
beginningOfNextWordPosition = null
@editor.scanInBufferRange (options.wordRegex ? @wordRegExp()), scanRange, ({range, stop}) =>
@editor.scanInBufferRange (options.wordRegex ? @wordRegExp()), scanRange, ({range, stop}) ->
beginningOfNextWordPosition = range.start
stop()
@@ -443,9 +531,9 @@ class Cursor extends Model
# Public: Returns the buffer Range occupied by the word located under the cursor.
#
# options -
# :wordRegex - A {RegExp} indicating what constitutes a "word"
# (default: {::wordRegExp}).
# * `options` (optional) {Object}
# * `wordRegex` A {RegExp} indicating what constitutes a "word"
# (default: {::wordRegExp}).
getCurrentWordBufferRange: (options={}) ->
startOptions = _.extend(_.clone(options), allowPrevious: false)
endOptions = _.extend(_.clone(options), allowNext: false)
@@ -453,9 +541,9 @@ class Cursor extends Model
# Public: Returns the buffer Range for the current line.
#
# options -
# :includeNewline: - A {Boolean} which controls whether the Range should
# include the newline.
# * `options` (optional) {Object}
# * `includeNewline` A {Boolean} which controls whether the Range should
# include the newline.
getCurrentLineBufferRange: (options) ->
@editor.bufferRangeForBufferRow(@getBufferRow(), options)
@@ -477,7 +565,7 @@ class Cursor extends Model
{row, column} = eof
position = new Point(row, column - 1)
@editor.scanInBufferRange /^\n*$/g, scanRange, ({range, stop}) =>
@editor.scanInBufferRange /^\n*$/g, scanRange, ({range, stop}) ->
if !range.start.isEqual(start)
position = range.start
stop()
@@ -490,7 +578,7 @@ class Cursor extends Model
scanRange = [[row-1, column], [0,0]]
position = new Point(0, 0)
zero = new Point(0,0)
@editor.backwardsScanInBufferRange /^\n*$/g, scanRange, ({range, stop}) =>
@editor.backwardsScanInBufferRange /^\n*$/g, scanRange, ({range, stop}) ->
if !range.start.isEqual(zero)
position = range.start
stop()
@@ -533,10 +621,18 @@ class Cursor extends Model
# its current position.
hasPrecedingCharactersOnLine: ->
bufferPosition = @getBufferPosition()
line = @editor.lineForBufferRow(bufferPosition.row)
line = @editor.lineTextForBufferRow(bufferPosition.row)
firstCharacterColumn = line.search(/\S/)
if firstCharacterColumn is -1
false
else
bufferPosition.column > firstCharacterColumn
# Public: Compare this cursor's buffer position to another cursor's buffer position.
#
# See {Point::compare} for more details.
#
# * `otherCursor`{Cursor} to compare against
compare: (otherCursor) ->
@getBufferPosition().compare(otherCursor.getBufferPosition())
+112 -54
Ver Arquivo
@@ -1,10 +1,12 @@
_ = require 'underscore-plus'
{Subscriber, Emitter} = require 'emissary'
EmitterMixin = require('emissary').Emitter
{Emitter} = require 'event-kit'
Grim = require 'grim'
idCounter = 0
nextId = -> idCounter++
# Public: Represents a decoration that follows a {Marker}. A decoration is
# Essential: Represents a decoration that follows a {Marker}. A decoration is
# basically a visual representation of a marker. It allows you to add CSS
# classes to line numbers in the gutter, lines, and add selection-line regions
# around marked ranges of text.
@@ -20,88 +22,144 @@ nextId = -> idCounter++
#
# Best practice for destorying the decoration is by destroying the {Marker}.
#
# ```
# ```coffee
# marker.destroy()
# ```
#
# You should only use {Decoration::destroy} when you still need or do not own
# the marker.
#
# ### IDs
# Each {Decoration} has a unique ID available via `decoration.id`.
#
# ### Events
# A couple of events are emitted:
#
# * `destroyed`: When the {Decoration} is destroyed
# * `updated`: When the {Decoration} is updated via {Decoration::update}.
# Event object has properties `oldParams` and `newParams`
#
module.exports =
class Decoration
Emitter.includeInto(this)
EmitterMixin.includeInto(this)
@isType: (decorationParams, type) ->
if _.isArray(decorationParams.type)
type in decorationParams.type
# Extended: Check if the `decorationProperties.type` matches `type`
#
# * `decorationProperties` {Object} eg. `{type: 'gutter', class: 'my-new-class'}`
# * `type` {String} type like `'gutter'`, `'line'`, etc. `type` can also
# be an {Array} of {String}s, where it will return true if the decoration's
# type matches any in the array.
#
# Returns {Boolean}
@isType: (decorationProperties, type) ->
if _.isArray(decorationProperties.type)
type in decorationProperties.type
else
type is decorationParams.type
type is decorationProperties.type
constructor: (@marker, @displayBuffer, @params) ->
constructor: (@marker, @displayBuffer, @properties) ->
@emitter = new Emitter
@id = nextId()
@params.id = @id
@properties.id = @id
@flashQueue = null
@isDestroyed = false
@destroyed = false
# Public: Destroy this marker.
@markerDestroyDisposable = @marker.onDidDestroy => @destroy()
###
Section: Events
###
# Essential: When the {Decoration} is updated via {Decoration::update}.
#
# * `event` {Object}
# * `oldProperties` {Object} the old parameters the decoration used to have
# * `newProperties` {Object} the new parameters the decoration now has
onDidChangeProperties: (callback) ->
@emitter.on 'did-change-properties', callback
# Essential: Invoke the given callback when the {Decoration} is destroyed
onDidDestroy: (callback) ->
@emitter.on 'did-destroy', callback
###
Section: Methods
###
# Essential: An id unique across all {Decoration} objects
getId: -> @id
# Essential: Returns the marker associated with this {Decoration}
getMarker: -> @marker
# Public: Check if this decoration is of type `type`
#
# * `type` {String} type like `'gutter'`, `'line'`, etc. `type` can also
# be an {Array} of {String}s, where it will return true if the decoration's
# type matches any in the array.
#
# Returns {Boolean}
isType: (type) ->
Decoration.isType(@properties, type)
# Essential: Returns the {Decoration}'s properties.
getProperties: ->
@properties
getParams: ->
Grim.deprecate 'Use Decoration::getProperties instead'
@getProperties()
# Essential: Update the marker with new Properties. Allows you to change the decoration's class.
#
# ## Examples
#
# ```coffee
# decoration.update({type: 'gutter', class: 'my-new-class'})
# ```
#
# * `newProperties` {Object} eg. `{type: 'gutter', class: 'my-new-class'}`
setProperties: (newProperties) ->
return if @destroyed
oldProperties = @properties
@properties = newProperties
@properties.id = @id
@emit 'updated', {oldParams: oldProperties, newParams: newProperties}
@emitter.emit 'did-change-properties', {oldProperties, newProperties}
update: (newProperties) ->
Grim.deprecate 'Use Decoration::setProperties instead'
@setProperties(newProperties)
# Essential: Destroy this marker.
#
# If you own the marker, you should use {Marker::destroy} which will destroy
# this decoration.
destroy: ->
return if @isDestroyed
@isDestroyed = true
@displayBuffer.removeDecoration(this)
return if @destroyed
@markerDestroyDisposable.dispose()
@markerDestroyDisposable = null
@destroyed = true
@emit 'destroyed'
# Public: Update the marker with new params. Allows you to change the decoration's class.
#
# ```
# decoration.update({type: 'gutter', class: 'my-new-class'})
# ```
update: (newParams) ->
return if @isDestroyed
oldParams = @params
@params = newParams
@params.id = @id
@displayBuffer.decorationUpdated(this)
@emit 'updated', {oldParams, newParams}
# Public: Returns the marker associated with this {Decoration}
getMarker: -> @marker
# Public: Returns the {Decoration}'s params.
getParams: -> @params
# Public: Check if this decoration is of type `type`
#
# type - A {String} type like `'gutter'`
#
# Returns a {Boolean}
isType: (type) ->
Decoration.isType(@params, type)
@emitter.emit 'did-destroy'
@emitter.dispose()
matchesPattern: (decorationPattern) ->
return false unless decorationPattern?
for key, value of decorationPattern
return false if @params[key] != value
return false if @properties[key] != value
true
onDidFlash: (callback) ->
@emitter.on 'did-flash', callback
flash: (klass, duration=500) ->
flashObject = {class: klass, duration}
@flashQueue ?= []
@flashQueue.push(flashObject)
@emit 'flash'
@emitter.emit 'did-flash'
consumeNextFlash: ->
return @flashQueue.shift() if @flashQueue?.length > 0
null
on: (eventName) ->
switch eventName
when 'updated'
Grim.deprecate 'Use Decoration::onDidChangeProperties instead'
when 'destroyed'
Grim.deprecate 'Use Decoration::onDidDestroy instead'
when 'flash'
Grim.deprecate 'Use Decoration::onDidFlash instead'
else
Grim.deprecate 'Decoration::on is deprecated. Use event subscription methods instead.'
EmitterMixin::on.apply(this, arguments)
+7 -7
Ver Arquivo
@@ -3,7 +3,7 @@
# An instance of this class is always available as the `atom.deserializers`
# global.
#
# ### Registering a deserializer
# ## Examples
#
# ```coffee
# class MyPackageView extends View
@@ -24,21 +24,21 @@ class DeserializerManager
# Public: Register the given class(es) as deserializers.
#
# classes - One or more classes to register.
# * `classes` One or more classes to register.
add: (classes...) ->
@deserializers[klass.name] = klass for klass in classes
# Public: Remove the given class(es) as deserializers.
#
# classes - One or more classes to remove.
# * `classes` One or more classes to remove.
remove: (classes...) ->
delete @deserializers[name] for {name} in classes
# Public: Deserialize the state and params.
#
# state - The state {Object} to deserialize.
# params - The params {Object} to pass as the second arguments to the
# deserialize method of the deserializer.
# * `state` The state {Object} to deserialize.
# * `params` The params {Object} to pass as the second arguments to the
# deserialize method of the deserializer.
deserialize: (state, params) ->
return unless state?
@@ -51,7 +51,7 @@ class DeserializerManager
# Get the deserializer for the state.
#
# state - The state {Object} being deserialized.
# * `state` The state {Object} being deserialized.
get: (state) ->
return unless state?
+44 -5
Ver Arquivo
@@ -1,10 +1,13 @@
{Range} = require 'text-buffer'
_ = require 'underscore-plus'
{Emitter, Subscriber} = require 'emissary'
{Subscriber} = require 'emissary'
EmitterMixin = require('emissary').Emitter
{Emitter} = require 'event-kit'
Grim = require 'grim'
module.exports =
class DisplayBufferMarker
Emitter.includeInto(this)
EmitterMixin.includeInto(this)
Subscriber.includeInto(this)
bufferMarkerSubscription: null
@@ -13,8 +16,10 @@ class DisplayBufferMarker
oldTailBufferPosition: null
oldTailScreenPosition: null
wasValid: true
deferredChangeEvents: null
constructor: ({@bufferMarker, @displayBuffer}) ->
@emitter = new Emitter
@id = @bufferMarker.id
@oldHeadBufferPosition = @getHeadBufferPosition()
@oldHeadScreenPosition = @getHeadScreenPosition()
@@ -22,8 +27,23 @@ class DisplayBufferMarker
@oldTailScreenPosition = @getTailScreenPosition()
@wasValid = @isValid()
@subscribe @bufferMarker, 'destroyed', => @destroyed()
@subscribe @bufferMarker, 'changed', (event) => @notifyObservers(event)
@subscribe @bufferMarker.onDidDestroy => @destroyed()
@subscribe @bufferMarker.onDidChange (event) => @notifyObservers(event)
onDidChange: (callback) ->
@emitter.on 'did-change', callback
onDidDestroy: (callback) ->
@emitter.on 'did-destroy', callback
on: (eventName) ->
switch eventName
when 'changed'
Grim.deprecate("Use DisplayBufferMarker::onDidChange instead")
when 'destroyed'
Grim.deprecate("Use DisplayBufferMarker::onDidDestroy instead")
EmitterMixin::on.apply(this, arguments)
copy: (attributes) ->
@displayBuffer.getMarker(@bufferMarker.copy(attributes).id)
@@ -199,6 +219,8 @@ class DisplayBufferMarker
destroyed: ->
delete @displayBuffer.markers[@id]
@emit 'destroyed'
@emitter.emit 'did-destroy'
@emitter.dispose()
notifyObservers: ({textChanged}) ->
textChanged ?= false
@@ -215,7 +237,7 @@ class DisplayBufferMarker
_.isEqual(newTailBufferPosition, @oldTailBufferPosition) and
_.isEqual(newTailScreenPosition, @oldTailScreenPosition)
@emit 'changed', {
changeEvent = {
@oldHeadScreenPosition, newHeadScreenPosition,
@oldTailScreenPosition, newTailScreenPosition,
@oldHeadBufferPosition, newHeadBufferPosition,
@@ -224,8 +246,25 @@ class DisplayBufferMarker
isValid
}
if @deferredChangeEvents?
@deferredChangeEvents.push(changeEvent)
else
@emit 'changed', changeEvent
@emitter.emit 'did-change', changeEvent
@oldHeadBufferPosition = newHeadBufferPosition
@oldHeadScreenPosition = newHeadScreenPosition
@oldTailBufferPosition = newTailBufferPosition
@oldTailScreenPosition = newTailScreenPosition
@wasValid = isValid
pauseChangeEvents: ->
@deferredChangeEvents = []
resumeChangeEvents: ->
if deferredChangeEvents = @deferredChangeEvents
@deferredChangeEvents = null
for event in deferredChangeEvents
@emit 'changed', event
@emitter.emit 'did-change', event
+135 -87
Ver Arquivo
@@ -1,8 +1,9 @@
_ = require 'underscore-plus'
{Emitter} = require 'emissary'
EmitterMixin = require('emissary').Emitter
guid = require 'guid'
Serializable = require 'serializable'
{Model} = require 'theorist'
{Emitter} = require 'event-kit'
{Point, Range} = require 'text-buffer'
TokenizedBuffer = require './tokenized-buffer'
RowMap = require './row-map'
@@ -10,6 +11,7 @@ Fold = require './fold'
Token = require './token'
Decoration = require './decoration'
DisplayBufferMarker = require './display-buffer-marker'
Grim = require 'grim'
class BufferToScreenConversionError extends Error
constructor: (@message, @metadata) ->
@@ -22,7 +24,7 @@ class DisplayBuffer extends Model
@properties
manageScrollPosition: false
softWrap: null
softWrapped: null
editorWidthInChars: null
lineHeightInPixels: null
defaultCharWidth: null
@@ -31,16 +33,19 @@ class DisplayBuffer extends Model
scrollTop: 0
scrollLeft: 0
scrollWidth: 0
verticalScrollbarWidth: 15
horizontalScrollbarHeight: 15
verticalScrollMargin: 2
horizontalScrollMargin: 6
horizontalScrollbarHeight: 15
verticalScrollbarWidth: 15
scopedCharacterWidthsChangeCount: 0
constructor: ({tabLength, @editorWidthInChars, @tokenizedBuffer, buffer, @invisibles}={}) ->
super
@softWrap ?= atom.config.get('editor.softWrap') ? false
@emitter = new Emitter
@softWrapped ?= atom.config.get('editor.softWrapped') ? false
@tokenizedBuffer ?= new TokenizedBuffer({tabLength, buffer, @invisibles})
@buffer = @tokenizedBuffer.buffer
@charWidthsByScope = {}
@@ -48,33 +53,28 @@ class DisplayBuffer extends Model
@foldsByMarkerId = {}
@decorationsById = {}
@decorationsByMarkerId = {}
@decorationMarkerChangedSubscriptions = {}
@decorationMarkerDestroyedSubscriptions = {}
@updateAllScreenLines()
@createFoldForMarker(marker) for marker in @buffer.findMarkers(@getFoldMarkerAttributes())
@subscribe @tokenizedBuffer, 'grammar-changed', (grammar) => @emit 'grammar-changed', grammar
@subscribe @tokenizedBuffer, 'tokenized', => @emit 'tokenized'
@subscribe @tokenizedBuffer, 'changed', @handleTokenizedBufferChange
@subscribe @buffer, 'markers-updated', @handleBufferMarkersUpdated
@subscribe @buffer, 'marker-created', @handleBufferMarkerCreated
@subscribe @$softWrap, (softWrap) =>
@emit 'soft-wrap-changed', softWrap
@updateWrappedScreenLines()
@subscribe @tokenizedBuffer.onDidChange @handleTokenizedBufferChange
@subscribe @buffer.onDidUpdateMarkers @handleBufferMarkersUpdated
@subscribe @buffer.onDidCreateMarker @handleBufferMarkerCreated
@subscribe atom.config.observe 'editor.preferredLineLength', callNow: false, =>
@updateWrappedScreenLines() if @softWrap and atom.config.get('editor.softWrapAtPreferredLineLength')
@updateWrappedScreenLines() if @isSoftWrapped() and atom.config.get('editor.softWrapAtPreferredLineLength')
@subscribe atom.config.observe 'editor.softWrapAtPreferredLineLength', callNow: false, =>
@updateWrappedScreenLines() if @softWrap
@updateWrappedScreenLines() if @isSoftWrapped()
@updateAllScreenLines()
serializeParams: ->
id: @id
softWrap: @softWrap
softWrapped: @isSoftWrapped()
editorWidthInChars: @editorWidthInChars
scrollTop: @scrollTop
scrollLeft: @scrollLeft
tokenizedBuffer: @tokenizedBuffer.serialize()
invisibles: _.clone(@invisibles)
deserializeParams: (params) ->
params.tokenizedBuffer = TokenizedBuffer.deserialize(params.tokenizedBuffer)
@@ -95,12 +95,71 @@ class DisplayBuffer extends Model
@rowMap = new RowMap
@updateScreenLines(0, @buffer.getLineCount(), null, suppressChangeEvent: true)
emitChanged: (eventProperties, refreshMarkers=true) ->
onDidChangeSoftWrapped: (callback) ->
@emitter.on 'did-change-soft-wrapped', callback
onDidChangeGrammar: (callback) ->
@tokenizedBuffer.onDidChangeGrammar(callback)
onDidTokenize: (callback) ->
@tokenizedBuffer.onDidTokenize(callback)
onDidChange: (callback) ->
@emitter.on 'did-change', callback
onDidChangeCharacterWidths: (callback) ->
@emitter.on 'did-change-character-widths', callback
observeDecorations: (callback) ->
callback(decoration) for decoration in @getDecorations()
@onDidAddDecoration(callback)
onDidAddDecoration: (callback) ->
@emitter.on 'did-add-decoration', callback
onDidRemoveDecoration: (callback) ->
@emitter.on 'did-remove-decoration', callback
onDidCreateMarker: (callback) ->
@emitter.on 'did-create-marker', callback
onDidUpdateMarkers: (callback) ->
@emitter.on 'did-update-markers', callback
on: (eventName) ->
switch eventName
when 'changed'
Grim.deprecate("Use DisplayBuffer::onDidChange instead")
when 'grammar-changed'
Grim.deprecate("Use DisplayBuffer::onDidChangeGrammar instead")
when 'soft-wrap-changed'
Grim.deprecate("Use DisplayBuffer::onDidChangeSoftWrap instead")
when 'character-widths-changed'
Grim.deprecate("Use DisplayBuffer::onDidChangeCharacterWidths instead")
when 'decoration-added'
Grim.deprecate("Use DisplayBuffer::onDidAddDecoration instead")
when 'decoration-removed'
Grim.deprecate("Use DisplayBuffer::onDidRemoveDecoration instead")
when 'decoration-changed'
Grim.deprecate("Use decoration.getMarker().onDidChange() instead")
when 'decoration-updated'
Grim.deprecate("Use Decoration::onDidChangeProperties instead")
when 'marker-created'
Grim.deprecate("Use Decoration::onDidCreateMarker instead")
when 'markers-updated'
Grim.deprecate("Use Decoration::onDidUpdateMarkers instead")
else
Grim.deprecate("DisplayBuffer::on is deprecated. Use event subscription methods instead.")
EmitterMixin::on.apply(this, arguments)
emitDidChange: (eventProperties, refreshMarkers=true) ->
if refreshMarkers
@pauseMarkerObservers()
@pauseMarkerChangeEvents()
@refreshMarkerScreenPositions()
@emit 'changed', eventProperties
@resumeMarkerObservers()
@emitter.emit 'did-change', eventProperties
@resumeMarkerChangeEvents()
updateWrappedScreenLines: ->
start = 0
@@ -108,7 +167,7 @@ class DisplayBuffer extends Model
@updateAllScreenLines()
screenDelta = @getLastRow() - end
bufferDelta = 0
@emitChanged({ start, end, screenDelta, bufferDelta })
@emitDidChange({ start, end, screenDelta, bufferDelta })
# Sets the visibility of the tokenized buffer.
#
@@ -152,7 +211,7 @@ class DisplayBuffer extends Model
horizontallyScrollable: (reentrant) ->
return false unless @width?
return false if @getSoftWrap()
return false if @isSoftWrapped()
if reentrant
@getScrollWidth() > @getWidth()
else
@@ -177,7 +236,7 @@ class DisplayBuffer extends Model
setWidth: (newWidth) ->
oldWidth = @width
@width = newWidth
@updateWrappedScreenLines() if newWidth isnt oldWidth and @softWrap
@updateWrappedScreenLines() if newWidth isnt oldWidth and @isSoftWrapped()
@setScrollTop(@getScrollTop()) # Ensure scrollTop is still valid in case horizontal scrollbar disappeared
@width
@@ -250,6 +309,7 @@ class DisplayBuffer extends Model
characterWidthsChanged: ->
@computeScrollWidth()
@emit 'character-widths-changed', @scopedCharacterWidthsChangeCount
@emitter.emit 'did-change-character-widths', @scopedCharacterWidthsChangeCount
clearScopedCharWidths: ->
@charWidthsByScope = {}
@@ -343,11 +403,15 @@ class DisplayBuffer extends Model
setInvisibles: (@invisibles) ->
@tokenizedBuffer.setInvisibles(@invisibles)
# Deprecated: Use the softWrap property directly
setSoftWrap: (@softWrap) -> @softWrap
setSoftWrapped: (softWrapped) ->
if softWrapped isnt @softWrapped
@softWrapped = softWrapped
@updateWrappedScreenLines()
@emit 'soft-wrap-changed', @softWrapped
@emitter.emit 'did-change-soft-wrapped', @softWrapped
@softWrapped
# Deprecated: Use the softWrap property directly
getSoftWrap: -> @softWrap
isSoftWrapped: -> @softWrapped
# Set the number of characters that fit horizontally in the editor.
#
@@ -356,7 +420,7 @@ class DisplayBuffer extends Model
if editorWidthInChars > 0
previousWidthInChars = @editorWidthInChars
@editorWidthInChars = editorWidthInChars
if editorWidthInChars isnt previousWidthInChars and @softWrap
if editorWidthInChars isnt previousWidthInChars and @isSoftWrapped()
@updateWrappedScreenLines()
# Returns the editor width in characters for soft wrap.
@@ -376,25 +440,25 @@ class DisplayBuffer extends Model
# Gets the screen line for the given screen row.
#
# screenRow - A {Number} indicating the screen row.
# * `screenRow` - A {Number} indicating the screen row.
#
# Returns a {ScreenLine}.
lineForRow: (row) ->
@screenLines[row]
# Returns {TokenizedLine}
tokenizedLineForScreenRow: (screenRow) ->
@screenLines[screenRow]
# Gets the screen lines for the given screen row range.
#
# startRow - A {Number} indicating the beginning screen row.
# endRow - A {Number} indicating the ending screen row.
#
# Returns an {Array} of {ScreenLine}s.
linesForRows: (startRow, endRow) ->
# Returns an {Array} of {TokenizedLine}s.
tokenizedLinesForScreenRows: (startRow, endRow) ->
@screenLines[startRow..endRow]
# Gets all the screen lines.
#
# Returns an {Array} of {ScreenLines}s.
getLines: ->
# Returns an {Array} of {TokenizedLine}s.
getTokenizedLines: ->
new Array(@screenLines...)
indentLevelForLine: (line) ->
@@ -554,7 +618,7 @@ class DisplayBuffer extends Model
top = targetRow * @lineHeightInPixels
left = 0
column = 0
for token in @lineForRow(targetRow).tokens
for token in @tokenizedLineForScreenRow(targetRow).tokens
charWidths = @getScopedCharWidths(token.scopes)
for char in token.value
return {top, left} if column is targetColumn
@@ -572,7 +636,7 @@ class DisplayBuffer extends Model
left = 0
column = 0
for token in @lineForRow(row).tokens
for token in @tokenizedLineForScreenRow(row).tokens
charWidths = @getScopedCharWidths(token.scopes)
for char in token.value
charWidth = charWidths[char] ? defaultCharWidth
@@ -626,7 +690,7 @@ class DisplayBuffer extends Model
unless screenLine?
throw new BufferToScreenConversionError "No screen line exists when converting buffer row to screen row",
softWrapEnabled: @getSoftWrap()
softWrapEnabled: @isSoftWrapped()
foldCount: @findFoldMarkers().length
lastBufferRow: @buffer.getLastRow()
lastScreenRow: @getLastRow()
@@ -742,7 +806,7 @@ class DisplayBuffer extends Model
# Returns a {Number} representing the `line` position where the wrap would take place.
# Returns `null` if a wrap wouldn't occur.
findWrapColumn: (line, softWrapColumn=@getSoftWrapColumn()) ->
return unless @softWrap
return unless @isSoftWrapped()
return unless line.length > softWrapColumn
if /\s/.test(line[softWrapColumn])
@@ -765,6 +829,12 @@ class DisplayBuffer extends Model
decorationForId: (id) ->
@decorationsById[id]
getDecorations: ->
allDecorations = []
for markerId, decorations of @decorationsByMarkerId
allDecorations = allDecorations.concat(decorations) if decorations?
allDecorations
decorationsForScreenRowRange: (startScreenRow, endScreenRow) ->
decorationsByMarkerId = {}
for marker in @findMarkers(intersectsScreenRowRange: [startScreenRow, endScreenRow])
@@ -774,24 +844,13 @@ class DisplayBuffer extends Model
decorateMarker: (marker, decorationParams) ->
marker = @getMarker(marker.id)
@decorationMarkerDestroyedSubscriptions[marker.id] ?= @subscribe marker, 'destroyed', =>
@removeAllDecorationsForMarker(marker)
@decorationMarkerChangedSubscriptions[marker.id] ?= @subscribe marker, 'changed', (event) =>
decorations = @decorationsByMarkerId[marker.id]
# Why check existence? Markers may get destroyed or decorations removed
# in the change handler. Bookmarks does this.
if decorations?
for decoration in decorations
@emit 'decoration-changed', marker, decoration, event
decoration = new Decoration(marker, this, decorationParams)
@subscribe decoration.onDidDestroy => @removeDecoration(decoration)
@decorationsByMarkerId[marker.id] ?= []
@decorationsByMarkerId[marker.id].push(decoration)
@decorationsById[decoration.id] = decoration
@emit 'decoration-added', marker, decoration
@emit 'decoration-added', decoration
@emitter.emit 'did-add-decoration', decoration
decoration
removeDecoration: (decoration) ->
@@ -802,25 +861,9 @@ class DisplayBuffer extends Model
if index > -1
decorations.splice(index, 1)
delete @decorationsById[decoration.id]
@emit 'decoration-removed', marker, decoration
@removedAllMarkerDecorations(marker) if decorations.length is 0
removeAllDecorationsForMarker: (marker) ->
decorations = @decorationsByMarkerId[marker.id].slice()
for decoration in decorations
@emit 'decoration-removed', marker, decoration
@removedAllMarkerDecorations(marker)
removedAllMarkerDecorations: (marker) ->
@decorationMarkerChangedSubscriptions[marker.id].off()
@decorationMarkerDestroyedSubscriptions[marker.id].off()
delete @decorationsByMarkerId[marker.id]
delete @decorationMarkerChangedSubscriptions[marker.id]
delete @decorationMarkerDestroyedSubscriptions[marker.id]
decorationUpdated: (decoration) ->
@emit 'decoration-updated', decoration
@emit 'decoration-removed', decoration
@emitter.emit 'did-remove-decoration', decoration
delete @decorationsByMarkerId[marker.id] if decorations.length is 0
# Retrieves a {DisplayBufferMarker} based on its id.
#
@@ -964,12 +1007,13 @@ class DisplayBuffer extends Model
getFoldMarkerAttributes: (attributes={}) ->
_.extend(attributes, class: 'fold', displayBufferId: @id)
pauseMarkerObservers: ->
marker.pauseEvents() for marker in @getMarkers()
pauseMarkerChangeEvents: ->
marker.pauseChangeEvents() for marker in @getMarkers()
resumeMarkerObservers: ->
marker.resumeEvents() for marker in @getMarkers()
resumeMarkerChangeEvents: ->
marker.resumeChangeEvents() for marker in @getMarkers()
@emit 'markers-updated'
@emitter.emit 'did-update-markers'
refreshMarkerScreenPositions: ->
for marker in @getMarkers()
@@ -980,9 +1024,9 @@ class DisplayBuffer extends Model
@tokenizedBuffer.destroy()
@unsubscribe()
logLines: (start=0, end=@getLastRow())->
logLines: (start=0, end=@getLastRow()) ->
for row in [start..end]
line = @lineForRow(row).text
line = @tokenizedLineForScreenRow(row).text
console.log row, @bufferRowForScreenRow(row), line, line.length
handleTokenizedBufferChange: (tokenizedBufferChange) =>
@@ -1011,10 +1055,10 @@ class DisplayBuffer extends Model
bufferDelta: bufferDelta
if options.delayChangeEvent
@pauseMarkerObservers()
@pauseMarkerChangeEvents()
@pendingChangeEvent = changeEvent
else
@emitChanged(changeEvent, options.refreshMarkers)
@emitDidChange(changeEvent, options.refreshMarkers)
buildScreenLines: (startBufferRow, endBufferRow) ->
screenLines = []
@@ -1023,7 +1067,7 @@ class DisplayBuffer extends Model
bufferRow = startBufferRow
while bufferRow < endBufferRow
tokenizedLine = @tokenizedBuffer.lineForScreenRow(bufferRow)
tokenizedLine = @tokenizedBuffer.tokenizedLineForRow(bufferRow)
if fold = @largestFoldStartingAtBufferRow(bufferRow)
foldLine = tokenizedLine.copy()
@@ -1086,17 +1130,21 @@ class DisplayBuffer extends Model
computeScrollWidth: ->
@scrollWidth = @pixelPositionForScreenPosition([@longestScreenRow, @maxLineLength]).left
@scrollWidth += 1 unless @getSoftWrap()
@scrollWidth += 1 unless @isSoftWrapped()
@setScrollLeft(Math.min(@getScrollLeft(), @getMaxScrollLeft()))
handleBufferMarkersUpdated: =>
if event = @pendingChangeEvent
@pendingChangeEvent = null
@emitChanged(event, false)
@emitDidChange(event, false)
handleBufferMarkerCreated: (marker) =>
@createFoldForMarker(marker) if marker.matchesAttributes(@getFoldMarkerAttributes())
@emit 'marker-created', @getMarker(marker.id)
if displayBufferMarker = @getMarker(marker.id)
# The marker might have been removed in some other handler called before
# this one. Only emit when the marker still exists.
@emit 'marker-created', displayBufferMarker
@emitter.emit 'did-create-marker', displayBufferMarker
createFoldForMarker: (marker) ->
@decorateMarker(marker, type: 'gutter', class: 'folded')
+255 -186
Ver Arquivo
@@ -1,3 +1,4 @@
_ = require 'underscore-plus'
React = require 'react-atom-fork'
{div, span} = require 'reactionary-atom-fork'
{debounce, defaults, isEqualForProperties} = require 'underscore-plus'
@@ -30,29 +31,27 @@ EditorComponent = React.createClass
updateRequested: false
updatesPaused: false
updateRequestedWhilePaused: false
cursorsMoved: false
cursorMoved: false
selectionChanged: false
selectionAdded: false
scrollingVertically: false
refreshingScrollbars: false
measuringScrollbars: true
mouseWheelScreenRow: null
mouseWheelScreenRowClearDelay: 150
scrollSensitivity: 0.4
heightAndWidthMeasurementRequested: false
measureLineHeightAndDefaultCharWidthWhenShown: false
remeasureCharacterWidthsWhenShown: false
inputEnabled: true
scopedCharacterWidthsChangeCount: null
domPollingInterval: 100
domPollingIntervalId: null
domPollingPaused: false
measureScrollbarsWhenShown: true
measureLineHeightAndDefaultCharWidthWhenShown: true
remeasureCharacterWidthsWhenShown: false
render: ->
{focused, showIndentGuide, showLineNumbers, visible} = @state
{editor, mini, cursorBlinkPeriod, cursorBlinkResumeDelay} = @props
maxLineNumberDigits = editor.getLineCount().toString().length
hasSelection = editor.getSelection()? and !editor.getSelection().isEmpty()
hasSelection = editor.getLastSelection()? and !editor.getLastSelection().isEmpty()
style = {}
if @performedInitialMeasurement
@@ -60,10 +59,13 @@ EditorComponent = React.createClass
[renderedStartRow, renderedEndRow] = renderedRowRange
cursorPixelRects = @getCursorPixelRects(renderedRowRange)
tokenizedLines = editor.tokenizedLinesForScreenRows(renderedStartRow, renderedEndRow - 1)
decorations = editor.decorationsForScreenRowRange(renderedStartRow, renderedEndRow)
highlightDecorations = @getHighlightDecorations(decorations)
lineDecorations = @getLineDecorations(decorations)
placeholderText = @props.placeholderText if @props.placeholderText? and editor.isEmpty()
visible = @isVisible()
scrollHeight = editor.getScrollHeight()
scrollWidth = editor.getScrollWidth()
@@ -107,10 +109,10 @@ EditorComponent = React.createClass
LinesComponent {
ref: 'lines',
editor, lineHeightInPixels, defaultCharWidth, lineDecorations, highlightDecorations,
editor, lineHeightInPixels, defaultCharWidth, tokenizedLines, lineDecorations, highlightDecorations,
showIndentGuide, renderedRowRange, @pendingChanges, scrollTop, scrollLeft,
@scrollingVertically, scrollHeight, scrollWidth, mouseWheelScreenRow,
@visible, scrollViewHeight, @scopedCharacterWidthsChangeCount, lineWidth, @useHardwareAcceleration,
visible, scrollViewHeight, @scopedCharacterWidthsChangeCount, lineWidth, @useHardwareAcceleration,
placeholderText, @performedInitialMeasurement, @backgroundColor, cursorPixelRects,
cursorBlinkPeriod, cursorBlinkResumeDelay, mini
}
@@ -122,7 +124,7 @@ EditorComponent = React.createClass
onScroll: @onHorizontalScroll
scrollLeft: scrollLeft
scrollWidth: scrollWidth
visible: horizontallyScrollable and not @refreshingScrollbars and not @measuringScrollbars
visible: horizontallyScrollable
scrollableInOppositeDirection: verticallyScrollable
verticalScrollbarWidth: verticalScrollbarWidth
horizontalScrollbarHeight: horizontalScrollbarHeight
@@ -134,7 +136,7 @@ EditorComponent = React.createClass
onScroll: @onVerticalScroll
scrollTop: scrollTop
scrollHeight: scrollHeight
visible: verticallyScrollable and not @refreshingScrollbars and not @measuringScrollbars
visible: verticallyScrollable
scrollableInOppositeDirection: horizontallyScrollable
verticalScrollbarWidth: verticalScrollbarWidth
horizontalScrollbarHeight: horizontalScrollbarHeight
@@ -142,7 +144,7 @@ EditorComponent = React.createClass
# Also used to measure the height/width of scrollbars after the initial render
ScrollbarCornerComponent
ref: 'scrollbarCorner'
visible: not @refreshingScrollbars and (@measuringScrollbars or horizontallyScrollable and verticallyScrollable)
visible: horizontallyScrollable and verticallyScrollable
measuringScrollbars: @measuringScrollbars
height: horizontalScrollbarHeight
width: verticalScrollbarWidth
@@ -170,8 +172,6 @@ EditorComponent = React.createClass
componentDidMount: ->
{editor} = @props
@domPollingIntervalId = setInterval(@pollDOM, @domPollingInterval)
@observeEditor()
@listenForDOMEvents()
@listenForCommands()
@@ -179,55 +179,47 @@ EditorComponent = React.createClass
@subscribe atom.themes, 'stylesheet-added stylesheet-removed stylesheet-updated', @onStylesheetsChanged
@subscribe scrollbarStyle.changes, @refreshScrollbars
if @visible = @isVisible()
@performInitialMeasurement()
@forceUpdate()
@domPollingIntervalId = setInterval(@pollDOM, @domPollingInterval)
@checkForVisibilityChange()
componentWillUnmount: ->
@props.parentView.trigger 'editor:will-be-removed', [@props.parentView]
{editor, parentView} = @props
parentView.trigger 'editor:will-be-removed', [parentView]
@unsubscribe()
window.removeEventListener 'resize', @requestHeightAndWidthMeasurement
clearInterval(@domPollingIntervalId)
@domPollingIntervalId = null
@props.editor.destroy()
componentWillReceiveProps: (newProps) ->
@props.editor.setMini(newProps.mini)
componentWillUpdate: ->
wasVisible = @visible
@visible = @isVisible()
@performInitialMeasurement() if @visible and not wasVisible
componentDidUpdate: (prevProps, prevState) ->
cursorsMoved = @cursorsMoved
cursorMoved = @cursorMoved
selectionChanged = @selectionChanged
@pendingChanges.length = 0
@cursorsMoved = false
@cursorMoved = false
@selectionChanged = false
@refreshingScrollbars = false
if @props.editor.isAlive()
@updateParentViewFocusedClassIfNeeded(prevState)
@updateParentViewMiniClassIfNeeded(prevState)
@props.parentView.trigger 'cursor:moved' if cursorsMoved
@props.parentView.trigger 'cursor:moved' if cursorMoved
@props.parentView.trigger 'selection:changed' if selectionChanged
@props.parentView.trigger 'editor:display-updated'
if @performedInitialMeasurement
@measureScrollbars() if @measuringScrollbars
performInitialMeasurement: ->
becameVisible: ->
@updatesPaused = true
@measureHeightAndWidth()
@sampleFontStyling()
@sampleBackgroundColors()
@measureScrollbars()
@measureHeightAndWidth()
@measureScrollbars() if @measureScrollbarsWhenShown
@measureLineHeightAndDefaultCharWidth() if @measureLineHeightAndDefaultCharWidthWhenShown
@remeasureCharacterWidths() if @remeasureCharacterWidthsWhenShown
@props.editor.setVisible(true)
@updatesPaused = false
@performedInitialMeasurement = true
@updatesPaused = false
@forceUpdate() if @updateRequestedWhilePaused
requestUpdate: ->
return unless @isMounted()
@@ -267,9 +259,9 @@ EditorComponent = React.createClass
getHiddenInputPosition: ->
{editor} = @props
{focused} = @state
return {top: 0, left: 0} unless @isMounted() and focused and editor.getCursor()?
return {top: 0, left: 0} unless @isMounted() and focused and editor.getLastCursor()?
{top, left, height, width} = editor.getCursor().getPixelRect()
{top, left, height, width} = editor.getLastCursor().getPixelRect()
width = 2 if width is 0 # Prevent autoscroll at the end of longest line
top -= editor.getScrollTop()
left -= editor.getScrollLeft()
@@ -313,7 +305,7 @@ EditorComponent = React.createClass
if marker.isValid()
for decoration in decorations
if decoration.isType('gutter') or decoration.isType('line')
decorationParams = decoration.getParams()
decorationParams = decoration.getProperties()
screenRange ?= marker.getScreenRange()
headScreenRow ?= marker.getHeadScreenPosition().row
startRow = screenRange.start.row
@@ -340,7 +332,7 @@ EditorComponent = React.createClass
if marker.isValid() and not screenRange.isEmpty()
for decoration in decorations
if decoration.isType('highlight')
decorationParams = decoration.getParams()
decorationParams = decoration.getProperties()
filteredDecorations[markerId] ?=
id: markerId
startPixelPosition: editor.pixelPositionForScreenPosition(screenRange.start)
@@ -352,17 +344,16 @@ EditorComponent = React.createClass
observeEditor: ->
{editor} = @props
@subscribe editor, 'screen-lines-changed', @onScreenLinesChanged
@subscribe editor, 'cursors-moved', @onCursorsMoved
@subscribe editor, 'selection-removed selection-screen-range-changed', @onSelectionChanged
@subscribe editor, 'selection-added', @onSelectionAdded
@subscribe editor, 'decoration-added', @onDecorationChanged
@subscribe editor, 'decoration-removed', @onDecorationChanged
@subscribe editor, 'decoration-changed', @onDecorationChanged
@subscribe editor, 'decoration-updated', @onDecorationChanged
@subscribe editor, 'character-widths-changed', @onCharacterWidthsChanged
@subscribe editor.onDidChangeScreenLines(@onScreenLinesChanged)
@subscribe editor.observeCursors(@onCursorAdded)
@subscribe editor.observeSelections(@onSelectionAdded)
@subscribe editor.observeDecorations(@onDecorationAdded)
@subscribe editor.onDidRemoveDecoration(@onDecorationRemoved)
@subscribe editor.onDidChangeCharacterWidths(@onCharacterWidthsChanged)
@subscribe editor.$scrollTop.changes, @onScrollTopChanged
@subscribe editor.$scrollLeft.changes, @requestUpdate
@subscribe editor.$verticalScrollbarWidth.changes, @requestUpdate
@subscribe editor.$horizontalScrollbarHeight.changes, @requestUpdate
@subscribe editor.$height.changes, @requestUpdate
@subscribe editor.$width.changes, @requestUpdate
@subscribe editor.$defaultCharWidth.changes, @requestUpdate
@@ -410,105 +401,105 @@ EditorComponent = React.createClass
{parentView, editor, mini} = @props
@addCommandListeners
'core:move-left': => editor.moveCursorLeft()
'core:move-right': => editor.moveCursorRight()
'core:select-left': => editor.selectLeft()
'core:select-right': => editor.selectRight()
'core:select-all': => editor.selectAll()
'core:backspace': => editor.backspace()
'core:delete': => editor.delete()
'core:undo': => editor.undo()
'core:redo': => editor.redo()
'core:cut': => editor.cutSelectedText()
'core:copy': => editor.copySelectedText()
'core:paste': => editor.pasteText()
'editor:move-to-previous-word': => editor.moveCursorToPreviousWord()
'editor:select-word': => editor.selectWord()
'core:move-left': -> editor.moveLeft()
'core:move-right': -> editor.moveRight()
'core:select-left': -> editor.selectLeft()
'core:select-right': -> editor.selectRight()
'core:select-all': -> editor.selectAll()
'core:backspace': -> editor.backspace()
'core:delete': -> editor.delete()
'core:undo': -> editor.undo()
'core:redo': -> editor.redo()
'core:cut': -> editor.cutSelectedText()
'core:copy': -> editor.copySelectedText()
'core:paste': -> editor.pasteText()
'editor:move-to-previous-word': -> editor.moveToPreviousWord()
'editor:select-word': -> editor.selectWordsContainingCursors()
'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()
'editor:move-to-beginning-of-next-paragraph': => editor.moveCursorToBeginningOfNextParagraph()
'editor:move-to-beginning-of-previous-paragraph': => editor.moveCursorToBeginningOfPreviousParagraph()
'editor:move-to-beginning-of-screen-line': => editor.moveCursorToBeginningOfScreenLine()
'editor:move-to-beginning-of-line': => editor.moveCursorToBeginningOfLine()
'editor:move-to-end-of-screen-line': => editor.moveCursorToEndOfScreenLine()
'editor:move-to-end-of-line': => editor.moveCursorToEndOfLine()
'editor:move-to-first-character-of-line': => editor.moveCursorToFirstCharacterOfLine()
'editor:move-to-beginning-of-word': => editor.moveCursorToBeginningOfWord()
'editor:move-to-end-of-word': => editor.moveCursorToEndOfWord()
'editor:move-to-beginning-of-next-word': => editor.moveCursorToBeginningOfNextWord()
'editor:move-to-previous-word-boundary': => editor.moveCursorToPreviousWordBoundary()
'editor:move-to-next-word-boundary': => editor.moveCursorToNextWordBoundary()
'editor:select-to-beginning-of-next-paragraph': => editor.selectToBeginningOfNextParagraph()
'editor:select-to-beginning-of-previous-paragraph': => editor.selectToBeginningOfPreviousParagraph()
'editor:select-to-end-of-line': => editor.selectToEndOfLine()
'editor:select-to-beginning-of-line': => editor.selectToBeginningOfLine()
'editor:select-to-end-of-word': => editor.selectToEndOfWord()
'editor:select-to-beginning-of-word': => editor.selectToBeginningOfWord()
'editor:select-to-beginning-of-next-word': => editor.selectToBeginningOfNextWord()
'editor:select-to-next-word-boundary': => editor.selectToNextWordBoundary()
'editor:select-to-previous-word-boundary': => editor.selectToPreviousWordBoundary()
'editor:select-to-first-character-of-line': => editor.selectToFirstCharacterOfLine()
'editor:select-line': => editor.selectLine()
'editor:transpose': => editor.transpose()
'editor:upper-case': => editor.upperCase()
'editor:lower-case': => editor.lowerCase()
'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()
'editor:move-to-beginning-of-next-paragraph': -> editor.moveToBeginningOfNextParagraph()
'editor:move-to-beginning-of-previous-paragraph': -> editor.moveToBeginningOfPreviousParagraph()
'editor:move-to-beginning-of-screen-line': -> editor.moveToBeginningOfScreenLine()
'editor:move-to-beginning-of-line': -> editor.moveToBeginningOfLine()
'editor:move-to-end-of-screen-line': -> editor.moveToEndOfScreenLine()
'editor:move-to-end-of-line': -> editor.moveToEndOfLine()
'editor:move-to-first-character-of-line': -> editor.moveToFirstCharacterOfLine()
'editor:move-to-beginning-of-word': -> editor.moveToBeginningOfWord()
'editor:move-to-end-of-word': -> editor.moveToEndOfWord()
'editor:move-to-beginning-of-next-word': -> editor.moveToBeginningOfNextWord()
'editor:move-to-previous-word-boundary': -> editor.moveToPreviousWordBoundary()
'editor:move-to-next-word-boundary': -> editor.moveToNextWordBoundary()
'editor:select-to-beginning-of-next-paragraph': -> editor.selectToBeginningOfNextParagraph()
'editor:select-to-beginning-of-previous-paragraph': -> editor.selectToBeginningOfPreviousParagraph()
'editor:select-to-end-of-line': -> editor.selectToEndOfLine()
'editor:select-to-beginning-of-line': -> editor.selectToBeginningOfLine()
'editor:select-to-end-of-word': -> editor.selectToEndOfWord()
'editor:select-to-beginning-of-word': -> editor.selectToBeginningOfWord()
'editor:select-to-beginning-of-next-word': -> editor.selectToBeginningOfNextWord()
'editor:select-to-next-word-boundary': -> editor.selectToNextWordBoundary()
'editor:select-to-previous-word-boundary': -> editor.selectToPreviousWordBoundary()
'editor:select-to-first-character-of-line': -> editor.selectToFirstCharacterOfLine()
'editor:select-line': -> editor.selectLinesContainingCursors()
'editor:transpose': -> editor.transpose()
'editor:upper-case': -> editor.upperCase()
'editor:lower-case': -> editor.lowerCase()
unless mini
@addCommandListeners
'core:move-up': => editor.moveCursorUp()
'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()
'editor:outdent-selected-rows': => editor.outdentSelectedRows()
'editor:newline': => editor.insertNewline()
'editor:newline-below': => editor.insertNewlineBelow()
'editor:newline-above': => editor.insertNewlineAbove()
'editor:add-selection-below': => editor.addSelectionBelow()
'editor:add-selection-above': => editor.addSelectionAbove()
'editor:split-selections-into-lines': => editor.splitSelectionsIntoLines()
'editor:toggle-soft-tabs': => editor.toggleSoftTabs()
'editor:toggle-soft-wrap': => editor.toggleSoftWrap()
'editor:fold-all': => editor.foldAll()
'editor:unfold-all': => editor.unfoldAll()
'editor:fold-current-row': => editor.foldCurrentRow()
'editor:unfold-current-row': => editor.unfoldCurrentRow()
'editor:fold-selection': => editor.foldSelectedLines()
'editor:fold-at-indent-level-1': => editor.foldAllAtIndentLevel(0)
'editor:fold-at-indent-level-2': => editor.foldAllAtIndentLevel(1)
'editor:fold-at-indent-level-3': => editor.foldAllAtIndentLevel(2)
'editor:fold-at-indent-level-4': => editor.foldAllAtIndentLevel(3)
'editor:fold-at-indent-level-5': => editor.foldAllAtIndentLevel(4)
'editor:fold-at-indent-level-6': => editor.foldAllAtIndentLevel(5)
'editor:fold-at-indent-level-7': => editor.foldAllAtIndentLevel(6)
'editor:fold-at-indent-level-8': => editor.foldAllAtIndentLevel(7)
'editor:fold-at-indent-level-9': => editor.foldAllAtIndentLevel(8)
'editor:toggle-line-comments': => editor.toggleLineCommentsInSelection()
'editor:log-cursor-scope': => editor.logCursorScope()
'editor:checkout-head-revision': => atom.project.getRepo()?.checkoutHeadForEditor(editor)
'editor:copy-path': => editor.copyPathToClipboard()
'editor:move-line-up': => editor.moveLineUp()
'editor:move-line-down': => editor.moveLineDown()
'editor:duplicate-lines': => editor.duplicateLines()
'editor:join-lines': => editor.joinLines()
'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:move-up': -> editor.moveUp()
'core:move-down': -> editor.moveDown()
'core:move-to-top': -> editor.moveToTop()
'core:move-to-bottom': -> editor.moveToBottom()
'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()
'editor:outdent-selected-rows': -> editor.outdentSelectedRows()
'editor:newline': -> editor.insertNewline()
'editor:newline-below': -> editor.insertNewlineBelow()
'editor:newline-above': -> editor.insertNewlineAbove()
'editor:add-selection-below': -> editor.addSelectionBelow()
'editor:add-selection-above': -> editor.addSelectionAbove()
'editor:split-selections-into-lines': -> editor.splitSelectionsIntoLines()
'editor:toggle-soft-tabs': -> editor.toggleSoftTabs()
'editor:toggle-soft-wrapped': -> editor.toggleSoftWrapped()
'editor:fold-all': -> editor.foldAll()
'editor:unfold-all': -> editor.unfoldAll()
'editor:fold-current-row': -> editor.foldCurrentRow()
'editor:unfold-current-row': -> editor.unfoldCurrentRow()
'editor:fold-selection': -> editor.foldSelectedLines()
'editor:fold-at-indent-level-1': -> editor.foldAllAtIndentLevel(0)
'editor:fold-at-indent-level-2': -> editor.foldAllAtIndentLevel(1)
'editor:fold-at-indent-level-3': -> editor.foldAllAtIndentLevel(2)
'editor:fold-at-indent-level-4': -> editor.foldAllAtIndentLevel(3)
'editor:fold-at-indent-level-5': -> editor.foldAllAtIndentLevel(4)
'editor:fold-at-indent-level-6': -> editor.foldAllAtIndentLevel(5)
'editor:fold-at-indent-level-7': -> editor.foldAllAtIndentLevel(6)
'editor:fold-at-indent-level-8': -> editor.foldAllAtIndentLevel(7)
'editor:fold-at-indent-level-9': -> editor.foldAllAtIndentLevel(8)
'editor:toggle-line-comments': -> editor.toggleLineCommentsInSelection()
'editor:log-cursor-scope': -> editor.logCursorScope()
'editor:checkout-head-revision': -> atom.project.getRepo()?.checkoutHeadForEditor(editor)
'editor:copy-path': -> editor.copyPathToClipboard()
'editor:move-line-up': -> editor.moveLineUp()
'editor:move-line-down': -> editor.moveLineDown()
'editor:duplicate-lines': -> editor.duplicateLines()
'editor:join-lines': -> editor.joinLines()
'editor:toggle-indent-guide': -> atom.config.toggle('editor.showIndentGuide')
'editor:toggle-line-numbers': -> atom.config.toggle('editor.showLineNumbers')
'editor:scroll-to-cursor': -> editor.scrollToCursorPosition()
'benchmark:scroll': @runScrollBenchmark
addCommandListeners: (listenersByCommandName) ->
@@ -613,9 +604,17 @@ EditorComponent = React.createClass
onMouseDown: (event) ->
return unless event.button is 0 # only handle the left mouse button
return if event.target?.classList.contains('horizontal-scrollbar')
{editor} = @props
{detail, shiftKey, metaKey, ctrlKey} = event
# CTRL+click brings up the context menu on OSX, so don't handle those either
return if ctrlKey and process.platform is 'darwin'
# Prevent focusout event on hidden input if editor is already focused
event.preventDefault() if @state.focused
screenPosition = @screenPositionForMouseEvent(event)
if event.target?.classList.contains('fold-marker')
@@ -642,8 +641,12 @@ EditorComponent = React.createClass
onGutterMouseDown: (event) ->
return unless event.button is 0 # only handle the left mouse button
if event.shiftKey
{shiftKey, metaKey, ctrlKey} = event
if shiftKey
@onGutterShiftClick(event)
else if metaKey or (ctrlKey and process.platform isnt 'darwin')
@onGutterMetaClick(event)
else
@onGutterClick(event)
@@ -651,19 +654,43 @@ EditorComponent = React.createClass
{editor} = @props
clickedRow = @screenPositionForMouseEvent(event).row
editor.setCursorScreenPosition([clickedRow, 0])
editor.setSelectedScreenRange([[clickedRow, 0], [clickedRow + 1, 0]], preserveFolds: true)
@handleDragUntilMouseUp event, (screenPosition) ->
dragRow = screenPosition.row
if dragRow < clickedRow # dragging up
editor.setSelectedScreenRange([[dragRow, 0], [clickedRow + 1, 0]])
editor.setSelectedScreenRange([[dragRow, 0], [clickedRow + 1, 0]], preserveFolds: true)
else
editor.setSelectedScreenRange([[clickedRow, 0], [dragRow + 1, 0]])
editor.setSelectedScreenRange([[clickedRow, 0], [dragRow + 1, 0]], preserveFolds: true)
onGutterMetaClick: (event) ->
{editor} = @props
clickedRow = @screenPositionForMouseEvent(event).row
bufferRange = editor.bufferRangeForScreenRange([[clickedRow, 0], [clickedRow + 1, 0]])
rowSelection = editor.addSelectionForBufferRange(bufferRange, preserveFolds: true)
@handleDragUntilMouseUp event, (screenPosition) ->
dragRow = screenPosition.row
if dragRow < clickedRow # dragging up
rowSelection.setScreenRange([[dragRow, 0], [clickedRow + 1, 0]], preserveFolds: true)
else
rowSelection.setScreenRange([[clickedRow, 0], [dragRow + 1, 0]], preserveFolds: true)
# After updating the selected screen range, merge overlapping selections
editor.mergeIntersectingSelections(preserveFolds: true)
# The merge process will possibly destroy the current selection because
# it will be merged into another one. Therefore, we need to obtain a
# reference to the new selection that contains the originally selected row
rowSelection = _.find editor.getSelections(), (selection) ->
selection.intersectsBufferRange(bufferRange)
onGutterShiftClick: (event) ->
{editor} = @props
clickedRow = @screenPositionForMouseEvent(event).row
tailPosition = editor.getSelection().getTailScreenPosition()
tailPosition = editor.getLastSelection().getTailScreenPosition()
if clickedRow < tailPosition.row
editor.selectToScreenPosition([clickedRow, 0])
@@ -673,9 +700,9 @@ EditorComponent = React.createClass
@handleDragUntilMouseUp event, (screenPosition) ->
dragRow = screenPosition.row
if dragRow < tailPosition.row # dragging up
editor.setSelectedScreenRange([[dragRow, 0], tailPosition])
editor.setSelectedScreenRange([[dragRow, 0], tailPosition], preserveFolds: true)
else
editor.setSelectedScreenRange([tailPosition, [dragRow + 1, 0]])
editor.setSelectedScreenRange([tailPosition, [dragRow + 1, 0]], preserveFolds: true)
onStylesheetsChanged: (stylesheet) ->
return unless @performedInitialMeasurement
@@ -690,17 +717,22 @@ EditorComponent = React.createClass
@pendingChanges.push(change)
@requestUpdate() if editor.intersectsVisibleRowRange(change.start, change.end + 1) # TODO: Use closed-open intervals for change events
onSelectionChanged: (selection) ->
onSelectionAdded: (selection) ->
{editor} = @props
@subscribe selection.onDidChangeRange => @onSelectionChanged(selection)
@subscribe selection.onDidDestroy =>
@onSelectionChanged(selection)
@unsubscribe(selection)
if editor.selectionIntersectsVisibleRowRange(selection)
@selectionChanged = true
@requestUpdate()
onSelectionAdded: (selection) ->
onSelectionChanged: (selection) ->
{editor} = @props
if editor.selectionIntersectsVisibleRowRange(selection)
@selectionChanged = true
@selectionAdded = true
@requestUpdate()
onScrollTopChanged: ->
@@ -718,13 +750,24 @@ EditorComponent = React.createClass
onStoppedScrollingAfterDelay: null # created lazily
onCursorsMoved: ->
@cursorsMoved = true
onCursorAdded: (cursor) ->
@subscribe cursor.onDidChangePosition @onCursorMoved
onCursorMoved: ->
@cursorMoved = true
@requestUpdate()
onDecorationAdded: (decoration) ->
@subscribe decoration.onDidChangeProperties(@onDecorationChanged)
@subscribe decoration.getMarker().onDidChange(@onDecorationChanged)
@requestUpdate()
onDecorationChanged: ->
@requestUpdate()
onDecorationRemoved: ->
@requestUpdate()
onCharacterWidthsChanged: (@scopedCharacterWidthsChangeCount) ->
@requestUpdate()
@@ -777,15 +820,20 @@ EditorComponent = React.createClass
pollDOM: ->
return if @domPollingPaused or @updateRequested or not @isMounted()
wasVisible = @visible
if @visible = @isVisible()
if wasVisible
@measureHeightAndWidth()
@sampleFontStyling()
@sampleBackgroundColors()
unless @checkForVisibilityChange()
@sampleBackgroundColors()
@measureHeightAndWidth()
@sampleFontStyling()
checkForVisibilityChange: ->
if @isVisible()
if @wasVisible
false
else
@performInitialMeasurement()
@forceUpdate()
@becameVisible()
@wasVisible = true
else
@wasVisible = false
requestHeightAndWidthMeasurement: ->
return if @heightAndWidthMeasurementRequested
@@ -811,7 +859,7 @@ EditorComponent = React.createClass
if position is 'absolute' or height
if @autoHeight
@autoHeight = false
@forceUpdate()
@forceUpdate() unless @updatesPaused
clientHeight = scrollViewNode.clientHeight
editor.setHeight(clientHeight) if clientHeight > 0
@@ -853,30 +901,36 @@ EditorComponent = React.createClass
@requestUpdate() unless suppressUpdate
measureLineHeightAndDefaultCharWidth: ->
if @visible
if @isVisible()
@measureLineHeightAndDefaultCharWidthWhenShown = false
@refs.lines.measureLineHeightAndDefaultCharWidth()
else
@measureLineHeightAndDefaultCharWidthWhenShown = true
remeasureCharacterWidths: ->
if @visible
if @isVisible()
@remeasureCharacterWidthsWhenShown = false
@refs.lines.remeasureCharacterWidths()
else
@remeasureCharacterWidthsWhenShown = true
measureScrollbars: ->
return unless @visible
@measuringScrollbars = false
@measureScrollbarsWhenShown = false
{editor} = @props
scrollbarCornerNode = @refs.scrollbarCorner.getDOMNode()
width = (scrollbarCornerNode.offsetWidth - scrollbarCornerNode.clientWidth) or 15
height = (scrollbarCornerNode.offsetHeight - scrollbarCornerNode.clientHeight) or 15
cornerNode = @refs.scrollbarCorner.getDOMNode()
originalDisplayValue = cornerNode.style.display
cornerNode.style.display = 'block'
width = (cornerNode.offsetWidth - cornerNode.clientWidth) or 15
height = (cornerNode.offsetHeight - cornerNode.clientHeight) or 15
editor.setVerticalScrollbarWidth(width)
editor.setHorizontalScrollbarHeight(height)
cornerNode.style.display = originalDisplayValue
containsScrollbarSelector: (stylesheet) ->
for rule in stylesheet.cssRules
if rule.selectorText?.indexOf('scrollbar') > -1
@@ -884,24 +938,39 @@ EditorComponent = React.createClass
false
refreshScrollbars: ->
# Believe it or not, proper handling of changes to scrollbar styles requires
# three DOM updates.
if @isVisible()
@measureScrollbarsWhenShown = false
else
@measureScrollbarsWhenShown = true
return
# Scrollbar style changes won't apply to scrollbars that are already
# visible, so first we need to hide scrollbars so we can redisplay them and
# force Chromium to apply updates.
@refreshingScrollbars = true
@forceUpdate()
{verticalScrollbar, horizontalScrollbar, scrollbarCorner} = @refs
# Next, we display only the scrollbar corner so we can measure the new
# scrollbar dimensions. The ::measuringScrollbars property will be set back
# to false after the scrollbars are measured.
@measuringScrollbars = true
@forceUpdate()
verticalNode = verticalScrollbar.getDOMNode()
horizontalNode = verticalScrollbar.getDOMNode()
cornerNode = scrollbarCorner.getDOMNode()
# Finally, we restore the scrollbars based on the newly-measured dimensions
# if the editor's content and dimensions require them to be visible.
@forceUpdate()
originalVerticalDisplayValue = verticalNode.style.display
originalHorizontalDisplayValue = horizontalNode.style.display
originalCornerDisplayValue = cornerNode.style.display
# First, hide all scrollbars in case they are visible so they take on new
# styles when they are shown again.
verticalNode.style.display = 'none'
horizontalNode.style.display = 'none'
cornerNode.style.display = 'none'
# Force a reflow
cornerNode.offsetWidth
# Now measure the new scrollbar dimensions
@measureScrollbars()
# Now restore the display value for all scrollbars, since they were
# previously hidden
verticalNode.style.display = originalVerticalDisplayValue
horizontalNode.style.display = originalHorizontalDisplayValue
cornerNode.style.display = originalCornerDisplayValue
clearMouseWheelScreenRow: ->
if @mouseWheelScreenRow?
+252 -1462
Ver Arquivo
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
+2101 -1567
Ver Arquivo
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
+2 -2
Ver Arquivo
@@ -14,8 +14,8 @@ class Fold
@id = @marker.id
@displayBuffer.foldsByMarkerId[@marker.id] = this
@updateDisplayBuffer()
@marker.on 'destroyed', => @destroyed()
@marker.on 'changed', ({isValid}) => @destroy() unless isValid
@marker.onDidDestroy => @destroyed()
@marker.onDidChange ({isValid}) => @destroy() unless isValid
# Returns whether this fold is contained within another fold
isInsideLargerFold: ->
+65 -57
Ver Arquivo
@@ -25,17 +25,19 @@ Task = require './task'
# repo.getShortHead('vendor/path/to/a/submodule') # 'dead1234'
# ```
#
# ## Example
# ## Examples
#
# ```coffeescript
# ### Logging the URL of the origin remote
#
# ```coffee
# git = atom.project.getRepo()
# console.log git.getOriginUrl()
# ```
#
# ## Requiring in packages
# ### Requiring in packages
#
# ```coffee
# {Git} = require 'atom'
# {Git} = require 'atom'
# ```
module.exports =
class Git
@@ -44,12 +46,12 @@ class Git
# Public: Creates a new Git instance.
#
# path - The path to the Git repository to open.
# options - An object with the following keys (default: {}):
# :refreshOnWindowFocus - `true` to refresh the index and statuses when the
# window is focused.
# * `path` The {String} path to the Git repository to open.
# * `options` An optinal {Object} with the following keys:
# * `refreshOnWindowFocus` A {Boolean}, `true` to refresh the index and
# statuses when the window is focused.
#
# Returns a Git instance or null if the repository could not be opened.
# Returns a {Git} instance or `null` if the repository could not be opened.
@open: (path, options) ->
return null unless path
try
@@ -88,10 +90,14 @@ class Git
# Subscribes to buffer events.
subscribeToBuffer: (buffer) ->
@subscribe buffer, 'saved reloaded path-changed', =>
getBufferPathStatus = =>
if path = buffer.getPath()
@getPathStatus(path)
@subscribe buffer, 'destroyed', => @unsubscribe(buffer)
@subscribe buffer.onDidSave(getBufferPathStatus)
@subscribe buffer.onDidReload(getBufferPathStatus)
@subscribe buffer.onDidChangePath(getBufferPathStatus)
@subscribe buffer.onDidDestroy => @unsubscribe(buffer)
# Subscribes to editor view event.
checkoutHeadForEditor: (editor) ->
@@ -114,8 +120,10 @@ class Git
else
checkoutHead()
# Public: Destroy this `Git` object. This destroys any tasks and subscriptions
# and releases the underlying libgit2 repository handle.
# Public: Destroy this {Git} object.
#
# This destroys any tasks and subscriptions and releases the underlying
# libgit2 repository handle.
destroy: ->
if @statusTask?
@statusTask.terminate()
@@ -147,7 +155,7 @@ class Git
# Public: Get the status of a single path in the repository.
#
# path - A {String} repository-relative path.
# `path` A {String} repository-relative path.
#
# Returns a {Number} representing the status. This value can be passed to
# {::isStatusModified} or {::isStatusNew} to get more information.
@@ -196,8 +204,8 @@ class Git
# `refs/remotes`. It also shortens the SHA-1 of a detached `HEAD` to 7
# characters.
#
# path - An optional {String} path in the repository to get this information
# for, only needed if the repository contains submodules.
# * `path` An optional {String} path in the repository to get this information
# for, only needed if the repository contains submodules.
#
# Returns a {String}.
getShortHead: (path) -> @getRepo(path).getShortHead()
@@ -207,12 +215,12 @@ class Git
#
# This is essentially the same as running:
#
# ```
# git reset HEAD -- <path>
# git checkout HEAD -- <path>
# ```sh
# git reset HEAD -- <path>
# git checkout HEAD -- <path>
# ```
#
# path - The {String} path to checkout.
# * `path` The {String} path to checkout.
#
# Returns a {Boolean} that's true if the method was successful.
checkoutHead: (path) ->
@@ -223,9 +231,9 @@ class Git
# Public: Checks out a branch in your repository.
#
# reference - The String reference to checkout
# create - A Boolean value which, if true creates the new reference if it
# doesn't exist.
# * `reference` The {String} reference to checkout.
# * `create` A {Boolean} value which, if true creates the new reference if
# it doesn't exist.
#
# Returns a Boolean that's true if the method was successful.
checkoutReference: (reference, create) ->
@@ -236,18 +244,18 @@ class Git
# This compares the working directory contents of the path to the `HEAD`
# version.
#
# path - The {String} path to check.
# * `path` The {String} path to check.
#
# Returns an {Object} with the following keys:
# :added - The {Number} of added lines.
# :deleted - The {Number} of deleted lines.
# * `added` The {Number} of added lines.
# * `deleted` The {Number} of deleted lines.
getDiffStats: (path) ->
repo = @getRepo(path)
repo.getDiffStats(repo.relativize(path))
# Public: Is the given path a submodule in the repository?
#
# path - The {String} path to check.
# * `path` The {String} path to check.
#
# Returns a {Boolean}.
isSubmodule: (path) ->
@@ -262,7 +270,7 @@ class Git
# Public: Get the status of a directory in the repository's working directory.
#
# path - The {String} path to check.
# * `path` The {String} path to check.
#
# Returns a {Number} representing the status. This value can be passed to
# {::isStatusModified} or {::isStatusNew} to get more information.
@@ -276,14 +284,14 @@ class Git
# Public: Retrieves the line diffs comparing the `HEAD` version of the given
# path and the given text.
#
# path - The {String} path relative to the repository.
# text - The {String} to compare against the `HEAD` contents
# * `path` The {String} path relative to the repository.
# * `text` The {String} to compare against the `HEAD` contents
#
# Returns an {Array} of hunk {Object}s with the following keys:
# :oldStart - The line {Number} of the old hunk.
# :newStart - The line {Number} of the new hunk.
# :oldLines - The {Number} of lines in the old hunk.
# :newLines - The {Number} of lines in the new hunk
# * `oldStart` The line {Number} of the old hunk.
# * `newStart` The line {Number} of the new hunk.
# * `oldLines` The {Number} of lines in the old hunk.
# * `newLines` The {Number} of lines in the new hunk
getLineDiffs: (path, text) ->
# Ignore eol of line differences on windows so that files checked in as
# LF don't report every line modified when the text contains CRLF endings.
@@ -293,68 +301,68 @@ class Git
# Public: Returns the git configuration value specified by the key.
#
# path - An optional {String} path in the repository to get this information
# for, only needed if the repository has submodules.
# * `path` An optional {String} path in the repository to get this information
# for, only needed if the repository has submodules.
getConfigValue: (key, path) -> @getRepo(path).getConfigValue(key)
# Public: Returns the origin url of the repository.
#
# path - An optional {String} path in the repository to get this information
# for, only needed if the repository has submodules.
# * `path` An optional {String} path in the repository to get this information
# for, only needed if the repository has submodules.
getOriginUrl: (path) -> @getConfigValue('remote.origin.url', path)
# Public: Returns the upstream branch for the current HEAD, or null if there
# is no upstream branch for the current HEAD.
#
# path - An optional {String} path in the repo to get this information for,
# only needed if the repository contains submodules.
# * `path` An optional {String} path in the repo to get this information for,
# only needed if the repository contains submodules.
#
# Returns a {String} branch name such as `refs/remotes/origin/master`.
getUpstreamBranch: (path) -> @getRepo(path).getUpstreamBranch()
# Public: Returns the current SHA for the given reference.
# Public: Returns the current {String} SHA for the given reference.
#
# reference - The {String} reference to get the target of.
# path - An optional {String} path in the repo to get the reference target
# for. Only needed if the repository contains submodules.
# * `reference` The {String} reference to get the target of.
# * `path` An optional {String} path in the repo to get the reference target
# for. Only needed if the repository contains submodules.
getReferenceTarget: (reference, path) ->
@getRepo(path).getReferenceTarget(reference)
# Public: Gets all the local and remote references.
#
# path - An optional {String} path in the repository to get this information
# for, only needed if the repository has submodules.
# * `path` An optional {String} path in the repository to get this information
# for, only needed if the repository has submodules.
#
# Returns an {Object} with the following keys:
# :heads - An {Array} of head reference names.
# :remotes - An {Array} of remote reference names.
# :tags - An {Array} of tag reference names.
# * `heads` An {Array} of head reference names.
# * `remotes` An {Array} of remote reference names.
# * `tags` An {Array} of tag reference names.
getReferences: (path) -> @getRepo(path).getReferences()
# Public: Returns the number of commits behind the current branch is from the
# its upstream remote branch.
#
# reference - The {String} branch reference name.
# path - The {String} path in the repository to get this information for,
# only needed if the repository contains submodules.
# * `reference` The {String} branch reference name.
# * `path` The {String} path in the repository to get this information for,
# only needed if the repository contains submodules.
getAheadBehindCount: (reference, path) ->
@getRepo(path).getAheadBehindCount(reference)
# Public: Get the cached ahead/behind commit counts for the current branch's
# upstream branch.
#
# path - An optional {String} path in the repository to get this information
# for, only needed if the repository has submodules.
# * `path` An optional {String} path in the repository to get this information
# for, only needed if the repository has submodules.
#
# Returns an {Object} with the following keys:
# :ahead - The {Number} of commits ahead.
# :behind - The {Number} of commits behind.
# * `ahead` The {Number} of commits ahead.
# * `behind` The {Number} of commits behind.
getCachedUpstreamAheadBehindCount: (path) ->
@getRepo(path).upstream ? @upstream
# Public: Get the cached status for the given path.
#
# path - A {String} path in the repository, relative or absolute.
# * `path` A {String} path in the repository, relative or absolute.
#
# Returns a status {Number} or null if the path is not in the cache.
getCachedPathStatus: (path) ->
+9 -1
Ver Arquivo
@@ -21,7 +21,7 @@ GutterComponent = React.createClass
if gutterBackgroundColor isnt 'rbga(0, 0, 0, 0)'
backgroundColor = gutterBackgroundColor
div className: 'gutter', onClick: @onClick, onMouseDown: onMouseDown,
div className: 'gutter', onClick: @onClick, onMouseDown: @onMouseDown,
div className: 'line-numbers', ref: 'lineNumbers', style:
height: Math.max(scrollHeight, scrollViewHeight)
WebkitTransform: @getTransform()
@@ -215,6 +215,14 @@ GutterComponent = React.createClass
lineNumberNodeForScreenRow: (screenRow) ->
@lineNumberNodesById[@lineNumberIdsByScreenRow[screenRow]]
onMouseDown: (event) ->
{editor} = @props
{target} = event
lineNumber = target.parentNode
unless target.classList.contains('icon-right') and lineNumber.classList.contains('foldable')
@props.onMouseDown(event)
onClick: (event) ->
{editor} = @props
{target} = event
-275
Ver Arquivo
@@ -1,275 +0,0 @@
{View, $, $$, $$$} = require './space-pen-extensions'
{Range} = require 'text-buffer'
_ = require 'underscore-plus'
# Represents the portion of the {EditorView} containing row numbers.
#
# The gutter also indicates if rows are folded.
module.exports =
class GutterView extends View
@content: ->
@div class: 'gutter', =>
@div outlet: 'lineNumbers', class: 'line-numbers'
firstScreenRow: null
lastScreenRow: null
initialize: ->
@elementBuilder = document.createElement('div')
afterAttach: (onDom) ->
return if @attached or not onDom
@attached = true
highlightLines = => @highlightLines()
@getEditorView().on 'cursor:moved', highlightLines
@getEditorView().on 'selection:changed', highlightLines
@on 'mousedown', (e) => @handleMouseEvents(e)
beforeRemove: ->
$(document).off(".gutter-#{@getEditorView().id}")
handleMouseEvents: (e) ->
editorView = @getEditorView()
editor = @getEditor()
startRow = editorView.screenPositionFromMouseEvent(e).row
if e.shiftKey
editor.selectToScreenPosition([startRow + 1, 0])
return
else
editor.getSelection().setScreenRange([[startRow, 0], [startRow, 0]])
moveHandler = (e) =>
start = startRow
end = editorView.screenPositionFromMouseEvent(e).row
if end > start then end++ else start++
editor.getSelection().setScreenRange([[start, 0], [end, 0]])
$(document).on "mousemove.gutter-#{editorView.id}", moveHandler
$(document).one "mouseup.gutter-#{editorView.id}", => $(document).off 'mousemove', moveHandler
# Retrieves the containing {EditorView}.
#
# Returns an {EditorView}.
getEditorView: ->
@parentView
getEditor: ->
@getEditorView().getEditor()
# Defines whether to show the gutter or not.
#
# showLineNumbers - A {Boolean} which, if `false`, hides the gutter
setShowLineNumbers: (showLineNumbers) ->
if showLineNumbers then @lineNumbers.show() else @lineNumbers.hide()
# Get all the line-number divs.
#
# Returns a list of {HTMLElement}s.
getLineNumberElements: ->
@lineNumbers[0].children
# Get all the line-number divs.
#
# Returns a list of {HTMLElement}s.
getLineNumberElementsForClass: (klass) ->
@lineNumbers[0].getElementsByClassName(klass)
# Get a single line-number div.
#
# * bufferRow: 0 based line number
#
# Returns a list of {HTMLElement}s that correspond to the bufferRow. More than
# one in the list indicates a wrapped line.
getLineNumberElement: (bufferRow) ->
@getLineNumberElementsForClass("line-number-#{bufferRow}")
# Add a class to all line-number divs.
#
# * klass: string class name
#
# Returns true if the class was added to any lines
addClassToAllLines: (klass)->
elements = @getLineNumberElements()
el.classList.add(klass) for el in elements
!!elements.length
# Remove a class from all line-number divs.
#
# * klass: string class name. Can only be one class name. i.e. 'my-class'
#
# Returns true if the class was removed from any lines
removeClassFromAllLines: (klass)->
# This is faster than calling $.removeClass on all lines, and faster than
# making a new array and iterating through it.
elements = @getLineNumberElementsForClass(klass)
willRemoveClasses = !!elements.length
elements[0].classList.remove(klass) while elements.length > 0
willRemoveClasses
# Add a class to a single line-number div
#
# * bufferRow: 0 based line number
# * klass: string class name
#
# Returns true if there were lines the class was added to
addClassToLine: (bufferRow, klass)->
elements = @getLineNumberElement(bufferRow)
el.classList.add(klass) for el in elements
!!elements.length
# Remove a class from a single line-number div
#
# * bufferRow: 0 based line number
# * klass: string class name
#
# Returns true if there were lines the class was removed from
removeClassFromLine: (bufferRow, klass)->
classesRemoved = false
elements = @getLineNumberElement(bufferRow)
for el in elements
hasClass = el.classList.contains(klass)
classesRemoved |= hasClass
el.classList.remove(klass) if hasClass
classesRemoved
updateLineNumbers: (changes, startScreenRow, endScreenRow) ->
# Check if we have something already rendered that overlaps the requested range
updateAllLines = not (startScreenRow? and endScreenRow?)
updateAllLines |= endScreenRow <= @firstScreenRow or startScreenRow >= @lastScreenRow
unless updateAllLines
for change in changes
if change.screenDelta or change.bufferDelta
updateAllLines = true
break
# Rebuild the entire gutter if a change added or removed lines
if updateAllLines
@lineNumbers[0].innerHTML = @buildLineElementsHtml(startScreenRow, endScreenRow)
# Handle changes that didn't add/remove lines more optimally
else
if startScreenRow < @firstScreenRow
@prependLineElements(@buildLineElements(startScreenRow, @firstScreenRow-1))
else if startScreenRow != @firstScreenRow
@removeLineElements(startScreenRow - @firstScreenRow)
if endScreenRow > @lastScreenRow
@appendLineElements(@buildLineElements(@lastScreenRow+1, endScreenRow))
else if endScreenRow != @lastScreenRow
@removeLineElements(endScreenRow - @lastScreenRow)
@updateFoldableClasses(changes)
@firstScreenRow = startScreenRow
@lastScreenRow = endScreenRow
@highlightedRows = null
@highlightLines()
prependLineElements: (lineElements) ->
anchor = @lineNumbers[0].children[0]
return appendLineElements(lineElements) unless anchor?
@lineNumbers[0].insertBefore(lineElements[0], anchor) while lineElements.length > 0
null # defeat coffeescript array return
appendLineElements: (lineElements) ->
@lineNumbers[0].appendChild(lineElements[0]) while lineElements.length > 0
null # defeat coffeescript array return
removeLineElements: (numberOfElements) ->
children = @getLineNumberElements()
# children is a live NodeList, so remove from the desired end {numberOfElements} times
if numberOfElements < 0
@lineNumbers[0].removeChild(children[children.length-1]) while numberOfElements++
else if numberOfElements > 0
@lineNumbers[0].removeChild(children[0]) while numberOfElements--
null # defeat coffeescript array return
buildLineElements: (startScreenRow, endScreenRow) ->
@elementBuilder.innerHTML = @buildLineElementsHtml(startScreenRow, endScreenRow)
@elementBuilder.children
buildLineElementsHtml: (startScreenRow, endScreenRow) =>
editor = @getEditor()
maxDigits = editor.getLineCount().toString().length
rows = editor.bufferRowsForScreenRows(startScreenRow, endScreenRow)
html = ''
for row in rows
if row is lastRow
rowValue = ''
else
rowValue = (row + 1).toString()
classes = "line-number line-number-#{row}"
classes += ' foldable' if row isnt lastRow and editor.isFoldableAtBufferRow(row)
classes += ' folded' if editor.isFoldedAtBufferRow(row)
rowValuePadding = _.multiplyString('&nbsp;', maxDigits - rowValue.length)
html += """<div class="#{classes}" data-buffer-row=#{row}>#{rowValuePadding}#{rowValue}<div class="icon-right"></div></div>"""
lastRow = row
html
# Called to update the 'foldable' class of line numbers when there's
# a change to the display buffer that doesn't regenerate all the line numbers
# anyway.
updateFoldableClasses: (changes) ->
editor = @getEditor()
for {start, end} in changes when start <= @lastScreenRow and end >= @firstScreenRow
startScreenRow = Math.max(start - 1, @firstScreenRow)
endScreenRow = Math.min(end + 1, @lastScreenRow)
lastBufferRow = null
for bufferRow in editor.bufferRowsForScreenRows(startScreenRow, endScreenRow) when bufferRow isnt lastBufferRow
lastBufferRow = bufferRow
if lineNumberElement = @getLineNumberElement(bufferRow)[0]
if editor.isFoldableAtBufferRow(bufferRow)
lineNumberElement.classList.add('foldable')
else
lineNumberElement.classList.remove('foldable')
removeLineHighlights: ->
return unless @highlightedLineNumbers
for line in @highlightedLineNumbers
line.classList.remove('cursor-line')
line.classList.remove('cursor-line-no-selection')
@highlightedLineNumbers = null
addLineHighlight: (row, emptySelection) ->
return if row < @firstScreenRow or row > @lastScreenRow
@highlightedLineNumbers ?= []
if highlightedLineNumber = @lineNumbers[0].children[row - @firstScreenRow]
highlightedLineNumber.classList.add('cursor-line')
highlightedLineNumber.classList.add('cursor-line-no-selection') if emptySelection
@highlightedLineNumbers.push(highlightedLineNumber)
highlightLines: ->
editor = @getEditor()
return unless editor?.isAlive()
if editor.getSelection().isEmpty()
row = editor.getCursorScreenPosition().row
rowRange = new Range([row, 0], [row, 0])
return if @selectionEmpty and @highlightedRows?.isEqual(rowRange)
@removeLineHighlights()
@addLineHighlight(row, true)
@highlightedRows = rowRange
@selectionEmpty = true
else
selectedRows = editor.getSelection().getScreenRange()
endRow = selectedRows.end.row
endRow-- if selectedRows.end.column is 0
selectedRows = new Range([selectedRows.start.row, 0], [endRow, 0])
return if not @selectionEmpty and @highlightedRows?.isEqual(selectedRows)
@removeLineHighlights()
for row in [selectedRows.start.row..selectedRows.end.row]
@addLineHighlight(row, false)
@highlightedRows = selectedRows
@selectionEmpty = false
+3 -2
Ver Arquivo
@@ -22,11 +22,12 @@ HighlightComponent = React.createClass
{editor, decoration} = @props
if decoration.id?
@decoration = editor.decorationForId(decoration.id)
@decoration.on 'flash', @startFlashAnimation
@decorationDisposable = @decoration.onDidFlash @startFlashAnimation
@startFlashAnimation()
componentWillUnmount: ->
@decoration?.off 'flash', @startFlashAnimation
@decorationDisposable?.dispose()
@decorationDisposable = null
startFlashAnimation: ->
return unless flash = @decoration.consumeNextFlash()
+9 -9
Ver Arquivo
@@ -145,17 +145,17 @@ class LanguageMode
rowRange
rowRangeForCommentAtBufferRow: (bufferRow) ->
return unless @editor.displayBuffer.tokenizedBuffer.lineForScreenRow(bufferRow).isComment()
return unless @editor.displayBuffer.tokenizedBuffer.tokenizedLineForRow(bufferRow).isComment()
startRow = bufferRow
for currentRow in [bufferRow-1..0]
break if @buffer.isRowBlank(currentRow)
break unless @editor.displayBuffer.tokenizedBuffer.lineForScreenRow(currentRow).isComment()
break unless @editor.displayBuffer.tokenizedBuffer.tokenizedLineForRow(currentRow).isComment()
startRow = currentRow
endRow = bufferRow
for currentRow in [bufferRow+1..@buffer.getLastRow()]
break if @buffer.isRowBlank(currentRow)
break unless @editor.displayBuffer.tokenizedBuffer.lineForScreenRow(currentRow).isComment()
break unless @editor.displayBuffer.tokenizedBuffer.tokenizedLineForRow(currentRow).isComment()
endRow = currentRow
return [startRow, endRow] if startRow isnt endRow
@@ -168,7 +168,7 @@ class LanguageMode
continue if @editor.isBufferRowBlank(row)
indentation = @editor.indentationForBufferRow(row)
if indentation <= startIndentLevel
includeRowInFold = indentation == startIndentLevel and @foldEndRegexForScopes(scopes)?.searchSync(@editor.lineForBufferRow(row))
includeRowInFold = indentation == startIndentLevel and @foldEndRegexForScopes(scopes)?.searchSync(@editor.lineTextForBufferRow(row))
foldEndRow = row if includeRowInFold
break
@@ -201,13 +201,13 @@ class LanguageMode
# row is a comment.
isLineCommentedAtBufferRow: (bufferRow) ->
return false unless 0 <= bufferRow <= @editor.getLastBufferRow()
@editor.displayBuffer.tokenizedBuffer.lineForScreenRow(bufferRow).isComment()
@editor.displayBuffer.tokenizedBuffer.tokenizedLineForRow(bufferRow).isComment()
# Find a row range for a 'paragraph' around specified bufferRow.
# Right now, a paragraph is a block of text bounded by and empty line or a
# block of text that is not the same type (comments next to source code).
rowRangeForParagraphAtBufferRow: (bufferRow) ->
return unless /\w/.test(@editor.lineForBufferRow(bufferRow))
return unless /\w/.test(@editor.lineTextForBufferRow(bufferRow))
if @isLineCommentedAtBufferRow(bufferRow)
isOriginalRowComment = true
@@ -220,17 +220,17 @@ class LanguageMode
startRow = bufferRow
while startRow > firstRow
break if @isLineCommentedAtBufferRow(startRow - 1) != isOriginalRowComment
break unless /\w/.test(@editor.lineForBufferRow(startRow - 1))
break unless /\w/.test(@editor.lineTextForBufferRow(startRow - 1))
startRow--
endRow = bufferRow
lastRow = @editor.getLastBufferRow()
while endRow < lastRow
break if @isLineCommentedAtBufferRow(endRow + 1) != isOriginalRowComment
break unless /\w/.test(@editor.lineForBufferRow(endRow + 1))
break unless /\w/.test(@editor.lineTextForBufferRow(endRow + 1))
endRow++
new Range([startRow, 0], [endRow, @editor.lineLengthForBufferRow(endRow)])
new Range([startRow, 0], [endRow, @editor.lineTextForBufferRow(endRow).length])
# Given a buffer row, this returns a suggested indentation level.
#
+12 -11
Ver Arquivo
@@ -91,12 +91,11 @@ LinesComponent = React.createClass
@lineIdsByScreenRow = {}
updateLines: (updateWidth) ->
{editor, renderedRowRange, showIndentGuide, selectionChanged, lineDecorations} = @props
[startRow, endRow] = renderedRowRange
{tokenizedLines, renderedRowRange, showIndentGuide, selectionChanged, lineDecorations} = @props
[startRow] = renderedRowRange
visibleLines = editor.linesForScreenRows(startRow, endRow - 1)
@removeLineNodes(visibleLines)
@appendOrUpdateVisibleLineNodes(visibleLines, startRow, updateWidth)
@removeLineNodes(tokenizedLines)
@appendOrUpdateVisibleLineNodes(tokenizedLines, startRow, updateWidth)
removeLineNodes: (visibleLines=[]) ->
{mouseWheelScreenRow} = @props
@@ -147,7 +146,7 @@ LinesComponent = React.createClass
@lineNodesByLineId.hasOwnProperty(lineId)
buildLineHTML: (line, screenRow) ->
{editor, mini, showIndentGuide, lineHeightInPixels, lineDecorations, lineWidth} = @props
{mini, showIndentGuide, lineHeightInPixels, lineDecorations, lineWidth} = @props
{tokens, text, lineEnding, fold, isSoftWrapped, indentLevel} = line
classes = ''
@@ -202,7 +201,7 @@ LinesComponent = React.createClass
lineIsWhitespaceOnly = firstTrailingWhitespacePosition is 0
for token in tokens
innerHTML += @updateScopeStack(scopeStack, token.scopes)
hasIndentGuide = not mini and showIndentGuide and (token.hasLeadingWhitespace or (token.hasTrailingWhitespace and lineIsWhitespaceOnly))
hasIndentGuide = not mini and showIndentGuide and (token.hasLeadingWhitespace() or (token.hasTrailingWhitespace() and lineIsWhitespaceOnly))
innerHTML += token.getValueAsHtml({hasIndentGuide})
innerHTML += @popScope(scopeStack) while scopeStack.length > 0
@@ -244,7 +243,7 @@ LinesComponent = React.createClass
"<span class=\"#{scope.replace(/\.+/g, ' ')}\">"
updateLineNode: (line, screenRow, updateWidth) ->
{editor, lineHeightInPixels, lineDecorations, lineWidth} = @props
{lineHeightInPixels, lineDecorations, lineWidth} = @props
lineNode = @lineNodesByLineId[line.id]
decorations = lineDecorations[screenRow]
@@ -286,16 +285,18 @@ LinesComponent = React.createClass
editor.setDefaultCharWidth(charWidth)
remeasureCharacterWidths: ->
return unless @props.performedInitialMeasurement
@clearScopedCharWidths()
@measureCharactersInNewLines()
measureCharactersInNewLines: ->
{editor} = @props
[visibleStartRow, visibleEndRow] = @props.renderedRowRange
{editor, tokenizedLines, renderedRowRange} = @props
[visibleStartRow] = renderedRowRange
node = @getDOMNode()
editor.batchCharacterMeasurement =>
for tokenizedLine in editor.linesForScreenRows(visibleStartRow, visibleEndRow - 1)
for tokenizedLine in tokenizedLines
unless @measuredLines.has(tokenizedLine)
lineNode = @lineNodesByLineId[tokenizedLine.id]
@measureCharactersInLine(tokenizedLine, lineNode)
+10 -11
Ver Arquivo
@@ -17,9 +17,9 @@ class MenuManager
atom.keymaps.on 'bundled-keymaps-loaded', => @loadPlatformItems()
atom.packages.on 'activated', => @sortPackagesMenu()
# Public: Adds the given item definition to the existing template.
# Public: Adds the given items to the application menu.
#
# ## Example
# ## Examples
# ```coffee
# atom.menu.add [
# {
@@ -29,23 +29,22 @@ class MenuManager
# ]
# ```
#
# items - An {Array} of menu item {Object}s containing the keys:
# :label - The {String} menu label.
# :submenu - An optional {Array} of sub menu items.
# :command - An optional {String} command to trigger when the item is
# clicked.
#
# Returns nothing.
# * `items` An {Array} of menu item {Object}s containing the keys:
# * `label` The {String} menu label.
# * `submenu` An optional {Array} of sub menu items.
# * `command` An optional {String} command to trigger when the item is
# clicked.
add: (items) ->
@merge(@template, item) for item in items
@update()
undefined
# Should the binding for the given selector be included in the menu
# commands.
#
# selector - A {String} selector to check.
# * `selector` A {String} selector to check.
#
# Returns true to include the selector, false otherwise.
# Returns a {Boolean}, true to include the selector, false otherwise.
includeSelector: (selector) ->
try
return true if document.body.webkitMatchesSelector(selector)
+55 -19
Ver Arquivo
@@ -21,7 +21,7 @@ ThemePackage = require './theme-package'
# `deactivate()` on the package's main module.
# * Unloading a package removes it completely from the package manager.
#
# Packages can also be enabled/disabled via the `core.disabledPackages` config
# Packages can be enabled/disabled via the `core.disabledPackages` config
# settings and also by calling `enablePackage()/disablePackage()`.
module.exports =
class PackageManager
@@ -41,15 +41,17 @@ class PackageManager
@packageActivators = []
@registerPackageActivator(this, ['atom', 'textmate'])
# Public: Get the path to the apm command
# Extended: Get the path to the apm command.
#
# Return a {String} file path to apm.
getApmPath: ->
commandName = 'apm'
commandName += '.cmd' if process.platform is 'win32'
@apmPath ?= path.resolve(__dirname, '..', 'apm', 'node_modules', 'atom-package-manager', 'bin', commandName)
# Public: Get the paths being used to look for packages.
# Extended: Get the paths being used to look for packages.
#
# Returns an Array of String directory paths.
# Returns an {Array} of {String} directory paths.
getPackageDirPaths: ->
_.clone(@packageDirPaths)
@@ -59,13 +61,17 @@ class PackageManager
setPackageState: (name, state) ->
@packageStates[name] = state
# Public: Enable the package with the given name
# Extended: Enable the package with the given name.
#
# Returns the {Package} that was enabled or null if it isn't loaded.
enablePackage: (name) ->
pack = @loadPackage(name)
pack?.enable()
pack
# Public: Disable the package with the given name
# Extended: Disable the package with the given name.
#
# Returns the {Package} that was disabled or null if it isn't loaded.
disablePackage: (name) ->
pack = @loadPackage(name)
pack?.disable()
@@ -110,15 +116,23 @@ class PackageManager
pack.deactivate()
delete @activePackages[pack.name]
# Public: Get an array of all the active packages
# Essential: Get an {Array} of all the active {Package}s.
getActivePackages: ->
_.values(@activePackages)
# Public: Get the active package with the given name
# Essential: Get the active {Package} with the given name.
#
# * `name` - The {String} package name.
#
# Returns a {Package} or undefined.
getActivePackage: (name) ->
@activePackages[name]
# Public: Is the package with the given name active?
# Public: Is the {Package} with the given name active?
#
# * `name` - The {String} package name.
#
# Returns a {Boolean}.
isPackageActive: (name) ->
@getActivePackage(name)?
@@ -147,6 +161,8 @@ class PackageManager
@emit 'loaded'
loadPackage: (nameOrPath) ->
return pack if pack = @getLoadedPackage(nameOrPath)
if packagePath = @resolvePackagePath(nameOrPath)
name = path.basename(nameOrPath)
return pack if pack = @getLoadedPackage(name)
@@ -179,25 +195,37 @@ class PackageManager
else
throw new Error("No loaded package for name '#{name}'")
# Public: Get the loaded package with the given name
# Essential: Get the loaded {Package} with the given name.
#
# * `name` - The {String} package name.
#
# Returns a {Package} or undefined.
getLoadedPackage: (name) ->
@loadedPackages[name]
# Public: Is the package with the given name loaded?
# Essential: Is the package with the given name loaded?
#
# * `name` - The {String} package name.
#
# Returns a {Boolean}.
isPackageLoaded: (name) ->
@getLoadedPackage(name)?
# Public: Get an array of all the loaded packages
# Essential: Get an {Array} of all the loaded {Package}s
getLoadedPackages: ->
_.values(@loadedPackages)
# Get packages for a certain package type
#
# types - an {Array} of {String}s like ['atom', 'textmate'].
# * `types` an {Array} of {String}s like ['atom', 'textmate'].
getLoadedPackagesForTypes: (types) ->
pack for pack in @getLoadedPackages() when pack.getType() in types
# Public: Resolve the given package name to a path on disk.
# Extended: Resolve the given package name to a path on disk.
#
# * `name` - The {String} package name.
#
# Return a {String} folder path or undefined if it could not be resolved.
resolvePackagePath: (name) ->
return name if fs.isDirectorySync(name)
@@ -207,7 +235,11 @@ class PackageManager
packagePath = path.join(@resourcePath, 'node_modules', name)
return packagePath if @hasAtomEngine(packagePath)
# Public: Is the package with the given name disabled?
# Essential: Is the package with the given name disabled?
#
# * `name` - The {String} package name.
#
# Returns a {Boolean}.
isPackageDisabled: (name) ->
_.include(atom.config.get('core.disabledPackages') ? [], name)
@@ -215,7 +247,11 @@ class PackageManager
metadata = Package.loadMetadata(packagePath, true)
metadata?.engines?.atom?
# Public: Is the package with the given name bundled with Atom?
# Extended: Is the package with the given name bundled with Atom?
#
# * `name` - The {String} package name.
#
# Returns a {Boolean}.
isBundledPackage: (name) ->
@getPackageDependencies().hasOwnProperty(name)
@@ -228,7 +264,7 @@ class PackageManager
@packageDependencies
# Public: Get an array of all the available package paths.
# Extended: Get an {Array} of {String}s of all the available package paths.
getAvailablePackagePaths: ->
packagePaths = []
@@ -243,11 +279,11 @@ class PackageManager
_.uniq(packagePaths)
# Public: Get an array of all the available package names.
# Extended: Get an {Array} of {String}s of all the available package names.
getAvailablePackageNames: ->
_.uniq _.map @getAvailablePackagePaths(), (packagePath) -> path.basename(packagePath)
# Public: Get an array of all the available package metadata.
# Extended: Get an {Array} of {String}s of all the available package metadata.
getAvailablePackageMetadata: ->
packages = []
for packagePath in @getAvailablePackagePaths()
+16 -7
Ver Arquivo
@@ -1,11 +1,17 @@
{CompositeDisposable} = require 'event-kit'
{View} = require './space-pen-extensions'
PaneView = null
module.exports =
class PaneAxisView extends View
initialize: (@model) ->
@onChildAdded(child) for child in @model.children
@subscribe @model.children, 'changed', @onChildrenChanged
@subscriptions = new CompositeDisposable
@onChildAdded({child, index}) for child, index in @model.getChildren()
@subscriptions.add @model.onDidAddChild(@onChildAdded)
@subscriptions.add @model.onDidRemoveChild(@onChildRemoved)
@subscriptions.add @model.onDidReplaceChild(@onChildReplaced)
afterAttach: ->
@container = @closest('.panes').view()
@@ -14,19 +20,22 @@ class PaneAxisView extends View
viewClass = model.getViewClass()
model._view ?= new viewClass(model)
onChildrenChanged: ({index, removedValues, insertedValues}) =>
onChildReplaced: ({index, oldChild, newChild}) =>
focusedElement = document.activeElement if @hasFocus()
@onChildRemoved(child, index) for child in removedValues
@onChildAdded(child, index + i) for child, i in insertedValues
@onChildRemoved({child: oldChild, index})
@onChildAdded({child: newChild, index})
focusedElement?.focus() if document.activeElement is document.body
onChildAdded: (child, index) =>
onChildAdded: ({child, index}) =>
view = @viewForModel(child)
@insertAt(index, view)
onChildRemoved: (child) =>
onChildRemoved: ({child}) =>
view = @viewForModel(child)
view.detach()
PaneView ?= require './pane-view'
if view instanceof PaneView and view.model.isDestroyed()
@container?.trigger 'pane:removed', [view]
beforeRemove: ->
@subscriptions.dispose()
+76 -18
Ver Arquivo
@@ -1,4 +1,5 @@
{Model, Sequence} = require 'theorist'
{Model} = require 'theorist'
{Emitter, CompositeDisposable} = require 'event-kit'
{flatten} = require 'underscore-plus'
Serializable = require 'serializable'
@@ -10,18 +11,17 @@ class PaneAxis extends Model
atom.deserializers.add(this)
Serializable.includeInto(this)
parent: null
container: null
orientation: null
constructor: ({@container, @orientation, children}) ->
@children = Sequence.fromArray(children ? [])
@subscribe @children.onEach (child) =>
child.parent = this
child.container = @container
@subscribe child, 'destroyed', => @removeChild(child)
@subscribe @children.onRemoval (child) => @unsubscribe(child)
@when @children.$length.becomesLessThan(2), 'reparentLastChild'
@when @children.$length.becomesLessThan(1), 'destroy'
@emitter = new Emitter
@subscriptionsByChild = new WeakMap
@subscriptions = new CompositeDisposable
@children = []
if children?
@addChild(child) for child in children
deserializeParams: (params) ->
{container} = params
@@ -32,35 +32,93 @@ class PaneAxis extends Model
children: @children.map (child) -> child.serialize()
orientation: @orientation
getParent: -> @parent
setParent: (@parent) -> @parent
getContainer: -> @container
setContainer: (@container) -> @container
getViewClass: ->
if @orientation is 'vertical'
PaneColumnView ?= require './pane-column-view'
else
PaneRowView ?= require './pane-row-view'
getChildren: -> @children.slice()
getPanes: ->
flatten(@children.map (child) -> child.getPanes())
addChild: (child, index=@children.length) ->
@children.splice(index, 0, child)
getItems: ->
flatten(@children.map (child) -> child.getItems())
removeChild: (child) ->
onDidAddChild: (fn) ->
@emitter.on 'did-add-child', fn
onDidRemoveChild: (fn) ->
@emitter.on 'did-remove-child', fn
onDidReplaceChild: (fn) ->
@emitter.on 'did-replace-child', fn
onDidDestroy: (fn) ->
@emitter.on 'did-destroy', fn
addChild: (child, index=@children.length) ->
child.setParent(this)
child.setContainer(@container)
@subscribeToChild(child)
@children.splice(index, 0, child)
@emitter.emit 'did-add-child', {child, index}
removeChild: (child, replacing=false) ->
index = @children.indexOf(child)
throw new Error("Removing non-existent child") if index is -1
@unsubscribeFromChild(child)
@children.splice(index, 1)
@emitter.emit 'did-remove-child', {child, index}
@reparentLastChild() if not replacing and @children.length < 2
replaceChild: (oldChild, newChild) ->
@unsubscribeFromChild(oldChild)
@subscribeToChild(newChild)
newChild.setParent(this)
newChild.setContainer(@container)
index = @children.indexOf(oldChild)
throw new Error("Replacing non-existent child") if index is -1
@children.splice(index, 1, newChild)
@emitter.emit 'did-replace-child', {oldChild, newChild, index}
insertChildBefore: (currentChild, newChild) ->
index = @children.indexOf(currentChild)
@children.splice(index, 0, newChild)
@addChild(newChild, index)
insertChildAfter: (currentChild, newChild) ->
index = @children.indexOf(currentChild)
@children.splice(index + 1, 0, newChild)
@addChild(newChild, index + 1)
reparentLastChild: ->
@parent.replaceChild(this, @children[0])
@destroy()
subscribeToChild: (child) ->
subscription = child.onDidDestroy => @removeChild(child)
@subscriptionsByChild.set(child, subscription)
@subscriptions.add(subscription)
unsubscribeFromChild: (child) ->
subscription = @subscriptionsByChild.get(child)
@subscriptions.remove(subscription)
subscription.dispose()
destroyed: ->
@subscriptions.dispose()
@emitter.emit 'did-destroy'
@emitter.dispose()
+9 -3
Ver Arquivo
@@ -1,5 +1,6 @@
{deprecate} = require 'grim'
Delegator = require 'delegato'
{CompositeDisposable} = require 'event-kit'
{$, View} = require './space-pen-extensions'
PaneView = require './pane-view'
PaneContainer = require './pane-container'
@@ -15,13 +16,15 @@ class PaneContainerView extends View
@div class: 'panes'
initialize: (params) ->
@subscriptions = new CompositeDisposable
if params instanceof PaneContainer
@model = params
else
@model = new PaneContainer({root: params?.root?.model})
@subscribe @model.$root, @onRootChanged
@subscribe @model.$activePaneItem.changes, @onActivePaneItemChanged
@subscriptions.add @model.observeRoot(@onRootChanged)
@subscriptions.add @model.onDidChangeActivePaneItem(@onActivePaneItemChanged)
viewForModel: (model) ->
if model?
@@ -88,7 +91,7 @@ class PaneContainerView extends View
@viewForModel(@model.activePane)
getActivePaneItem: ->
@model.activePaneItem
@model.getActivePaneItem()
getActiveView: ->
@getActivePaneView()?.activeView
@@ -153,3 +156,6 @@ class PaneContainerView extends View
getPanes: ->
deprecate("Use PaneContainerView::getPaneViews() instead")
@getPaneViews()
beforeRemove: ->
@subscriptions.dispose()
+99 -22
Ver Arquivo
@@ -1,5 +1,6 @@
{find} = require 'underscore-plus'
{find, flatten} = require 'underscore-plus'
{Model} = require 'theorist'
{Emitter, CompositeDisposable} = require 'event-kit'
Serializable = require 'serializable'
Pane = require './pane'
@@ -11,10 +12,9 @@ class PaneContainer extends Model
@version: 1
@properties
root: -> new Pane
activePane: null
previousRoot: null
root: null
@behavior 'activePaneItem', ->
@$activePane
@@ -23,9 +23,16 @@ class PaneContainer extends Model
constructor: (params) ->
super
@subscribe @$root, @onRootChanged
@emitter = new Emitter
@subscriptions = new CompositeDisposable
@setRoot(params?.root ? new Pane)
@destroyEmptyPanes() if params?.destroyEmptyPanes
@monitorActivePaneItem()
@monitorPaneItems()
deserializeParams: (params) ->
params.root = atom.deserializers.deserialize(params.root, container: this)
params.destroyEmptyPanes = atom.config.get('core.destroyEmptyPanes')
@@ -36,16 +43,75 @@ class PaneContainer extends Model
root: @root?.serialize()
activePaneId: @activePane.id
onDidChangeRoot: (fn) ->
@emitter.on 'did-change-root', fn
observeRoot: (fn) ->
fn(@getRoot())
@onDidChangeRoot(fn)
onDidAddPane: (fn) ->
@emitter.on 'did-add-pane', fn
observePanes: (fn) ->
fn(pane) for pane in @getPanes()
@onDidAddPane ({pane}) -> fn(pane)
onDidChangeActivePane: (fn) ->
@emitter.on 'did-change-active-pane', fn
observeActivePane: (fn) ->
fn(@getActivePane())
@onDidChangeActivePane(fn)
onDidAddPaneItem: (fn) ->
@emitter.on 'did-add-pane-item', fn
observePaneItems: (fn) ->
fn(item) for item in @getPaneItems()
@onDidAddPaneItem ({item}) -> fn(item)
onDidChangeActivePaneItem: (fn) ->
@emitter.on 'did-change-active-pane-item', fn
observeActivePaneItem: (fn) ->
fn(@getActivePaneItem())
@onDidChangeActivePaneItem(fn)
onDidDestroyPaneItem: (fn) ->
@emitter.on 'did-destroy-pane-item', fn
getRoot: -> @root
setRoot: (@root) ->
@root.setParent(this)
@root.setContainer(this)
@emitter.emit 'did-change-root', @root
if not @getActivePane()? and @root instanceof Pane
@setActivePane(@root)
replaceChild: (oldChild, newChild) ->
throw new Error("Replacing non-existent child") if oldChild isnt @root
@root = newChild
@setRoot(newChild)
getPanes: ->
@root?.getPanes() ? []
@getRoot().getPanes()
getPaneItems: ->
@getRoot().getItems()
getActivePane: ->
@activePane
setActivePane: (activePane) ->
if activePane isnt @activePane
@activePane = activePane
@emitter.emit 'did-change-active-pane', @activePane
@activePane
getActivePaneItem: ->
@getActivePane().getActiveItem()
paneForUri: (uri) ->
find @getPanes(), (pane) -> pane.itemForUri(uri)?
@@ -73,26 +139,37 @@ class PaneContainer extends Model
else
false
onRootChanged: (root) =>
@unsubscribe(@previousRoot) if @previousRoot?
@previousRoot = root
unless root?
@activePane = null
return
root.parent = this
root.container = this
@activePane ?= root if root instanceof Pane
destroyEmptyPanes: ->
pane.destroy() for pane in @getPanes() when pane.items.length is 0
itemDestroyed: (item) ->
@emit 'item-destroyed', item
paneItemDestroyed: (item) ->
@emitter.emit 'did-destroy-pane-item', item
didAddPane: (pane) ->
@emitter.emit 'did-add-pane', pane
# Called by Model superclass when destroyed
destroyed: ->
pane.destroy() for pane in @getPanes()
@subscriptions.dispose()
@emitter.dispose()
monitorActivePaneItem: ->
childSubscription = null
@subscriptions.add @observeActivePane (activePane) =>
if childSubscription?
@subscriptions.remove(childSubscription)
childSubscription.dispose()
childSubscription = activePane.observeActiveItem (activeItem) =>
@emitter.emit 'did-change-active-pane-item', activeItem
@subscriptions.add(childSubscription)
monitorPaneItems: ->
@subscriptions.add @observePanes (pane) =>
for item, index in pane.getItems()
@emitter.emit 'did-add-pane-item', {item, pane, index}
pane.onDidAddItem ({item, index}) =>
@emitter.emit 'did-add-pane-item', {item, pane, index}
+22 -15
Ver Arquivo
@@ -1,11 +1,12 @@
{$, View} = require './space-pen-extensions'
Delegator = require 'delegato'
{deprecate} = require 'grim'
{CompositeDisposable} = require 'event-kit'
PropertyAccessors = require 'property-accessors'
Pane = require './pane'
# Public: A container which can contains multiple items to be switched between.
# Extended: A container which can contains multiple items to be switched between.
#
# Items can be almost anything however most commonly they're {EditorView}s.
#
@@ -33,6 +34,8 @@ class PaneView extends View
previousActiveItem: null
initialize: (args...) ->
@subscriptions = new CompositeDisposable
if args[0] instanceof Pane
@model = args[0]
else
@@ -44,13 +47,13 @@ class PaneView extends View
@handleEvents()
handleEvents: ->
@subscribe @model.$activeItem, @onActiveItemChanged
@subscribe @model, 'item-added', @onItemAdded
@subscribe @model, 'item-removed', @onItemRemoved
@subscribe @model, 'item-moved', @onItemMoved
@subscribe @model, 'before-item-destroyed', @onBeforeItemDestroyed
@subscribe @model, 'activated', @onActivated
@subscribe @model.$active, @onActiveStatusChanged
@subscriptions.add @model.observeActiveItem(@onActiveItemChanged)
@subscriptions.add @model.onDidAddItem(@onItemAdded)
@subscriptions.add @model.onDidRemoveItem(@onItemRemoved)
@subscriptions.add @model.onDidMoveItem(@onItemMoved)
@subscriptions.add @model.onWillDestroyItem(@onBeforeItemDestroyed)
@subscriptions.add @model.onDidActivate(@onActivated)
@subscriptions.add @model.observeActive(@onActiveStatusChanged)
@subscribe this, 'focusin', => @model.focus()
@subscribe this, 'focusout', => @model.blur()
@@ -72,15 +75,18 @@ class PaneView extends View
@command 'pane:show-item-8', => @activateItemAtIndex(7)
@command 'pane:show-item-9', => @activateItemAtIndex(8)
@command 'pane:split-left', => @splitLeft(@copyActiveItem())
@command 'pane:split-right', => @splitRight(@copyActiveItem())
@command 'pane:split-up', => @splitUp(@copyActiveItem())
@command 'pane:split-down', => @splitDown(@copyActiveItem())
@command 'pane:split-left', => @model.splitLeft(copyActiveItem: true)
@command 'pane:split-right', => @model.splitRight(copyActiveItem: true)
@command 'pane:split-up', => @model.splitUp(copyActiveItem: true)
@command 'pane:split-down', => @model.splitDown(copyActiveItem: true)
@command 'pane:close', =>
@model.destroyItems()
@model.destroy()
@command 'pane:close-other-items', => @destroyInactiveItems()
# Essential: Returns the {Pane} model underlying this pane view
getModel: -> @model
# Deprecated: Use ::destroyItem
removeItem: (item) ->
deprecate("Use PaneView::destroyItem instead")
@@ -160,10 +166,10 @@ class PaneView extends View
@trigger 'pane:active-item-changed', [item]
onItemAdded: (item, index) =>
onItemAdded: ({item, index}) =>
@trigger 'pane:item-added', [item, index]
onItemRemoved: (item, index, destroyed) =>
onItemRemoved: ({item, index, destroyed}) =>
if item instanceof $
viewToRemove = item
else if viewToRemove = @viewsByItem.get(item)
@@ -177,7 +183,7 @@ class PaneView extends View
@trigger 'pane:item-removed', [item, index]
onItemMoved: (item, newIndex) =>
onItemMoved: ({item, newIndex}) =>
@trigger 'pane:item-moved', [item, newIndex]
onBeforeItemDestroyed: (item) =>
@@ -219,6 +225,7 @@ class PaneView extends View
@closest('.panes').view()
beforeRemove: ->
@subscriptions.dispose()
@model.destroy() unless @model.isDestroyed()
remove: (selector, keepData) ->
+334 -94
Ver Arquivo
@@ -1,13 +1,16 @@
{find, compact, extend, last} = require 'underscore-plus'
{Model, Sequence} = require 'theorist'
{Model} = require 'theorist'
{Emitter} = require 'event-kit'
Serializable = require 'serializable'
Grim = require 'grim'
PaneAxis = require './pane-axis'
Editor = require './editor'
PaneView = null
# Public: A container for multiple items, one of which is *active* at a given
# time. With the default packages, a tab is displayed for each item and the
# active item's view is displayed.
# Extended: A container for presenting content in the center of the workspace.
# Panes can contain multiple items, one of which is *active* at a given time.
# The view corresponding to the active item is displayed in the interface. In
# the default configuration, tabs are also displayed for each item.
module.exports =
class Pane extends Model
atom.deserializers.add(this)
@@ -30,15 +33,11 @@ class Pane extends Model
constructor: (params) ->
super
@items = Sequence.fromArray(compact(params?.items ? []))
@activeItem ?= @items[0]
@emitter = new Emitter
@items = []
@subscribe @items.onEach (item) =>
if typeof item.on is 'function'
@subscribe item, 'destroyed', => @removeItem(item, true)
@subscribe @items.onRemoval (item, index) =>
@unsubscribe item if typeof item.on is 'function'
@addItems(compact(params?.items ? []))
@setActiveItem(@items[0]) unless @getActiveItem()?
# Called by the Serializable mixin during serialization.
serializeParams: ->
@@ -57,7 +56,174 @@ class Pane extends Model
# Called by the view layer to construct a view for this model.
getViewClass: -> PaneView ?= require './pane-view'
isActive: -> @active
getParent: -> @parent
setParent: (@parent) -> @parent
getContainer: -> @container
setContainer: (container) ->
container.didAddPane({pane: this}) unless container is @container
@container = container
###
Section: Event Subscription
###
# Public: Invoke the given callback when the pane is activated.
#
# The given callback will be invoked whenever {::activate} is called on the
# pane, even if it is already active at the time.
#
# * `callback` {Function} to be called when the pane is activated.
#
# Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
onDidActivate: (callback) ->
@emitter.on 'did-activate', callback
# Public: Invoke the given callback when the pane is destroyed.
#
# * `callback` {Function} to be called when the pane is destroyed.
#
# Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
onDidDestroy: (callback) ->
@emitter.on 'did-destroy', callback
# Public: Invoke the given callback when the value of the {::isActive}
# property changes.
#
# * `callback` {Function} to be called when the value of the {::isActive}
# property changes.
# * `active` {Boolean} indicating whether the pane is active.
#
# Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
onDidChangeActive: (callback) ->
@container.onDidChangeActivePane (activePane) =>
callback(this is activePane)
# Public: Invoke the given callback with the current and future values of the
# {::isActive} property.
#
# * `callback` {Function} to be called with the current and future values of
# the {::isActive} property.
# * `active` {Boolean} indicating whether the pane is active.
#
# Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
observeActive: (callback) ->
callback(@isActive())
@onDidChangeActive(callback)
# Public: Invoke the given callback when an item is added to the pane.
#
# * `callback` {Function} to be called with when items are added.
# * `event` {Object} with the following keys:
# * `item` The added pane item.
# * `index` {Number} indicating where the item is located.
#
# Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
onDidAddItem: (callback) ->
@emitter.on 'did-add-item', callback
# Public: Invoke the given callback when an item is removed from the pane.
#
# * `callback` {Function} to be called with when items are removed.
# * `event` {Object} with the following keys:
# * `item` The removed pane item.
# * `index` {Number} indicating where the item was located.
#
# Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
onDidRemoveItem: (callback) ->
@emitter.on 'did-remove-item', callback
# Public: Invoke the given callback when an item is moved within the pane.
#
# * `callback` {Function} to be called with when items are moved.
# * `event` {Object} with the following keys:
# * `item` The removed pane item.
# * `oldIndex` {Number} indicating where the item was located.
# * `newIndex` {Number} indicating where the item is now located.
#
# Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
onDidMoveItem: (callback) ->
@emitter.on 'did-move-item', callback
# Public: Invoke the given callback with all current and future items.
#
# * `callback` {Function} to be called with current and future items.
# * `item` An item that is present in {::getItems} at the time of
# subscription or that is added at some later time.
#
# Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
observeItems: (callback) ->
callback(item) for item in @getItems()
@onDidAddItem ({item}) -> callback(item)
# Public: Invoke the given callback when the value of {::getActiveItem}
# changes.
#
# * `callback` {Function} to be called with when the active item changes.
# * `activeItem` The current active item.
#
# Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
onDidChangeActiveItem: (callback) ->
@emitter.on 'did-change-active-item', callback
# Public: Invoke the given callback with the current and future values of
# {::getActiveItem}.
#
# * `callback` {Function} to be called with the current and future active
# items.
# * `activeItem` The current active item.
#
# Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
observeActiveItem: (callback) ->
callback(@getActiveItem())
@onDidChangeActiveItem(callback)
# Public: Invoke the given callback before items are destroyed.
#
# * `callback` {Function} to be called before items are destroyed.
# * `event` {Object} with the following keys:
# * `item` The item that will be destroyed.
# * `index` The location of the item.
#
# Returns a {Disposable} on which `.dispose()` can be called to
# unsubscribe.
onWillDestroyItem: (callback) ->
@emitter.on 'will-destroy-item', callback
on: (eventName) ->
switch eventName
when 'activated'
Grim.deprecate("Use Pane::onDidActivate instead")
when 'destroyed'
Grim.deprecate("Use Pane::onDidDestroy instead")
when 'item-added'
Grim.deprecate("Use Pane::onDidAddItem instead")
when 'item-removed'
Grim.deprecate("Use Pane::onDidRemoveItem instead")
when 'item-moved'
Grim.deprecate("Use Pane::onDidMoveItem instead")
when 'before-item-destroyed'
Grim.deprecate("Use Pane::onWillDestroyItem instead")
else
Grim.deprecate("Subscribing via ::on is deprecated. Use documented event subscription methods instead.")
super
behavior: (behaviorName) ->
switch behaviorName
when 'active'
Grim.deprecate("The $active behavior property is deprecated. Use ::observeActive or ::onDidChangeActive instead.")
when 'container'
Grim.deprecate("The $container behavior property is deprecated.")
when 'activeItem'
Grim.deprecate("The $activeItem behavior property is deprecated. Use ::observeActiveItem or ::onDidChangeActiveItem instead.")
when 'focused'
Grim.deprecate("The $focused behavior property is deprecated.")
else
Grim.deprecate("Pane::behavior is deprecated. Use event subscription methods instead.")
super
# Called by the view layer to indicate that the pane has gained focus.
focus: ->
@@ -69,14 +235,12 @@ class Pane extends Model
@focused = false
true # if this is called from an event handler, don't cancel it
# Public: Makes this pane the *active* pane, causing it to gain focus
# immediately.
activate: ->
@container?.activePane = this
@emit 'activated'
getPanes: -> [this]
###
Section: Items
###
# Public: Get the items in this pane.
#
# Returns an {Array} of items.
@@ -86,15 +250,23 @@ class Pane extends Model
# Public: Get the active pane item in this pane.
#
# Returns a pane item.
getActiveItem: ->
getActiveItem: -> @activeItem
setActiveItem: (activeItem) ->
unless activeItem is @activeItem
@activeItem = activeItem
@emitter.emit 'did-change-active-item', @activeItem
@activeItem
# Public: Returns an {Editor} if the pane item is an {Editor}, or null
# otherwise.
# Return an {Editor} if the pane item is an {Editor}, or null otherwise.
getActiveEditor: ->
@activeItem if @activeItem instanceof Editor
# Public: Returns the item at the specified index.
# Public: Return the item at the given index.
#
# * `index` {Number}
#
# Returns an item or `null` if no item exists at the given index.
itemAtIndex: (index) ->
@items[index]
@@ -114,86 +286,115 @@ class Pane extends Model
else
@activateItemAtIndex(@items.length - 1)
# Returns the index of the current active item.
# Public: Get the index of the active item.
#
# Returns a {Number}.
getActiveItemIndex: ->
@items.indexOf(@activeItem)
# Makes the item at the given index active.
# Public: Activate the item at the given index.
#
# * `index` {Number}
activateItemAtIndex: (index) ->
@activateItem(@itemAtIndex(index))
# Makes the given item active, adding the item if necessary.
# Public: Make the given item *active*, causing it to be displayed by
# the pane's view.
activateItem: (item) ->
if item?
@addItem(item)
@activeItem = item
@setActiveItem(item)
# Public: Adds the item to the pane.
# Public: Add the given item to the pane.
#
# item - The item to add. It can be a model with an associated view or a view.
# index - An optional index at which to add the item. If omitted, the item is
# added after the current active item.
# * `item` The item to add. It can be a model with an associated view or a
# view.
# * `index` (optional) {Number} indicating the index at which to add the item.
# If omitted, the item is added after the current active item.
#
# Returns the added item
# Returns the added item.
addItem: (item, index=@getActiveItemIndex() + 1) ->
return if item in @items
if typeof item.on is 'function'
@subscribe item, 'destroyed', => @removeItem(item, true)
@items.splice(index, 0, item)
@emit 'item-added', item, index
@activeItem ?= item
@emitter.emit 'did-add-item', {item, index}
@setActiveItem(item) unless @getActiveItem()?
item
# Public: Adds the given items to the pane.
# Public: Add the given items to the pane.
#
# items - An {Array} of items to add. Items can be models with associated
# views or views. Any items that are already present in items will
# not be added.
# index - An optional index at which to add the item. If omitted, the item is
# added after the current active item.
# * `items` An {Array} of items to add. Items can be views or models with
# associated views. Any objects that are already present in the pane's
# current items will not be added again.
# * `index` (optional) {Number} index at which to add the items. If omitted,
# the item is # added after the current active item.
#
# Returns an {Array} of the added items
# Returns an {Array} of added items.
addItems: (items, index=@getActiveItemIndex() + 1) ->
items = items.filter (item) => not (item in @items)
@addItem(item, index + i) for item, i in items
items
removeItem: (item, destroying) ->
removeItem: (item, destroyed=false) ->
index = @items.indexOf(item)
return if index is -1
if typeof item.on is 'function'
@unsubscribe item
if item is @activeItem
if @items.length is 1
@activeItem = undefined
@setActiveItem(undefined)
else if index is 0
@activateNextItem()
else
@activatePreviousItem()
@items.splice(index, 1)
@emit 'item-removed', item, index, destroying
@container?.itemDestroyed(item) if destroying
@emit 'item-removed', item, index, destroyed
@emitter.emit 'did-remove-item', {item, index, destroyed}
@container?.paneItemDestroyed(item) if destroyed
@destroy() if @items.length is 0 and atom.config.get('core.destroyEmptyPanes')
# Public: Moves the given item to the specified index.
# Public: Move the given item to the given index.
#
# * `item` The item to move.
# * `index` {Number} indicating the index to which to move the item.
moveItem: (item, newIndex) ->
oldIndex = @items.indexOf(item)
@items.splice(oldIndex, 1)
@items.splice(newIndex, 0, item)
@emit 'item-moved', item, newIndex
@emitter.emit 'did-move-item', {item, oldIndex, newIndex}
# Public: Moves the given item to the given index at another pane.
# Public: Move the given item to the given index on another pane.
#
# * `item` The item to move.
# * `pane` {Pane} to which to move the item.
# * `index` {Number} indicating the index to which to move the item in the
# given pane.
moveItemToPane: (item, pane, index) ->
pane.addItem(item, index)
@removeItem(item)
# Public: Destroys the currently active item and make the next item active.
# Public: Destroy the active item and activate the next item.
destroyActiveItem: ->
@destroyItem(@activeItem)
false
# Public: Destroys the given item. If it is the active item, activate the next
# one. If this is the last item, also destroys the pane.
# Public: Destroy the given item.
#
# If the item is active, the next item will be activated. If the item is the
# last item, the pane will be destroyed if the `core.destroyEmptyPanes` config
# setting is `true`.
destroyItem: (item) ->
if item?
index = @items.indexOf(item)
if index isnt -1
@emit 'before-item-destroyed', item
@emitter.emit 'will-destroy-item', {item, index}
if @promptToSaveItem(item)
@removeItem(item, true)
item.destroy?()
@@ -201,27 +402,14 @@ class Pane extends Model
else
false
# Public: Destroys all items and destroys the pane.
# Public: Destroy all items.
destroyItems: ->
@destroyItem(item) for item in @getItems()
# Public: Destroys all items but the active one.
# Public: Destroy all items except for the active item.
destroyInactiveItems: ->
@destroyItem(item) for item in @getItems() when item isnt @activeItem
destroy: ->
if @container?.isAlive() and @container.getPanes().length is 1
@destroyItems()
else
super
# Called by model superclass.
destroyed: ->
@container.activateNextPane() if @isActive()
item.destroy?() for item in @items.slice()
# Public: Prompts the user to save the given item if it can be saved and is
# currently unsaved.
promptToSaveItem: (item) ->
return true unless item.shouldPromptToSave?()
@@ -236,19 +424,23 @@ class Pane extends Model
when 1 then false
when 2 then true
# Public: Saves the active item.
saveActiveItem: ->
@saveItem(@activeItem)
# Public: Save the active item.
saveActiveItem: (nextAction) ->
@saveItem(@getActiveItem(), nextAction)
# Public: Saves the active item at a prompted-for location.
saveActiveItemAs: ->
@saveItemAs(@activeItem)
# Public: Saves the specified item.
# Public: Prompt the user for a location and save the active item with the
# path they select.
#
# item - The item to save.
# nextAction - An optional function which will be called after the item is
# saved.
# * `nextAction` (optional) {Function} which will be called after the item is
# successfully saved.
saveActiveItemAs: (nextAction) ->
@saveItemAs(@getActiveItem(), nextAction)
# Public: Save the given item.
#
# * `item` The item to save.
# * `nextAction` (optional) {Function} which will be called after the item is
# successfully saved.
saveItem: (item, nextAction) ->
if item?.getUri?()
item.save?()
@@ -256,11 +448,12 @@ class Pane extends Model
else
@saveItemAs(item, nextAction)
# Public: Saves the given item at a prompted-for location.
# Public: Prompt the user for a location and save the active item with the
# path they select.
#
# item - The item to save.
# nextAction - An optional function which will be called after the item is
# saved.
# * `item` The item to save.
# * `nextAction` (optional) {Function} which will be called after the item is
# successfully saved.
saveItemAs: (item, nextAction) ->
return unless item?.saveAs?
@@ -270,17 +463,20 @@ class Pane extends Model
item.saveAs(newItemPath)
nextAction?()
# Public: Saves all items.
# Public: Save all items.
saveItems: ->
@saveItem(item) for item in @getItems()
# Public: Returns the first item that matches the given URI or undefined if
# Public: Return the first item that matches the given URI or undefined if
# none exists.
#
# * `uri` {String} containing a URI.
itemForUri: (uri) ->
find @items, (item) -> item.getUri?() is uri
# Public: Activates the first item that matches the given URI. Returns a
# boolean indicating whether a matching item was found.
# Public: Activate the first item that matches the given URI.
#
# Returns a {Boolean} indicating whether an item matching the URI was found.
activateItemForUri: (uri) ->
if item = @itemForUri(uri)
@activateItem(item)
@@ -292,19 +488,57 @@ class Pane extends Model
if @activeItem?
@activeItem.copy?() ? atom.deserializers.deserialize(@activeItem.serialize())
# Public: Creates a new pane to the left of the receiver.
###
Section: Lifecycle
###
# Public: Determine whether the pane is active.
#
# params - An object with keys:
# :items - An optional array of items with which to construct the new pane.
# Returns a {Boolean}.
isActive: ->
@container?.getActivePane() is this
# Public: Makes this pane the *active* pane, causing it to gain focus.
activate: ->
@container?.setActivePane(this)
@emit 'activated'
@emitter.emit 'did-activate'
# Public: Close the pane and destroy all its items.
#
# If this is the last pane, all the items will be destroyed but the pane
# itself will not be destroyed.
destroy: ->
if @container?.isAlive() and @container.getPanes().length is 1
@destroyItems()
else
super
# Called by model superclass.
destroyed: ->
@container.activateNextPane() if @isActive()
@emitter.emit 'did-destroy'
item.destroy?() for item in @items.slice()
###
Section: Splitting
###
# Public: Create a new pane to the left of this pane.
#
# * `params` (optional) {Object} with the following keys:
# * `items` (optional) {Array} of items to add to the new pane.
# * `copyActiveItem` (optional) {Boolean} true will copy the active item into the new split pane
#
# Returns the new {Pane}.
splitLeft: (params) ->
@split('horizontal', 'before', params)
# Public: Creates a new pane to the right of the receiver.
# Public: Create a new pane to the right of this pane.
#
# params - An object with keys:
# :items - An optional array of items with which to construct the new pane.
# * `params` (optional) {Object} with the following keys:
# * `items` (optional) {Array} of items to add to the new pane.
# * `copyActiveItem` (optional) {Boolean} true will copy the active item into the new split pane
#
# Returns the new {Pane}.
splitRight: (params) ->
@@ -312,8 +546,9 @@ class Pane extends Model
# Public: Creates a new pane above the receiver.
#
# params - An object with keys:
# :items - An optional array of items with which to construct the new pane.
# * `params` (optional) {Object} with the following keys:
# * `items` (optional) {Array} of items to add to the new pane.
# * `copyActiveItem` (optional) {Boolean} true will copy the active item into the new split pane
#
# Returns the new {Pane}.
splitUp: (params) ->
@@ -321,14 +556,19 @@ class Pane extends Model
# Public: Creates a new pane below the receiver.
#
# params - An object with keys:
# :items - An optional array of items with which to construct the new pane.
# * `params` (optional) {Object} with the following keys:
# * `items` (optional) {Array} of items to add to the new pane.
# * `copyActiveItem` (optional) {Boolean} true will copy the active item into the new split pane
#
# Returns the new {Pane}.
splitDown: (params) ->
@split('vertical', 'after', params)
split: (orientation, side, params) ->
if params?.copyActiveItem
params.items ?= []
params.items.push(@copyActiveItem())
if @parent.orientation isnt orientation
@parent.replaceChild(this, new PaneAxis({@container, orientation, children: [this]}))
+45 -23
Ver Arquivo
@@ -15,15 +15,30 @@ Editor = require './editor'
Task = require './task'
Git = require './git'
# Public: Represents a project that's opened in Atom.
# Extended: Represents a project that's opened in Atom.
#
# An instance of this class is always available as the `atom.project` global.
#
# ## Events
#
# ### path-changed
#
# Extended: Emit when the project's path has changed. Use {::getPath} to get the new path
#
# ### buffer-created
#
# Extended: Emit when a buffer is created. For example, when {::open} is called, this is fired.
#
# * `buffer` {TextBuffer} the new buffer that was created.
#
module.exports =
class Project extends Model
atom.deserializers.add(this)
Serializable.includeInto(this)
# Public: Find the local path for the given repository URL.
#
# * `repoUrl` {String} url to a git repository
@pathForRepositoryUrl: (repoUrl) ->
[repoName] = url.parse(repoUrl).path.split('/')[-1..]
repoName = repoName.replace(/\.git$/, '')
@@ -34,7 +49,7 @@ class Project extends Model
for buffer in @buffers
do (buffer) =>
buffer.once 'destroyed', => @removeBuffer(buffer)
buffer.onDidDestroy => @removeBuffer(buffer)
@setPath(path)
@@ -61,12 +76,15 @@ class Project extends Model
# Public: Returns the {Git} repository if available.
getRepo: -> @repo
# Public: Returns the project's fullpath.
# Public: Returns the project's {String} fullpath.
getPath: ->
@rootDirectory?.path
# Public: Sets the project's fullpath.
#
# * `projectPath` {String} path
setPath: (projectPath) ->
projectPath = path.normalize(projectPath) if projectPath
@path = projectPath
@rootDirectory?.off()
@@ -90,7 +108,7 @@ class Project extends Model
# the path is already absolute or if it is prefixed with a scheme, it is
# returned unchanged.
#
# uri - The {String} name of the path to convert.
# * `uri` The {String} name of the path to convert.
#
# Returns a {String} or undefined if the uri is not missing or empty.
resolve: (uri) ->
@@ -100,26 +118,30 @@ class Project extends Model
uri
else
if fs.isAbsolute(uri)
fs.absolute(uri)
path.normalize(fs.absolute(uri))
else if projectPath = @getPath()
fs.absolute(path.join(projectPath, uri))
path.normalize(fs.absolute(path.join(projectPath, uri)))
else
undefined
# Public: Make the given path relative to the project directory.
#
# * `fullPath` {String} full path
relativize: (fullPath) ->
return fullPath if fullPath?.match(/[A-Za-z0-9+-.]+:\/\//) # leave path alone if it has a scheme
@rootDirectory?.relativize(fullPath) ? fullPath
# Public: Returns whether the given path is inside this project.
#
# * `pathToCheck` {String} path
contains: (pathToCheck) ->
@rootDirectory?.contains(pathToCheck) ? false
# Given a path to a file, this constructs and associates a new
# {Editor}, showing the file.
#
# filePath - The {String} path of the file to associate with.
# options - Options that you can pass to the {Editor} constructor.
# * `filePath` The {String} path of the file to associate with.
# * `options` Options that you can pass to the {Editor} constructor.
#
# Returns a promise that resolves to an {Editor}.
open: (filePath, options={}) ->
@@ -158,7 +180,7 @@ class Project extends Model
# If the `filePath` already has a `buffer`, that value is used instead. Otherwise,
# `text` is used as the contents of the new buffer.
#
# filePath - A {String} representing a path. If `null`, an "Untitled" buffer is created.
# * `filePath` A {String} representing a path. If `null`, an "Untitled" buffer is created.
#
# Returns a promise that resolves to the {TextBuffer}.
bufferForPath: (filePath) ->
@@ -178,13 +200,13 @@ class Project extends Model
# Given a file path, this sets its {TextBuffer}.
#
# absoluteFilePath - A {String} representing a path.
# text - The {String} text to use as a buffer.
# * `absoluteFilePath` A {String} representing a path.
# * `text` The {String} text to use as a buffer.
#
# Returns a promise that resolves to the {TextBuffer}.
buildBuffer: (absoluteFilePath) ->
if fs.getSizeSync(absoluteFilePath) >= 2 * 1048576 # 2MB
throw new Error("Atom can only handle files < 2MB, for now.")
throw new Error("Atom can only handle files < 2MB for now.")
buffer = new TextBuffer({filePath: absoluteFilePath})
@addBuffer(buffer)
@@ -194,11 +216,11 @@ class Project extends Model
addBuffer: (buffer, options={}) ->
@addBufferAtIndex(buffer, @buffers.length, options)
buffer.once 'destroyed', => @removeBuffer(buffer)
buffer.onDidDestroy => @removeBuffer(buffer)
addBufferAtIndex: (buffer, index, options={}) ->
@buffers.splice(index, 0, buffer)
buffer.once 'destroyed', => @removeBuffer(buffer)
buffer.onDidDestroy => @removeBuffer(buffer)
@emit 'buffer-created', buffer
buffer
@@ -215,10 +237,10 @@ class Project extends Model
# Public: Performs a search across all the files in the project.
#
# regex - A {RegExp} to search with.
# options - An optional options {Object} (default: {}):
# :paths - An {Array} of glob patterns to search within
# iterator - A {Function} callback on each file found
# * `regex` {RegExp} to search with.
# * `options` (optional) {Object} (default: {})
# * `paths` An {Array} of glob patterns to search within
# * `iterator` {Function} callback on each file found
scan: (regex, options={}, iterator) ->
if _.isFunction(options)
iterator = options
@@ -261,11 +283,11 @@ class Project extends Model
# Public: Performs a replace across all the specified files in the project.
#
# regex - A {RegExp} to search with.
# replacementText - Text to replace all matches of regex with
# filePaths - List of file path strings to run the replace on.
# iterator - A {Function} callback on each file with replacements:
# `({filePath, replacements}) ->`.
# * `regex` A {RegExp} to search with.
# * `replacementText` Text to replace all matches of regex with
# * `filePaths` List of file path strings to run the replace on.
# * `iterator` A {Function} callback on each file with replacements:
# * `options` {Object} with keys `filePath` and `replacements`
replace: (regex, replacementText, filePaths, iterator) ->
deferred = Q.defer()
-244
Ver Arquivo
@@ -1,244 +0,0 @@
{View, $} = require 'space-pen'
React = require 'react-atom-fork'
{defaults} = require 'underscore-plus'
TextBuffer = require 'text-buffer'
Editor = require './editor'
EditorComponent = require './editor-component'
module.exports =
class ReactEditorView extends View
@content: (params) ->
attributes = params.attributes ? {}
attributes.class = 'editor react editor-colors'
attributes.tabIndex = -1
@div attributes
focusOnAttach: false
constructor: (editorOrParams, props) ->
super
if editorOrParams instanceof Editor
@editor = editorOrParams
else
{@editor, mini, placeholderText} = editorOrParams
props ?= {}
props.mini = mini
props.placeholderText = placeholderText
@editor ?= new Editor
buffer: new TextBuffer
softWrap: false
tabLength: 2
softTabs: true
mini: mini
props = defaults({@editor, parentView: this}, props)
@component = React.renderComponent(EditorComponent(props), @element)
node = @component.getDOMNode()
@scrollView = $(node).find('.scroll-view')
@underlayer = $(node).find('.highlights').addClass('underlayer')
@overlayer = $(node).find('.lines').addClass('overlayer')
@hiddenInput = $(node).find('.hidden-input')
# FIXME: there should be a better way to deal with the gutter element
@subscribe atom.config.observe 'editor.showLineNumbers', =>
@gutter = $(node).find('.gutter')
@gutter.removeClassFromAllLines = (klass) =>
@gutter.find('.line-number').removeClass(klass)
@gutter.getLineNumberElement = (bufferRow) =>
@gutter.find("[data-buffer-row='#{bufferRow}']")
@gutter.addClassToLine = (bufferRow, klass) =>
lines = @gutter.find("[data-buffer-row='#{bufferRow}']")
lines.addClass(klass)
lines.length > 0
@on 'focus', =>
if @component?
@component.onFocus()
else
@focusOnAttach = true
getEditor: -> @editor
getModel: -> @editor
Object.defineProperty @::, 'lineHeight', get: -> @editor.getLineHeightInPixels()
Object.defineProperty @::, 'charWidth', get: -> @editor.getDefaultCharWidth()
Object.defineProperty @::, 'firstRenderedScreenRow', get: -> @component.getRenderedRowRange()[0]
Object.defineProperty @::, 'lastRenderedScreenRow', get: -> @component.getRenderedRowRange()[1]
Object.defineProperty @::, 'active', get: -> @is(@getPane()?.activeView)
Object.defineProperty @::, 'isFocused', get: -> @component?.state.focused
Object.defineProperty @::, 'mini', get: -> @component?.props.mini
afterAttach: (onDom) ->
return unless onDom
return if @attached
@attached = true
@component.pollDOM()
@focus() if @focusOnAttach
@trigger 'editor:attached', [this]
scrollTop: (scrollTop) ->
if scrollTop?
@editor.setScrollTop(scrollTop)
else
@editor.getScrollTop()
scrollLeft: (scrollLeft) ->
if scrollLeft?
@editor.setScrollLeft(scrollLeft)
else
@editor.getScrollLeft()
scrollToBottom: ->
@editor.setScrollBottom(Infinity)
scrollToScreenPosition: (screenPosition, options) ->
@editor.scrollToScreenPosition(screenPosition, options)
scrollToBufferPosition: (bufferPosition, options) ->
@editor.scrollToBufferPosition(bufferPosition, options)
scrollToCursorPosition: ->
@editor.scrollToCursorPosition()
scrollToPixelPosition: (pixelPosition) ->
screenPosition = screenPositionForPixelPosition(pixelPosition)
@editor.scrollToScreenPosition(screenPosition)
pixelPositionForBufferPosition: (bufferPosition) ->
@editor.pixelPositionForBufferPosition(bufferPosition)
pixelPositionForScreenPosition: (screenPosition) ->
@editor.pixelPositionForScreenPosition(screenPosition)
appendToLinesView: (view) ->
view.css('position', 'absolute')
view.css('z-index', 1)
@find('.lines').prepend(view)
beforeRemove: ->
return unless @attached
@attached = false
React.unmountComponentAtNode(@element) if @component.isMounted()
@trigger 'editor:detached', this
# Public: Split the editor view left.
splitLeft: ->
pane = @getPane()
pane?.splitLeft(pane?.copyActiveItem()).activeView
# Public: Split the editor view right.
splitRight: ->
pane = @getPane()
pane?.splitRight(pane?.copyActiveItem()).activeView
# Public: Split the editor view up.
splitUp: ->
pane = @getPane()
pane?.splitUp(pane?.copyActiveItem()).activeView
# Public: Split the editor view down.
splitDown: ->
pane = @getPane()
pane?.splitDown(pane?.copyActiveItem()).activeView
getPane: ->
@parent('.item-views').parents('.pane').view()
hide: ->
super
@pollComponentDOM()
show: ->
super
@pollComponentDOM()
pollComponentDOM: ->
return unless @component?
valueToRestore = @component.performSyncUpdates
@component.performSyncUpdates = true
@component.pollDOM()
@component.performSyncUpdates = valueToRestore
pageDown: ->
@editor.pageDown()
pageUp: ->
@editor.pageUp()
getFirstVisibleScreenRow: ->
@editor.getVisibleRowRange()[0]
getLastVisibleScreenRow: ->
@editor.getVisibleRowRange()[1]
getFontFamily: ->
@component?.getFontFamily()
setFontFamily: (fontFamily)->
@component?.setFontFamily(fontFamily)
getFontSize: ->
@component?.getFontSize()
setFontSize: (fontSize)->
@component?.setFontSize(fontSize)
setWidthInChars: (widthInChars) ->
@component.getDOMNode().style.width = (@editor.getDefaultCharWidth() * widthInChars) + 'px'
setLineHeight: (lineHeight) ->
@component.setLineHeight(lineHeight)
setShowIndentGuide: (showIndentGuide) ->
@component.setShowIndentGuide(showIndentGuide)
setSoftWrap: (softWrap) ->
@editor.setSoftWrap(softWrap)
setShowInvisibles: (showInvisibles) ->
@component.setShowInvisibles(showInvisibles)
toggleSoftWrap: ->
@editor.toggleSoftWrap()
toggleSoftTabs: ->
@editor.toggleSoftTabs()
getText: ->
@editor.getText()
setText: (text) ->
@editor.setText(text)
insertText: (text) ->
@editor.insertText(text)
isInputEnabled: ->
@component.isInputEnabled()
setInputEnabled: (inputEnabled) ->
@component.setInputEnabled(inputEnabled)
requestDisplayUpdate: -> # No-op shim for find-and-replace
updateDisplay: -> # No-op shim for package specs
resetDisplay: -> # No-op shim for package specs
redraw: -> # No-op shim
setPlaceholderText: (placeholderText) ->
if @component?
@component.setProps({placeholderText})
else
@props.placeholderText = placeholderText
lineElementForScreenRow: (screenRow) ->
$(@component.lineNodeForScreenRow(screenRow))
+20 -11
Ver Arquivo
@@ -2,22 +2,31 @@
# Public: Represents a view that scrolls.
#
# Subclasses must call `super` if overriding the `initialize` method or else
# the following events won't be handled by the ScrollView.
# Handles several core events to update scroll position:
#
# ## Events
# * `core:move-up`
# * `core:move-down`
# * `core:page-up`
# * `core:page-down`
# * `core:move-to-top`
# * `core:move-to-bottom`
# * `core:move-up` Scrolls the view up
# * `core:move-down` Scrolls the view down
# * `core:page-up` Scrolls the view up by the height of the page
# * `core:page-down` Scrolls the view down by the height of the page
# * `core:move-to-top` Scrolls the editor to the top
# * `core:move-to-bottom` Scroll the editor to the bottom
#
# ## Requiring in packages
# Subclasses must call `super` if overriding the `initialize` method.
#
# ## Examples
#
# ```coffee
# {ScrollView} = require 'atom'
# {ScrollView} = require 'atom'
#
# class MyView extends ScrollView
# @content: ->
# @div()
#
# initialize: ->
# super
# @text('super long content that will scroll')
# ```
#
module.exports =
class ScrollView extends View
initialize: ->
+2 -2
Ver Arquivo
@@ -39,9 +39,9 @@ ScrollbarComponent = React.createClass
switch @props.orientation
when 'vertical'
not isEqualForProperties(newProps, @props, 'scrollHeight', 'scrollTop', 'scrollableInOppositeDirection')
not isEqualForProperties(newProps, @props, 'scrollHeight', 'scrollTop', 'scrollableInOppositeDirection', 'verticalScrollbarWidth')
when 'horizontal'
not isEqualForProperties(newProps, @props, 'scrollWidth', 'scrollLeft', 'scrollableInOppositeDirection')
not isEqualForProperties(newProps, @props, 'scrollWidth', 'scrollLeft', 'scrollableInOppositeDirection', 'horizontalScrollbarHeight')
componentDidUpdate: ->
{orientation, scrollTop, scrollLeft} = @props
+13 -17
Ver Arquivo
@@ -1,11 +1,8 @@
{$, View} = require './space-pen-extensions'
if atom.config.get('core.useReactMiniEditors')
EditorView = require './react-editor-view'
else
EditorView = require './editor-view'
EditorView = require './editor-view'
fuzzyFilter = require('fuzzaldrin').filter
# Public: Provides a view that renders a list of items with an editor that
# Essential: Provides a view that renders a list of items with an editor that
# filters the items. Used by many packages such as the fuzzy-finder,
# command-palette, symbols-view and autocomplete.
#
@@ -54,7 +51,7 @@ class SelectListView extends View
# This method can be overridden by subclasses but `super` should always
# be called.
initialize: ->
@filterEditorView.getEditor().getBuffer().on 'changed', =>
@filterEditorView.getEditor().getBuffer().onDidChange =>
@schedulePopulateList()
@filterEditorView.hiddenInput.on 'focusout', =>
@cancel() unless @cancelling
@@ -99,14 +96,14 @@ class SelectListView extends View
# This should be model items not actual views. {::viewForItem} will be
# called to render the item when it is being appended to the list view.
#
# items - The {Array} of model items to display in the list (default: []).
# * `items` The {Array} of model items to display in the list (default: []).
setItems: (@items=[]) ->
@populateList()
@setLoading()
# Public: Set the error message to display.
#
# message - The {String} error message (default: '').
# * `message` The {String} error message (default: '').
setError: (message='') ->
if message.length is 0
@error.text('').hide()
@@ -116,7 +113,7 @@ class SelectListView extends View
# Public: Set the loading message to display.
#
# message - The {String} loading message (default: '').
# * `message` The {String} loading message (default: '').
setLoading: (message='') ->
if message.length is 0
@loading.text("")
@@ -168,15 +165,15 @@ class SelectListView extends View
#
# Subclasses may override this method to customize the message.
#
# itemCount - The {Number} of items in the array specified to {::setItems}
# filteredItemCount - The {Number} of items that pass the fuzzy filter test.
# * `itemCount` The {Number} of items in the array specified to {::setItems}
# * `filteredItemCount` The {Number} of items that pass the fuzzy filter test.
#
# Returns a {String} message (default: 'No matches found').
getEmptyMessage: (itemCount, filteredItemCount) -> 'No matches found'
# Public: Set the maximum numbers of items to display in the list.
#
# maxItems - The maximum {Number} of items to display.
# * `maxItems` The maximum {Number} of items to display.
setMaxItems: (@maxItems) ->
selectPreviousItemView: ->
@@ -227,8 +224,8 @@ class SelectListView extends View
#
# This is called when the item is about to appended to the list view.
#
# item - The model item being rendered. This will always be one of the items
# previously passed to {::setItems}.
# * `item` The model item being rendered. This will always be one of the items
# previously passed to {::setItems}.
#
# Returns a String of HTML, DOM element, jQuery object, or View.
viewForItem: (item) ->
@@ -238,8 +235,8 @@ class SelectListView extends View
#
# This method must be overridden by subclasses.
#
# item - The selected model item. This will always be one of the items
# previously passed to {::setItems}.
# * `item` The selected model item. This will always be one of the items
# previously passed to {::setItems}.
#
# Returns a DOM element, jQuery object, or {View}.
confirmed: (item) ->
@@ -275,7 +272,6 @@ class SelectListView extends View
cancelled: ->
@filterEditorView.getEditor().setText('')
@filterEditorView.updateDisplay()
# Public: Cancel and close this select list view.
#
-85
Ver Arquivo
@@ -1,85 +0,0 @@
{Point, Range} = require 'text-buffer'
{View, $$} = require './space-pen-extensions'
module.exports =
class SelectionView extends View
@content: ->
@div class: 'selection'
regions: null
needsRemoval: false
initialize: ({@editorView, @selection} = {}) ->
@regions = []
@selection.on 'screen-range-changed', => @editorView.requestDisplayUpdate()
@selection.on 'destroyed', =>
@needsRemoval = true
@editorView.requestDisplayUpdate()
updateDisplay: ->
@clearRegions()
range = @getScreenRange()
@trigger 'selection:changed'
@editorView.highlightFoldsContainingBufferRange(@getBufferRange())
return if range.isEmpty()
rowSpan = range.end.row - range.start.row
if rowSpan == 0
@appendRegion(1, range.start, range.end)
else
@appendRegion(1, range.start, null)
if rowSpan > 1
@appendRegion(rowSpan - 1, { row: range.start.row + 1, column: 0}, null)
@appendRegion(1, { row: range.end.row, column: 0 }, range.end)
appendRegion: (rows, start, end) ->
{ lineHeight, charWidth } = @editorView
css = @editorView.pixelPositionForScreenPosition(start)
css.height = lineHeight * rows
if end
css.width = @editorView.pixelPositionForScreenPosition(end).left - css.left
else
css.right = 0
region = ($$ -> @div class: 'region').css(css)
@append(region)
@regions.push(region)
getCenterPixelPosition: ->
{ start, end } = @getScreenRange()
startRow = start.row
endRow = end.row
endRow-- if end.column == 0
@editorView.pixelPositionForScreenPosition([((startRow + endRow + 1) / 2), start.column])
clearRegions: ->
region.remove() for region in @regions
@regions = []
getScreenRange: ->
@selection.getScreenRange()
getBufferRange: ->
@selection.getBufferRange()
needsAutoscroll: ->
@selection.needsAutoscroll
clearAutoscroll: ->
@selection.clearAutoscroll()
highlight: ->
@unhighlight()
@addClass('highlighted')
clearTimeout(@unhighlightTimeout)
@unhighlightTimeout = setTimeout((=> @unhighlight()), 1000)
unhighlight: ->
@removeClass('highlighted')
remove: ->
@editorView.removeSelectionView(this)
super
+96 -50
Ver Arquivo
@@ -1,8 +1,10 @@
{Point, Range} = require 'text-buffer'
{Model} = require 'theorist'
{pick} = require 'underscore-plus'
{Emitter} = require 'event-kit'
Grim = require 'grim'
# Public: Represents a selection in the {Editor}.
# Extended: Represents a selection in the {Editor}.
module.exports =
class Selection extends Model
cursor: null
@@ -13,15 +15,50 @@ class Selection extends Model
needsAutoscroll: null
constructor: ({@cursor, @marker, @editor, id}) ->
@emitter = new Emitter
@assignId(id)
@cursor.selection = this
@decoration = @editor.decorateMarker(@marker, type: 'highlight', class: 'selection')
@marker.on 'changed', => @screenRangeChanged()
@marker.on 'destroyed', =>
@destroyed = true
@editor.removeSelection(this)
@emit 'destroyed' unless @editor.isDestroyed()
@marker.onDidChange => @screenRangeChanged()
@marker.onDidDestroy =>
unless @editor.isDestroyed()
@destroyed = true
@editor.removeSelection(this)
@emit 'destroyed'
@emitter.emit 'did-destroy'
@emitter.dispose()
###
Section: Events
###
# Extended: Calls your `callback` when the selection was moved.
#
# * `callback` {Function}
# * `screenRange` {Range} indicating the new screenrange
onDidChangeRange: (callback) ->
@emitter.on 'did-change-range', callback
# Extended: Calls your `callback` when the selection was destroyed
#
# * `callback` {Function}
onDidDestroy: (callback) ->
@emitter.on 'did-destroy', callback
on: (eventName) ->
switch eventName
when 'screen-range-changed'
Grim.deprecate("Use Selection::onDidChangeRange instead. Call ::getScreenRange() yourself in your callback if you need the range.")
when 'destroyed'
Grim.deprecate("Use Selection::onDidDestroy instead.")
super
###
Section: Methods
###
destroy: ->
@marker.destroy()
@@ -56,8 +93,8 @@ class Selection extends Model
# Public: Modifies the screen range for the selection.
#
# screenRange - The new {Range} to use.
# options - A hash of options matching those found in {::setBufferRange}.
# * `screenRange` The new {Range} to use.
# * `options` (optional) {Object} options matching those found in {::setBufferRange}.
setScreenRange: (screenRange, options) ->
@setBufferRange(@editor.bufferRangeForScreenRange(screenRange), options)
@@ -67,11 +104,10 @@ class Selection extends Model
# Public: Modifies the buffer {Range} for the selection.
#
# screenRange - The new {Range} to select.
# options - An {Object} with the keys:
# :preserveFolds - if `true`, the fold settings are preserved after the
# selection moves.
# :autoscroll - if `true`, the {Editor} scrolls to the new selection.
# * `screenRange` The new {Range} to select.
# * `options` (optional) {Object} with the keys:
# * `preserveFolds` if `true`, the fold settings are preserved after the selection moves.
# * `autoscroll` if `true`, the {Editor} scrolls to the new selection.
setBufferRange: (bufferRange, options={}) ->
bufferRange = Range.fromObject(bufferRange)
@needsAutoscroll = options.autoscroll
@@ -141,7 +177,7 @@ class Selection extends Model
# Public: Selects an entire line in the buffer.
#
# row - The line {Number} to select (default: the row of the cursor).
# * `row` The line {Number} to select (default: the row of the cursor).
selectLine: (row=@cursor.getBufferPosition().row) ->
range = @editor.bufferRangeForBufferRow(row, includeNewline: true)
@setBufferRange(@getBufferRange().union(range))
@@ -160,7 +196,7 @@ class Selection extends Model
# Public: Selects the text from the current cursor position to a given screen
# position.
#
# position - An instance of {Point}, with a given `row` and `column`.
# * `position` An instance of {Point}, with a given `row` and `column`.
selectToScreenPosition: (position) ->
position = Point.fromObject(position)
@@ -181,23 +217,31 @@ class Selection extends Model
# Public: Selects the text from the current cursor position to a given buffer
# position.
#
# position - An instance of {Point}, with a given `row` and `column`.
# * `position` An instance of {Point}, with a given `row` and `column`.
selectToBufferPosition: (position) ->
@modifySelection => @cursor.setBufferPosition(position)
# Public: Selects the text one position right of the cursor.
selectRight: ->
@modifySelection => @cursor.moveRight()
#
# * `columnCount` (optional) {Number} number of columns to select (default: 1)
selectRight: (columnCount) ->
@modifySelection => @cursor.moveRight(columnCount)
# Public: Selects the text one position left of the cursor.
selectLeft: ->
@modifySelection => @cursor.moveLeft()
#
# * `columnCount` (optional) {Number} number of columns to select (default: 1)
selectLeft: (columnCount) ->
@modifySelection => @cursor.moveLeft(columnCount)
# Public: Selects all the text one position above the cursor.
#
# * `rowCount` (optional) {Number} number of rows to select (default: 1)
selectUp: (rowCount) ->
@modifySelection => @cursor.moveUp(rowCount)
# Public: Selects all the text one position below the cursor.
#
# * `rowCount` (optional) {Number} number of rows to select (default: 1)
selectDown: (rowCount) ->
@modifySelection => @cursor.moveDown(rowCount)
@@ -306,14 +350,14 @@ class Selection extends Model
# Public: Replaces text at the current selection.
#
# text - A {String} representing the text to add
# options - An {Object} with keys:
# :select - if `true`, selects the newly added text.
# :autoIndent - if `true`, indents all inserted text appropriately.
# :autoIndentNewline - if `true`, indent newline appropriately.
# :autoDecreaseIndent - if `true`, decreases indent level appropriately
# (for example, when a closing bracket is inserted).
# :undo - if `skip`, skips the undo stack for this operation.
# * `text` A {String} representing the text to add
# * `options` (optional) {Object} with keys:
# * `select` if `true`, selects the newly added text.
# * `autoIndent` if `true`, indents all inserted text appropriately.
# * `autoIndentNewline` if `true`, indent newline appropriately.
# * `autoDecreaseIndent` if `true`, decreases indent level appropriately
# (for example, when a closing bracket is inserted).
# * `undo` if `skip`, skips the undo stack for this operation.
insertText: (text, options={}) ->
oldBufferRange = @getBufferRange()
@editor.unfoldBufferRow(oldBufferRange.end.row)
@@ -345,8 +389,8 @@ class Selection extends Model
# Public: Indents the given text to the suggested level based on the grammar.
#
# text - The {String} to indent within the selection.
# indentBasis - The beginning indent level.
# * `text` The {String} to indent within the selection.
# * `indentBasis` The beginning indent level.
normalizeIndents: (text, indentBasis) ->
textPrecedingCursor = @cursor.getCurrentBufferLine()[0...@cursor.getBufferColumn()]
isCursorInsideExistingLine = /\S/.test(textPrecedingCursor)
@@ -378,10 +422,10 @@ class Selection extends Model
# non-whitespace characters, and otherwise inserts a tab. If the selection is
# non empty, calls {::indentSelectedRows}.
#
# options - A {Object} with the keys:
# :autoIndent - If `true`, the line is indented to an automatically-inferred
# level. Otherwise, {Editor::getTabText} is inserted.
indent: ({ autoIndent }={})->
# * `options` (optional) {Object} with the keys:
# * `autoIndent` If `true`, the line is indented to an automatically-inferred
# level. Otherwise, {Editor::getTabText} is inserted.
indent: ({ autoIndent }={}) ->
{ row, column } = @cursor.getBufferPosition()
if @isEmpty()
@@ -549,16 +593,18 @@ class Selection extends Model
@cut(maintainClipboard)
# Public: Copies the selection to the clipboard and then deletes it.
#
# * `maintainClipboard` {Boolean} (default: false) See {::copy}
cut: (maintainClipboard=false) ->
@copy(maintainClipboard)
@delete()
# Public: Copies the current selection to the clipboard.
#
# If the `maintainClipboard` is set to `true`, a specific metadata property
# is created to store each content copied to the clipboard. The clipboard
# `text` still contains the concatenation of the clipboard with the
# current selection.
# * `maintainClipboard` {Boolean} if `true`, a specific metadata property
# is created to store each content copied to the clipboard. The clipboard
# `text` still contains the concatenation of the clipboard with the
# current selection. (default: false)
copy: (maintainClipboard=false) ->
return if @isEmpty()
text = @editor.buffer.getTextInRange(@getBufferRange())
@@ -598,9 +644,9 @@ class Selection extends Model
# Public: Identifies if a selection intersects with a given buffer range.
#
# bufferRange - A {Range} to check against.
# * `bufferRange` A {Range} to check against.
#
# Returns a Boolean.
# Returns a {Boolean}
intersectsBufferRange: (bufferRange) ->
@getBufferRange().intersectsWith(bufferRange)
@@ -612,17 +658,17 @@ class Selection extends Model
# Public: Identifies if a selection intersects with another selection.
#
# otherSelection - A {Selection} to check against.
# * `otherSelection` A {Selection} to check against.
#
# Returns a Boolean.
intersectsWith: (otherSelection) ->
@getBufferRange().intersectsWith(otherSelection.getBufferRange())
# Returns a {Boolean}
intersectsWith: (otherSelection, exclusive) ->
@getBufferRange().intersectsWith(otherSelection.getBufferRange(), exclusive)
# Public: Combines the given selection into this selection and then destroys
# the given selection.
#
# otherSelection - A {Selection} to merge with.
# options - A hash of options matching those found in {::setBufferRange}.
# * `otherSelection` A {Selection} to merge with.
# * `options` (optional) {Object} options matching those found in {::setBufferRange}.
merge: (otherSelection, options) ->
myGoalBufferRange = @getGoalBufferRange()
otherGoalBufferRange = otherSelection.getGoalBufferRange()
@@ -638,11 +684,11 @@ class Selection extends Model
#
# See {Range::compare} for more details.
#
# otherSelection - A {Selection} to compare against.
# * `otherSelection` A {Selection} to compare against
compare: (otherSelection) ->
@getBufferRange().compare(otherSelection.getBufferRange())
screenRangeChanged: ->
screenRange = @getScreenRange()
@emit 'screen-range-changed', screenRange
@editor.selectionScreenRangeChanged(this)
@emit 'screen-range-changed', @getScreenRange()
@emitter.emit 'did-change-range'
@editor.selectionRangeChanged(this)
+5 -4
Ver Arquivo
@@ -14,7 +14,7 @@ Token = require './token'
# An instance of this class is always available as the `atom.syntax` global.
#
# The Syntax class also contains properties for things such as the
# language-specific comment regexes.
# language-specific comment regexes. See {::getProperty} for more details.
module.exports =
class Syntax extends GrammarRegistry
PropertyAccessors.includeInto(this)
@@ -55,14 +55,15 @@ class Syntax extends GrammarRegistry
# Public: Get a property for the given scope and key path.
#
# ## Example
# ## Examples
#
# ```coffee
# comment = atom.syntax.getProperty(['.source.ruby'], 'editor.commentStart')
# console.log(comment) # '# '
# ```
#
# scope - An {Array} of {String} scopes.
# keyPath - A {String} key path.
# * `scope` An {Array} of {String} scopes.
# * `keyPath` A {String} key path.
#
# Returns a {String} property value or undefined.
getProperty: (scope, keyPath) ->
+46 -21
Ver Arquivo
@@ -6,27 +6,41 @@ child_process = require 'child_process'
#
# Used by the fuzzy-finder.
#
# ## Events
#
# * task:log - Emitted when console.log is called within the task.
# * task:warn - Emitted when console.warn is called within the task.
# * task:error - Emitted when console.error is called within the task.
# * task:completed - Emitted when the task has succeeded or failed.
#
# ## Requiring in packages
# ## Examples
#
# ```coffee
# {Task} = require 'atom'
# {Task} = require 'atom'
# ```
#
# ## Events
#
# ### task:log
#
# Emitted when console.log is called within the task.
#
# ### task:warn
#
# Emitted when console.warn is called within the task.
#
# ### task:error
#
# Emitted when console.error is called within the task.
#
# ### task:completed
#
# Emitted when the task has succeeded or failed.
#
module.exports =
class Task
Emitter.includeInto(this)
# Public: A helper method to easily launch and run a task once.
#
# taskPath - The {String} path to the CoffeeScript/JavaScript file which
# exports a single {Function} to execute.
# args - The arguments to pass to the exported function.
# * `taskPath` The {String} path to the CoffeeScript/JavaScript file which
# exports a single {Function} to execute.
# * `args` The arguments to pass to the exported function.
#
# Returns the created {Task}.
@once: (taskPath, args...) ->
task = new Task(taskPath)
task.once 'task:completed', -> task.terminate()
@@ -43,8 +57,8 @@ class Task
# Public: Creates a task.
#
# taskPath - The {String} path to the CoffeeScript/JavaScript file that
# exports a single {Function} to execute.
# * `taskPath` The {String} path to the CoffeeScript/JavaScript file that
# exports a single {Function} to execute.
constructor: (taskPath) ->
coffeeCacheRequire = "require('#{require.resolve('./coffee-cache')}').register();"
coffeeScriptRequire = "require('#{require.resolve('coffee-script')}').register();"
@@ -78,10 +92,13 @@ class Task
# Public: Starts the task.
#
# args - The arguments to pass to the function exported by this task's script.
# callback - An optional {Function} to call when the task completes.
# Throws an error if this task has already been terminated or if sending a
# message to the child process fails.
#
# * `args` The arguments to pass to the function exported by this task's script.
# * `callback` (optional) A {Function} to call when the task completes.
start: (args..., callback) ->
throw new Error("Cannot start terminated process") unless @childProcess?
throw new Error('Cannot start terminated process') unless @childProcess?
@handleEvents()
if _.isFunction(callback)
@@ -89,17 +106,24 @@ class Task
else
args.push(callback)
@send({event: 'start', args})
undefined
# Public: Send message to the task.
#
# message - The message to send to the task.
# Throws an error if this task has already been terminated or if sending a
# message to the child process fails.
#
# * `message` The message to send to the task.
send: (message) ->
throw new Error("Cannot send message to terminated process") unless @childProcess?
@childProcess.send(message)
if @childProcess?
@childProcess.send(message)
else
throw new Error('Cannot send message to terminated process')
undefined
# Public: Forcefully stop the running task.
#
# No events are emitted.
# No more events are emitted once this method is called.
terminate: ->
return unless @childProcess?
@@ -108,3 +132,4 @@ class Task
@childProcess = null
@off()
undefined
+72 -32
Ver Arquivo
@@ -2,16 +2,38 @@ path = require 'path'
_ = require 'underscore-plus'
{Emitter} = require 'emissary'
{File} = require 'pathwatcher'
fs = require 'fs-plus'
Q = require 'q'
{$} = require './space-pen-extensions'
Package = require './package'
{File} = require 'pathwatcher'
# Public: Handles loading and activating available themes.
# Extended: Handles loading and activating available themes.
#
# An instance of this class is always available as the `atom.themes` global.
#
# ## Events
#
# ### reloaded
#
# Extended: Emitted when all styles have been reloaded.
#
# ### stylesheet-added
#
# Extended: Emitted when a stylesheet has been added.
#
# * `stylesheet` {StyleSheet} object that was removed
#
# ### stylesheet-removed
#
# Extended: Emitted when a stylesheet has been removed.
#
# * `stylesheet` {StyleSheet} object that was removed
#
# ### stylesheets-changed
#
# Extended: Emitted anytime any style sheet is added or removed from the editor
#
module.exports =
class ThemeManager
Emitter.includeInto(this)
@@ -94,25 +116,37 @@ class ThemeManager
console.warn("Failed to activate theme '#{themeName}' because it isn't installed.")
Q.all(promises).then =>
@addActiveThemeClasses()
@refreshLessCache() # Update cache again now that @getActiveThemes() is populated
@loadUserStylesheet()
@reloadBaseStylesheets()
@emit('reloaded')
@emit 'reloaded'
deferred.resolve()
deferred.promise
deactivateThemes: ->
@removeActiveThemeClasses()
@unwatchUserStylesheet()
@packageManager.deactivatePackage(pack.name) for pack in @getActiveThemes()
null
addActiveThemeClasses: ->
for pack in @getActiveThemes()
atom.workspaceView?[0]?.classList.add("theme-#{pack.name}")
return
removeActiveThemeClasses: ->
for pack in @getActiveThemes()
atom.workspaceView?[0]?.classList.remove("theme-#{pack.name}")
return
refreshLessCache: ->
@lessCache?.setImportPaths(@getImportPaths())
# Public: Set the list of enabled themes.
#
# enabledThemeNames - An {Array} of {String} theme names.
# * `enabledThemeNames` An {Array} of {String} theme names.
setEnabledThemes: (enabledThemeNames) ->
atom.config.set('core.themes', enabledThemeNames)
@@ -162,8 +196,8 @@ class ThemeManager
if nativeStylesheetPath = fs.resolveOnLoadPath(process.platform, ['css', 'less'])
@requireStylesheet(nativeStylesheetPath)
stylesheetElementForId: (id, htmlElement=$('html')) ->
htmlElement.find("""head style[id="#{id}"]""")
stylesheetElementForId: (id) ->
document.head.querySelector("""style[id="#{id}"]""")
resolveStylesheet: (stylesheetPath) ->
if path.extname(stylesheetPath).length > 0
@@ -173,17 +207,16 @@ class ThemeManager
# Public: Resolve and apply the stylesheet specified by the path.
#
# This supports both CSS and LESS stylsheets.
# This supports both CSS and Less stylsheets.
#
# stylesheetPath - A {String} path to the stylesheet that can be an absolute
# path or a relative path that will be resolved against the
# load path.
# * `stylesheetPath` A {String} path to the stylesheet that can be an absolute
# path or a relative path that will be resolved against the load path.
#
# Returns the absolute path to the required stylesheet.
requireStylesheet: (stylesheetPath, type = 'bundled', htmlElement) ->
requireStylesheet: (stylesheetPath, type='bundled') ->
if fullPath = @resolveStylesheet(stylesheetPath)
content = @loadStylesheet(fullPath)
@applyStylesheet(fullPath, content, type = 'bundled', htmlElement)
@applyStylesheet(fullPath, content, type)
else
throw new Error("Could not find a file at path '#{stylesheetPath}'")
@@ -210,11 +243,11 @@ class ThemeManager
@lessCache.cssForFile(lessStylesheetPath, [baseVarImports, less].join('\n'))
else
@lessCache.read(lessStylesheetPath)
catch e
catch error
console.error """
Error compiling less stylesheet: #{lessStylesheetPath}
Line number: #{e.line}
#{e.message}
Error compiling Less stylesheet: #{lessStylesheetPath}
Line number: #{error.line}
#{error.message}
"""
stringToId: (string) ->
@@ -223,23 +256,30 @@ class ThemeManager
removeStylesheet: (stylesheetPath) ->
fullPath = @resolveStylesheet(stylesheetPath) ? stylesheetPath
element = @stylesheetElementForId(@stringToId(fullPath))
if element.length > 0
stylesheet = element[0].sheet
if element?
{sheet} = element
element.remove()
@emit 'stylesheet-removed', stylesheet
@emit 'stylesheet-removed', sheet
@emit 'stylesheets-changed'
applyStylesheet: (path, text, type = 'bundled', htmlElement=$('html')) ->
styleElement = @stylesheetElementForId(@stringToId(path), htmlElement)
if styleElement.length
@emit 'stylesheet-removed', styleElement[0].sheet
styleElement.text(text)
else
styleElement = $("<style class='#{type}' id='#{@stringToId(path)}'>#{text}</style>")
if htmlElement.find("head style.#{type}").length
htmlElement.find("head style.#{type}:last").after(styleElement)
else
htmlElement.find("head").append(styleElement)
applyStylesheet: (path, text, type='bundled') ->
styleId = @stringToId(path)
styleElement = @stylesheetElementForId(styleId)
@emit 'stylesheet-added', styleElement[0].sheet
if styleElement?
@emit 'stylesheet-removed', styleElement.sheet
styleElement.textContent = text
else
styleElement = document.createElement('style')
styleElement.setAttribute('class', type)
styleElement.setAttribute('id', styleId)
styleElement.textContent = text
elementToInsertBefore = _.last(document.head.querySelectorAll("style.#{type}"))?.nextElementSibling
if elementToInsertBefore?
document.head.insertBefore(styleElement, elementToInsertBefore)
else
document.head.appendChild(styleElement)
@emit 'stylesheet-added', styleElement.sheet
@emit 'stylesheets-changed'
+30 -9
Ver Arquivo
@@ -16,8 +16,6 @@ class Token
scopes: null
isAtomic: null
isHardTab: null
hasLeadingWhitespace: false
hasTrailingWhitespace: false
firstNonWhitespaceIndex: null
firstTrailingWhitespaceIndex: null
hasInvisibleCharacters: false
@@ -34,9 +32,26 @@ class Token
/^meta\.brace\b/.test(_.last(@scopes))
splitAt: (splitIndex) ->
value1 = @value.substring(0, splitIndex)
value2 = @value.substring(splitIndex)
[new Token(value: value1, scopes: @scopes), new Token(value: value2, scopes: @scopes)]
leftToken = new Token(value: @value.substring(0, splitIndex), scopes: @scopes)
rightToken = new Token(value: @value.substring(splitIndex), scopes: @scopes)
if @firstNonWhitespaceIndex?
leftToken.firstNonWhitespaceIndex = Math.min(splitIndex, @firstNonWhitespaceIndex)
leftToken.hasInvisibleCharacters = @hasInvisibleCharacters
if @firstNonWhitespaceIndex > splitIndex
rightToken.firstNonWhitespaceIndex = @firstNonWhitespaceIndex - splitIndex
rightToken.hasInvisibleCharacters = @hasInvisibleCharacters
if @firstTrailingWhitespaceIndex?
rightToken.firstTrailingWhitespaceIndex = Math.max(0, @firstTrailingWhitespaceIndex - splitIndex)
rightToken.hasInvisibleCharacters = @hasInvisibleCharacters
if @firstTrailingWhitespaceIndex < splitIndex
leftToken.firstTrailingWhitespaceIndex = @firstTrailingWhitespaceIndex
leftToken.hasInvisibleCharacters = @hasInvisibleCharacters
[leftToken, rightToken]
whitespaceRegexForTabLength: (tabLength) ->
WhitespaceRegexesByTabLength[tabLength] ?= new RegExp("([ ]{#{tabLength}})|(\t)|([^\t]+)", "g")
@@ -135,7 +150,7 @@ class Token
scopeClasses = scope.split('.')
_.isSubset(targetClasses, scopeClasses)
getValueAsHtml: ({hasIndentGuide})->
getValueAsHtml: ({hasIndentGuide}) ->
if @isHardTab
classes = 'hard-tab'
classes += ' indent-guide' if hasIndentGuide
@@ -148,7 +163,7 @@ class Token
leadingHtml = ''
trailingHtml = ''
if @hasLeadingWhitespace
if @hasLeadingWhitespace()
leadingWhitespace = @value.substring(0, @firstNonWhitespaceIndex)
classes = 'leading-whitespace'
@@ -158,12 +173,12 @@ class Token
leadingHtml = "<span class='#{classes}'>#{leadingWhitespace}</span>"
startIndex = @firstNonWhitespaceIndex
if @hasTrailingWhitespace
if @hasTrailingWhitespace()
tokenIsOnlyWhitespace = @firstTrailingWhitespaceIndex is 0
trailingWhitespace = @value.substring(@firstTrailingWhitespaceIndex)
classes = 'trailing-whitespace'
classes += ' indent-guide' if hasIndentGuide and not @hasLeadingWhitespace and tokenIsOnlyWhitespace
classes += ' indent-guide' if hasIndentGuide and not @hasLeadingWhitespace() and tokenIsOnlyWhitespace
classes += ' invisible-character' if @hasInvisibleCharacters
trailingHtml = "<span class='#{classes}'>#{trailingWhitespace}</span>"
@@ -198,3 +213,9 @@ class Token
when '<' then '&lt;'
when '>' then '&gt;'
else match
hasLeadingWhitespace: ->
@firstNonWhitespaceIndex? and @firstNonWhitespaceIndex > 0
hasTrailingWhitespace: ->
@firstTrailingWhitespaceIndex? and @firstTrailingWhitespaceIndex < @value.length
+54 -26
Ver Arquivo
@@ -1,9 +1,12 @@
_ = require 'underscore-plus'
{Model} = require 'theorist'
EmitterMixin = require('emissary').Emitter
{Emitter} = require 'event-kit'
{Point, Range} = require 'text-buffer'
Serializable = require 'serializable'
TokenizedLine = require './tokenized-line'
Token = require './token'
Grim = require 'grim'
module.exports =
class TokenizedBuffer extends Model
@@ -20,6 +23,8 @@ class TokenizedBuffer extends Model
visible: false
constructor: ({@buffer, @tabLength, @invisibles}) ->
@emitter = new Emitter
@tabLength ?= atom.config.getPositiveInt('editor.tabLength', 2)
@subscribe atom.syntax, 'grammar-added grammar-updated', (grammar) =>
@@ -29,11 +34,8 @@ class TokenizedBuffer extends Model
newScore = grammar.getScore(@buffer.getPath(), @buffer.getText())
@setGrammar(grammar, newScore) if newScore > @currentGrammarScore
@on 'grammar-changed grammar-updated', => @retokenizeLines()
@subscribe @buffer, "changed", (e) => @handleBufferChange(e)
@subscribe @buffer, "path-changed", =>
@bufferPath = @buffer.getPath()
@reloadGrammar()
@subscribe @buffer.onDidChange (e) => @handleBufferChange(e)
@subscribe @buffer.onDidChangePath (@bufferPath) => @reloadGrammar()
@subscribe @$tabLength.changes, (tabLength) => @retokenizeLines()
@@ -45,18 +47,43 @@ class TokenizedBuffer extends Model
serializeParams: ->
bufferPath: @buffer.getPath()
tabLength: @tabLength
invisibles: _.clone(@invisibles)
deserializeParams: (params) ->
params.buffer = atom.project.bufferForPathSync(params.bufferPath)
params
onDidChangeGrammar: (callback) ->
@emitter.on 'did-change-grammar', callback
onDidChange: (callback) ->
@emitter.on 'did-change', callback
onDidTokenize: (callback) ->
@emitter.on 'did-tokenize', callback
on: (eventName) ->
switch eventName
when 'changed'
Grim.deprecate("Use TokenizedBuffer::onDidChange instead")
when 'grammar-changed'
Grim.deprecate("Use TokenizedBuffer::onDidChangeGrammar instead")
when 'tokenized'
Grim.deprecate("Use TokenizedBuffer::onDidTokenize instead")
else
Grim.deprecate("TokenizedBuffer::on is deprecated. Use event subscription methods instead.")
EmitterMixin::on.apply(this, arguments)
setGrammar: (grammar, score) ->
return if grammar is @grammar
@unsubscribe(@grammar) if @grammar
@grammar = grammar
@currentGrammarScore = score ? grammar.getScore(@buffer.getPath(), @buffer.getText())
@subscribe @grammar, 'grammar-updated', => @retokenizeLines()
@retokenizeLines()
@emit 'grammar-changed', grammar
@emitter.emit 'did-change-grammar', grammar
reloadGrammar: ->
if grammar = atom.syntax.selectGrammar(@buffer.getPath(), @buffer.getText())
@@ -76,7 +103,9 @@ class TokenizedBuffer extends Model
@invalidRows = []
@invalidateRow(0)
@fullyTokenized = false
@emit "changed", {start: 0, end: lastRow, delta: 0}
event = {start: 0, end: lastRow, delta: 0}
@emit 'changed', event
@emitter.emit 'did-change', event
setVisible: (@visible) ->
@tokenizeInBackground() if @visible
@@ -93,7 +122,7 @@ class TokenizedBuffer extends Model
setTabLength: (@tabLength) ->
setInvisibles: (invisibles) ->
if invisibles isnt @invisibles
unless _.isEqual(invisibles, @invisibles)
@invisibles = invisibles
@retokenizeLines()
@@ -126,12 +155,16 @@ class TokenizedBuffer extends Model
@validateRow(row)
@invalidateRow(row + 1) unless filledRegion
@emit "changed", { start: invalidRow, end: row, delta: 0 }
event = { start: invalidRow, end: row, delta: 0 }
@emit 'changed', event
@emitter.emit 'did-change', event
if @firstInvalidRow()?
@tokenizeInBackground()
else
@emit "tokenized" unless @fullyTokenized
unless @fullyTokenized
@emit 'tokenized'
@emitter.emit 'did-tokenize'
@fullyTokenized = true
firstInvalidRow: ->
@@ -172,7 +205,9 @@ class TokenizedBuffer extends Model
if newEndStack and not _.isEqual(newEndStack, previousEndStack)
@invalidateRow(end + delta + 1)
@emit "changed", { start, end, delta, bufferChange: e }
event = { start, end, delta, bufferChange: e }
@emit 'changed', event
@emitter.emit 'did-change', event
retokenizeWhitespaceRowsIfIndentLevelChanged: (row, increment) ->
line = @tokenizedLines[row]
@@ -220,25 +255,18 @@ class TokenizedBuffer extends Model
{tokens, ruleStack} = @grammar.tokenizeLine(line, ruleStack, row is 0)
new TokenizedLine({tokens, ruleStack, tabLength, lineEnding, indentLevel, @invisibles})
# FIXME: benogle says: These are actually buffer rows as all buffer rows are
# accounted for in @tokenizedLines
lineForScreenRow: (row) ->
@linesForScreenRows(row, row)[0]
tokenizedLineForRow: (bufferRow) ->
@tokenizedLines[bufferRow]
# FIXME: benogle says: These are actually buffer rows as all buffer rows are
# accounted for in @tokenizedLines
linesForScreenRows: (startRow, endRow) ->
@tokenizedLines[startRow..endRow]
stackForRow: (bufferRow) ->
@tokenizedLines[bufferRow]?.ruleStack
stackForRow: (row) ->
@tokenizedLines[row]?.ruleStack
indentLevelForRow: (row) ->
line = @buffer.lineForRow(row)
indentLevelForRow: (bufferRow) ->
line = @buffer.lineForRow(bufferRow)
indentLevel = 0
if line is ''
nextRow = row + 1
nextRow = bufferRow + 1
lineCount = @getLineCount()
while nextRow < lineCount
nextLine = @buffer.lineForRow(nextRow)
@@ -247,7 +275,7 @@ class TokenizedBuffer extends Model
break
nextRow++
previousRow = row - 1
previousRow = bufferRow - 1
while previousRow >= 0
previousLine = @buffer.lineForRow(previousRow)
unless previousLine is ''
@@ -372,5 +400,5 @@ class TokenizedBuffer extends Model
logLines: (start=0, end=@buffer.getLastRow()) ->
for row in [start..end]
line = @lineForScreenRow(row).text
line = @tokenizedLineForRow(row).text
console.log row, line, line.length
+2 -4
Ver Arquivo
@@ -150,11 +150,9 @@ class TokenizedLine
index = 0
for token in @tokens
if index < firstNonWhitespaceIndex
token.hasLeadingWhitespace = true
token.firstNonWhitespaceIndex = Math.min(index + token.value.length, firstNonWhitespaceIndex - index)
# Only the *last* segment of a soft-wrapped line can have trailing whitespace
if @lineEnding? and (index + token.value.length > firstTrailingWhitespaceIndex)
token.hasTrailingWhitespace = true
token.firstTrailingWhitespaceIndex = Math.max(0, firstTrailingWhitespaceIndex - index)
index += token.value.length
@@ -170,12 +168,12 @@ class TokenizedLine
changedText = true
else
if invisibles.space
if token.hasLeadingWhitespace
if token.hasLeadingWhitespace()
token.value = token.value.replace LeadingWhitespaceRegex, (leadingWhitespace) ->
leadingWhitespace.replace RepeatedSpaceRegex, invisibles.space
token.hasInvisibleCharacters = true
changedText = true
if token.hasTrailingWhitespace
if token.hasTrailingWhitespace()
token.value = token.value.replace TrailingWhitespaceRegex, (leadingWhitespace) ->
leadingWhitespace.replace RepeatedSpaceRegex, invisibles.space
token.hasInvisibleCharacters = true

Alguns arquivos não foram exibidos porque demasiados arquivos foram alterados neste diff Mostrar Mais