Comparar commits

...

326 Commits

Autor SHA1 Mensagem Data
Kevin Sawicki 392ffce8c7 Remove unused requires 2013-05-16 15:54:10 -07:00
Kevin Sawicki d7a238b5ec Guard against possibly null event handler 2013-05-16 15:49:53 -07:00
Kevin Sawicki & Nathan Sobo a29015551c Extract link click callback to method 2013-05-16 15:04:30 -07:00
Kevin Sawicki & Nathan Sobo 83e6a4a57e Rely on core:focus-next/previous in sign in view 2013-05-16 15:01:23 -07:00
Kevin Sawicki & Nathan Sobo 001183245e Add core:focus-next/focus-previous
Focus now cycles between elements with a positive tabindex on
tab and shift-tab.
2013-05-16 15:00:55 -07:00
Kevin Sawicki & Nathan Sobo 76e7161608 Extract window event handling into separate class 2013-05-16 14:18:57 -07:00
Kevin Sawicki & Nathan Sobo 848ce7936f DRY up subscribe and subscribeToCommand
subscribeToCommand now supports unsubscribing by object.
2013-05-16 13:43:18 -07:00
Kevin Sawicki & Nathan Sobo d76d0a030f Forward all subscribe arguments from Subscriber to EventEmitter 2013-05-16 13:36:51 -07:00
Kevin Sawicki e2af4c964d Bind multiple events to single callback 2013-05-16 12:17:41 -07:00
Kevin Sawicki 4527d8bd51 Remove unneeded catch and default response to empty hash 2013-05-16 12:17:41 -07:00
Kevin Sawicki a17a282b03 Warn instead of log 2013-05-16 12:17:41 -07:00
Kevin Sawicki 2c050522cd Enable fields when attaching 2013-05-16 12:17:41 -07:00
Kevin Sawicki c98e7cab4b Upgrade to keytar 0.4 2013-05-16 12:17:41 -07:00
Kevin Sawicki 4b49562949 Add token to request when available 2013-05-16 12:17:40 -07:00
Kevin Sawicki efb388ded4 Add initial sign in screen
Signing in generates an OAuth2 token and saves
it to the keychain.
2013-05-16 12:17:40 -07:00
Kevin Sawicki 08e9f19644 Add repository field 2013-05-16 12:16:48 -07:00
Kevin Sawicki 3b35a4b63b Link atom.sh instead of copying 2013-05-16 12:16:33 -07:00
Garen Torikian b3b7f1ea97 Change to an H2
Otherwise the TOC renders wrong
2013-05-16 12:48:20 -06:00
Kevin Sawicki 075018d03d Mention disabled packages fix 2013-05-16 11:45:38 -07:00
Kevin Sawicki & Nathan Sobo 3b0748fe44 Add config.pushAtKeyPath/removeAtKeyPath 2013-05-16 10:51:23 -07:00
Kevin Sawicki & Nathan Sobo 2b49a04227 Return a cloned object from config.get()
This prevents mutations to the values of the default settings.
2013-05-16 10:29:08 -07:00
Kevin Sawicki & Nathan Sobo 097ee9f2a9 Remove unneeeded calls to config.update() 2013-05-16 10:14:21 -07:00
Kevin Sawicki e2821cbea8 Use map instead of iterating 2013-05-16 09:42:09 -07:00
Kevin Sawicki e9f9c6e777 Mention fuzzy finder 2013-05-16 08:51:04 -07:00
Kevin Sawicki 95d4391680 Run fuzzy filter on project relative paths
This prevents the score being influenced by the
segments contained in the project path.

Closes #553
2013-05-16 08:49:43 -07:00
Garen Torikian 04d4085759 Update manifest to include new "Your First Package!" information 2013-05-15 14:41:13 -06:00
Garen Torikian a75896c85b Merge pull request #544 from github/api/extension-docs
Level up the "creating packages" documentation
2013-05-15 13:37:36 -07:00
Kevin Sawicki 61675c2e77 Install apm command when Atom starts
This changes the command installation to use symlinks instead
of copying over the contents of the file.
2013-05-15 10:29:27 -07:00
Kevin Sawicki 718175eb34 Make atom.sh executable 2013-05-15 10:29:27 -07:00
Nathan Sobo aefb84cdeb Don't modify uri's with a scheme prefix in Project.resolve 2013-05-14 20:55:14 -06:00
Nathan Sobo 5aac826ec1 Register a custom opener for ImageEditSessions on the Project class 2013-05-14 20:32:16 -06:00
Nathan Sobo aa86362a0a Add Project@registerOpener(fn) to register custom openers on project
Now packages can register custom handlers for different kinds of file
paths. If the function you register returns a resource for the given
path, it will be returned from Project.open instead of an EditSession.
2013-05-14 20:23:25 -06:00
Nathan Sobo 6104927cb6 Rename Project.buildEditSession -> Project.open 2013-05-14 20:03:53 -06:00
Kevin Sawicki 74c889062b Upgrade apm 2013-05-14 13:14:55 -07:00
Kevin Sawicki 7b2539c4f4 Correct typo in console warning message 2013-05-14 12:30:48 -07:00
Kevin Sawicki 851febd495 Use apm to install modules 2013-05-14 12:30:35 -07:00
Kevin Sawicki a12abd7377 💄 2013-05-14 12:01:21 -07:00
Kevin Sawicki 2f54cb4c22 Use season module internally 2013-05-14 11:58:34 -07:00
Kevin Sawicki 81f7354fb5 Use season for compiling cson to json 2013-05-14 11:08:32 -07:00
Kevin Sawicki f8c20e1659 Use correct dev mode check when opening config window
Closes #545
2013-05-14 10:12:25 -07:00
Kevin Sawicki 56394f6d8d Don't wrap the path text in the status bar
Also set a min width so the branch icon doesn't overlap
the path text when the window isn't wide enough to display
both.
2013-05-14 09:59:16 -07:00
Nathan Sobo e0e6d96c84 Fix #550. Use min-width: 0 after flexbox orientation switch.
By default, flexbox items have a min size of -webkit-min-content in the
the flex axis. I previously switched the pane item-views to flex
horizontally for performance reasons (preventing over-aggressive
repaints), but forgot to change min-height to min-width.
2013-05-14 10:10:25 -06:00
Kevin Sawicki f41b558fe5 Exclude symlinked folders from fuzzy finder
This can put the fuzzy finder in an infinite loop if a cycle
occurs.
2013-05-13 17:35:51 -07:00
Kevin Sawicki 5507febe09 Upgrade to git-utils 0.17
This release handles relativizing paths to a symlinked repository.

Close #549
2013-05-13 17:35:51 -07:00
Kevin Sawicki b8ee3187ec doc: task instead of web worker 2013-05-13 17:35:51 -07:00
probablycorey f74842cd4c Use new ruby scopes in TextMateGrammar spec 2013-05-13 16:51:30 -07:00
probablycorey 2e61201f86 Update ruby TextMate bundle
Closes #541
2013-05-13 16:51:30 -07:00
Kevin Sawicki 4108d572b8 Use real path to determine if repository root 2013-05-13 15:52:49 -07:00
probablycorey 423b133e75 Comments can't increase the indent level of the next line 2013-05-13 14:28:16 -07:00
probablycorey eb25d2e6a8 TokenizedBuffer.tokenForPosition(position) now works correctly
It was previously returning the wrong token at token boundaries.
2013-05-13 14:28:16 -07:00
probablycorey eae06b62bc Spec 💄 2013-05-13 14:28:16 -07:00
probablycorey 49aeef99b6 Add config option editor.normalizeIndentOnPaste 2013-05-13 14:28:16 -07:00
probablycorey 8e37d2ada6 Turn auto-indent on for all 'newline' specs 2013-05-13 14:28:16 -07:00
probablycorey 7513fe9c69 Set editor.autoIndent to true in specs 2013-05-13 14:28:16 -07:00
probablycorey 34d3091fc9 Remove console.log 2013-05-13 14:28:16 -07:00
probablycorey 992f520698 Normalize pasted text when there is an indent basis 2013-05-13 14:28:16 -07:00
probablycorey b23e1d3d52 In specs, use local copyText function to put text on the pasteboard 2013-05-13 14:28:16 -07:00
probablycorey dd53539799 Moving auto-indent specs to appropriate places 2013-05-13 14:28:15 -07:00
probablycorey 93b1ce53c0 Rename autoDecreaseIndentForRow to autoDecreaseIndentForBufferRow 2013-05-13 14:28:15 -07:00
probablycorey e70d6d1f8f Remove unused method autoIncreaseIndentForBufferRow 2013-05-13 14:28:15 -07:00
probablycorey c34db290e4 Only indent the line following a '\n' not the line preceding it 2013-05-13 14:28:15 -07:00
probablycorey 9d2b7875b9 💄 2013-05-13 14:28:15 -07:00
probablycorey 1f025817f1 Rename autoIndentNewlines to autoIndentNewline 2013-05-13 14:28:15 -07:00
probablycorey 38d4e3a097 Move all auto-indent specs to the auto-indent describe block 2013-05-13 14:28:15 -07:00
probablycorey c03d44da00 Add autoIndentNewline and autoDecreaseIndent options to insertText
insertText now takes these options:
* autoIndent will auto indent all inserted text based
* autoIndentNewline will indent a line when a '\n' is inserted
* autoDecreaseIndent will decrease the indent if the line matches a 
decreaseIndent pattern (such as a `}` in javavascript)
2013-05-13 14:28:15 -07:00
probablycorey 537c507efe Use suggested indent when auto indenting a buffer row 2013-05-13 14:28:15 -07:00
probablycorey 0fbd08f1ab Allow suggested indent level to be less than the current indent level 2013-05-13 14:28:15 -07:00
probablycorey 0f469a3269 Add Range.getRows 2013-05-13 14:28:15 -07:00
probablycorey 9713bc8c02 💄 2013-05-13 14:28:15 -07:00
probablycorey 7f0b2c54f3 editor.autoIndentOnPaste will auto indent every pasted line
I've removed normalizeLines and its tests. They will be added back in
a later commit.
2013-05-13 14:28:14 -07:00
probablycorey e9fd61a1ea Remove unused indent methods from Selection 2013-05-13 14:28:14 -07:00
probablycorey 33c95d1a58 Rename insideExistingLine to isCursorInsideExistingLine 2013-05-13 14:28:14 -07:00
probablycorey c5aa670569 Document methods as internal 2013-05-13 14:28:14 -07:00
probablycorey 533b11f991 💄 2013-05-13 14:28:14 -07:00
Kevin Sawicki ebb68c3386 Mention new icon 2013-05-13 09:33:17 -07:00
Nathan Sobo c876876d5e Upgrade the icon 2013-05-11 10:35:45 -06:00
Nathan Sobo 7dde2fec55 Fix wrap guide spec again. Previously it only passed w/ a wide window. 2013-05-11 09:08:10 -06:00
Nathan Sobo 49cf92c2a2 Fix perf regression: Don't update line numbers for single line changes 2013-05-10 19:39:33 -06:00
Nathan Sobo 60319c30ac Fix wrap guide specs after panes css change 2013-05-10 18:30:01 -06:00
Nathan Sobo 191e8ab240 Fix perf regression caused by flex-flow: column on pane item views
For some reason, having flex-flow column on the pane item views
container was forcing the entire view to be repainted on cursor
movement and line edits. Allowing the container to flow row-wise and
setting the min-height to 0 instead of min-content-height achieves
the same layout effect without the huge repaint cost. Flexbox is a
fragile bitch.
2013-05-10 18:19:41 -06:00
Nathan Sobo b4dec8ccbb In Editor.updateRenderedLines, cap renderFrom to the last screen row
If we remove a large number of screen lines when we are scrolled down,
the current @firstRenderedScreenRow may end up being larger than the
number of screen rows we now have. Setting renderFrom to the
@firstRenderedScreenRow in this case was causing the renderFrom to be
larger than the renderTo, which was causing problems downstream with
the new mapping code.
2013-05-10 13:20:43 -06:00
Nathan Sobo 7d28edc116 Rename ScreenLine -> TokenizedLine
Now that ScreenLines don't contain the bufferRows property, which was
essential to the functioning of the LineMap, they're just containers
for tokens. Since they're stored in the TokenizedBuffer in a form that
doesn't necessarily match what ends up on screen, it makes more sense
to call them tokenized lines. A tokenized line is a screen line if it's
in the `.screenLines` array of the DisplayBuffer, but "screenness" is
not an inherent property, so it shouldn't be in the name of the class.
2013-05-10 13:20:43 -06:00
Nathan Sobo 4d314f99ac Eliminate ScreenLine.bufferRows property
Previously, we used bufferRows to determine how many buffer rows were
spanned by the screen line in the event it was folded. Now that we have
RowMap, screen lines don't need to serve this purpose. We still need
to know if a screen line is wrapped, which we solve via setting the
lineEnd property to null for a soft-wrapped screen line.
2013-05-10 13:20:43 -06:00
Nathan Sobo 1d146640e5 Break some methods out of DisplayBuffer.updateScreenLines 2013-05-10 13:20:43 -06:00
Nathan Sobo b4c95d4fc9 Merge adjacent isomorphic regions after adding new regions 2013-05-10 13:20:42 -06:00
Nathan Sobo d9c258f27e Handle new regions overlapping screen wise but not buffer wise 2013-05-10 13:20:42 -06:00
Nathan Sobo 4a56cc3693 Kick docs threshold down again because I removed public methods 2013-05-10 13:20:42 -06:00
Nathan Sobo 8b149ff827 Delete LineMap 2013-05-10 13:20:42 -06:00
Nathan Sobo f3145e671a Declare screenLines ivar in DisplayBuffer 2013-05-10 13:20:42 -06:00
Nathan Sobo d3cb001d65 Use RowMap instead of LineMap in DisplayBuffer 2013-05-10 13:20:42 -06:00
Nathan Sobo 16da9b0506 Document RowMap 2013-05-10 13:20:42 -06:00
Nathan Sobo d7914d2c54 Ensure row map applies negative deltas only after the given start row 2013-05-10 13:20:42 -06:00
Nathan Sobo 9849c62d80 Properly handle regions that straddle existing regions
When we're creating folds that contain other folds, we'll need the
region based on the new fold's row mapping to overwrite the regions
from the old folds. This commit ensures that the new region cleanly
slots in, replacing any regions it completely contains and splitting
regions that it only partially overlaps.
2013-05-10 13:20:42 -06:00
Nathan Sobo 3c630fb7f4 Rename mapping -> region within RowMap 2013-05-10 13:20:42 -06:00
Nathan Sobo 55f0b6a1f8 Replace existing regions when inserting a mapping that surrounds them 2013-05-10 13:20:42 -06:00
Nathan Sobo 1b21fdda3b Don't throw errors when applying screen deltas after the last mapping 2013-05-10 13:20:41 -06:00
Nathan Sobo c17d6ba487 Adjust multiple mappings if needed when applying screen deltas
If there are multiple mappings following the start row of the delta
that span fewer screen rows than the delta, we collapse mappings to 0
screen rows until we have removed the number of rows specified by the
delta.

Expanding positive deltas expands the first mapping following the
start row as it did previously.
2013-05-10 13:20:41 -06:00
Nathan Sobo c6ff7e8934 Inserting a mapping within an existing mapping preserves its shape
Inserting a mapping should never change the position of any existing
mappings on screen or in the buffer. It's simply a statement about a
range of rows in the buffer mapping to a range of existing rows on
screen, but shouldn't add or remove any rows. Adding and removing rows
on screen or in the buffer is the job of the applyBufferDelta and
applyScreenDelta methods.
2013-05-10 13:20:41 -06:00
Nathan Sobo 1895e8143d Allow the screen row count of N-screen:1-buffer mappings to be updated 2013-05-10 13:20:41 -06:00
Nathan Sobo 5c74a02688 Add row map specs for mappings of N screen rows to 1 buffer row 2013-05-10 13:20:41 -06:00
Nathan Sobo 2f900e11ed Add RowMap.bufferRowRangeForScreenRow 2013-05-10 13:20:41 -06:00
Nathan Sobo 1123ca4bc1 Helpful comments 2013-05-10 13:20:41 -06:00
Nathan Sobo 4973c2afc6 Don't return a mapping from traverseToBufferRow if we fall off the end 2013-05-10 13:20:41 -06:00
Nathan Sobo 502e3ceb3b Factor mapping traversal its own traverseToBufferRow method 2013-05-10 13:20:41 -06:00
Nathan Sobo 32b40fe1ad Eliminate dummy implementation of bufferRowRangeForScreenRow 2013-05-10 13:20:41 -06:00
Nathan Sobo 9a12b170f6 Allow insertion of multiple buffer row range mappings 2013-05-10 13:20:41 -06:00
Nathan Sobo 9f4b594bd0 Start on RowMap 2013-05-10 13:20:41 -06:00
Kevin Sawicki ea7a8c9670 Render markdown when buffer is reloaded
This will keep the preview in sync with the editor.
2013-05-10 12:16:45 -07:00
Kevin Sawicki 113dac6e2b Add more entries to fence name hash 2013-05-10 12:13:37 -07:00
Kevin Sawicki 642427d015 Remove unneeded guards
Syntax will always return a grammar and tokenizing
with the null grammar is acceptable.
2013-05-10 12:12:25 -07:00
Kevin Sawicki 26dad65c8d Only set code color outside of editor-colors parent 2013-05-10 12:12:03 -07:00
Kevin Sawicki 827bab90bf Add tokenizeLines to null grammar 2013-05-10 12:09:58 -07:00
Kevin Sawicki 1a90f1c017 Include source.shell inside of sh/bash code blocks 2013-05-10 11:50:21 -07:00
Kevin Sawicki 8172618bec Remove unused libgit2 script 2013-05-10 11:19:40 -07:00
Garen Torikian & Nathan Sobo e12d779f00 Add listing of package.json keys 2013-05-08 15:07:55 -07:00
Garen Torikian & Nathan Sobo 1300955c13 Explicitly mention node.js core modules 2013-05-08 15:07:38 -07:00
Garen Torikian & Nathan Sobo 7714c35829 Finish package sample 2013-05-08 15:07:25 -07:00
Kevin Sawicki 3de070708f Mention class layout 2013-05-08 11:30:34 -07:00
Kevin Sawicki 7aba7b0854 Use consistent rm flag order 2013-05-08 11:17:55 -07:00
Kevin Sawicki f16c7247a9 Clean cefode cache during prebuild 2013-05-08 11:17:30 -07:00
Kevin Sawicki ef2ece3600 Treat .tgz extension as compressed 2013-05-08 10:27:26 -07:00
Kevin Sawicki 962b559638 Mention fuzy finder fix 2013-05-08 10:25:54 -07:00
Garen Torikian & Nathan Sobo 9ab4707d43 More words on creating a pane 2013-05-07 12:53:09 -07:00
Kevin Sawicki c3759feaa0 Show number of paths loaded after indexing message 2013-05-07 08:56:06 -07:00
Kevin Sawicki 96418f3640 Respect core.excludeVcsIgnoredPaths in fuzzy finder 2013-05-06 18:06:10 -07:00
Kevin Sawicki a96c354f4f Fork with harmony collections enabled 2013-05-06 18:04:01 -07:00
Kevin Sawicki 81462d9050 Hide/show entire loading area instead of only message 2013-05-06 16:53:34 -07:00
Kevin Sawicki 919bdca8f4 Style select list loading area similar to command panel 2013-05-06 16:34:39 -07:00
Kevin Sawicki 130e5cd3ec Use task handler to load fuzzy finder paths
This use completely async calls from a process task and
also respects the repository-defined ignore rules.

Closes #509 and #514
2013-05-06 16:34:39 -07:00
Kevin Sawicki 42301210c4 Log uncaught exceptions to parent process 2013-05-06 16:34:39 -07:00
probablycorey 43a3c93d9b Substitution commands can replace text with empty string
Closes #488
2013-05-06 12:11:48 -07:00
Kevin Sawicki cc98b88e28 Expect Error instead of JSON string in markdown preview view 2013-05-06 09:42:45 -07:00
Kevin Sawicki 212a192abf 💄 2013-05-06 09:38:10 -07:00
Kevin Sawicki ebc75b1ba8 Use site color for code elements 2013-05-06 08:34:05 -07:00
Kevin Sawicki 7be6421989 Remove incorrect error message
The markdown API is no longer used.
2013-05-06 08:26:02 -07:00
Nathan Sobo b4206d3202 Merge branch 'master' into know-when-to-foldem
Conflicts:
	src/app/edit-session.coffee
2013-05-06 08:35:13 -06:00
Nathan Sobo e8e0d5dd02 Fix regression: folds can be destroyed by clicking them 2013-05-06 08:27:43 -06:00
Garen Torikian be229d374e Finish the working with styles section 2013-05-04 11:49:22 -07:00
Garen Torikian 14b28a8a89 Update package creation docs 2013-05-04 11:49:22 -07:00
Garen Torikian c71bccd72c Add new Included Libs doc 2013-05-04 11:49:22 -07:00
Garen Torikian cee9605fc5 Updates to first package information 2013-05-04 11:49:22 -07:00
Garen Torikian 7b5b942c47 Start the Creating a Package doc 2013-05-04 11:49:22 -07:00
Garen Torikian d5e0af2646 Move themes file 2013-05-04 11:49:22 -07:00
Garen Torikian a12e1d8b0e Update note on activationEvents 2013-05-04 11:49:22 -07:00
Garen Torikian d9d9b6800b Finish authoring packages section 2013-05-04 11:49:21 -07:00
Garen Torikian 8d7fd982e1 Start leveling up extension docs 2013-05-04 11:49:21 -07:00
Nathan Sobo 3e937e9811 Merge remote-tracking branch 'origin/master' into know-when-to-foldem
Conflicts:
	src/app/buffer-marker.coffee
	src/app/cursor.coffee
	src/app/display-buffer-marker.coffee
	src/app/display-buffer.coffee
	src/app/edit-session.coffee
	src/app/fold.coffee
	src/app/line-map.coffee
	src/app/range.coffee
	src/app/selection.coffee
	src/app/text-buffer.coffee
2013-05-03 18:29:52 -06:00
Garen Torikian 4b993b99cd Merge pull request #507 from github/markdown-and-out
Swap out Markdown render logic with module
2013-05-03 16:59:00 -07:00
Garen Torikian & Nathan Sobo 839f0b642c Merge Kevin's fix for empty line invisibles with the reshuffling of buildLineHtml methods to be on the class level 2013-05-03 16:57:42 -07:00
Garen Torikian & Nathan Sobo f4d7586034 Refactor syntax highlighting of Markdown pre blocks
Extract out editor-colors class to apply background and default text color
Remove pre background coloring from Markdown preview stylesheet
2013-05-03 16:57:42 -07:00
Garen Torikian & Nathan Sobo ab23f624af Add pairs file for gjt 2013-05-03 16:57:42 -07:00
Garen Torikian 86484e7426 💄 for renamed method call 2013-05-03 16:57:42 -07:00
Garen Torikian 734b6778b1 Add more langauges to mapping 2013-05-03 16:57:42 -07:00
Garen Torikian 252682dd99 Syntax highlight based entirely on editor colors 2013-05-03 16:57:42 -07:00
Garen Torikian 95e14486af Fix specs for testing highlighted lines 2013-05-03 16:57:42 -07:00
Garen Torikian cf239ccac0 Implement highlighting of Markdown pre blocks 2013-05-03 16:57:41 -07:00
Garen Torikian dd041945d2 Transform specs to something useful 2013-05-03 16:57:41 -07:00
Garen Torikian 1cc4c52c7a Style emoji 2013-05-03 16:57:41 -07:00
Nathan Sobo a2c894bcc9 Fix some docs issues 2013-05-03 17:35:01 -06:00
Nathan Sobo ebfd8ca4c3 Give docs errors a better failure message 2013-05-03 17:34:48 -06:00
Corey Johnson & Nathan Sobo da938d8212 Fix edit session specs concerning delete/backspace + folds 2013-05-03 17:24:08 -06:00
Corey Johnson & Nathan Sobo 855f955dec Make BufferMarker return copies of its points so they can't be mutated 2013-05-03 17:23:24 -06:00
Corey Johnson & Nathan Sobo 1b8a8cf939 Make UndoManager.clear not cancel the transaction
If an exception is thrown while running an operation, we clear the
undo manager and re-throw it. But if we were in a transaction, we will
commit it in a `finally` block. If the transaction is set to null,
committing the transaction will then throw an exception which hides
the original exception… we don't want that. Now clear just empties the
current transaction out.
2013-05-03 17:21:50 -06:00
Nathan Sobo 9147e86d96 Delete unused param 2013-05-03 17:19:11 -06:00
Garen Torikian 6c748c2f87 Fix tests 2013-05-02 18:33:28 -07:00
Garen Torikian 4b6e0916c9 Swap out Markdown render logic 2013-05-02 18:33:20 -07:00
Kevin Sawicki fa5861a626 Include $self inside heading rule
This allows emoji, links, etc. to be parsed
and highlighted in headings
2013-05-02 16:21:22 -07:00
Kevin Sawicki f556890873 Mention icon 2013-05-02 14:21:22 -07:00
Kevin Sawicki 55f185f980 Tweak select list colors 2013-05-02 14:08:51 -07:00
Garen Torikian d4474e1d4d Merge pull request #512 from github/change-status-block-tag
Change status block tag
2013-05-02 13:50:06 -07:00
Kevin Sawicki b64f6c5c27 Upgrade icon to orange cube circles 2013-05-02 13:46:42 -07:00
probablycorey 616bb487f4 Correct config.observe documentation 2013-05-02 13:24:08 -07:00
probablycorey 9f4fb29b58 💄 2013-05-02 13:22:08 -07:00
Garen Torikian 1262d3fce9 💄 2013-05-02 12:22:37 -07:00
probablycorey f4f74e1171 💄 2013-05-02 12:05:56 -07:00
probablycorey f0c8c044c9 No longer need extra event handler
Since config doesn't store default values anymore, this line isn't
needed.
2013-05-02 12:05:56 -07:00
probablycorey c13b291c64 Update config panel spec 2013-05-02 12:05:56 -07:00
probablycorey 70191ea368 Don't store config values that equal their default config value
If 'foo' has not been set and has a default value of 1, 
config.set('foo', 1) will not store the 1.

If 'foo' has been set to X and has a default value of 1, 
config.set('foo', 1) will remove 'foo' from config
2013-05-02 12:05:56 -07:00
probablycorey d294bf4d05 💄
Keep the bind commands close together
2013-05-02 12:05:56 -07:00
Kevin Sawicki 10ca03f238 Don't show indent guide in truly trailing whitespace
Lines that are all whitespace are considered trailing
whitespace and should display the indent guide.

But lines with leading and trailing whitespace should never
have the indent guide rendered in the trailing area.
2013-05-02 11:44:49 -07:00
probablycorey 67f96e0fdd Remove shift from all non-alphanumeric key bindings
There is no guarantee that the shift values for
non-alphanumeric keys will be the same across keyboards.
2013-05-02 10:29:10 -07:00
Cameron McEfee 879ce3b30d ctrl-| to ctrl-shift-| 2013-05-02 10:01:32 -07:00
probablycorey 3de96f1e7a Only cancel termination if there are AtomWindowControllers open.
Sometimes an application has other windows open, like the open
panel. Even when these are closed they seem to live on in the
application's window array. We now ignore those windows when trying
to terminate the app.

Closes #534
2013-05-02 09:38:51 -07:00
Kevin Sawicki ee621bcace Bind link click handler in window.handleEvents() 2013-05-01 22:18:39 -07:00
Kevin Sawicki c2118a8cb9 Remove unneeded trailing if 2013-05-01 21:55:37 -07:00
Kevin Sawicki 21f1579f73 Return early when href is a hash 2013-05-01 21:48:43 -07:00
Kevin Sawicki 4dce9d659f Open http/https links in an external browser
Listen for all links being clicked and open any http/https
hrefs by spawning a call to the 'open' command.

Closes #531
2013-05-01 21:36:36 -07:00
Kevin Sawicki ca4cfe358f Mention update and indent guide fixes 2013-05-01 21:26:15 -07:00
Kevin Sawicki 7c8c4bd233 Remove unused requires 2013-05-01 20:57:16 -07:00
Kevin Sawicki 35cf96e15f Render empty line invisibles at correct position
End of line invisibles are not rendered at the correct
position for empty lines instead of always after the
last indent guide span.

Closes #456
2013-05-01 20:54:09 -07:00
Corey Johnson & Nathan Sobo 7a9d408425 Add rootView to tab spec 2013-05-01 17:41:25 -07:00
Nathan Sobo 69300e0766 Make Buffer.transact restore marker ranges on undo/redo of transaction
We no longer need to restore selection ranges before and after
transactions now because selections are based on markers so they go
along for the ride for free. This allows us to delegate directly to
Buffer.transact from EditSession.
2013-05-01 18:38:40 -06:00
Corey Johnson & Nathan Sobo 559f76e887 Dragging a tab from one pane to another clears all placement markers
Closes #528
2013-05-01 17:36:54 -07:00
Kevin Sawicki 6eb88278ae Only return false if focused
Previously the editor would not gain focus if the underlayer was clicked.
2013-05-01 17:27:33 -07:00
Garen Torikian 662ddf9fd6 Update documentation to push threshold back past 80% 2013-05-01 17:21:05 -07:00
Kevin Sawicki 698f670350 Remove mini class from grammar selector
Grammars were wrapping when miniaturized even though
there was plenty of room.
2013-05-01 17:13:46 -07:00
Kevin Sawicki aebaf688a9 Add secondar-line class to light theme 2013-05-01 17:13:17 -07:00
Kevin Sawicki 7e15a343f3 Add primary/secondary line classes to select list
This will be a style that can be used by extending
classes to provide two lines that don't wrap with
color and padding.

This is initially used by symbols view and fuzzy finder.
2013-05-01 17:09:37 -07:00
Garen Torikian f47eea1e6c 💄 2013-05-01 17:03:12 -07:00
Garen Torikian 5a944514f3 Rewrite all hash key options 2013-05-01 17:01:55 -07:00
Garen Torikian fb955667f5 Resolve references to Internal 2013-05-01 17:01:55 -07:00
Garen Torikian 31aaa23b20 Update tokenizedbuffers 2013-05-01 17:01:55 -07:00
Garen Torikian 4d239b46e2 Update textmatescopeselector 2013-05-01 17:01:55 -07:00
Garen Torikian 856b07197a Update textbuffer 2013-05-01 17:01:55 -07:00
Garen Torikian d07751605a Update textbuffer 2013-05-01 17:01:55 -07:00
Garen Torikian ddb09e98e8 Update selection 2013-05-01 17:01:55 -07:00
Garen Torikian 75b40f603a Update screen-line 2013-05-01 17:01:55 -07:00
Garen Torikian 5c21d71461 Update root-view 2013-05-01 17:01:54 -07:00
Garen Torikian 6c5f310cba Update range 2013-05-01 17:01:54 -07:00
Garen Torikian 909a951ff0 Update project 2013-05-01 17:01:54 -07:00
Garen Torikian 51a07bdb31 Update point 2013-05-01 17:01:54 -07:00
Garen Torikian 3dcdf0863e Update line-map 2013-05-01 17:01:54 -07:00
Garen Torikian 46b5731ac4 Update line-map 2013-05-01 17:01:54 -07:00
Garen Torikian 9be438d2f7 Update language-mode 2013-05-01 17:01:54 -07:00
Garen Torikian bb8f4fb092 Update image-view 2013-05-01 17:01:54 -07:00
Garen Torikian 7b65a68450 Update image-edit-session 2013-05-01 17:01:54 -07:00
Garen Torikian 7e6eecc6cf Update gutter 2013-05-01 17:01:54 -07:00
Garen Torikian 9d709ea152 Update git 2013-05-01 17:01:54 -07:00
Garen Torikian eadb3cf98c Update fold 2013-05-01 17:01:54 -07:00
Garen Torikian 09e9c2f224 Update file 2013-05-01 17:01:53 -07:00
Garen Torikian dbae05605b Update eventemitter 2013-05-01 17:01:53 -07:00
Garen Torikian 3c5b721177 Update Editor 2013-05-01 17:01:53 -07:00
Garen Torikian b0e4ce7bb8 Update EditSession 2013-05-01 17:01:53 -07:00
Garen Torikian 2bf7adef5c A slight modification to EditSession 2013-05-01 17:01:53 -07:00
Garen Torikian 34fb03fbd3 Update DisplayBuffer 2013-05-01 17:01:53 -07:00
Garen Torikian a8a28fea18 Update DisplayBufferMarker 2013-05-01 17:01:53 -07:00
Garen Torikian 16cef29925 Update directory 2013-05-01 17:01:53 -07:00
Garen Torikian 26564ee020 Update cursor 2013-05-01 17:01:53 -07:00
Garen Torikian 66a80ade30 Update config 2013-05-01 17:01:53 -07:00
Garen Torikian 685ec5d8d7 Update buffer-marker docs 2013-05-01 16:58:43 -07:00
Garen Torikian 52139dc24e Bump biscotto 2013-05-01 16:58:43 -07:00
Garen Torikian 2357f46761 Fix block comments in src 2013-05-01 16:58:43 -07:00
Garen Torikian 64fea21a38 Bump biscotto 2013-05-01 16:58:05 -07:00
Kevin Sawicki 71e73750e2 Kill negative top margin on path 2013-05-01 16:52:13 -07:00
Kevin Sawicki 37b298fcba Lessify the base fuzzy finder stylehsheet 2013-05-01 16:51:39 -07:00
Kevin Sawicki 01ad97e7b0 Show ellipsis for long paths in fuzzy finder
Previously the directory was shown after the file name
and it would wrap if too long causing inconsistent row
heights.

Now the full path is shown below the name in a smaller font
and both the path and file are set to overflow with an ellipsis.
2013-05-01 16:46:22 -07:00
Nathan Sobo 988f4da6e1 Always restore marker ranges on undo/redo, even for valid markers 2013-05-01 17:32:39 -06:00
Nathan Sobo f48ee846e9 💄 2013-05-01 17:23:21 -06:00
Nathan Sobo a9c599abeb Isolate folds on a per-display-buffer basis
Creating a fold in one pane now has no effect on an edit session
for the same buffer in another pane. Before they were polluting each
other by sharing the same fold markers.
2013-05-01 16:24:32 -06:00
Nathan Sobo c717d23901 🔫 dead code 2013-05-01 16:15:28 -06:00
Nathan Sobo 6153375c76 Base folds on buffer markers, since they don't rely on screen coords
When creating a display buffer for buffers that already have fold
markers, which we'll eventually do on refresh, basing folds on
full-blown display buffer markers is a problem because we end up
creating display buffer markers during construction of the line map.
When we create a display buffer marker it memoizes its screen positions,
but since the line map isn't built yet we have nothing to translate
against. Basically, since the line map depends on folds, folds can't
depend on the line map. Luckily, they don't need to. Buffer markers
work just fine.
2013-05-01 16:14:25 -06:00
probablycorey 850ecb23df Atom will auto-update on close if a newer version is on Speakeasy
The first call to terminate is canceled so that every window can be 
closed. On AtomCefClient the OnBeforeClose method is called when a
browser is finished closing. Once all windows have finished closing, AtomCefClient calls terminate again, which will terminate the 
application.

Closes #527
2013-05-01 13:54:38 -07:00
probablycorey 8f03ff2952 💄 2013-05-01 13:54:38 -07:00
probablycorey 0be166bb59 Remove the bottomPaddingInLines from editor 2013-05-01 13:54:37 -07:00
probablycorey cb2d24baca Listen for clicks outside of the render lines on the underlayer 2013-05-01 13:54:37 -07:00
probablycorey 7609b08e86 💄 2013-05-01 13:54:37 -07:00
Kevin Sawicki aeffa0e150 Remove duplicate property 2013-05-01 13:10:14 -07:00
Kevin Sawicki 932d552ae7 Only show scrolls when needed in tree view 2013-05-01 08:22:03 -07:00
Kevin Sawicki 78d8485243 Allow config window to be closed with meta-W
window.confirmClose() should close the window immediately when
there is no rootView which is the case in the config window.
2013-05-01 08:02:17 -07:00
Kevin Sawicki c3232a463c Use ems instead of pixels for config editor widths
This allows the editor to be big enough to still show
multiple characters in large font sizes.
2013-05-01 07:58:12 -07:00
Kevin Sawicki 536beb0d40 Support setting number fields to 0
Previously entering 0 would end up as '0' in the config
value instead of as a numeric value.
2013-04-30 23:10:22 -07:00
Kevin Sawicki 1afe4f0cd9 Mention missing wrap guide fix 2013-04-30 23:02:48 -07:00
Kevin Sawicki 334c4095bb Write empty object values on same line as keys 2013-04-30 22:43:51 -07:00
Kevin Sawicki 986e5f9c7a Default to 80 when editor.preferredLineLength <= 0
Previously any non-null value would be used as the target
column in the wrap guide and autoflow packages when really
80 should have been used if the value was non-postive.

Now config.getPositiveInt() is called with a default value
of 80 if the current value isn't already positive.
2013-04-30 22:37:02 -07:00
Kevin Sawicki 6077cf2389 Mention save error with markdow preview 2013-04-30 22:06:05 -07:00
Kevin Sawicki f417e898f6 Only call save on active item when it exists
Closes #529
2013-04-30 22:02:46 -07:00
Kevin Sawicki 047d9525e7 Ignore first modified event in config editor
This event comes from initially setting the text for the current
config value on the editor and was causing the config to be
immediately saved multiple times when opened.
2013-04-30 21:55:43 -07:00
Kevin Sawicki ca7da8a0da Don't set un-parseable numbers to 0 in the config
Previously if an integer or float field was empty it would
default to zero instead of undefined which made it inconsistent
with string value fields.

Now the config value is only set as a Number when it can be
parsed as one.
2013-04-30 21:36:27 -07:00
Nathan Sobo 49b17023a5 Fix method missing exception in LineMap.logLines 2013-04-30 18:40:14 -06:00
Nathan Sobo 693c4f8270 Preserve folds when restoring selections on undo/redo 2013-04-30 18:39:54 -06:00
Nathan Sobo df08c14aef Restore markers before triggering buffer events
This allows folds to be restored on undo/redo
2013-04-30 18:21:37 -06:00
Nathan Sobo af903f6690 Don't trigger marker-added/removed events anymore
I missed this previously
2013-04-30 18:20:15 -06:00
Nathan Sobo cef25898bb Properly unsubscribe DisplayBuffer from Buffer on destruction 2013-04-30 18:14:53 -06:00
Nathan Sobo 0dea901368 Use instance method 2013-04-30 18:14:31 -06:00
Nathan Sobo 9086171f45 💄 2013-04-30 18:14:13 -06:00
Nathan Sobo 9aec992693 Hold marker [in]validation events until after buffer change events
For a while, the goal has been to prevent marker update events from
firing until after the buffer change event. But at the same time, we
want all the markers to be updated when the buffer change fires. This
commit irons out some issues for markers that are invalidated or
revalidated by the change, making their behavior consistent with the
rest of marker updates.
2013-04-30 17:37:28 -06:00
Kevin Sawicki a788006d7c Default editor.preferredLineLength config to 80 2013-04-30 15:03:46 -07:00
Nathan Sobo 40512a4823 Don't update the DisplayBuffer when a fold's marker changes
Because we don't currently allow for direct manipulation of fold
marker ranges, if a fold's marker changes it will be because of a
buffer change. This buffer change will bubble to the display buffer
anyway, where the screen line update will account for the change to
the fold's marker range.
2013-04-30 15:07:37 -06:00
Nathan Sobo 2e77f0ed7d Eliminate special behavior for changes straddling start / end of folds
Previously, we were attempting to preserve the fold and adjust in an
intuitive way in the face of changes that straddled the beginning or
the end of the fold. That added complexity… we would have to add a new
marker invalidation strategy to support it and I'm not convinced it's
actually a big deal to destroy folds in the face of straddling changes.
So that's what we do now.
2013-04-30 14:28:42 -06:00
probablycorey af5392b8ac Add extra padding to the bottom of an editor's vertical scrollbar
This gives us some space when we scroll to the bottom of a file.
Similar to how MacVim works when using the mouse.

Closes #464
2013-04-30 11:52:28 -07:00
probablycorey 471ac4976f Test correct editor in font size spec 2013-04-30 11:52:28 -07:00
probablycorey 16c892caa6 Update click and drag spec
Stop relying on setInterval to trigger the scroll events in the spec. Instead simulate the mouse moving at the top of the screen. I found 
testing the setInterval approach required so much mocking that it 
wasn't worth it.
2013-04-30 11:52:28 -07:00
Corey Johnson 35b0e06260 💄 2013-04-30 11:52:28 -07:00
Corey Johnson bbb7dfdd07 Only the underlayer needs height: 100% 2013-04-30 11:52:28 -07:00
Nathan Sobo 56a9316a28 Fix docs references 2013-04-30 11:37:31 -06:00
Nathan Sobo 7d676f51bd Document TextBuffer.getMarker[s] 2013-04-30 05:12:43 -06:00
Nathan Sobo 7ca8e31087 Document Fold methods 2013-04-30 05:12:13 -06:00
Nathan Sobo 029d8b58db Remove outdated method 2013-04-30 04:58:02 -06:00
Nathan Sobo 160a336b48 Fix command interpreter spec 2013-04-30 04:57:08 -06:00
Nathan Sobo 1ecd195e6e 💄 2013-04-30 04:53:26 -06:00
Nathan Sobo 9f8a07fc27 Fix some edit session specs. The remaining failures are less trivial. 2013-04-30 04:43:28 -06:00
Nathan Sobo 1f3e3c698f Add DisplayBuffer.foldsStartingAtBufferRow 2013-04-30 04:37:11 -06:00
Nathan Sobo 1f9e33d995 WIP: Base folds on markers.
Still a couple of specs failing on this for changes that straddle the
start / end of a fold. We need a new marker invalidation strategy for
these cases.
2013-04-30 04:30:08 -06:00
Nathan Sobo 32178541fe Emit 'destroyed' events from display buffer markers 2013-04-29 21:01:48 -06:00
Nathan Sobo 51e915c423 Replace marker 'observe' methods w/ ordinary 'changed' event 2013-04-29 21:01:48 -06:00
Nathan Sobo 4ca00f7347 Simplify marker events
Instead of marker-added and marker-removed events which are emitted
when markers are created/invalidated/revalidated/destroyed, we now
just have marker-created events that are triggered *only* when markers
are created for the first time. The marker itself emits a 'destroyed'
event when it is destroyed. The marker already notifies observers when
its validation status changes, so that's covered.
2013-04-29 21:01:48 -06:00
Nathan Sobo b2d34d93ab Emit 'marker-added/removed' events on DisplayBuffer 2013-04-29 21:01:48 -06:00
Nathan Sobo 78bb074c0d Add DisplayBufferMarker.inspect 2013-04-29 21:01:48 -06:00
Nathan Sobo 3a39c92ae4 Emit 'marker-added/removed' events when buffer marker validity changes 2013-04-29 21:01:48 -06:00
Nathan Sobo 883127f0d6 Emit 'marker-added/removed' when buffer markers are created/destroyed 2013-04-29 21:01:48 -06:00
Nathan Sobo 3b52cd018b Reduce documentation threshold
I deleted a bunch of documented methods. I haven't added any new
undocumented methods, but now there are fewer overall documented
methods, which is breaking the docs threshold spec. I don't think it
makes sense to start documenting non-related methods in this branch
right now.
2013-04-29 21:01:48 -06:00
Nathan Sobo 6f34d0b346 Document new methods 2013-04-29 21:01:48 -06:00
Nathan Sobo b35bd36ec7 Get spell check specs passing with new marker api 2013-04-29 21:01:48 -06:00
Nathan Sobo 8c037bf425 Destroy markers properly in command panel operations 2013-04-29 21:01:48 -06:00
Nathan Sobo ae5e07ddd6 Make bracket matcher work with new marker API 2013-04-29 21:01:48 -06:00
Nathan Sobo b16529bf9f Use new marker API to get marker ranges in command panel operations 2013-04-29 21:01:47 -06:00
Nathan Sobo db0676cc51 Use new API to destroy markers in snippets package 2013-04-29 21:01:47 -06:00
Nathan Sobo 2d1cb8b519 Fix selection spec to to check marker destruction properly 2013-04-29 21:01:47 -06:00
Nathan Sobo 3a11c56438 Add .isDestroyed method to markers 2013-04-29 21:01:47 -06:00
Nathan Sobo caf34d6a3a EditSession.selectMarker checks if the marker is valid
Also, it returns the selected range if it's valid, and otherwise
returns a falsy value. This has more utility than just true/false.
2013-04-29 21:01:47 -06:00
Nathan Sobo a028dff6e9 Add DisplayBufferMarker.isValid 2013-04-29 21:01:47 -06:00
Nathan Sobo 0563ee4a97 Make DisplayBuffer.findMarkers return DisplayBufferMarkers 2013-04-29 21:01:47 -06:00
Nathan Sobo d466abdc64 Fix DisplayBuffer spec 2013-04-29 21:01:47 -06:00
Nathan Sobo f4be899ae9 Switch EditSession over to object-oriented markers API 2013-04-29 21:01:47 -06:00
Nathan Sobo 5403bc647a Convert display buffer markers to object-oriented API 2013-04-29 21:01:47 -06:00
Nathan Sobo 482eb6c0de Give TextBuffer an object-oriented marker interface
The previous API revolved around methods on TextBuffer for querying
and manipulating markers based on their id. Now marker creation
methods return marker objects. These are still retrievable by id so
they can be dealt with across serialization boundaries in the future,
but you deal with them directly as objects.
2013-04-29 21:01:47 -06:00
Nathan Sobo e02e4cd975 Add DisplayBuffer.findMarker[s] 2013-04-29 21:01:47 -06:00
Nathan Sobo ac90b13032 Add missing Buffer.findMarker implementation 2013-04-29 21:01:46 -06:00
Nathan Sobo 64aaf670ed Add startRow and endRow special attributes to TextBuffer.findMarker[s] 2013-04-29 21:01:46 -06:00
Nathan Sobo 4a764c2d9d 💄 2013-04-29 21:01:46 -06:00
Nathan Sobo 0f0480b79f Add Buffer.findMarker[s], which returns marker[s] matching attributes 2013-04-29 21:01:46 -06:00
Nathan Sobo df6feab346 Add Range.compare 2013-04-29 21:01:46 -06:00
Nathan Sobo aef34c11b7 Use new DisplayBuffer.updateScreenLines method for .destroyFold 2013-04-29 21:01:46 -06:00
Nathan Sobo 0292ba959f Use the same code path for fold creation and tokenized buffer changes 2013-04-29 21:01:46 -06:00
Kevin Sawicki 58275a2f0a Use ems instead of pixels for loading octicon size 2013-04-29 18:23:10 -07:00
Kevin Sawicki f0d3a06655 Mention dev mode fixes 2013-04-29 18:02:40 -07:00
Kevin Sawicki 0ce67b307c Remove unneeded resource path assignment
There is a fall back case after this that sets the resource
path to the bundle path if not already set.
2013-04-29 17:58:41 -07:00
Kevin Sawicki 598cdea22a Don't crash when running dev with no resource path
Previously if the --dev flag was used and no resource was
specified or found in ~/github/atom then the app would
crash trying to add a null resource path to the node paths
list.

Now the bundle resource path is the fallback whenever the
dev resource path cannot be found or does not exist.
2013-04-29 17:54:31 -07:00
Kevin Sawicki 32d23d6303 Allow Atom to be run in stable mode
Previously isDevMode was returning true when it was called
before the resource path was initially set forcing Atom to
always run in dev mode.

Now we just use the new isDevFlagSpecified() method to check
where the bundle resource path should be used.
2013-04-29 17:51:13 -07:00
Kevin Sawicki 3279a575f6 Reuse search when re-toggling find in project
Previously the prior find in file search was thrown
away when toggling find-in-project subsequent times.

Now the previous search text is retained and selected when toggled.
2013-04-29 15:25:07 -07:00
Kevin Sawicki 16ffbd1920 Reuse search when re-toggling find in file
Previously the prior find in file search was thrown
away when toggling find-in-file subsequent times.

Now the previous search text is retained and selected when toggled.
2013-04-29 15:24:44 -07:00
Corey Johnson 9180060920 Revert "Listen for mouse events on scrollView instead of renderedLines"
You can't listen on scroll view because that makes
it impossible to scroll using the scrollbar

Opens #464

This reverts commit fdae5fd89c.
2013-04-29 15:07:38 -07:00
Corey Johnson fdae5fd89c Listen for mouse events on scrollView instead of renderedLines
It was not possible to click on the lower part of the last line
when the editor was scrollable and listening for mousedown events on renderedLines.

Closes #464
2013-04-29 14:59:23 -07:00
Corey Johnson 8e218de3e7 💄 2013-04-29 14:59:23 -07:00
Kevin Sawicki a6c89b75fc Retain command panel text between toggles
Previously the mini editor was cleared on toggle but now
the existing text retained and selected when toggled.
2013-04-29 14:48:06 -07:00
Kevin Sawicki 15cf475d04 Use loading octicon in command panel
This removes the SVG spinner which had been incorrectly
rendering since the bootstrap upgrade due to box sizing
issues.
2013-04-29 14:13:21 -07:00
172 arquivos alterados com 5218 adições e 4436 exclusões
+3
Ver Arquivo
@@ -88,3 +88,6 @@
[submodule "vendor/packages/hyperlink-helper.tmbundle"]
path = vendor/packages/hyperlink-helper.tmbundle
url = https://github.com/textmate/hyperlink-helper.tmbundle
[submodule "vendor/apm"]
path = vendor/apm
url = https://github.com/atom/apm.git
+1 -1
Ver Arquivo
@@ -6,7 +6,7 @@ pairs:
jc: Jerry Cheung; jerry
bl: Brian Lopez; brian
jp: Justin Palmer; justin
gt: Garen Torikian; garen
email:
domain: github.com
#global: true
+15
Ver Arquivo
@@ -1,3 +1,18 @@
* Fixed: Not being able to disable packages from configuration UI.
* Fixed: Fuzzy finder showing poor results for entered text
* Improved: App icon
* Fixed: Fuzzy finder being empty sometimes
* Improved: App icon
* Fixed: End of line invisibles rendering incorrectly with the indent guide
* Fixed: Updates not installing automatically on restart
* Fixed: Wrap guide not displaying
* Fixed: Error when saving with the markdown preview focused
* Fixed: Atom always running in dev mode
* Fixed: Crash when running in dev mode without a path to the Atom source
* Fixed: Freeze when editing a RoR class
* Added: meta-N to open a new untitled editor in the current window
+5
Ver Arquivo
@@ -21,3 +21,8 @@
* Add 3rd-party packages by submoduling in `vendor/packages/`
* Commit messages are in the present tense
* Files end with a newline
* Class variables and methods should be in the following order:
* Class variables (variables starting with a `@`)
* Class methods (methods starting with a `@`)
* Instance variables
* Instance methods
+6 -2
Ver Arquivo
@@ -63,8 +63,7 @@ task :install => [:build] do
raise "Missing directory for `atom` binary"
end
FileUtils.cp("#{ATOM_SRC_PATH}/atom.sh", cli_path)
FileUtils.chmod(0755, cli_path)
FileUtils.ln_s "#{ATOM_SRC_PATH}/atom.sh", cli_path, :force => true
Rake::Task["clone-default-bundles"].invoke()
@@ -91,6 +90,11 @@ task :clean do
`rm -rf cef`
end
desc "Delete cached cefodes"
task "clean-cefode-cache" do
`rm -rf /tmp/atom-cached-cefodes`
end
desc "Run the specs"
task :test => ["update-cef", "clone-default-bundles", "build"] do
`pkill Atom`
Arquivo normal → Arquivo executável
Ver Arquivo
+1 -1
Ver Arquivo
@@ -106,7 +106,7 @@ describe "TokenizedBuffer.", ->
[languageMode, buffer] = []
beforeEach ->
editSession = benchmarkFixturesProject.buildEditSession('medium.coffee')
editSession = benchmarkFixturesProject.open('medium.coffee')
{ languageMode, buffer } = editSession
benchmark "construction", 20, ->
-133
Ver Arquivo
@@ -1,133 +0,0 @@
# Authoring Packages
A package can contain a variety of different resource types to change Atom's
behavior. The basic package layout is as follows (not every package will
have all of these directories):
```text
my-package/
lib/
config/
stylesheets/
keymaps/
snippets/
grammars/
package.json
index.coffee
```
**NOTE:** NPM behavior is partially implemented until we get a working Node.js
API built into Atom. The goal is to make Atom packages be a superset of NPM
packages
## package.json
Similar to npm packages, Atom packages can contain a `package.json` file in their
top-level directory. This file contains metadata about the package, such as the
path to its "main" module, library dependencies, and manifests specifying the
order in which its resources should be loaded.
## Source Code
If you want to extend Atom's behavior, your package should contain a single
top-level module, which you export from `index.coffee` or another file as
indicated by the `main` key in your `package.json` file. The remainder of your
code should be placed in the `lib` directory, and required from your top-level
file.
Your package's top-level module is a singleton object that manages the lifecycle
of your extensions to Atom. Even if your package creates ten different views and
appends them to different parts of the DOM, it's all managed from your top-level
object. Your package's top-level module should implement the following methods:
- `activate(rootView, state)` **Required**: This method is called when your
package is loaded. It is always passed the window's global `rootView`, and is
sometimes passed state data if the window has been reloaded and your module
implements the `serialize` method.
- `serialize()` **Optional**: This method is called when the window is shutting
down, allowing you to return JSON to represent the state of your component. When
the window is later restored, the data you returned will be passed to your
module's `activate` method so you can restore your view to where the user left
off.
- `deactivate()` **Optional**: This method is called when the window is shutting
down. If your package is watching any files or holding external resources in any
other way, release them here. If you're just subscribing to things on window
you don't need to worry because that's getting torn down anyway.
## A Simple Package Layout:
```text
my-package/
package.json # optional
index.coffee
lib/
my-package.coffee
```
`index.coffee`:
```coffeescript
module.exports = require "./lib/my-package"
```
`my-package/my-package.coffee`:
```coffeescript
module.exports =
activate: (rootView, state) -> # ...
deactivate: -> # ...
serialize: -> # ...
```
Beyond this simple contract, your package has full access to Atom's internal
API. Anything we call internally, you can call as well. Be aware that since we
are early in development, APIs are subject to change and we have not yet
established clear boundaries between what is public and what is private. Also,
Please collaborate with us if you need an API that doesn't exist. Our goal is
to build out Atom's API organically based on the needs of package authors like
you. See [Atom's built-in packages](https://github.com/github/atom/tree/master/src/packages)
for examples of Atom's API in action.
## Stylesheets
Stylesheets for your package should be placed in the `stylesheets` directory.
Any stylesheets in this directory will be loaded and attached to the DOM when
your package is activated. An optional `stylesheets` key in your `package.json`
can list the stylesheets by name in order to specify a load order; otherwise
stylesheets are loaded alphabetically.
## Keymaps
Keymaps are placed in the `keymaps` subdirectory. By default, all keymaps will be
loaded in alphabetical order unless there is a `keymaps` array in `package.json`
specifying which keymaps to load and in what order. It's a good idea to provide
default keymaps for your extension. They can be customized by users later. See
the (main keymaps documentation)[#keymaps] for more information on how keymaps
work.
## Snippets
An extension can supply snippets in a `snippets` directory as `.cson` or `.json`
files:
```coffeescript
".source.coffee .specs":
"Expect":
prefix: "ex"
body: "expect($1).to$2"
"Describe":
prefix: "de"
body: """
describe "${1:description}", ->
${2:body}
"""
```
A snippets file contains scope selectors at its top level. Each scope selector
contains a hash of snippets keyed by their name. Each snippet specifies a
`prefix` and a `body` key.
All files in the directory will be automatically loaded, unless the
`package.json` supplies a `snippets` key as a manifest. As with all scoped
items, snippets loaded later take precedence over earlier snippets when two
snippets match a scope with the same specificity.
+12 -8
Ver Arquivo
@@ -1,22 +1,26 @@
## Command Panel
A partial implementation of the [Sam command language](http://man.cat-v.org/plan_9/1/sam)
The command panel contains a partial implementation of the [Sam command language](http://man.cat-v.org/plan_9/1/sam).
In addition, packages are free to design and define any scoped command.
*Examples*
Pop open the command line by hitting .
You can get a list of commands available to Atom (including any keybindings) by hitting `meta-p`.
`,` selects entire file
## Examples
`1,4` selects lines 1-4
`,` selects the entire file
`1,4` selects lines 1-4 in the current file
`/pattern` selects the first match after the cursor/selection
`s/pattern/replacement` replace first text matching pattern in current selection
`s/pattern/replacement` replaces the first text matching pattern in current selection
`s/pattern/replacement/g` replace all text matching pattern in current selection
`s/pattern/replacement/g` replaces all text matching pattern in current selection
`,s/pattern/replacement/g` replace all text matching pattern in file
`,s/pattern/replacement/g` replaces all text matching pattern in file
`1,4s/pattern/replacement` replace all text matching pattern in lines 1-4
`1,4s/pattern/replacement` replaces all text matching pattern in lines 1-4
`x/pattern` selects all matches in the current selections
+4 -5
Ver Arquivo
@@ -1,17 +1,16 @@
## Wrap Guide
The `wrap-guide` extension places a vertical line in each editor at a certain
column to guide your formatting so lines do not exceed a certain width.
column to guide your formatting, so lines do not exceed a certain width.
By default the wrap-guide is placed at the 80th column.
By default, the wrap-guide is placed at the 80th column.
### Configuration
You can customize where the column is placed using the `wrapGuide.columns`
config option.
config option:
config.cson:
```coffee-cript
```coffeescript
"wrap-guide":
columns: [
{ pattern: "\.mm$", column: 200 },
+138
Ver Arquivo
@@ -0,0 +1,138 @@
# Configuration Settings
## Your .atom Directory
When you install Atom, an _.atom_ directory is created in your home directory.
If you press `meta-,`, that directory is opened in a new window. For the
time being, this serves as the primary interface for adjusting configuration
settings, adding and changing key bindings, tweaking styles, etc.
Atom loads configuration settings from the `config.cson` file in your _~/.atom_
directory, which contains CoffeeScript-style JSON:
```coffeescript
core:
hideGitIgnoredFiles: true
editor:
fontSize: 18
```
Configuration is broken into namespaces, which are defined by the config hash's
top-level keys. In addition to Atom's core components, each package may define
its own namespace.
## Glossary of Config Keys
- `core`
- `disablePackages`: An array of package names to disable
- `hideGitIgnoredFiles`: Whether files in the _.gitignore_ should be hidden
- `ignoredNames`: File names to ignore across all of Atom (not fully implemented)
- `themes`: An array of theme names to load, in cascading order
- `autosave`: Save a buffer when its view loses focus
- `editor`
- `autoIndent`: Enable/disable basic auto-indent (defaults to `true`)
- `autoIndentOnPaste`: Enable/disable auto-indented pasted text (defaults to `false`)
- `nonWordCharacters`: A string of non-word characters to define word boundaries
- `fontSize`: The editor font size
- `fontFamily`: The editor font family
- `invisibles`: Specify characters that Atom renders for invisibles in this hash
- `tab`: Hard tab characters
- `cr`: Carriage return (for Microsoft-style line endings)
- `eol`: `\n` characters
- `space`: Leading and trailing space characters
- `preferredLineLength`: Identifies the length of a line (defaults to `80`)
- `showInvisibles`: Whether to render placeholders for invisible characters (defaults to `false`)
- `fuzzyFinder`
- `ignoredNames`: Files to ignore *only* in the fuzzy-finder
- `whitespace`
- `ensureSingleTrailingNewline`: Whether to reduce multiple newlines to one at the end of files
- `wrapGuide`
- `columns`: Array of hashes with a `pattern` and `column` key to match the
the path of the current editor to a column position.
## Customizing Key Bindings
Atom keymaps work similarly to stylesheets. Just as stylesheets use selectors
to apply styles to elements, Atom keymaps use selectors to associate keystrokes
with events in specific contexts. Here's a small example, excerpted from Atom's
built-in keymaps:
```coffee-script
'.editor':
'enter': 'editor:newline'
".select-list .editor.mini":
'enter': 'core:confirm',
```
This keymap defines the meaning of `enter` in two different contexts. In a
normal editor, pressing `enter` emits the `editor:newline` event, which causes
the editor to insert a newline. But if the same keystroke occurs inside of a
select list's mini-editor, it instead emits the `core:confirm` event based on
the binding in the more-specific selector.
By default, any keymap files in your `~/.atom/keymaps` directory are loaded
in alphabetical order when Atom is started. They will always be loaded last,
giving you the chance to override bindings that are defined by Atom's core
keymaps or third-party packages.
## Changing The Theme
Atom comes bundled with two themes `atom-dark-*` and `atom-light-*`.
Because Atom themes are based on CSS, it's possible to have multiple themes
active at the same time. For example, you'll usually select a theme for the UI
and another theme for syntax highlighting. You can select themes by specifying
them in the `core.themes` array in your `config.cson`:
```coffee-script
core:
themes: ["atom-light-ui", "atom-light-syntax"]
# or, if the sun is going down:
# themes: ["atom-dark-ui", "atom-dark-syntax"]
```
You install new themes by placing them in the _~/.atom/themes_ directory. A
theme can be a CSS file, a directory containing multiple CSS files, or a
TextMate theme (either _.tmTheme_ or _.plist_).
## Installing Packages (Partially Implemented)
To install a package, clone it into the _~/.atom/packages_ directory. Atom will
also load grammars and snippets from TextMate bundles. If you want to disable a
package without removing it from the packages directory, insert its name into
_config.core.disabledPackages_:
```coffeescript
core:
disabledPackages: [
"fuzzy-finder",
"tree-view"
]
```
## Quick Personal Hacks
### user.coffee
When Atom finishes loading, it will evaluate _user.coffee_ in your _~/.atom_
directory, giving you a chance to run arbitrary personal CoffeeScript code to
make customizations. You have full access to Atom's API from code in this file.
Please refer to the [Atom Internals Guide](./internals/intro,md) for more information. If your
customizations become extensive, consider [creating a package](./packages/creating_packages.md).
### user.css
If you want to apply quick-and-dirty personal styling changes without creating
an entire theme that you intend to distribute, you can add styles to
_user.css_ in your _~/.atom_ directory.
For example, to change the color of the highlighted line number for the line that
contains the cursor, you could add the following style to _user.css_:
```css
.editor .line-number.cursor-line {
color: pink;
}
```
+40 -35
Ver Arquivo
@@ -1,36 +1,37 @@
# Getting Started
Welcome to Atom. This documentation is intended to offer a basic introduction
of how to get productive with this editor. Then we'll delve into more details
about configuring, theming, and extending Atom.
Welcome to Atom. This documentation provides a basic introduction to being
productive with this editor. We'll then delve into more details about configuring,
theming, and extending Atom.
## The Command Palette
If there's one key-command you learn in Atom, it should be `meta-p` (`meta` is
If there's one key-command you must remember in Atom, it should be `meta-p` (`meta` is
synonymous with the ⌘ key). You can always hit `meta-p` to bring up a list of
commands that are relevant to the currently focused UI element. If there is a
key binding for a given command, it is also displayed. This is a great way to
explore the system and get to know the key commands interactively. If you'd like
to add or change a binding for a command, refer to the [key
bindings](#customizing-key-bindings) section to learn how.
to learn about adding or changing a binding for a command, refer to the [key
bindings](#customizing-key-bindings) section.
![Command Palette](http://f.cl.ly/items/32041o3w471F3C0F0V2O/Screen%20Shot%202013-02-13%20at%207.27.41%20PM.png)
## Basic Key Bindings
Remember you can always use `meta-p` to explore available commands and their
You can always use `meta-p` to explore available commands and their
bindings, but here's a list of a few useful commands.
- `meta-o` : open file/directory
- `meta-n` : new window
- `meta-o` : open a file or directory
- `meta-n` : open new window
- `meta-r` : reload the current window
- `meta-alt-ctrl-s` : run specs
- `meta-t` : open fuzzy file finder
- `meta-alt-ctrl-s` : run test specs
- `meta-t` : open file finder to navigate files in your project
- `meta-;` : open command prompt
- `meta-f` : open command prompt with `/`
- `meta-f` : open command prompt with `/` for a local file search
- `meta-g` : repeat the last local search
- `meta-shift-f` : open command prompt with `Xx/` for a project-wide search
- `meta-\` : focus/open tree view, or close it when it is focused
- `meta-shift-\` : open tree view with the current file selected
- `meta-|` : open tree view with the current file selected
- `ctrl-w v`, `ctrl-|` : split screen vertically
- `ctrl-w s`, `ctrl--` : split screen horizontally
- `meta-l` : go to line
@@ -52,8 +53,8 @@ issue so you can keep working.
The fastest way to find a file in your project is to use the fuzzy finder. Just
hit `meta-t` and start typing the name of the file you're looking for. If you
already have the file open and want to jump to it, hit `meta-b` to bring up a
searchable list of open buffers.
already have the file open as a tab and want to jump to it, hit `meta-b` to bring
up a searchable list of open buffers.
You can also use the tree view to navigate to a file. To open or move focus to
the tree view, hit `meta-\`. You can then navigate to a file and select it with
@@ -73,28 +74,32 @@ To delete a file, select it in the tree view and hit `delete`.
#### Using the Command Line
Atom has a command line similar to editors Emacs and Vim, which is currently the
only interface for performing searches. Hitting `meta-f` will open the command
line prepopulated with the `/` command, which finds forward in the current
buffer from the location of the cursor. Pressing `meta-g` will repeat the
search. Hitting `meta-shift-f` will open the command line prepopulated with
`Xx/`, which is a composite command that performs a global search. The results
of the search will appear in the operation preview list, which you can focus
Atom has a command line similar to old-school editors such as emacs and vim. Nearly
every command has a key binding which you can discover with `meta-p`.
The command line is also (currently) the only place you can perform a search. Hitting
`meta-f` opens the command line and prepopulates it with the `/` command. This finds
text in the current buffer, starting at the location of the cursor. Pressing `meta-g`
repeats the search. Hitting `meta-shift-f` opens the command line and prepopulates
it with `Xx/`, which is a composite command that performs a global search. The results
of the search appear in the operation preview list, which you can focus
with `meta-:`.
Atom's command language is still under construction and is loosely based on
Atom's command language is still under construction, and is loosely based on
the [Sam editor](http://doc.cat-v.org/bell_labs/sam_lang_tutorial/) from the
Plan 9 operating system. It's similar to Ex mode in Vim, but is selection-based
Plan 9 operating system. It's similar to Ex mode in vim, but is selection-based
rather than line-based. It allows you to compose commands together in
interesting ways.
#### Navigating By Symbols
If you want to jump to a method, you can use the ctags-based symbols package.
The `meta-j` binding will open a list of all symbols in the current file. The
`meta-shift-j` binding will open a list of all symbols for the current project
based on a tags file. And `meta-.` will jump to the tag for the word currently
under the cursor. Make sure you have a tags file generated for the project for
The `meta-j` binding opens a list of all symbols in the current file. The
`meta-shift-j` binding opens a list of all symbols for the current project
based on a tags file. `meta-.` jumps to the tag for the word currently
under the cursor.
Make sure you have a tags file generated for the project for
the latter of these two bindings to work. Also, if you're editing CoffeeScript,
it's a good idea to update your `~/.ctags` file to understand the language. Here
is [a good example](https://github.com/kevinsawicki/dotfiles/blob/master/.ctags).
@@ -106,31 +111,31 @@ command, as follows: `s/foo/bar/g`. Note that if you have a selection, the
replacement will only occur inside the selected text. An empty selection will
cause the replacement to occur across the whole buffer. If you want to run the
command on the whole buffer even if you have a selection, precede your
substitution with the `,` address, which specifies that the command following it
operate on the whole buffer.
substitution with the `,` address; this indicates that the following command should
run on the whole buffer.
### Split Panes
You can split any editor pane horizontally or vertically by using `ctrl-shift-|` or
You can split any editor pane horizontally or vertically by using `ctrl-\` or
`ctrl-w v`. Once you have a split pane, you can move focus between them with
`ctrl-tab` or `ctrl-w w`. To close a pane, close all tabs inside it.
### Folding
You can fold everything with `ctrl-shift-[` and unfold everything with
`ctrl-shift-]`. Or, you can fold / unfold by a single level with `ctrl-[` and
You can fold everything with `ctrl-{` and unfold everything with
`ctrl-}`. Or, you can fold / unfold by a single level with `ctrl-[` and
`ctrl-]`. The user interaction around folds is still a bit rough, but we're
planning to improve it soon.
### Soft-Wrap
If you want to toggle soft wrap, trigger the command from the command palette.
Press `meta-p` to open the palette, then type "wrap" to find the correct
Hit `meta-p` to open the palette, then type "wrap" to find the correct
command.
## Your .atom Directory
When you install Atom, a `.atom` directory is created in your home directory.
When you install Atom, an `.atom` directory is created in your home directory.
If you press `meta-,`, that directory will be opened in a new window. For the
time being, this will serve as the primary interface for adjusting configuration
settings, adding and changing key bindings, tweaking styles, etc.
+1 -4
Ver Arquivo
@@ -49,10 +49,7 @@ the following way:
# basic key update
config.set("core.autosave", true)
# if you mutate a config key, you'll need to call `config.update` to inform
# observers of the change
config.get("fuzzyFinder.ignoredPaths").push "vendor"
config.update()
config.pushAtKeyPath("core.disabledPackages", "wrap-guide")
```
You can also use `setDefaults`, which will assign default values for keys that
+6 -2
Ver Arquivo
@@ -1,10 +1,14 @@
getting-started.md
configuring-atom.md
built-in-packages/intro.md
built-in-packages/command-panel.md
built-in-packages/markdown-preview.md
built-in-packages/wrap-guide.md
authoring-themes.md
authoring-packages..md
packages/authoring-packages.md
packages/creating_a_package.md
packages/included_libraries.md
packages/package_json.md
themes/authoring-themes.md
internals/intro.md
internals/configuration.md
internals/keymaps.md
+211
Ver Arquivo
@@ -0,0 +1,211 @@
# Authoring Packages
Packages are at the core of Atom. Nearly everything outside of the main editor manipulation
is handled by a package. That includes "core" pieces like the command panel, status bar,
file tree, and more.
A package can contain a variety of different resource types to change Atom's
behavior. The basic package layout is as follows (though not every package will
have all of these directories):
```text
my-package/
lib/
stylesheets/
keymaps/
snippets/
grammars/
spec/
package.json
index.coffee
```
**NOTE:** NPM behavior is partially implemented until we get a working Node.js
API built into Atom. The goal is to make Atom packages be a superset of NPM
packages.
Below, we'll break down each directory. There's also [a tutorial](./creating_a_package.md)
on creating your first package.
## package.json
Similar to [npm packages](http://en.wikipedia.org/wiki/Npm_(software)), Atom packages
can contain a _package.json_ file in their top-level directory. This file contains metadata
about the package, such as the path to its "main" module, library dependencies,
and manifests specifying the order in which its resources should be loaded.
In addition to the regular [npm package.json keys](https://npmjs.org/doc/json.html)
available, Atom package.json files [have their own additions](./package_json.md).
## Source Code
If you want to extend Atom's behavior, your package should contain a single
top-level module, which you export from _index.coffee_ (or whichever file is
indicated by the `main` key in your _package.json_ file). The remainder of your
code should be placed in the `lib` directory, and required from your top-level
file.
Your package's top-level module is a singleton object that manages the lifecycle
of your extensions to Atom. Even if your package creates ten different views and
appends them to different parts of the DOM, it's all managed from your top-level
object.
Your package's top-level module should implement the following methods:
- `activate(rootView, state)`: This **required** method is called when your
package is loaded. It is always passed the window's global `rootView`, and is
sometimes passed state data if the window has been reloaded and your module
implements the `serialize` method. Use this to do initialization work when your
package is started (like setting up DOM elements or binding events).
- `serialize()`: This **optional** method is called when the window is shutting
down, allowing you to return JSON to represent the state of your component. When
the window is later restored, the data you returned is passed to your
module's `activate` method so you can restore your view to where the user left
off.
- `deactivate()`: This **optional** method is called when the window is shutting
down. If your package is watching any files or holding external resources in any
other way, release them here. If you're just subscribing to things on window,
you don't need to worry because that's getting torn down anyway.
### Simple Package Code
```text
my-package/
package.json # optional
index.coffee
lib/
my-package.coffee
```
`index.coffee`:
```coffeescript
module.exports = require "./lib/my-package"
```
`my-package/my-package.coffee`:
```coffeescript
module.exports =
activate: (rootView, state) -> # ...
deactivate: -> # ...
serialize: -> # ...
```
Beyond this simple contract, your package has full access to Atom's internal
API. Anything we call internally, you can call as well. Be aware that since we
are early in development, APIs are subject to change and we have not yet
established clear boundaries between what is public and what is private. Also,
please collaborate with us if you need an API that doesn't exist. Our goal is
to build out Atom's API organically based on the needs of package authors like
you.
See [Atom's built-in packages](https://github.com/github/atom/tree/master/src/packages)
for examples of Atom's API in action.
## Stylesheets
Stylesheets for your package should be placed in the _stylesheets_ directory.
Any stylesheets in this directory will be loaded and attached to the DOM when
your package is activated. Stylesheets can be written as CSS or LESS.
An optional `stylesheets` array in your _package.json_ can list the stylesheets by
name to specify a loading order; otherwise, stylesheets are loaded alphabetically.
## Keymaps
Keymaps are placed in the _keymaps_ subdirectory. It's a good idea to provide
default keymaps for your extension, especially if you're also adding a new command.
By default, all keymaps are loaded in alphabetical order. An optional `keymaps`
array in your _package.json_ can specify which keymaps to load and in what order.
See the [main keymaps documentation](../internals/keymaps.md) for more information on
how keymaps work.
## Snippets
An extension can supply language snippets in the _snippets_ directory. These can
be `.cson` or `.json` files. Here's an example:
```coffeescript
".source.coffee .specs":
"Expect":
prefix: "ex"
body: "expect($1).to$2"
"Describe":
prefix: "de"
body: """
describe "${1:description}", ->
${2:body}
"""
```
A snippets file contains scope selectors at its top level (`.source.coffee .spec`).
Each scope selector contains a hash of snippets keyed by their name (`Expect`, `Describe`).
Each snippet also specifies a `prefix` and a `body` key. The `prefix` represents
the first few letters to type before hitting the `tab` key to autocomplete. The
`body` defines the autofilled text. You can use placeholders like `$1`, `$2`, to indicate
regions in the body the user can navigate to every time they hit `tab`.
All files in the directory are automatically loaded, unless the
_package.json_ supplies a `snippets` key. As with all scoped
items, snippets loaded later take precedence over earlier snippets when two
snippets match a scope with the same specificity.
## Language Grammars
If you're developing a new language grammar, you'll want to place your file in
the _grammars_ directory. Each grammar is a pairing of two keys, `match` and
`captures`. `match` is a regular expression identifying the pattern to highlight,
while `captures` is a JSON representing what to do with each matching group.
For example:
```json
{
'match': '(?:^|\\s)(__[^_]+__)'
'captures':
'1': 'name': 'markup.bold.gfm'
}
```
This indicates that the first matching capture (`(__[^_]+__)`) should have the
`markup.bold.gfm` token applied to it.
To capture a single group, simply use the `name` key instead:
```json
{
'match': '^#{1,6}\\s+.+$'
'name': 'markup.heading.gfm'
}
```
This indicates that Markdown header lines (`#`, `##`, `###`) should be applied with
the `markup.heading.gfm` token.
More information about the significance of these tokens can be found in
[section 12.4 of the TextMate Manual](http://manual.macromates.com/en/language_grammars.html).
Your grammar should also include a `filetypes` array, which is a list of file extensions
your grammar supports:
```
'fileTypes': [
'markdown'
'md'
'mkd'
'mkdown'
'ron'
]
```
## Writing Tests
Your package **should** have tests, and if they're placed in the _spec_ directory,
they can be run by Atom.
Under the hood, [Jasmine](https://github.com/pivotal/jasmine) is being used to run
to execute the tests, so you can assume that any DSL available there is available
to your package as well.
+254
Ver Arquivo
@@ -0,0 +1,254 @@
# Creating Packages
Let's take a look at creating our first package.
Atom has a command you can enter that'll create a package for you:
`package-generator:generate`. Otherwise, you can hit `meta-p`, and start typing
"Package Generator." Once you activate this package, it'll ask you for a name for
your new package. Let's call ours _changer_.
Now, _changer_ is going to have a default set of folders and files created for us.
Hit `meta-R` to reload Atom, then hit `meta-p` and start typing "Changer." You'll
see a new `Changer:Toggle` command which, if selected, pops up a new message. So
far, so good!
In order to demonstrate the capabilities of Atom and its API, our Changer plugin
is going to do two things:
1. It'll show only modified files in the file tree
2. It'll append a new pane to the editor with some information about the modified
files
Let's get started!
## Changing Keybindings and Commands
Since Changer is primarily concerned with the file tree, let's write a keybinding
that works only when the tree is focused. Instead of using the default `toggle`,
our keybinding executes a new command called `magic`.
_keymaps/changer.cson_ can easily become this:
```cson
'.tree-view-scroller':
'ctrl-V': 'changer:magic'
```
Notice that the keybinding is called `ctrl-V`--that's actually `ctrl-shift-v`.
You can use capital letters to denote using `shift` for your binding.
`.tree-view-scroller` represents the parent container for the tree view. Keybindings
only work within the context of where they're entered. For example, hitting `ctrl-V`
anywhere other than tree won't do anything. You can map to `body` if you want
to scope to anywhere in Atom, or just `.editor` for the editor portion.
To bind keybindings to a command, we'll use the `rootView.command` method. This
takes a command name and executes a function in the code. For example:
```coffeescript
rootView.command "changer:magic", => @magic()
```
It's common practice to namespace your commands with your package name, and separate
it with a colon (`:`). Rename the existing `toggle` method to `magic` to get the
binding to work.
Reload the editor, click on the tree, hit your keybinding, and...nothing happens! What the heck?!
Open up the _package.json_ file, and notice the key that says `activationEvents`.
Basically, this tells Atom to not load a package until it hears a certain event.
Let's change the event to `changer:magic` and reload the editor.
Hitting the key binding on the tree now works!
## Working with styles
The next step is to hide elements in the tree that aren't modified. To do that,
we'll first try and get a list of files that have not changed.
All packages are able to use jQuery in their code. In fact, we have [a list of
some of the bundled libraries Atom provides by default](./included_libraries.md).
Let's bring in jQuery:
```coffeescript
$ = require 'jquery'
```
Now, we can query the tree to get us a list of every file that _wasn't_ modified:
```coffeescript
magic: ->
$('ol.entries li').each (i, el) ->
if !$(el).hasClass("modified")
console.log el
```
You can access the dev console by hitting `alt-meta-i`. When we execute the
`changer:magic` command, the browser console lists the items that are not being
modified. Let's add a class to each of these elements called `hide-me`:
```coffeescript
magic: ->
$('ol.entries li').each (i, el) ->
if !$(el).hasClass("modified")
$(el).addClass("hide-me")
```
With our newly added class, we can manipulate the visibility of the elements
with a simple stylesheet. Open up _changer.css_ in the _stylesheets_ directory,
and add a single entry:
```css
ol.entries .hide-me {
display: none;
}
```
Refresh atom, and run the `changer` command. You'll see all the non-changed files
disappear from the tree. There are a number of ways you can get the list back;
let's just naively iterate over the same elements and remove the class:
```coffeescript
magic: ->
$('ol.entries li').each (i, el) ->
if !$(el).hasClass("modified")
if !$(el).hasClass("hide-me")
$(el).addClass("hide-me")
else
$(el).removeClass("hide-me")
```
## Creating a New Pane
The next goal of this package is to append a pane to the Atom editor that lists
some information about the modified files.
To do that, we're going to first create a new class method called `content`. Every
package that extends from the `View` class can provide an optional class method
called `content`. The `content` method constructs the DOM that your package uses
as its UI. The principals of `content` are built entirely on [SpacePen](https://github.com/nathansobo/space-pen),
which we'll touch upon only briefly here.
Our display will simply be an unordered list of the file names, and their
modified times. Let's start by carving out a `div` to hold the filenames:
```coffeescript
@content: ->
@div class: 'modified-files-container', =>
@ul class: 'modified-files-list', outlet: 'modifiedFilesList', =>
@li 'Test'
@li 'Test2'
```
You can add any HTML5 attribute you like. `outlet` names the variable
your package can uses to manipulate the element directly. The fat pipe (`=>`) indicates
that the next set are nested children.
We'll add one more line to `magic` to make this pane appear:
```coffeescript
rootView.vertical.append(this)
```
If you hit the key command, you'll see a box appear right underneath the editor.
Success!
Before we populate this, let's apply some logic to toggle the pane off and on, just
like we did with the tree view:
```coffeescript
# toggles the pane
if @hasParent()
rootView.vertical.children().last().remove()
else
rootView.vertical.append(this)
```
There are about a hundred different ways to toggle a pane on and off, and this
might not be the most efficient one. If you know your package needs to be toggled
on and off more freely, it might be better to draw the UI during the initialization,
then immediately call `hide()` on the element to remove it from the view. You can
then swap between `show()` and `hide()`, and instead of forcing Atom to add and remove
the element as we're doing here, it'll just set a CSS property to control your package's
visibility.
You might have noticed that our two `li` elements aren't showing up. Let's set
a color on them so that they pop. Open up `changer.css` and add this CSS:
```css
ul.modified-files-list {
color: white;
}
```
Refresh Atom, hit the key combo, and see your brilliantly white test list.
## Calling Node.js Code
Since Atom is built on top of Node.js, you can call any of its libraries, including
other modules that your package requires.
We'll iterate through our resulting tree, and construct the path to our modified
file based on its depth in the tree:
```coffeescript
path = require 'path'
# ...
modifiedFiles = []
# for each single entry...
$('ol.entries li.file.modified span.name').each (i, el) ->
filePath = []
# ...grab its name...
filePath.unshift($(el).text())
# ... then find its parent directories, and grab their names
parents = $(el).parents('.directory.modified')
parents.each (i, el) ->
filePath.unshift($(el).find('div.header span.name').eq(0).text())
modifiedFilePath = path.join(project.rootDirectory.path, filePath.join(path.sep))
modifiedFiles.push modifiedFilePath
```
`modifiedFiles` is an array containing a list of our modified files. We're also using
the node.js [`path` library](http://nodejs.org/docs/latest/api/path.html) to get
the proper directory separator for our system.
Let's remove the two `@li` elements we added in `@content`, so that we can populate
our `modifiedFilesList` with real information. We'll do that by iterating over
`modifiedFiles`, accessing a file's last modified time, and appending it to
`modifiedFilesList`:
```coffeescript
# toggles the pane
if @hasParent()
rootView.vertical.children().last().remove()
else
for file in modifiedFiles
stat = fs.lstatSync(file)
mtime = stat.mtime
@modifiedFilesList.append("<li>#{file} - Modified at #{mtime}")
rootView.vertical.append(this)
```
When you toggle the modified files list, your pane is now populated with the filenames
and modified times of files in your project. You might notice that subsequent calls
to this command reduplicate information. We could provide an elegant way of rechecking
files already in the list, but for this demonstration, we'll just clear the `modifiedFilesList`
each time it's closed:
```coffeescript
# toggles the pane
if @hasParent()
@modifiedFilesList.empty()
rootView.vertical.children().last().remove()
else
for file in modifiedFiles
stat = fs.lstatSync(file)
mtime = stat.mtime
@modifiedFilesList.append("<li>#{file} - Modified at #{mtime}")
rootView.vertical.append(this)
```
+10
Ver Arquivo
@@ -0,0 +1,10 @@
# Included Libraries
In addition to core node.js modules, all packages can `require` the following popular
libraries into their packages:
* [SpacePen](https://github.com/nathansobo/space-pen) (as `require 'space-pen'`)
* [jQuery](http://jquery.com/) (as `require 'jquery'`)
* [Underscore](http://underscorejs.org/) (as `require 'underscore'`)
Additional libraries can be found by browsing Atom's _node_modules_ folder.
+18
Ver Arquivo
@@ -0,0 +1,18 @@
# package.json format
The following keys are available to your package's _package.json_ manifest file:
- `main` (**Required**): the path to the CoffeeScript file that's the entry point
to your package
- `stylesheets` (**Optional**): an Array of Strings identifying the order of the
stylesheets your package needs to load. If not specified, stylesheets in the _stylesheets_
directory are added alphabetically.
- `keymaps`(**Optional**): an Array of Strings identifying the order of the
key mappings your package needs to load. If not specified, mappings in the _keymaps_
directory are added alphabetically.
- `snippets` (**Optional**): an Array of Strings identifying the order of the
snippets your package needs to load. If not specified, snippets in the _snippets_
directory are added alphabetically.
- `activationEvents` (**Optional**): an Array of Strings identifying events that
trigger your package's activation. You can delay the loading of your package until
one of these events is trigged.
@@ -1,4 +1,4 @@
# Authoring A Theme
# Authoring Themes
If you understand CSS, you can write an Atom theme easily. Your theme can style
Atom's user interface, specify the appearance of syntax-highlighted code, or
@@ -9,9 +9,9 @@ translate scope names to CSS classes. To theme Atom's user interface, take a
look at the existing light and dark themes for an example. Pressing `alt-meta-i`
and inspecting the Atom's markup directly can also be helpful.
The most basic theme is just a `.css` file. More complex themes occupy their own
The most basic theme is just a _.css_ file. More complex themes occupy their own
folder, which can contain multiple stylesheets along with an optional
`package.cson` file containing a manifest to control their load-order:
_package.cson_ file containing a manifest to control their load-order:
```text
~/.atom/themes/
+19 -6
Ver Arquivo
@@ -278,13 +278,26 @@
}
}
- (NSApplicationTerminateReply)applicationShouldTerminate:
(NSApplication *)sender {
for (NSWindow *window in [self windows]) {
[window performClose:self];
// The first call to terminate is canceled so that every window can be closed.
// On AtomCefClient the OnBeforeClose method is called when a browser is
// finished closing. Once all browsers have finished closing, AtomCefClient
// calls terminate again, which will complete since no windows will be open.
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender {
bool atomWindowsAreOpen = NO;
for (NSWindow *window in self.windows) {
if ([window.windowController isKindOfClass:[AtomWindowController class]]) {
atomWindowsAreOpen = YES;
[window performClose:self];
}
}
if (atomWindowsAreOpen) {
return NSTerminateCancel;
}
else {
CefQuitMessageLoop();
return NSTerminateNow;
}
return NSTerminateCancel;
}
# pragma mark CefAppProtocol
+1 -2
Ver Arquivo
@@ -171,11 +171,10 @@ bool AtomCefClient::OnKeyEvent(CefRefPtr<CefBrowser> browser,
}
void AtomCefClient::OnBeforeClose(CefRefPtr<CefBrowser> browser) {
// REQUIRE_UI_THREAD(); // When uncommented this fails when app is terminated
m_Browser = NULL;
numberOfOpenBrowsers--;
if (numberOfOpenBrowsers == 0) {
CefQuitMessageLoop();
Terminate();
}
}
+1
Ver Arquivo
@@ -127,6 +127,7 @@ class AtomCefClient : public CefClient,
void ShowSaveDialog(int replyId, CefRefPtr<CefBrowser> browser);
CefRefPtr<CefListValue> CreateReplyDescriptor(int replyId, int callbackIndex);
void Exit(int status);
void Terminate();
void Log(const char *message);
void Show(CefRefPtr<CefBrowser> browser);
void ToggleFullScreen(CefRefPtr<CefBrowser> browser);
+4
Ver Arquivo
@@ -159,6 +159,10 @@ void AtomCefClient::Exit(int status) {
exit(status);
}
void AtomCefClient::Terminate() {
[NSApp terminate:NSApp];
}
void AtomCefClient::Log(const char *message) {
NSLog(@"%s", message);
}
+1
Ver Arquivo
@@ -35,6 +35,7 @@ class AtomCefClient;
- (id)initBenchmarksThenExit:(BOOL)exitWhenDone;
- (void)setPidToKillOnClose:(NSNumber *)pid;
- (BOOL)isDevMode;
- (BOOL)isDevFlagSpecified;
- (void)toggleDevTools;
- (void)showDevTools;
+12 -7
Ver Arquivo
@@ -34,10 +34,7 @@
AtomApplication *atomApplication = (AtomApplication *)[AtomApplication sharedApplication];
if (useBundleResourcePath) {
_resourcePath = [[NSBundle bundleForClass:self.class] resourcePath];
}
else {
if (!useBundleResourcePath) {
_resourcePath = [[atomApplication.arguments objectForKey:@"resource-path"] stringByStandardizingPath];
if (!_resourcePath) {
NSString *defaultRepositoryPath = [@"~/github/atom" stringByStandardizingPath];
@@ -53,6 +50,9 @@
}
}
}
if (!_resourcePath) {
_resourcePath = [[NSBundle bundleForClass:self.class] resourcePath];
}
if ([self isDevMode]) {
[self displayDevIcon];
@@ -110,7 +110,7 @@
- (id)initWithPath:(NSString *)path {
_pathToOpen = [path retain];
return [self initWithBootstrapScript:@"window-bootstrap" background:NO useBundleResourcePath:![self isDevMode]];
return [self initWithBootstrapScript:@"window-bootstrap" background:NO useBundleResourcePath:![self isDevFlagSpecified]];
}
- (id)initDevWithPath:(NSString *)path {
@@ -119,7 +119,7 @@
}
- (id)initInBackground {
[self initWithBootstrapScript:@"window-bootstrap" background:YES useBundleResourcePath:![self isDevMode]];
[self initWithBootstrapScript:@"window-bootstrap" background:YES useBundleResourcePath:![self isDevFlagSpecified]];
[self.window setFrame:NSMakeRect(0, 0, 0, 0) display:NO];
[self.window setExcludedFromWindowsMenu:YES];
[self.window setCollectionBehavior:NSWindowCollectionBehaviorStationary];
@@ -140,7 +140,7 @@
- (id)initConfig {
_isConfig = true;
return [self initWithBootstrapScript:@"config-bootstrap" background:NO useBundleResourcePath:![self isDevMode]];
return [self initWithBootstrapScript:@"config-bootstrap" background:NO useBundleResourcePath:![self isDevFlagSpecified]];
}
- (void)addBrowserToView:(NSView *)view url:(const char *)url cefHandler:(CefRefPtr<AtomCefClient>)cefClient {
@@ -283,6 +283,11 @@
return ![_resourcePath isEqualToString:bundleResourcePath];
}
- (BOOL)isDevFlagSpecified {
AtomApplication *atomApplication = (AtomApplication *)[AtomApplication sharedApplication];
return [atomApplication.arguments objectForKey:@"dev"] != nil;
}
- (void)displayDevIcon {
NSView *themeFrame = [self.window.contentView superview];
NSButton *fullScreenButton = nil;
Arquivo binário não exibido.
+13 -10
Ver Arquivo
@@ -1,13 +1,16 @@
{
"name" : "atom",
"version" : "0.0.0",
"name": "atom",
"version": "0.0.0",
"repository": {
"type": "git",
"url": "https://github.com/github/atom.git"
},
"dependencies": {
"coffee-script": "1.6.2",
"ctags": "0.3.0",
"oniguruma": "0.11.0",
"mkdirp": "0.3.5",
"git-utils": "0.15.0",
"git-utils": "0.17.0",
"underscore": "1.4.4",
"d3": "3.0.8",
"coffee-cache": "0.1.0",
@@ -16,18 +19,18 @@
"nak": "0.2.12",
"spellchecker": "0.3.0",
"pathwatcher": "0.3.0",
"keytar": "0.4.0",
"plist": "git://github.com/nathansobo/node-plist.git",
"space-pen": "git://github.com/nathansobo/space-pen.git",
"less": "git://github.com/nathansobo/less.js.git",
"jqueryui-browser": "1.10.2-1"
"roaster": "0.0.3",
"jqueryui-browser": "1.10.2-1",
"season": "0.7.0"
},
"devDependencies" : {
"biscotto" : "0.0.9"
"devDependencies": {
"biscotto": "0.0.11"
},
"private": true,
"scripts": {
"preinstall": "true"
}
+5 -10
Ver Arquivo
@@ -21,15 +21,10 @@ exit_unless_npm_exists() {
exit_unless_xcode_exists
exit_unless_npm_exists
npm install npm --silent
NODE_DIR="$HOME/.cefode-gyp"
NODE_VERSION="0.10.3"
NODE_URL="https://gh-contractor-zcbenz.s3.amazonaws.com/cefode2/dist"
if [ ! -d "node_modules/node-gyp" ]; then
./node_modules/.bin/npm install node-gyp --silent
HOME="$NODE_DIR" ./node_modules/.bin/node-gyp install --target="$NODE_VERSION" --dist-url="$NODE_URL" --arch=ia32
fi
HOME="$NODE_DIR" ./node_modules/.bin/npm install --target="$NODE_VERSION" --arch=ia32 --silent
git submodule --quiet sync
git submodule --quiet update --recursive --init
(cd vendor/apm && npm install .)
npm install vendor/apm
./node_modules/.bin/apm install
+1 -1
Ver Arquivo
@@ -18,4 +18,4 @@ node --version > /dev/null 2>&1 || {
INPUT_FILE="${1}"
OUTPUT_FILE="${2}"
node_modules/.bin/coffee script/compile-cson.coffee -- "${INPUT_FILE}" "${OUTPUT_FILE}"
node_modules/.bin/csonc --root "${INPUT_FILE}" "${OUTPUT_FILE}"
-23
Ver Arquivo
@@ -1,23 +0,0 @@
fs = require 'fs'
{exec} = require 'child_process'
inputFile = process.argv[2]
unless inputFile?.length > 0
console.error("Input file must be first argument")
process.exit(1)
outputFile = process.argv[3]
unless outputFile?.length > 0
console.error("Output file must be second argument")
process.exit(1)
contents = fs.readFileSync(inputFile)?.toString() ? ''
exec "node_modules/.bin/coffee -bcp #{inputFile}", (error, stdout, stderr) ->
if error
console.error(error)
process.exit(1)
json = eval(stdout.toString()) ? {}
if json isnt Object(json)
console.error("CSON file does not contain valid JSON")
process.exit(1)
fs.writeFileSync(outputFile, JSON.stringify(json, null, 2))
+1 -1
Ver Arquivo
@@ -4,5 +4,5 @@ set -ex
cd "$(dirname "$0")/../.."
export PATH="/usr/local/Cellar/node/0.8.21/bin:/usr/local/bin:${PATH}"
export VERSION=$VERSION
rake clean
rake clean clean-cefode-cache
rake setup-codesigning create-xcode-project
-19
Ver Arquivo
@@ -1,19 +0,0 @@
#!/bin/sh
# From root of libgit2 repo:
# mkdir build
# cd build
# cmake .. -DCMAKE_INSTALL_PREFIX=~/github/atom/git2 -DCMAKE_OSX_ARCHITECTURES="i386;x86_64" -DCMAKE_BUILD_TYPE=Release -DTHREADSAFE=1 -DBUILD_CLAR=OFF
# cmake --build . --target install
#
# From root of atom repo:
# mv git2/lib/libgit2.0.17.0.dylib git2/frameworks/libgit2.0.17.0.dylib
# rm -fr git2/lib
# script/update-libgit2
# update the id of the dylib
install_name_tool -id @executable_path/libgit2.0.17.0.dylib git2/frameworks/libgit2.0.17.0.dylib
# Verify @executable_path is in the output of:
otool -L git2/frameworks/libgit2.0.17.0.dylib
+4 -2
Ver Arquivo
@@ -350,10 +350,12 @@ describe "the `atom` global", ->
results.pop()
errors = parseInt results.pop().match(/\d+/)
expect(errors).toBe 0
if errors > 0
console.error results.join('\n')
throw new Error("There were errors compiling documentation. See console for details.")
coverage = parseFloat results.pop().match(/.+?%/)
expect(coverage).toBeGreaterThan 80
expect(coverage).toBeGreaterThan 78
# stderr
expect(docRunner.argsForCall[0][2]).toBe ''
+57 -1
Ver Arquivo
@@ -39,9 +39,28 @@ describe "ConfigPanel", ->
panel.floatInput.val('90.2').change()
expect(config.get('foo.float')).toBe 90.2
panel.intInput.val('0').change()
expect(config.get('foo.int')).toBe 0
panel.floatInput.val('0').change()
expect(config.get('foo.float')).toBe 0
panel.stringInput.val('moo').change()
expect(config.get('foo.string')).toBe 'moo'
panel.intInput.val('abcd').change()
expect(config.get('foo.int')).toBe 'abcd'
panel.floatInput.val('defg').change()
expect(config.get('foo.float')).toBe 'defg'
panel.intInput.val('').change()
expect(config.get('foo.int')).toBe undefined
panel.floatInput.val('').change()
expect(config.get('foo.float')).toBe undefined
panel.stringInput.val('').change()
expect(config.get('foo.string')).toBe undefined
it "automatically binds named editors to their corresponding config keys", ->
class TestPanel extends ConfigPanel
@content: ->
@@ -54,6 +73,7 @@ describe "ConfigPanel", ->
config.set('foo.float', 1.1)
config.set('foo.string', 'I think therefore I am.')
panel = new TestPanel
window.advanceClock(10000) # wait for contents-modified to be triggered
expect(panel.intEditor.getText()).toBe '1'
expect(panel.floatEditor.getText()).toBe '1.1'
expect(panel.stringEditor.getText()).toBe 'I think therefore I am.'
@@ -73,10 +93,46 @@ describe "ConfigPanel", ->
expect(config.get('foo.float')).toBe 3.3
expect(config.get('foo.string')).toBe 'All limitations are self imposed.'
panel.intEditor.setText('not an int')
panel.floatEditor.setText('not a float')
window.advanceClock(10000) # wait for contents-modified to be triggered
expect(config.get('foo.int')).toBe 'not an int'
expect(config.get('foo.float')).toBe 'not a float'
panel.intEditor.setText('')
panel.floatEditor.setText('')
panel.stringEditor.setText('')
window.advanceClock(10000) # wait for contents-modified to be triggered
expect(config.get('foo.int')).toBe undefined
expect(config.get('foo.float')).toBe undefined
expect(config.get('foo.string')).toBe undefined
panel.intEditor.setText('0')
panel.floatEditor.setText('0')
window.advanceClock(10000) # wait for contents-modified to be triggered
expect(config.get('foo.int')).toBe 0
expect(config.get('foo.float')).toBe 0
expect(config.get('foo.string')).toBe undefined
it "does not save the config value until it has been changed to a new value", ->
class TestPanel extends ConfigPanel
@content: ->
@div =>
@subview "fooInt", new Editor(mini: true, attributes: {id: 'foo.int', type: 'int'})
config.set('foo.int', 1)
observeHandler = jasmine.createSpy("observeHandler")
config.observe "foo.int", observeHandler
observeHandler.reset()
testPanel = new TestPanel
window.advanceClock(10000) # wait for contents-modified to be triggered
expect(observeHandler).not.toHaveBeenCalled()
testPanel.fooInt.setText("1")
window.advanceClock(10000) # wait for contents-modified to be triggered
expect(observeHandler).not.toHaveBeenCalled()
testPanel.fooInt.setText("2")
window.advanceClock(10000) # wait for contents-modified to be triggered
expect(observeHandler).toHaveBeenCalled()
+66 -24
Ver Arquivo
@@ -1,12 +1,25 @@
fs = require 'fs'
fsUtils = require 'fs-utils'
describe "Config", ->
describe ".get(keyPath) and .set(keyPath, value)", ->
it "allows a key path's value to be read and written", ->
describe ".get(keyPath)", ->
it "allows a key path's value to be read", ->
expect(config.set("foo.bar.baz", 42)).toBe 42
expect(config.get("foo.bar.baz")).toBe 42
expect(config.get("bogus.key.path")).toBeUndefined()
it "returns a deep clone of the key path's value", ->
config.set('value', array: [1, b: 2, 3])
retrievedValue = config.get('value')
retrievedValue.array[0] = 4
retrievedValue.array[1].b = 2.1
expect(config.get('value')).toEqual(array: [1, b: 2, 3])
describe ".set(keyPath, value)", ->
it "allows a key path's value to be written", ->
expect(config.set("foo.bar.baz", 42)).toBe 42
expect(config.get("foo.bar.baz")).toBe 42
it "updates observers and saves when a key path is set", ->
observeHandler = jasmine.createSpy "observeHandler"
config.observe "foo.bar.baz", observeHandler
@@ -17,9 +30,53 @@ describe "Config", ->
expect(config.save).toHaveBeenCalled()
expect(observeHandler).toHaveBeenCalledWith 42
describe "when the value equals the default value", ->
it "does not store the value", ->
config.setDefaults("foo", same: 1, changes: 1)
expect(config.settings.foo).toBeUndefined()
config.set('foo.same', 1)
config.set('foo.changes', 2)
expect(config.settings.foo).toEqual {changes: 2}
config.set('foo.changes', 1)
expect(config.settings.foo).toEqual {}
describe ".pushAtKeyPath(keyPath, value)", ->
it "pushes the given value to the array at the key path and updates observers", ->
config.set("foo.bar.baz", ["a"])
observeHandler = jasmine.createSpy "observeHandler"
config.observe "foo.bar.baz", observeHandler
observeHandler.reset()
expect(config.pushAtKeyPath("foo.bar.baz", "b")).toBe 2
expect(config.get("foo.bar.baz")).toEqual ["a", "b"]
expect(observeHandler).toHaveBeenCalledWith config.get("foo.bar.baz")
describe ".removeAtKeyPath(keyPath, value)", ->
it "removes the given value from the array at the key path and updates observers", ->
config.set("foo.bar.baz", ["a", "b", "c"])
observeHandler = jasmine.createSpy "observeHandler"
config.observe "foo.bar.baz", observeHandler
observeHandler.reset()
expect(config.removeAtKeyPath("foo.bar.baz", "b")).toEqual ["a", "c"]
expect(config.get("foo.bar.baz")).toEqual ["a", "c"]
expect(observeHandler).toHaveBeenCalledWith config.get("foo.bar.baz")
describe ".getPositiveInt(keyPath, defaultValue)", ->
it "returns the proper current or default value", ->
config.set('editor.preferredLineLength', 0)
expect(config.getPositiveInt('editor.preferredLineLength', 80)).toBe 80
config.set('editor.preferredLineLength', -1234)
expect(config.getPositiveInt('editor.preferredLineLength', 80)).toBe 80
config.set('editor.preferredLineLength', 'abcd')
expect(config.getPositiveInt('editor.preferredLineLength', 80)).toBe 80
config.set('editor.preferredLineLength', null)
expect(config.getPositiveInt('editor.preferredLineLength', 80)).toBe 80
describe ".save()", ->
beforeEach ->
spyOn(fsUtils, 'write')
spyOn(fs, 'writeFileSync')
jasmine.unspy config, 'save'
describe "when ~/.atom/config.json exists", ->
@@ -30,11 +87,11 @@ describe "Config", ->
config.set("x.y.z", 3)
config.setDefaults("a.b", e: 4, f: 5)
fsUtils.write.reset()
fs.writeFileSync.reset()
config.save()
expect(fsUtils.write.argsForCall[0][0]).toBe(fsUtils.join(config.configDirPath, "config.json"))
writtenConfig = JSON.parse(fsUtils.write.argsForCall[0][1])
expect(fs.writeFileSync.argsForCall[0][0]).toBe(fsUtils.join(config.configDirPath, "config.json"))
writtenConfig = JSON.parse(fs.writeFileSync.argsForCall[0][1])
expect(writtenConfig).toEqual config.settings
describe "when ~/.atom/config.json doesn't exist", ->
@@ -45,12 +102,12 @@ describe "Config", ->
config.set("x.y.z", 3)
config.setDefaults("a.b", e: 4, f: 5)
fsUtils.write.reset()
fs.writeFileSync.reset()
config.save()
expect(fsUtils.write.argsForCall[0][0]).toBe(fsUtils.join(config.configDirPath, "config.cson"))
expect(fs.writeFileSync.argsForCall[0][0]).toBe(fsUtils.join(config.configDirPath, "config.cson"))
CoffeeScript = require 'coffee-script'
writtenConfig = CoffeeScript.eval(fsUtils.write.argsForCall[0][1], bare: true)
writtenConfig = CoffeeScript.eval(fs.writeFileSync.argsForCall[0][1], bare: true)
expect(writtenConfig).toEqual config.settings
describe ".setDefaults(keyPath, defaults)", ->
@@ -65,21 +122,6 @@ describe "Config", ->
expect(config.get("foo.quux.x")).toBe 0
expect(config.get("foo.quux.y")).toBe 1
describe ".update()", ->
it "updates observers if a value is mutated without the use of .set", ->
config.set("foo.bar.baz", ["a"])
observeHandler = jasmine.createSpy "observeHandler"
config.observe "foo.bar.baz", observeHandler
observeHandler.reset()
config.get("foo.bar.baz").push("b")
config.update()
expect(observeHandler).toHaveBeenCalledWith config.get("foo.bar.baz")
observeHandler.reset()
config.update()
expect(observeHandler).not.toHaveBeenCalled()
describe ".observe(keyPath)", ->
observeHandler = null
+216 -122
Ver Arquivo
@@ -138,7 +138,6 @@ describe "DisplayBuffer", ->
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(changeHandler).toHaveBeenCalledWith(start: 0, end: 15, screenDelta: 3, bufferDelta: 0)
describe "primitive folding", ->
@@ -157,7 +156,6 @@ describe "DisplayBuffer", ->
[line4, line5] = displayBuffer.linesForRows(4, 5)
expect(line4.fold).toBe fold
expect(line4.text).toMatch /^4-+/
expect(line4.bufferRows).toBe 4
expect(line5.text).toBe '8'
expect(changeHandler).toHaveBeenCalledWith(start: 4, end: 7, screenDelta: -3, bufferDelta: 0)
@@ -167,7 +165,6 @@ describe "DisplayBuffer", ->
[line4, line5] = displayBuffer.linesForRows(4, 5)
expect(line4.fold).toBeUndefined()
expect(line4.text).toMatch /^4-+/
expect(line4.bufferRows).toEqual 1
expect(line5.text).toBe '5'
expect(changeHandler).toHaveBeenCalledWith(start: 4, end: 4, screenDelta: 3, bufferDelta: 0)
@@ -179,7 +176,6 @@ describe "DisplayBuffer", ->
[line4, line5] = displayBuffer.linesForRows(4, 5)
expect(line4.fold).toBe fold
expect(line4.text).toMatch /^4-+/
expect(line4.bufferRows).toEqual 1
expect(line5.text).toBe '5'
expect(changeHandler).toHaveBeenCalledWith(start: 4, end: 4, screenDelta: 0, bufferDelta: 0)
@@ -193,7 +189,6 @@ describe "DisplayBuffer", ->
[line4, line5] = displayBuffer.linesForRows(4, 5)
expect(line4.fold).toBeUndefined()
expect(line4.text).toMatch /^4-+/
expect(line4.bufferRows).toEqual 1
expect(line5.text).toBe '5'
expect(changeHandler).toHaveBeenCalledWith(start: 4, end: 4, screenDelta: 0, bufferDelta: 0)
@@ -206,19 +201,15 @@ describe "DisplayBuffer", ->
[line4, line5] = displayBuffer.linesForRows(4, 5)
expect(line4.fold).toBe outerFold
expect(line4.text).toMatch /4-+/
expect(line4.bufferRows).toEqual 5
expect(line5.text).toMatch /9-+/
outerFold.destroy()
[line4, line5, line6, line7] = displayBuffer.linesForRows(4, 7)
expect(line4.fold).toBeUndefined()
expect(line4.text).toMatch /^4-+/
expect(line4.bufferRows).toEqual 1
expect(line5.text).toBe '5'
expect(line6.fold).toBe innerFold
expect(line6.text).toBe '6'
expect(line6.bufferRows).toEqual 2
expect(line7.text).toBe '8'
it "allows the outer fold to start at the same location as the inner fold", ->
@@ -228,17 +219,16 @@ describe "DisplayBuffer", ->
[line4, line5] = displayBuffer.linesForRows(4, 5)
expect(line4.fold).toBe outerFold
expect(line4.text).toMatch /4-+/
expect(line4.bufferRows).toEqual 5
expect(line5.text).toMatch /9-+/
describe "when creating a fold where one already exists", ->
it "returns existing fold and does't create new fold", ->
fold = displayBuffer.createFold(0,10)
expect(displayBuffer.activeFolds[0].length).toBe 1
expect(displayBuffer.findMarkers(class: 'fold').length).toBe 1
newFold = displayBuffer.createFold(0,10)
expect(newFold).toBe fold
expect(displayBuffer.activeFolds[0].length).toBe 1
expect(displayBuffer.findMarkers(class: 'fold').length).toBe 1
describe "when a fold is created inside an existing folded region", ->
it "creates/destroys the fold, but does not trigger change event", ->
@@ -258,6 +248,12 @@ describe "DisplayBuffer", ->
expect(line0.fold).toBe outerFold
expect(line1.fold).toBeUndefined()
describe "when there is another display buffer pointing to the same buffer", ->
it "does not create folds in the other display buffer", ->
otherDisplayBuffer = new DisplayBuffer(buffer, { tabLength })
displayBuffer.createFold(2, 4)
expect(otherDisplayBuffer.foldsStartingAtBufferRow(2).length).toBe 0
describe "when the buffer changes", ->
[fold1, fold2] = []
beforeEach ->
@@ -266,9 +262,10 @@ describe "DisplayBuffer", ->
changeHandler.reset()
describe "when the old range surrounds a fold", ->
it "removes the fold and replaces the selection with the new text", ->
beforeEach ->
buffer.change([[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
@@ -276,6 +273,13 @@ describe "DisplayBuffer", ->
expect(changeHandler).toHaveBeenCalledWith(start: 1, end: 3, screenDelta: -2, bufferDelta: -4)
describe "when the changes is subsequently undone", ->
it "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'
describe "when the old range surrounds two nested folds", ->
it "removes both folds and replaces the selection with the new text", ->
displayBuffer.createFold(2, 9)
@@ -294,8 +298,8 @@ describe "DisplayBuffer", ->
buffer.delete([[1, 1], [2, 0]])
buffer.insert([0, 1], "\nnew")
expect(fold1.startRow).toBe 2
expect(fold1.endRow).toBe 4
expect(fold1.getStartRow()).toBe 2
expect(fold1.getEndRow()).toBe 4
describe "when the old range precedes lines with a fold", ->
describe "when the new range precedes lines with a fold", ->
@@ -322,15 +326,12 @@ describe "DisplayBuffer", ->
expect(changeHandler).toHaveBeenCalledWith(start: 1, end: 1, screenDelta: 2, bufferDelta: 0)
describe "when the old range straddles the beginning of a fold", ->
it "replaces lines in the portion of the range that precedes the fold and adjusts the end of the fold to encompass additional lines", ->
it "destroys the fold", ->
buffer.change([[1, 1], [3, 0]], "a\nb\nc\nd\n")
expect(fold1.startRow).toBe 2
expect(fold1.endRow).toBe 6
expect(displayBuffer.lineForRow(1).text).toBe '1a'
expect(displayBuffer.lineForRow(2).text).toBe 'b'
expect(displayBuffer.lineForRow(2).fold).toBe fold1
expect(displayBuffer.lineForRow(2).fold).toBeUndefined()
expect(displayBuffer.lineForRow(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", ->
@@ -348,13 +349,12 @@ describe "DisplayBuffer", ->
describe "when the end of the new range precedes the end of the fold", ->
it "updates the fold and ensures the change is present when the fold is destroyed", ->
buffer.insert([3, 0], '\n')
expect(fold1.startRow).toBe 2
expect(fold1.endRow).toBe 5
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(2).bufferRows).toEqual 4
expect(displayBuffer.lineForRow(3).text).toMatch "5"
expect(displayBuffer.lineForRow(4).fold).toBe fold2
expect(displayBuffer.lineForRow(5).text).toMatch /^9-+/
@@ -364,13 +364,12 @@ describe "DisplayBuffer", ->
describe "when the end of the new range exceeds the end of the fold", ->
it "expands the fold to contain all the inserted lines", ->
buffer.change([[3, 0], [4, 0]], 'a\nb\nc\nd\n')
expect(fold1.startRow).toBe 2
expect(fold1.endRow).toBe 7
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(2).bufferRows).toEqual 6
expect(displayBuffer.lineForRow(3).text).toMatch "5"
expect(displayBuffer.lineForRow(4).fold).toBe fold2
expect(displayBuffer.lineForRow(5).text).toMatch /^9-+/
@@ -379,12 +378,13 @@ describe "DisplayBuffer", ->
describe "when the old range straddles the end of the fold", ->
describe "when the end of the new range precedes the end of the fold", ->
it "shortens the fold so its end matches the end of the new range", ->
it "destroys the fold", ->
fold2.destroy()
buffer.change([[3, 0], [6, 0]], 'a\n')
expect(fold1.startRow).toBe 2
expect(fold1.endRow).toBe 4
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'
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", ->
@@ -401,7 +401,7 @@ describe "DisplayBuffer", ->
describe "position translation", ->
it "translates positions to account for folded lines and characters and the placeholder", ->
displayBuffer.createFold(4, 7)
fold = displayBuffer.createFold(4, 7)
# preceding fold: identity
expect(displayBuffer.screenPositionForBufferPosition([3, 0])).toEqual [3, 0]
@@ -425,23 +425,32 @@ describe "DisplayBuffer", ->
expect(displayBuffer.bufferPositionForScreenPosition([-5, -5])).toEqual([0, 0])
expect(displayBuffer.bufferPositionForScreenPosition([Infinity, Infinity])).toEqual([200, 0])
# after fold is destroyed
fold.destroy()
expect(displayBuffer.screenPositionForBufferPosition([8, 0])).toEqual [8, 0]
expect(displayBuffer.screenPositionForBufferPosition([11, 2])).toEqual [11, 2]
expect(displayBuffer.bufferPositionForScreenPosition([5, 0])).toEqual [5, 0]
expect(displayBuffer.bufferPositionForScreenPosition([9, 2])).toEqual [9, 2]
describe ".destroyFoldsContainingBufferRow(row)", ->
it "destroys all folds containing the given row", ->
displayBuffer.createFold(2, 4)
displayBuffer.createFold(2, 6)
displayBuffer.createFold(7, 8)
displayBuffer.createFold(1, 9)
displayBuffer.createFold(11, 12)
displayBuffer.createFold(2, 4)
displayBuffer.createFold(2, 6)
displayBuffer.createFold(7, 8)
displayBuffer.createFold(1, 9)
displayBuffer.createFold(11, 12)
expect(displayBuffer.lineForRow(1).text).toBe '1'
expect(displayBuffer.lineForRow(2).text).toBe '10'
expect(displayBuffer.lineForRow(1).text).toBe '1'
expect(displayBuffer.lineForRow(2).text).toBe '10'
displayBuffer.destroyFoldsContainingBufferRow(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()
displayBuffer.destroyFoldsContainingBufferRow(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()
describe ".clipScreenPosition(screenPosition, wrapBeyondNewlines: false, wrapAtSoftNewlines: false, skipAtomicTokens: false)", ->
beforeEach ->
@@ -510,9 +519,11 @@ describe "DisplayBuffer", ->
expect(displayBuffer.screenPositionForBufferPosition([0, 1])).toEqual [0, 2]
expect(displayBuffer.bufferPositionForScreenPosition([0, 2])).toEqual [0, 1]
describe ".maxLineLength()", ->
describe ".getMaxLineLength()", ->
it "returns the length of the longest screen line", ->
expect(displayBuffer.maxLineLength()).toBe 65
expect(displayBuffer.getMaxLineLength()).toBe 65
buffer.delete([[6, 0], [6, 65]])
expect(displayBuffer.getMaxLineLength()).toBe 62
describe "markers", ->
beforeEach ->
@@ -522,44 +533,52 @@ describe "DisplayBuffer", ->
it "allows markers to be created in terms of both screen and buffer coordinates", ->
marker1 = displayBuffer.markScreenRange([[5, 4], [5, 10]])
marker2 = displayBuffer.markBufferRange([[8, 4], [8, 10]])
expect(displayBuffer.getMarkerBufferRange(marker1)).toEqual [[8, 4], [8, 10]]
expect(displayBuffer.getMarkerScreenRange(marker2)).toEqual [[5, 4], [5, 10]]
expect(marker1.getBufferRange()).toEqual [[8, 4], [8, 10]]
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")
marker1 = displayBuffer.markScreenRange([[5, 4], [5, 10]])
expect(markerCreatedHandler).toHaveBeenCalledWith(marker1)
markerCreatedHandler.reset()
marker2 = buffer.markRange([[5, 4], [5, 10]])
expect(markerCreatedHandler).toHaveBeenCalledWith(displayBuffer.getMarker(marker2.id))
it "allows marker head and tail positions to be manipulated in both screen and buffer coordinates", ->
marker = displayBuffer.markScreenRange([[5, 4], [5, 10]])
displayBuffer.setMarkerHeadScreenPosition(marker, [5, 4])
displayBuffer.setMarkerTailBufferPosition(marker, [5, 4])
expect(displayBuffer.isMarkerReversed(marker)).toBeFalsy()
expect(displayBuffer.getMarkerBufferRange(marker)).toEqual [[5, 4], [8, 4]]
displayBuffer.setMarkerHeadBufferPosition(marker, [5, 4])
displayBuffer.setMarkerTailScreenPosition(marker, [5, 4])
expect(displayBuffer.isMarkerReversed(marker)).toBeTruthy()
expect(displayBuffer.getMarkerBufferRange(marker)).toEqual [[5, 4], [8, 4]]
marker.setHeadScreenPosition([5, 4])
marker.setTailBufferPosition([5, 4])
expect(marker.isReversed()).toBeFalsy()
expect(marker.getBufferRange()).toEqual [[5, 4], [8, 4]]
marker.setHeadBufferPosition([5, 4])
marker.setTailScreenPosition([5, 4])
expect(marker.isReversed()).toBeTruthy()
expect(marker.getBufferRange()).toEqual [[5, 4], [8, 4]]
it "returns whether a position changed when it is assigned", ->
marker = displayBuffer.markScreenRange([[0, 0], [0, 0]])
expect(displayBuffer.setMarkerHeadScreenPosition(marker, [5, 4])).toBeTruthy()
expect(displayBuffer.setMarkerHeadScreenPosition(marker, [5, 4])).toBeFalsy()
expect(displayBuffer.setMarkerHeadBufferPosition(marker, [1, 0])).toBeTruthy()
expect(displayBuffer.setMarkerHeadBufferPosition(marker, [1, 0])).toBeFalsy()
expect(displayBuffer.setMarkerTailScreenPosition(marker, [5, 4])).toBeTruthy()
expect(displayBuffer.setMarkerTailScreenPosition(marker, [5, 4])).toBeFalsy()
expect(displayBuffer.setMarkerTailBufferPosition(marker, [1, 0])).toBeTruthy()
expect(displayBuffer.setMarkerTailBufferPosition(marker, [1, 0])).toBeFalsy()
expect(marker.setHeadScreenPosition([5, 4])).toBeTruthy()
expect(marker.setHeadScreenPosition([5, 4])).toBeFalsy()
expect(marker.setHeadBufferPosition([1, 0])).toBeTruthy()
expect(marker.setHeadBufferPosition([1, 0])).toBeFalsy()
expect(marker.setTailScreenPosition([5, 4])).toBeTruthy()
expect(marker.setTailScreenPosition([5, 4])).toBeFalsy()
expect(marker.setTailBufferPosition([1, 0])).toBeTruthy()
expect(marker.setTailBufferPosition([1, 0])).toBeFalsy()
describe ".observeMarker(marker, callback)", ->
[observeHandler, marker, subscription] = []
describe "marker change events", ->
[markerChangedHandler, marker] = []
beforeEach ->
observeHandler = jasmine.createSpy("observeHandler")
marker = displayBuffer.markScreenRange([[5, 4], [5, 10]])
subscription = displayBuffer.observeMarker(marker, observeHandler)
marker.on 'changed', markerChangedHandler = jasmine.createSpy("markerChangedHandler")
it "calls the callback whenever the markers head's screen position changes in the buffer or on screen", ->
displayBuffer.setMarkerHeadScreenPosition(marker, [8, 20])
expect(observeHandler).toHaveBeenCalled()
expect(observeHandler.argsForCall[0][0]).toEqual {
it "triggers the 'changed' event whenever the markers head's screen position changes in the buffer or on screen", ->
marker.setHeadScreenPosition([8, 20])
expect(markerChangedHandler).toHaveBeenCalled()
expect(markerChangedHandler.argsForCall[0][0]).toEqual {
oldHeadScreenPosition: [5, 10]
oldHeadBufferPosition: [8, 10]
newHeadScreenPosition: [8, 20]
@@ -571,11 +590,11 @@ describe "DisplayBuffer", ->
bufferChanged: false
valid: true
}
observeHandler.reset()
markerChangedHandler.reset()
buffer.insert([11, 0], '...')
expect(observeHandler).toHaveBeenCalled()
expect(observeHandler.argsForCall[0][0]).toEqual {
expect(markerChangedHandler).toHaveBeenCalled()
expect(markerChangedHandler.argsForCall[0][0]).toEqual {
oldHeadScreenPosition: [8, 20]
oldHeadBufferPosition: [11, 20]
newHeadScreenPosition: [8, 23]
@@ -587,11 +606,11 @@ describe "DisplayBuffer", ->
bufferChanged: true
valid: true
}
observeHandler.reset()
markerChangedHandler.reset()
displayBuffer.destroyFoldsContainingBufferRow(4)
expect(observeHandler).toHaveBeenCalled()
expect(observeHandler.argsForCall[0][0]).toEqual {
expect(markerChangedHandler).toHaveBeenCalled()
expect(markerChangedHandler.argsForCall[0][0]).toEqual {
oldHeadScreenPosition: [8, 23]
oldHeadBufferPosition: [11, 23]
newHeadScreenPosition: [11, 23]
@@ -603,11 +622,11 @@ describe "DisplayBuffer", ->
bufferChanged: false
valid: true
}
observeHandler.reset()
markerChangedHandler.reset()
displayBuffer.createFold(4, 7)
expect(observeHandler).toHaveBeenCalled()
expect(observeHandler.argsForCall[0][0]).toEqual {
expect(markerChangedHandler).toHaveBeenCalled()
expect(markerChangedHandler.argsForCall[0][0]).toEqual {
oldHeadScreenPosition: [11, 23]
oldHeadBufferPosition: [11, 23]
newHeadScreenPosition: [8, 23]
@@ -620,10 +639,10 @@ describe "DisplayBuffer", ->
valid: true
}
it "calls the callback whenever the marker tail's position changes in the buffer or on screen", ->
displayBuffer.setMarkerTailScreenPosition(marker, [8, 20])
expect(observeHandler).toHaveBeenCalled()
expect(observeHandler.argsForCall[0][0]).toEqual {
it "triggers the 'changed' event whenever the marker tail's position changes in the buffer or on screen", ->
marker.setTailScreenPosition([8, 20])
expect(markerChangedHandler).toHaveBeenCalled()
expect(markerChangedHandler.argsForCall[0][0]).toEqual {
oldHeadScreenPosition: [5, 10]
oldHeadBufferPosition: [8, 10]
newHeadScreenPosition: [5, 10]
@@ -635,11 +654,11 @@ describe "DisplayBuffer", ->
bufferChanged: false
valid: true
}
observeHandler.reset()
markerChangedHandler.reset()
buffer.insert([11, 0], '...')
expect(observeHandler).toHaveBeenCalled()
expect(observeHandler.argsForCall[0][0]).toEqual {
expect(markerChangedHandler).toHaveBeenCalled()
expect(markerChangedHandler.argsForCall[0][0]).toEqual {
oldHeadScreenPosition: [5, 10]
oldHeadBufferPosition: [8, 10]
newHeadScreenPosition: [5, 10]
@@ -652,10 +671,10 @@ describe "DisplayBuffer", ->
valid: true
}
it "calls the callback whenever the marker is invalidated or revalidated", ->
it "triggers the 'changed' event whenever the marker is invalidated or revalidated", ->
buffer.deleteRow(8)
expect(observeHandler).toHaveBeenCalled()
expect(observeHandler.argsForCall[0][0]).toEqual {
expect(markerChangedHandler).toHaveBeenCalled()
expect(markerChangedHandler.argsForCall[0][0]).toEqual {
oldHeadScreenPosition: [5, 10]
oldHeadBufferPosition: [8, 10]
newHeadScreenPosition: [5, 10]
@@ -668,11 +687,11 @@ describe "DisplayBuffer", ->
valid: false
}
observeHandler.reset()
markerChangedHandler.reset()
buffer.undo()
expect(observeHandler).toHaveBeenCalled()
expect(observeHandler.argsForCall[0][0]).toEqual {
expect(markerChangedHandler).toHaveBeenCalled()
expect(markerChangedHandler.argsForCall[0][0]).toEqual {
oldHeadScreenPosition: [5, 10]
oldHeadBufferPosition: [8, 10]
newHeadScreenPosition: [5, 10]
@@ -687,47 +706,122 @@ describe "DisplayBuffer", ->
it "does not call the callback for screen changes that don't change the position of the marker", ->
displayBuffer.createFold(10, 11)
expect(observeHandler).not.toHaveBeenCalled()
expect(markerChangedHandler).not.toHaveBeenCalled()
it "allows observation subscriptions to be cancelled", ->
subscription.cancel()
displayBuffer.setMarkerHeadScreenPosition(marker, [8, 20])
displayBuffer.destroyFoldsContainingBufferRow(4)
expect(observeHandler).not.toHaveBeenCalled()
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()
it "updates the position of markers before emitting buffer change events, but does not notify their observers until the change event", ->
changeHandler = jasmine.createSpy("changeHandler").andCallFake ->
# New change ----
onDisplayBufferChange = ->
# calls change handler first
expect(observeHandler).not.toHaveBeenCalled()
expect(markerChangedHandler).not.toHaveBeenCalled()
expect(marker2ChangedHandler).not.toHaveBeenCalled()
# but still updates the markers
expect(displayBuffer.getMarkerScreenRange(marker)).toEqual [[5, 7], [5, 13]]
expect(displayBuffer.getMarkerHeadScreenPosition(marker)).toEqual [5, 13]
expect(displayBuffer.getMarkerTailScreenPosition(marker)).toEqual [5, 7]
displayBuffer.on 'changed', changeHandler
buffer.insert([8, 1], "...")
expect(marker.getScreenRange()).toEqual [[5, 7], [5, 13]]
expect(marker.getHeadScreenPosition()).toEqual [5, 13]
expect(marker.getTailScreenPosition()).toEqual [5, 7]
expect(marker2.isValid()).toBeFalsy()
buffer.change([[8, 0], [8, 2]], ".....")
expect(changeHandler).toHaveBeenCalled()
expect(observeHandler).toHaveBeenCalled()
expect(markerChangedHandler).toHaveBeenCalled()
expect(marker2ChangedHandler).toHaveBeenCalled()
# Undo change ----
changeHandler.reset()
markerChangedHandler.reset()
marker2ChangedHandler.reset()
marker3 = displayBuffer.markBufferRange([[8, 1], [8, 2]])
marker3.on 'changed', marker3ChangedHandler = jasmine.createSpy("marker3ChangedHandler")
onDisplayBufferChange = ->
# calls change handler first
expect(markerChangedHandler).not.toHaveBeenCalled()
expect(marker2ChangedHandler).not.toHaveBeenCalled()
expect(marker3ChangedHandler).not.toHaveBeenCalled()
# but still updates the markers
expect(marker.getScreenRange()).toEqual [[5, 4], [5, 10]]
expect(marker.getHeadScreenPosition()).toEqual [5, 10]
expect(marker.getTailScreenPosition()).toEqual [5, 4]
expect(marker2.isValid()).toBeTruthy()
expect(marker3.isValid()).toBeFalsy()
buffer.undo()
expect(changeHandler).toHaveBeenCalled()
expect(markerChangedHandler).toHaveBeenCalled()
expect(marker2ChangedHandler).toHaveBeenCalled()
expect(marker3ChangedHandler).toHaveBeenCalled()
# Redo change ----
changeHandler.reset()
markerChangedHandler.reset()
marker2ChangedHandler.reset()
marker3ChangedHandler.reset()
onDisplayBufferChange = ->
# calls change handler first
expect(markerChangedHandler).not.toHaveBeenCalled()
expect(marker2ChangedHandler).not.toHaveBeenCalled()
expect(marker3ChangedHandler).not.toHaveBeenCalled()
# but still updates the markers
expect(marker.getScreenRange()).toEqual [[5, 7], [5, 13]]
expect(marker.getHeadScreenPosition()).toEqual [5, 13]
expect(marker.getTailScreenPosition()).toEqual [5, 7]
expect(marker2.isValid()).toBeFalsy()
expect(marker3.isValid()).toBeTruthy()
buffer.redo()
expect(changeHandler).toHaveBeenCalled()
expect(markerChangedHandler).toHaveBeenCalled()
expect(marker2ChangedHandler).toHaveBeenCalled()
expect(marker3ChangedHandler).toHaveBeenCalled()
it "updates the position of markers before emitting change events that aren't caused by a buffer change", ->
changeHandler = jasmine.createSpy("changeHandler").andCallFake ->
displayBuffer.on 'changed', changeHandler = jasmine.createSpy("changeHandler").andCallFake ->
# calls change handler first
expect(observeHandler).not.toHaveBeenCalled()
expect(markerChangedHandler).not.toHaveBeenCalled()
# but still updates the markers
expect(displayBuffer.getMarkerScreenRange(marker)).toEqual [[8, 4], [8, 10]]
expect(displayBuffer.getMarkerHeadScreenPosition(marker)).toEqual [8, 10]
expect(displayBuffer.getMarkerTailScreenPosition(marker)).toEqual [8, 4]
displayBuffer.on 'changed', changeHandler
expect(marker.getScreenRange()).toEqual [[8, 4], [8, 10]]
expect(marker.getHeadScreenPosition()).toEqual [8, 10]
expect(marker.getTailScreenPosition()).toEqual [8, 4]
displayBuffer.destroyFoldsContainingBufferRow(4)
expect(changeHandler).toHaveBeenCalled()
expect(observeHandler).toHaveBeenCalled()
expect(markerChangedHandler).toHaveBeenCalled()
describe ".findMarkers(attributes)", ->
it "allows the startBufferRow and endBufferRow to be specified", ->
marker1 = displayBuffer.markBufferRange([[0, 0], [3, 0]], class: 'a')
marker2 = displayBuffer.markBufferRange([[0, 0], [5, 0]], class: 'a')
marker3 = displayBuffer.markBufferRange([[9, 0], [10, 0]], class: 'b')
expect(displayBuffer.findMarkers(class: 'a', startBufferRow: 0)).toEqual [marker2, marker1]
expect(displayBuffer.findMarkers(class: 'a', startBufferRow: 0, endBufferRow: 3)).toEqual [marker1]
expect(displayBuffer.findMarkers(endBufferRow: 10)).toEqual [marker3]
describe "marker destruction", ->
it "allows markers to be destroyed", ->
marker = displayBuffer.markScreenRange([[5, 4], [5, 10]])
displayBuffer.destroyMarker(marker)
expect(displayBuffer.getMarkerBufferRange(marker)).toBeUndefined()
marker.destroy()
expect(marker.isValid()).toBeFalsy()
expect(displayBuffer.getMarker(marker.id)).toBeUndefined()
it "emits 'destroyed' events when markers are destroyed", ->
destroyedHandler = jasmine.createSpy("destroyedHandler")
marker = displayBuffer.markScreenRange([[5, 4], [5, 10]])
marker.on 'destroyed', destroyedHandler
marker.destroy()
expect(destroyedHandler).toHaveBeenCalled()
destroyedHandler.reset()
marker2 = displayBuffer.markScreenRange([[5, 4], [5, 10]])
marker2.on 'destroyed', destroyedHandler
buffer.getMarker(marker2.id).destroy()
expect(destroyedHandler).toHaveBeenCalled()
+251 -299
Ver Arquivo
@@ -10,7 +10,7 @@ describe "EditSession", ->
beforeEach ->
atom.activatePackage('javascript.tmbundle', sync: true)
editSession = project.buildEditSession('sample.js', autoIndent: false)
editSession = project.open('sample.js', autoIndent: false)
buffer = editSession.buffer
lineLengths = buffer.getLines().map (line) -> line.length
@@ -744,17 +744,18 @@ describe "EditSession", ->
expect(editSession.lineForScreenRow(1).fold).toBeDefined()
describe ".selectMarker(marker)", ->
describe "when the marker exists", ->
it "selects the marker's range and returns true", ->
describe "if the marker is valid", ->
it "selects the marker's range and returns the selected range", ->
marker = editSession.markBufferRange([[0, 1], [3, 3]])
expect(editSession.selectMarker(marker)).toBeTruthy()
expect(editSession.selectMarker(marker)).toEqual [[0, 1], [3, 3]]
expect(editSession.getSelectedBufferRange()).toEqual [[0, 1], [3, 3]]
describe "when the marker does not exist", ->
it "does not select the marker's range and returns false", ->
rangeBefore = editSession.getSelectedBufferRange()
expect(editSession.selectMarker('bogus')).toBeFalsy()
expect(editSession.getSelectedBufferRange()).toEqual rangeBefore
describe "if the marker is invalid", ->
it "does not change the selection and returns a falsy value", ->
marker = editSession.markBufferRange([[0, 1], [3, 3]])
marker.destroy()
expect(editSession.selectMarker(marker)).toBeFalsy()
expect(editSession.getSelectedBufferRange()).toEqual [[0, 0], [0, 0]]
describe ".addSelectionBelow()", ->
describe "when the selection is non-empty", ->
@@ -1010,194 +1011,6 @@ describe "EditSession", ->
editSession.insertText('holy cow')
expect(editSession.lineForScreenRow(2).fold).toBeUndefined()
describe "when auto-indent is enabled", ->
describe "when a single newline is inserted", ->
describe "when the newline is inserted on a line that starts a new level of indentation", ->
it "auto-indents the new line to one additional level of indentation beyond the preceding line", ->
editSession.setCursorBufferPosition([1, Infinity])
editSession.insertText('\n', autoIndent: true)
expect(editSession.indentationForBufferRow(2)).toBe editSession.indentationForBufferRow(1) + 1
describe "when the newline is inserted on a normal line", ->
it "auto-indents the new line to the same level of indentation as the preceding line", ->
editSession.setCursorBufferPosition([5, 13])
editSession.insertText('\n', autoIndent: true)
expect(editSession.indentationForBufferRow(6)).toBe editSession.indentationForBufferRow(5)
describe "when text without newlines is inserted", ->
describe "when the current line matches an auto-outdent pattern", ->
describe "when the preceding line matches an auto-indent pattern", ->
it "auto-decreases the indentation of the line to match that of the preceding line", ->
editSession.setCursorBufferPosition([2, 4])
editSession.insertText('\n', autoIndent: true)
editSession.setCursorBufferPosition([2, 4])
expect(editSession.indentationForBufferRow(2)).toBe editSession.indentationForBufferRow(1) + 1
editSession.insertText(' }', autoIndent: true)
expect(editSession.indentationForBufferRow(2)).toBe editSession.indentationForBufferRow(1)
describe "when the preceding does not match an auto-indent pattern", ->
describe "when the inserted text is whitespace", ->
it "does not auto-decreases the indentation", ->
editSession.setCursorBufferPosition([12, 0])
editSession.insertText(' ', autoIndent: true)
expect(editSession.lineForBufferRow(12)).toBe ' };'
editSession.insertText('\t\t', autoIndent: true)
expect(editSession.lineForBufferRow(12)).toBe ' \t\t};'
describe "when the inserted text is non-whitespace", ->
it "auto-decreases the indentation of the line to be one level below that of the preceding line", ->
editSession.setCursorBufferPosition([3, Infinity])
editSession.insertText('\n', autoIndent: true)
expect(editSession.indentationForBufferRow(4)).toBe editSession.indentationForBufferRow(3)
editSession.insertText(' }', autoIndent: true)
expect(editSession.indentationForBufferRow(4)).toBe editSession.indentationForBufferRow(3) - 1
describe "when the current line does not match an auto-outdent pattern", ->
it "leaves the line unchanged", ->
editSession.setCursorBufferPosition([2, 4])
expect(editSession.indentationForBufferRow(2)).toBe editSession.indentationForBufferRow(1) + 1
editSession.insertText('foo', autoIndent: true)
expect(editSession.indentationForBufferRow(2)).toBe editSession.indentationForBufferRow(1) + 1
describe "when the `normalizeIndent` option is true", ->
describe "when the inserted text contains no newlines", ->
it "does not adjust the indentation level of the text", ->
editSession.setCursorBufferPosition([5, 2])
editSession.insertText("foo", normalizeIndent: true)
expect(editSession.lineForBufferRow(5)).toBe " foo current = items.shift();"
describe "when the inserted text contains newlines", ->
text = null
beforeEach ->
editSession.setCursorBufferPosition([2, Infinity])
text = [
" while (true) {"
" foo();"
" }"
" bar();"
].join('\n')
removeLeadingWhitespace = (text) -> text.replace(/^\s*/, '')
describe "when the cursor is preceded only by whitespace", ->
describe "when auto-indent is enabled", ->
describe "when the cursor's current column is less than the suggested indent level", ->
describe "when the indentBasis is inferred from the first line", ->
it "indents all lines relative to the suggested indent", ->
editSession.insertText('\n xx', autoIndent: true)
editSession.setCursorBufferPosition([3, 1])
editSession.insertText(text, normalizeIndent: true, autoIndent: true)
expect(editSession.lineForBufferRow(3)).toBe " while (true) {"
expect(editSession.lineForBufferRow(4)).toBe " foo();"
expect(editSession.lineForBufferRow(5)).toBe " }"
expect(editSession.lineForBufferRow(6)).toBe " bar();xx"
describe "when an indentBasis is provided", ->
it "indents all lines relative to the suggested indent", ->
editSession.insertText('\n xx')
editSession.setCursorBufferPosition([3, 1])
editSession.insertText(removeLeadingWhitespace(text), normalizeIndent: true, indentBasis: 2, autoIndent: true)
expect(editSession.lineForBufferRow(3)).toBe " while (true) {"
expect(editSession.lineForBufferRow(4)).toBe " foo();"
expect(editSession.lineForBufferRow(5)).toBe " }"
expect(editSession.lineForBufferRow(6)).toBe " bar();xx"
describe "when inserting on a line that has mixed tabs and whitespace in hard tabs mode (regression)", ->
it "correctly indents the inserted text", ->
editSession.softTabs = false
buffer.setText """
not indented
\tmixed indented
"""
editSession.setCursorBufferPosition([1, 0])
editSession.insertText(text, normalizeIndent: true, autoIndent: true)
expect(editSession.lineForBufferRow(1)).toBe "\t\t\twhile (true) {"
expect(editSession.lineForBufferRow(2)).toBe "\t\t\t\tfoo();"
expect(editSession.lineForBufferRow(3)).toBe "\t\t\t}"
expect(editSession.lineForBufferRow(4)).toBe "\t\tbar(); \tmixed indented"
describe "when inserting on a fractionally-indented line in hard tabs mode (regression)", ->
it "correctly indents the inserted text", ->
editSession.softTabs = false
buffer.setText """
not indented
fractional indentation
"""
editSession.setCursorBufferPosition([1, 0])
editSession.insertText(text, normalizeIndent: true, autoIndent: true)
expect(editSession.lineForBufferRow(1)).toBe "\t\twhile (true) {"
expect(editSession.lineForBufferRow(2)).toBe "\t\t\tfoo();"
expect(editSession.lineForBufferRow(3)).toBe "\t\t}"
expect(editSession.lineForBufferRow(4)).toBe "\tbar(); fractional indentation"
describe "when the cursor's current column is greater than the suggested indent level", ->
describe "when the indentBasis is inferred from the first line", ->
it "preserves the current indent level, indenting all lines relative to it", ->
editSession.insertText('\n ')
editSession.insertText(text, normalizeIndent: true)
expect(editSession.lineForBufferRow(3)).toBe " while (true) {"
expect(editSession.lineForBufferRow(4)).toBe " foo();"
expect(editSession.lineForBufferRow(5)).toBe " }"
expect(editSession.lineForBufferRow(6)).toBe " bar();"
describe "when an indentBasis is provided", ->
it "preserves the current indent level, indenting all lines relative to it", ->
editSession.insertText('\n ')
editSession.insertText(removeLeadingWhitespace(text), normalizeIndent: true, indentBasis: 2)
expect(editSession.lineForBufferRow(3)).toBe " while (true) {"
expect(editSession.lineForBufferRow(4)).toBe " foo();"
expect(editSession.lineForBufferRow(5)).toBe " }"
expect(editSession.lineForBufferRow(6)).toBe " bar();"
describe "if auto-indent is disabled", ->
describe "when the indentBasis is inferred from the first line", ->
it "always normalizes indented lines to the cursor's current indentation level", ->
editSession.insertText('\n ')
editSession.insertText(text, normalizeIndent: true)
expect(editSession.lineForBufferRow(3)).toBe " while (true) {"
expect(editSession.lineForBufferRow(4)).toBe " foo();"
expect(editSession.lineForBufferRow(5)).toBe " }"
expect(editSession.lineForBufferRow(6)).toBe "bar();"
describe "when an indentBasis is provided", ->
it "always normalizes indented lines to the cursor's current indentation level", ->
editSession.insertText('\n ')
editSession.insertText(removeLeadingWhitespace(text), normalizeIndent: true, indentBasis: 2)
expect(editSession.lineForBufferRow(3)).toBe " while (true) {"
expect(editSession.lineForBufferRow(4)).toBe " foo();"
expect(editSession.lineForBufferRow(5)).toBe " }"
describe "when the cursor is preceded by non-whitespace characters", ->
describe "when the indentBasis is inferred from the first line", ->
it "normalizes the indentation level of all lines based on the level of the existing first line", ->
editSession.buffer.delete([[2, 0], [2, 2]])
editSession.insertText(text, normalizeIndent:true)
expect(editSession.lineForBufferRow(2)).toBe " if (items.length <= 1) return items;while (true) {"
expect(editSession.lineForBufferRow(3)).toBe " foo();"
expect(editSession.lineForBufferRow(4)).toBe " }"
expect(editSession.lineForBufferRow(5)).toBe "bar();"
describe "when an indentBasis is provided", ->
it "normalizes the indentation level of all lines based on the level of the existing first line", ->
editSession.buffer.delete([[2, 0], [2, 2]])
editSession.insertText(removeLeadingWhitespace(text), normalizeIndent:true, indentBasis: 2)
expect(editSession.lineForBufferRow(2)).toBe " if (items.length <= 1) return items;while (true) {"
expect(editSession.lineForBufferRow(3)).toBe " foo();"
expect(editSession.lineForBufferRow(4)).toBe " }"
expect(editSession.lineForBufferRow(5)).toBe "bar();"
describe ".insertNewline()", ->
describe "when there is a single cursor", ->
describe "when the cursor is at the beginning of a line", ->
@@ -1346,14 +1159,14 @@ describe "EditSession", ->
editSession.backspace()
describe "when the cursor is on the first column of a line below a fold", ->
it "absorbs the current line into the fold", ->
it "deletes the folded lines", ->
editSession.setCursorScreenPosition([4,0])
editSession.foldCurrentRow()
editSession.setCursorScreenPosition([5,0])
editSession.backspace()
expect(buffer.lineForRow(7)).toBe " } return sort(left).concat(pivot).concat(sort(right));"
expect(buffer.lineForRow(8)).toBe " };"
expect(buffer.lineForRow(4)).toBe " return sort(left).concat(pivot).concat(sort(right));"
expect(buffer.lineForRow(4).fold).toBeUndefined()
describe "when the cursor is in the middle of a line below a fold", ->
it "backspaces as normal", ->
@@ -1370,6 +1183,7 @@ describe "EditSession", ->
editSession.setCursorBufferPosition([3, 0])
editSession.foldCurrentRow()
editSession.backspace()
expect(buffer.lineForRow(1)).toBe ""
expect(buffer.lineForRow(2)).toBe " return sort(Array.apply(this, arguments));"
expect(editSession.getCursorScreenPosition()).toEqual [1, 0]
@@ -1433,14 +1247,13 @@ describe "EditSession", ->
expect(editSession.buffer.lineForRow(0)).toBe 'var qsort = function () {'
describe "when the selection ends on a folded line", ->
it "destroys the fold", ->
it "preserves the fold", ->
editSession.setSelectedBufferRange([[3,0], [4,0]])
editSession.foldBufferRow(4)
editSession.backspace()
expect(buffer.lineForRow(3)).toBe " return sort(left).concat(pivot).concat(sort(right));"
expect(buffer.lineForRow(4)).toBe " };"
expect(editSession.getCursorScreenPosition()).toEqual [3, 0]
expect(buffer.lineForRow(3)).toBe " while(items.length > 0) {"
expect(editSession.lineForScreenRow(3).fold).toBeDefined()
describe "when there are multiple selections", ->
it "removes all selected text", ->
@@ -1778,17 +1591,6 @@ describe "EditSession", ->
expect(editSession.buffer.lineForRow(0)).toBe "var first = function () {"
expect(buffer.lineForRow(1)).toBe " var first = function(items) {"
it "preserves the indent level when copying and pasting multiple lines", ->
editSession.setSelectedBufferRange([[4, 4], [7, 5]])
editSession.copySelectedText()
editSession.setCursorBufferPosition([10, 0])
editSession.pasteText(autoIndent: true)
expect(editSession.lineForBufferRow(10)).toBe " while(items.length > 0) {"
expect(editSession.lineForBufferRow(11)).toBe " current = items.shift();"
expect(editSession.lineForBufferRow(12)).toBe " current < pivot ? left.push(current) : right.push(current);"
expect(editSession.lineForBufferRow(13)).toBe " }"
describe ".indentSelectedRows()", ->
describe "when nothing is selected", ->
describe "when softTabs is enabled", ->
@@ -1960,7 +1762,7 @@ describe "EditSession", ->
it "does not explode if the current language mode has no comment regex", ->
editSession.destroy()
editSession = project.buildEditSession(null, autoIndent: false)
editSession = project.open(null, autoIndent: false)
editSession.setSelectedBufferRange([[4, 5], [4, 5]])
editSession.toggleLineCommentsInSelection()
expect(buffer.lineForRow(4)).toBe " while(items.length > 0) {"
@@ -2037,15 +1839,28 @@ describe "EditSession", ->
editSession.redo()
expect(editSession.getSelectedBufferRanges()).toEqual [[[1, 6], [1, 6]], [[1, 18], [1, 18]]]
it "restores selected ranges even when the change occurred in another edit session", ->
otherEditSession = project.buildEditSession(editSession.getPath())
otherEditSession.setSelectedBufferRange([[2, 2], [3, 3]])
otherEditSession.delete()
it "restores folds after undo and redo", ->
editSession.foldBufferRow(1)
editSession.setSelectedBufferRange([[1, 0], [10, Infinity]], preserveFolds: true)
expect(editSession.isFoldedAtBufferRow(1)).toBeTruthy()
editSession.insertText """
\ // testing
function foo() {
return 1 + 2;
}
"""
expect(editSession.isFoldedAtBufferRow(1)).toBeFalsy()
editSession.foldBufferRow(2)
editSession.undo()
expect(editSession.isFoldedAtBufferRow(1)).toBeTruthy()
expect(editSession.isFoldedAtBufferRow(9)).toBeTruthy()
expect(editSession.isFoldedAtBufferRow(10)).toBeFalsy()
expect(editSession.getSelectedBufferRange()).toEqual [[2, 2], [3, 3]]
expect(otherEditSession.getSelectedBufferRange()).toEqual [[3, 3], [3, 3]]
editSession.redo()
expect(editSession.isFoldedAtBufferRow(1)).toBeFalsy()
expect(editSession.isFoldedAtBufferRow(2)).toBeTruthy()
describe ".transact([fn])", ->
describe "when called without a function", ->
@@ -2125,30 +1940,30 @@ describe "EditSession", ->
editSession.foldAll()
fold1 = editSession.lineForScreenRow(0).fold
expect([fold1.startRow, fold1.endRow]).toEqual [0, 12]
expect([fold1.getStartRow(), fold1.getEndRow()]).toEqual [0, 12]
fold1.destroy()
fold2 = editSession.lineForScreenRow(1).fold
expect([fold2.startRow, fold2.endRow]).toEqual [1, 9]
expect([fold2.getStartRow(), fold2.getEndRow()]).toEqual [1, 9]
fold2.destroy()
fold3 = editSession.lineForScreenRow(4).fold
expect([fold3.startRow, fold3.endRow]).toEqual [4, 7]
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", ->
editSession.foldBufferRow(1)
fold = editSession.lineForScreenRow(1).fold
expect(fold.startRow).toBe 1
expect(fold.endRow).toBe 9
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)", ->
editSession.foldBufferRow(8)
fold = editSession.lineForScreenRow(1).fold
expect(fold.startRow).toBe 1
expect(fold.endRow).toBe 9
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)", ->
@@ -2164,16 +1979,16 @@ describe "EditSession", ->
buffer.insert([1,0], " //this is a comment\n // and\n //more docs\n\n//second comment")
editSession.foldBufferRow(1)
fold = editSession.lineForScreenRow(1).fold
expect(fold.startRow).toBe 1
expect(fold.endRow).toBe 3
expect(fold.getStartRow()).toBe 1
expect(fold.getEndRow()).toBe 3
describe "when the bufferRow is a single-line comment", ->
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")
editSession.foldBufferRow(1)
fold = editSession.lineForScreenRow(0).fold
expect(fold.startRow).toBe 0
expect(fold.endRow).toBe 13
expect(fold.getStartRow()).toBe 0
expect(fold.getEndRow()).toBe 13
describe ".unfoldBufferRow(bufferRow)", ->
describe "when bufferRow can be unfolded", ->
@@ -2334,13 +2149,13 @@ describe "EditSession", ->
describe "soft-tabs detection", ->
it "assign soft / hard tabs based on the contents of the buffer, or uses the default if unknown", ->
editSession = project.buildEditSession('sample.js', softTabs: false)
editSession = project.open('sample.js', softTabs: false)
expect(editSession.softTabs).toBeTruthy()
editSession = project.buildEditSession('sample-with-tabs.coffee', softTabs: true)
editSession = project.open('sample-with-tabs.coffee', softTabs: true)
expect(editSession.softTabs).toBeFalsy()
editSession = project.buildEditSession(null, softTabs: false)
editSession = project.open(null, softTabs: false)
expect(editSession.softTabs).toBeFalsy()
describe ".indentLevelForLine(line)", ->
@@ -2369,7 +2184,7 @@ describe "EditSession", ->
jsGrammar = syntax.selectGrammar('a.js')
syntax.removeGrammar(jsGrammar)
editSession = project.buildEditSession('sample.js', autoIndent: false)
editSession = project.open('sample.js', autoIndent: false)
expect(editSession.getGrammar()).toBe syntax.nullGrammar
expect(editSession.lineForScreenRow(0).tokens.length).toBe 1
@@ -2378,82 +2193,219 @@ describe "EditSession", ->
expect(editSession.lineForScreenRow(0).tokens.length).toBeGreaterThan 1
describe "auto-indent", ->
copyText = (text, {startColumn}={}) ->
startColumn ?= 0
editSession.setCursorBufferPosition([0, 0])
editSession.insertText(text)
numberOfNewlines = text.match(/\n/g)?.length
endColumn = text.match(/[^\n]*$/)[0]?.length
editSession.getSelection().setBufferRange([[0,startColumn], [numberOfNewlines,endColumn]])
editSession.cutSelectedText()
describe "editor.autoIndent", ->
it "auto-indents newlines if editor.autoIndent is true", ->
config.set("editor.autoIndent", undefined)
editSession.setCursorBufferPosition([1, 30])
editSession.insertText("\n")
expect(editSession.lineForBufferRow(2)).toBe " "
describe "when editor.autoIndent is false (default)", ->
describe "when `indent` is triggered", ->
it "does not auto-indent the line", ->
editSession.setCursorBufferPosition([1, 30])
editSession.insertText("\n ")
expect(editSession.lineForBufferRow(2)).toBe " "
it "does not auto-indent newlines if editor.autoIndent is false", ->
config.set("editor.autoIndent", false)
editSession.setCursorBufferPosition([1, 30])
editSession.insertText("\n")
expect(editSession.lineForBufferRow(2)).toBe ""
config.set("editor.autoIndent", false)
editSession.indent()
expect(editSession.lineForBufferRow(2)).toBe " "
it "auto-indents calls to `indent` if editor.autoIndent is true", ->
config.set("editor.autoIndent", true)
editSession.setCursorBufferPosition([1, 30])
editSession.insertText("\n ")
expect(editSession.lineForBufferRow(2)).toBe " "
editSession.indent()
expect(editSession.lineForBufferRow(2)).toBe " "
describe "when editor.autoIndent is true", ->
beforeEach ->
config.set("editor.autoIndent", true)
it "does not auto-indents calls to `indent` if editor.autoIndent is false", ->
config.set("editor.autoIndent", false)
editSession.setCursorBufferPosition([1, 30])
editSession.insertText("\n ")
expect(editSession.lineForBufferRow(2)).toBe " "
editSession.indent()
expect(editSession.lineForBufferRow(2)).toBe " "
describe "when `indent` is triggered", ->
it "auto-indents the line", ->
editSession.setCursorBufferPosition([1, 30])
editSession.insertText("\n ")
expect(editSession.lineForBufferRow(2)).toBe " "
it "auto-indents selection when autoIndent is called", ->
editSession.setCursorBufferPosition([2, 0])
editSession.insertText(" 0\n 2\n4\n")
config.set("editor.autoIndent", true)
editSession.indent()
expect(editSession.lineForBufferRow(2)).toBe " "
editSession.setSelectedBufferRange([[2, 0], [4, 0]])
editSession.autoIndentSelectedRows()
describe "when a newline is added", ->
describe "when the line preceding the newline adds a new level of indentation", ->
it "indents the newline to one additional level of indentation beyond the preceding line", ->
editSession.setCursorBufferPosition([1, Infinity])
editSession.insertText('\n')
expect(editSession.indentationForBufferRow(2)).toBe editSession.indentationForBufferRow(1) + 1
expect(editSession.lineForBufferRow(2)).toBe " 0"
expect(editSession.lineForBufferRow(3)).toBe " 2"
expect(editSession.lineForBufferRow(4)).toBe "4"
describe "when the line preceding the newline does't add a level of indentation", ->
it "indents the new line to the same level a as the preceding line", ->
editSession.setCursorBufferPosition([5, 13])
editSession.insertText('\n')
expect(editSession.indentationForBufferRow(6)).toBe editSession.indentationForBufferRow(5)
describe "when the line preceding the newline is a comment", ->
it "maintains the indent of the commented line", ->
editSession.setCursorBufferPosition([0, 0])
editSession.insertText(' //')
editSession.setCursorBufferPosition([0, Infinity])
editSession.insertText('\n')
expect(editSession.indentationForBufferRow(1)).toBe 2
it "does not indent the line preceding the newline", ->
editSession.setCursorBufferPosition([2, 0])
editSession.insertText(' var this-line-should-be-indented-more\n')
expect(editSession.indentationForBufferRow(1)).toBe 1
config.set("editor.autoIndent", true)
editSession.setCursorBufferPosition([2, Infinity])
editSession.insertText('\n')
expect(editSession.indentationForBufferRow(1)).toBe 1
expect(editSession.indentationForBufferRow(2)).toBe 1
describe "when inserted text matches a decrease indent pattern", ->
describe "when the preceding line matches an increase indent pattern", ->
it "decreases the indentation to match that of the preceding line", ->
editSession.setCursorBufferPosition([1, Infinity])
editSession.insertText('\n')
expect(editSession.indentationForBufferRow(2)).toBe editSession.indentationForBufferRow(1) + 1
editSession.insertText('}')
expect(editSession.indentationForBufferRow(2)).toBe editSession.indentationForBufferRow(1)
describe "when the preceding line doesn't match an increase indent pattern", ->
it "decreases the indentation to be one level below that of the preceding line", ->
editSession.setCursorBufferPosition([3, Infinity])
editSession.insertText('\n ')
expect(editSession.indentationForBufferRow(4)).toBe editSession.indentationForBufferRow(3)
editSession.insertText('}')
expect(editSession.indentationForBufferRow(4)).toBe editSession.indentationForBufferRow(3) - 1
it "doesn't break when decreasing the indentation on a row that has no indentation", ->
editSession.setCursorBufferPosition([12, Infinity])
editSession.insertText("\n}; # too many closing brackets!")
expect(editSession.lineForBufferRow(13)).toBe "}; # too many closing brackets!"
describe "when inserted text does not match a decrease indent pattern", ->
it "does not the indentation", ->
editSession.setCursorBufferPosition([12, 0])
editSession.insertText(' ')
expect(editSession.lineForBufferRow(12)).toBe ' };'
editSession.insertText('\t\t')
expect(editSession.lineForBufferRow(12)).toBe ' \t\t};'
describe "when the current line does not match a decrease indent pattern", ->
it "leaves the line unchanged", ->
editSession.setCursorBufferPosition([2, 4])
expect(editSession.indentationForBufferRow(2)).toBe editSession.indentationForBufferRow(1) + 1
editSession.insertText('foo')
expect(editSession.indentationForBufferRow(2)).toBe editSession.indentationForBufferRow(1) + 1
describe "editor.autoIndentOnPaste", ->
it "does not auto-indent pasted text by default", ->
editSession.setCursorBufferPosition([2, 0])
editSession.insertText("0\n 2\n 4\n")
editSession.getSelection().setBufferRange([[2,0], [5,0]])
editSession.cutSelectedText()
describe "when the text contains multiple lines", ->
beforeEach ->
copyText("function() {\ninside=true\n}\n i=1\n")
editSession.setCursorBufferPosition([2, 0])
it "does not auto-indent pasted text by default", ->
editSession.pasteText()
expect(editSession.lineForBufferRow(2)).toBe "function() {"
expect(editSession.lineForBufferRow(3)).toBe "inside=true"
expect(editSession.lineForBufferRow(4)).toBe "}"
expect(editSession.lineForBufferRow(5)).toBe " i=1"
it "auto-indents pasted text when editor.autoIndentOnPaste is true", ->
config.set("editor.autoIndentOnPaste", true)
editSession.pasteText()
expect(editSession.lineForBufferRow(2)).toBe " function() {"
expect(editSession.lineForBufferRow(3)).toBe " inside=true"
expect(editSession.lineForBufferRow(4)).toBe " }"
expect(editSession.lineForBufferRow(5)).toBe " i=1"
describe "when the text contains no newlines", ->
it "increaseses indent of pasted text when editor.autoIndentOnPaste is true", ->
copyText("var number")
editSession.setCursorBufferPosition([10, 0])
config.set("editor.autoIndentOnPaste", true)
editSession.pasteText()
expect(editSession.lineForBufferRow(10)).toBe " var number"
it "decreaseses indent of pasted text when editor.autoIndentOnPaste is true", ->
copyText(" var number")
editSession.setCursorBufferPosition([10, 0])
config.set("editor.autoIndentOnPaste", true)
editSession.pasteText()
expect(editSession.lineForBufferRow(10)).toBe " var number"
describe "editor.normalizeIndentOnPaste", ->
beforeEach ->
config.set('editor.normalizeIndentOnPaste', true)
it "does not normalize the indentation level of the text when editor.autoIndentOnPaste is true", ->
copyText(" function() {\nvar cool = 1;\n }\n")
config.set('editor.autoIndentOnPaste', true)
editSession.setCursorBufferPosition([5, ])
editSession.pasteText()
expect(editSession.lineForBufferRow(2)).toBe "0"
expect(editSession.lineForBufferRow(3)).toBe " 2"
expect(editSession.lineForBufferRow(4)).toBe " 4"
it "auto-indents pasted text when editor.autoIndentOnPaste is true", ->
config.set("editor.autoIndentOnPaste", true)
editSession.setCursorBufferPosition([2, 0])
editSession.insertText("0\n 2\n 4\n")
editSession.getSelection().setBufferRange([[2,0], [5,0]])
editSession.cutSelectedText()
expect(editSession.lineForBufferRow(5)).toBe " function() {"
expect(editSession.lineForBufferRow(6)).toBe " var cool = 1;"
expect(editSession.lineForBufferRow(7)).toBe " }"
it "does not normalize the indentation level of the text when editor.normalizeIndentOnPaste is false", ->
copyText(" function() {\nvar cool = 1;\n }\n")
config.set('editor.normalizeIndentOnPaste', false)
editSession.setCursorBufferPosition([5, 2])
editSession.pasteText()
expect(editSession.lineForBufferRow(2)).toBe " 0"
expect(editSession.lineForBufferRow(3)).toBe " 2"
expect(editSession.lineForBufferRow(4)).toBe " 4"
expect(editSession.lineForBufferRow(5)).toBe " function() {"
expect(editSession.lineForBufferRow(6)).toBe "var cool = 1;"
expect(editSession.lineForBufferRow(7)).toBe " }"
describe ".autoDecreaseIndentForRow()", ->
it "doesn't outdent the first and only row", ->
editSession.selectAll()
editSession.insertText("}")
editSession.autoDecreaseIndentForRow(0)
expect(editSession.lineForBufferRow(0)).toBe "}"
describe "when the inserted text contains no newlines", ->
it "does not adjust the indentation level of the text", ->
editSession.setCursorBufferPosition([5, 2])
editSession.insertText("foo", indentBasis: 5)
expect(editSession.lineForBufferRow(5)).toBe " foo current = items.shift();"
it "doesn't outdent a row that is already fully outdented", ->
editSession.selectAll()
editSession.insertText("var i;\n}")
editSession.autoDecreaseIndentForRow(1)
expect(editSession.lineForBufferRow(1)).toBe "}"
describe "when the inserted text contains newlines", ->
describe "when copied text includes whitespace on first line", ->
describe "when cursor is preceded by whitespace and followed non-whitespace", ->
it "normalizes indented lines to the cursor's current indentation level", ->
copyText(" while (true) {\n foo();\n }\n", {startColumn: 4})
editSession.setCursorBufferPosition([3, 4])
editSession.pasteText()
expect(editSession.lineForBufferRow(3)).toBe " while (true) {"
expect(editSession.lineForBufferRow(4)).toBe " foo();"
expect(editSession.lineForBufferRow(5)).toBe " }"
expect(editSession.lineForBufferRow(6)).toBe "var pivot = items.shift(), current, left = [], right = [];"
describe "when cursor is preceded by whitespace and followed by whitespace", ->
it "normalizes indented lines to the cursor's current indentation level", ->
copyText(" while (true) {\n foo();\n }\n", {startColumn: 0})
editSession.setCursorBufferPosition([3, 4])
editSession.pasteText()
expect(editSession.lineForBufferRow(3)).toBe " while (true) {"
expect(editSession.lineForBufferRow(4)).toBe " foo();"
expect(editSession.lineForBufferRow(5)).toBe " }"
expect(editSession.lineForBufferRow(6)).toBe "var pivot = items.shift(), current, left = [], right = [];"
describe "when the cursor is preceded by non-whitespace characters", ->
it "normalizes the indentation level of all lines based on the level of the existing first line", ->
copyText(" while (true) {\n foo();\n }\n", {startColumn: 0})
editSession.setCursorBufferPosition([1, Infinity])
editSession.pasteText()
expect(editSession.lineForBufferRow(1)).toBe " var sort = function(items) { while (true) {"
expect(editSession.lineForBufferRow(2)).toBe " foo();"
expect(editSession.lineForBufferRow(3)).toBe " }"
expect(editSession.lineForBufferRow(4)).toBe ""
it "autoIndentSelectedRows auto-indents the selection", ->
editSession.setCursorBufferPosition([2, 0])
editSession.insertText("function() {\ninside=true\n}\n i=1\n")
editSession.getSelection().setBufferRange([[2,0], [6,0]])
editSession.autoIndentSelectedRows()
expect(editSession.lineForBufferRow(2)).toBe " function() {"
expect(editSession.lineForBufferRow(3)).toBe " inside=true"
expect(editSession.lineForBufferRow(4)).toBe " }"
expect(editSession.lineForBufferRow(5)).toBe " i=1"
describe ".destroy()", ->
it "destroys all markers associated with the edit session", ->
@@ -2503,7 +2455,7 @@ describe "EditSession", ->
expect(editSession.shouldPromptToSave()).toBeFalsy()
buffer.setText('changed')
expect(editSession.shouldPromptToSave()).toBeTruthy()
editSession2 = project.buildEditSession('sample.js', autoIndent: false)
editSession2 = project.open('sample.js', autoIndent: false)
expect(editSession.shouldPromptToSave()).toBeFalsy()
editSession2.destroy()
expect(editSession.shouldPromptToSave()).toBeTruthy()
+69 -35
Ver Arquivo
@@ -15,7 +15,7 @@ describe "Editor", ->
beforeEach ->
atom.activatePackage('text.tmbundle', sync: true)
atom.activatePackage('javascript.tmbundle', sync: true)
editSession = project.buildEditSession('sample.js')
editSession = project.open('sample.js')
buffer = editSession.buffer
editor = new Editor(editSession)
editor.lineOverdraw = 2
@@ -38,7 +38,7 @@ describe "Editor", ->
cachedCharWidth
calcDimensions = ->
editorForMeasurement = new Editor(editSession: project.buildEditSession('sample.js'))
editorForMeasurement = new Editor(editSession: project.open('sample.js'))
editorForMeasurement.attachToDom()
cachedLineHeight = editorForMeasurement.lineHeight
cachedCharWidth = editorForMeasurement.charWidth
@@ -85,7 +85,7 @@ describe "Editor", ->
it "triggers an alert", ->
path = "/tmp/atom-changed-file.txt"
fsUtils.write(path, "")
editSession = project.buildEditSession(path)
editSession = project.open(path)
editor.edit(editSession)
editor.insertText("now the buffer is modified")
@@ -111,7 +111,7 @@ describe "Editor", ->
[newEditSession, newBuffer] = []
beforeEach ->
newEditSession = project.buildEditSession('two-hundred.txt')
newEditSession = project.open('two-hundred.txt')
newBuffer = newEditSession.buffer
it "updates the rendered lines, cursors, selections, scroll position, and event subscriptions to match the given edit session", ->
@@ -149,7 +149,7 @@ describe "Editor", ->
it "triggers alert if edit session's buffer goes into conflict with changes on disk", ->
path = "/tmp/atom-changed-file.txt"
fsUtils.write(path, "")
tempEditSession = project.buildEditSession(path)
tempEditSession = project.open(path)
editor.edit(tempEditSession)
tempEditSession.insertText("a buffer change")
@@ -243,7 +243,7 @@ describe "Editor", ->
it "emits event when editor receives a new buffer", ->
eventHandler = jasmine.createSpy('eventHandler')
editor.on 'editor:path-changed', eventHandler
editor.edit(project.buildEditSession(path))
editor.edit(project.open(path))
expect(eventHandler).toHaveBeenCalled()
it "stops listening to events on previously set buffers", ->
@@ -251,7 +251,7 @@ describe "Editor", ->
oldBuffer = editor.getBuffer()
editor.on 'editor:path-changed', eventHandler
editor.edit(project.buildEditSession(path))
editor.edit(project.open(path))
expect(eventHandler).toHaveBeenCalled()
eventHandler.reset()
@@ -320,8 +320,9 @@ describe "Editor", ->
expect(editor.verticalScrollbarContent.height()).toBe buffer.getLineCount() * editor.lineHeight
newEditor = new Editor(editor.activeEditSession.copy())
editor.remove()
newEditor.attachToDom()
expect(editor.css('font-size')).toBe '30px'
expect(newEditor.css('font-size')).toBe '30px'
it "updates the position and size of selection regions", ->
config.set("editor.fontSize", 10)
@@ -371,7 +372,6 @@ describe "Editor", ->
describe "single-click", ->
it "re-positions the cursor to the clicked row / column", ->
expect(editor.getCursorScreenPosition()).toEqual(row: 0, column: 0)
editor.renderedLines.trigger mousedownEvent(editor: editor, point: [3, 10])
expect(editor.getCursorScreenPosition()).toEqual(row: 3, column: 10)
@@ -542,20 +542,23 @@ describe "Editor", ->
expect(editor.getCursorScreenPosition()).toEqual(row: 5, column: 27)
it "selects and scrolls if the mouse is dragged outside of the editor itself", ->
intervalFns = []
editor.vScrollMargin = 0
editor.attachToDom(heightInLines: 5)
editor.scrollToBottom()
spyOn(window, 'setInterval').andCallFake (fn) -> intervalFns.push(fn)
spyOn(window, 'setInterval').andCallFake ->
# start
editor.renderedLines.trigger mousedownEvent(editor: editor, point: [12, 0])
originalScrollTop = editor.scrollTop()
# moving changes selection
$(document).trigger mousemoveEvent(editor: editor, pageX: 0, pageY: -15)
expect(editor.scrollTop()).toBe 4 * editor.lineHeight
$(document).trigger mousemoveEvent(editor: editor, pageX: 0, pageY: -1)
expect(editor.scrollTop()).toBe originalScrollTop - editor.lineHeight
# if cursor stays off screen, we keep moving / scrolling up
fn() for fn in intervalFns
# every mouse move selects more text
for x in [0..10]
$(document).trigger mousemoveEvent(editor: editor, pageX: 0, pageY: -1)
expect(editor.scrollTop()).toBe 0
@@ -1211,7 +1214,7 @@ describe "Editor", ->
describe "when scrolling more than the editors height", ->
it "removes lines that are offscreen and not in range of the overdraw and builds lines that become visible", ->
editor.scrollTop(editor.scrollView.prop('scrollHeight') - editor.scrollView.height())
editor.scrollTop(editor.layerHeight - editor.scrollView.height())
expect(editor.renderedLines.find('.line').length).toBe 8
expect(editor.renderedLines.find('.line:first').text()).toBe buffer.lineForRow(5)
expect(editor.renderedLines.find('.line:last').text()).toBe buffer.lineForRow(12)
@@ -1285,7 +1288,7 @@ describe "Editor", ->
expect(editor.renderedLines.find(".line:last").text()).toBe buffer.lineForRow(6)
it "increases the width of the rendered lines element to be either the width of the longest line or the width of the scrollView (whichever is longer)", ->
maxLineLength = editor.maxScreenLineLength()
maxLineLength = editor.getMaxScreenLineLength()
setEditorWidthInChars(editor, maxLineLength)
widthBefore = editor.renderedLines.width()
expect(widthBefore).toBe editor.scrollView.width() + 20
@@ -1297,7 +1300,7 @@ describe "Editor", ->
editor.attachToDom(heightInLines: 5)
it "sets the rendered screen line's width to either the max line length or the scollView's width (whichever is greater)", ->
maxLineLength = editor.maxScreenLineLength()
maxLineLength = editor.getMaxScreenLineLength()
setEditorWidthInChars(editor, maxLineLength)
buffer.change([[12,0], [12,0]], [1..maxLineLength*2].join(''))
expect(editor.renderedLines.width()).toBeGreaterThan editor.scrollView.width()
@@ -1385,9 +1388,18 @@ describe "Editor", ->
expect(editor.renderedLines.find('.line').length).toBe 1
expect(editor.renderedLines.find('.line').text()).toBe buffer.lineForRow(0)
describe "when folding leaves fewer screen lines than the first rendered screen line (regression)", ->
it "clears all screen lines and does not throw any exceptions", ->
editor.lineOverdraw = 1
editor.attachToDom(heightInLines: 5)
editor.scrollToBottom()
editor.activeEditSession.foldBufferRow(0)
expect(editor.renderedLines.find('.line').length).toBe 1
expect(editor.renderedLines.find('.line').text()).toBe buffer.lineForRow(0)
describe "when autoscrolling at the end of the document", ->
it "renders lines properly", ->
editor.edit(project.buildEditSession('two-hundred.txt'))
editor.edit(project.open('two-hundred.txt'))
editor.attachToDom(heightInLines: 5.5)
expect(editor.renderedLines.find('.line').length).toBe 8
@@ -1548,6 +1560,31 @@ describe "Editor", ->
expect(editor.renderedLines.find('.line:eq(10) .indent-guide').length).toBe 2
expect(editor.renderedLines.find('.line:eq(10) .indent-guide').text()).toBe ' '
describe "when the line has leading and trailing whitespace", ->
it "does not display the indent guide in the trailing whitespace", ->
editor.attachToDom()
config.set("editor.showIndentGuide", true)
editor.insertText("/*\n * \n*/")
expect(editor.renderedLines.find('.line:eq(1) .indent-guide').length).toBe 1
expect(editor.renderedLines.find('.line:eq(1) .indent-guide')).toHaveClass('leading-whitespace')
describe "when the line is empty and end of show invisibles are enabled", ->
it "renders the indent guides interleaved the end of line invisibles", ->
editor.attachToDom()
config.set("editor.showIndentGuide", true)
config.set("editor.showInvisibles", true)
eol = editor.invisibles?.eol
expect(editor.renderedLines.find('.line:eq(10) .indent-guide').length).toBe 1
expect(editor.renderedLines.find('.line:eq(10) .indent-guide').text()).toBe "#{eol} "
editor.setCursorBufferPosition([9])
editor.indent()
expect(editor.renderedLines.find('.line:eq(10) .indent-guide').length).toBe 2
expect(editor.renderedLines.find('.line:eq(10) .indent-guide').text()).toBe "#{eol} "
describe "when soft-wrap is enabled", ->
beforeEach ->
editor.attachToDom()
@@ -1587,7 +1624,7 @@ describe "Editor", ->
expect(editor.bufferPositionForScreenPosition(editor.getCursorScreenPosition())).toEqual [3, 60]
it "does not wrap the lines of any newly assigned buffers", ->
otherEditSession = project.buildEditSession()
otherEditSession = project.open()
otherEditSession.buffer.setText([1..100].join(''))
editor.edit(otherEditSession)
expect(editor.renderedLines.find('.line').length).toBe(1)
@@ -1623,7 +1660,7 @@ describe "Editor", ->
expect(editor.getCursorScreenPosition()).toEqual [11, 0]
it "calls .setSoftWrapColumn() when the editor is attached because now its dimensions are available to calculate it", ->
otherEditor = new Editor(editSession: project.buildEditSession('sample.js'))
otherEditor = new Editor(editSession: project.open('sample.js'))
spyOn(otherEditor, 'setSoftWrapColumn')
otherEditor.setSoftWrap(true)
@@ -1727,7 +1764,7 @@ describe "Editor", ->
describe "when the switching from an edit session for a long buffer to an edit session for a short buffer", ->
it "updates the line numbers to reflect the shorter buffer", ->
emptyEditSession = project.buildEditSession(null)
emptyEditSession = project.open(null)
editor.edit(emptyEditSession)
expect(editor.gutter.lineNumbers.find('.line-number').length).toBe 1
@@ -1889,7 +1926,7 @@ describe "Editor", ->
describe "folding", ->
beforeEach ->
editSession = project.buildEditSession('two-hundred.txt')
editSession = project.open('two-hundred.txt')
buffer = editSession.buffer
editor.edit(editSession)
editor.attachToDom()
@@ -1915,9 +1952,11 @@ describe "Editor", ->
describe "when a fold placeholder line is clicked", ->
it "removes the associated fold and places the cursor at its beginning", ->
editor.setCursorBufferPosition([3,0])
editor.trigger 'editor:fold-current-row'
editSession.createFold(3, 5)
editor.find('.fold.line').mousedown()
foldLine = editor.find('.line.fold')
expect(foldLine).toExist()
foldLine.mousedown()
expect(editor.find('.fold')).not.toExist()
expect(editor.find('.fold-marker')).not.toExist()
@@ -2025,7 +2064,7 @@ describe "Editor", ->
beforeEach ->
path = project.resolve('git/working-dir/file.txt')
originalPathText = fsUtils.read(path)
editor.edit(project.buildEditSession(path))
editor.edit(project.open(path))
afterEach ->
fsUtils.write(path, originalPathText)
@@ -2045,7 +2084,6 @@ describe "Editor", ->
runs ->
expect(editor.getText()).toBe(originalPathText)
describe ".pixelPositionForBufferPosition(position)", ->
describe "when the editor is detached", ->
it "returns top and left values of 0", ->
@@ -2126,16 +2164,13 @@ describe "Editor", ->
it "move the cursor to the end of the file", ->
expect(editor.getCursorScreenPosition()).toEqual [0,0]
event = $.Event("click")
event.offsetY = Infinity
event = mousedownEvent(editor: editor, point: [Infinity, 10])
editor.underlayer.trigger event
expect(editor.getCursorScreenPosition()).toEqual [12,2]
it "selects to the end of the files when shift is pressed", ->
expect(editor.getSelection().getScreenRange()).toEqual [[0,0], [0,0]]
event = $.Event("click")
event.offsetY = Infinity
event.shiftKey = true
event = mousedownEvent(editor: editor, point: [Infinity, 10], shiftKey: true)
editor.underlayer.trigger event
expect(editor.getSelection().getScreenRange()).toEqual [[0,0], [12,2]]
@@ -2150,7 +2185,7 @@ describe "Editor", ->
fsUtils.remove(path) if fsUtils.exists(path)
it "updates all the rendered lines when the grammar changes", ->
editor.edit(project.buildEditSession(path))
editor.edit(project.open(path))
expect(editor.getGrammar().name).toBe 'Plain Text'
syntax.setGrammarOverrideForPath(path, 'source.js')
editor.reloadGrammar()
@@ -2170,7 +2205,7 @@ describe "Editor", ->
expect(editor.getGrammar().name).toBe 'JavaScript'
it "emits an editor:grammar-changed event when updated", ->
editor.edit(project.buildEditSession(path))
editor.edit(project.open(path))
eventHandler = jasmine.createSpy('eventHandler')
editor.on('editor:grammar-changed', eventHandler)
@@ -2584,7 +2619,6 @@ describe "Editor", ->
editor.getPane().destroyActiveItem()
expect(willBeRemovedHandler).toHaveBeenCalled()
describe "when setInvisibles is toggled (regression)", ->
it "renders inserted newlines properly", ->
editor.setShowInvisibles(true)
+1 -1
Ver Arquivo
@@ -214,7 +214,7 @@ describe "Git", ->
expect(repo.isStatusNew(statuses[newPath])).toBeTruthy()
expect(repo.isStatusModified(statuses[modifiedPath])).toBeTruthy()
it "only starts a single web worker at a time and schedules a restart if one is already running", =>
it "only starts a single task at a time and schedules a restart if one is already running", =>
fsUtils.write(modifiedPath, 'making this path modified')
statusHandler = jasmine.createSpy('statusHandler')
repo.on 'statuses-changed', statusHandler
+3 -3
Ver Arquivo
@@ -11,7 +11,7 @@ describe "LanguageMode", ->
describe "javascript", ->
beforeEach ->
atom.activatePackage('javascript.tmbundle', sync: true)
editSession = project.buildEditSession('sample.js', autoIndent: false)
editSession = project.open('sample.js', autoIndent: false)
{ buffer, languageMode } = editSession
describe ".toggleLineCommentsForBufferRows(start, end)", ->
@@ -53,7 +53,7 @@ describe "LanguageMode", ->
describe "coffeescript", ->
beforeEach ->
atom.activatePackage('coffee-script-tmbundle', sync: true)
editSession = project.buildEditSession('coffee.coffee', autoIndent: false)
editSession = project.open('coffee.coffee', autoIndent: false)
{ buffer, languageMode } = editSession
describe ".toggleLineCommentsForBufferRows(start, end)", ->
@@ -89,7 +89,7 @@ describe "LanguageMode", ->
describe "css", ->
beforeEach ->
atom.activatePackage('css.tmbundle', sync: true)
editSession = project.buildEditSession('css.css', autoIndent: false)
editSession = project.open('css.css', autoIndent: false)
{ buffer, languageMode } = editSession
describe ".toggleLineCommentsForBufferRows(start, end)", ->
+3 -2
Ver Arquivo
@@ -10,8 +10,8 @@ describe "Pane", ->
container = new PaneContainer
view1 = $$ -> @div id: 'view-1', tabindex: -1, 'View 1'
view2 = $$ -> @div id: 'view-2', tabindex: -1, 'View 2'
editSession1 = project.buildEditSession('sample.js')
editSession2 = project.buildEditSession('sample.txt')
editSession1 = project.open('sample.js')
editSession2 = project.open('sample.txt')
pane = new Pane(view1, editSession1, view2, editSession2)
container.append(pane)
@@ -303,6 +303,7 @@ describe "Pane", ->
describe "when the current item has no save method", ->
it "does nothing", ->
pane.activeItem.getUri = -> 'you are eye'
expect(pane.activeItem.save).toBeUndefined()
pane.trigger 'core:save'
+58 -39
Ver Arquivo
@@ -9,8 +9,8 @@ describe "Project", ->
describe "when an edit session is destroyed", ->
it "removes edit session and calls destroy on buffer (if buffer is not referenced by other edit sessions)", ->
editSession = project.buildEditSession("a")
anotherEditSession = project.buildEditSession("a")
editSession = project.open("a")
anotherEditSession = project.open("a")
expect(project.editSessions.length).toBe 2
expect(editSession.buffer).toBe anotherEditSession.buffer
@@ -26,13 +26,13 @@ describe "Project", ->
path = project.resolve('a')
project.setPath(undefined)
expect(project.getPath()).toBeUndefined()
editSession = project.buildEditSession()
editSession = project.open()
editSession.saveAs('/tmp/atom-test-save-sets-project-path')
expect(project.getPath()).toBe '/tmp'
fsUtils.remove('/tmp/atom-test-save-sets-project-path')
describe ".buildEditSession(path)", ->
[absolutePath, newBufferHandler, newEditSessionHandler] = []
describe ".open(path)", ->
[fooOpener, barOpener, absolutePath, newBufferHandler, newEditSessionHandler] = []
beforeEach ->
absolutePath = fsUtils.resolveOnLoadPath('fixtures/dir/a')
newBufferHandler = jasmine.createSpy('newBufferHandler')
@@ -40,35 +40,50 @@ describe "Project", ->
newEditSessionHandler = jasmine.createSpy('newEditSessionHandler')
project.on 'edit-session-created', newEditSessionHandler
describe "when given an absolute path that hasn't been opened previously", ->
it "returns a new edit session for the given path and emits 'buffer-created' and 'edit-session-created' events", ->
editSession = project.buildEditSession(absolutePath)
expect(editSession.buffer.getPath()).toBe absolutePath
expect(newBufferHandler).toHaveBeenCalledWith editSession.buffer
expect(newEditSessionHandler).toHaveBeenCalledWith editSession
fooOpener = (path, options) -> { foo: path, options } if path?.match(/\.foo/)
barOpener = (path) -> { bar: path } if path?.match(/^bar:\/\//)
Project.registerOpener(fooOpener)
Project.registerOpener(barOpener)
describe "when given a relative path that hasn't been opened previously", ->
it "returns a new edit session for the given path (relative to the project root) and emits 'buffer-created' and 'edit-session-created' events", ->
editSession = project.buildEditSession('a')
expect(editSession.buffer.getPath()).toBe absolutePath
expect(newBufferHandler).toHaveBeenCalledWith editSession.buffer
expect(newEditSessionHandler).toHaveBeenCalledWith editSession
afterEach ->
Project.unregisterOpener(fooOpener)
Project.unregisterOpener(barOpener)
describe "when passed the path to a buffer that has already been opened", ->
it "returns a new edit session containing previously opened buffer and emits a 'edit-session-created' event", ->
editSession = project.buildEditSession(absolutePath)
newBufferHandler.reset()
expect(project.buildEditSession(absolutePath).buffer).toBe editSession.buffer
expect(project.buildEditSession('a').buffer).toBe editSession.buffer
expect(newBufferHandler).not.toHaveBeenCalled()
expect(newEditSessionHandler).toHaveBeenCalledWith editSession
describe "when passed a path that doesn't match a custom opener", ->
describe "when given an absolute path that hasn't been opened previously", ->
it "returns a new edit session for the given path and emits 'buffer-created' and 'edit-session-created' events", ->
editSession = project.open(absolutePath)
expect(editSession.buffer.getPath()).toBe absolutePath
expect(newBufferHandler).toHaveBeenCalledWith editSession.buffer
expect(newEditSessionHandler).toHaveBeenCalledWith editSession
describe "when not passed a path", ->
it "returns a new edit session and emits 'buffer-created' and 'edit-session-created' events", ->
editSession = project.buildEditSession()
expect(editSession.buffer.getPath()).toBeUndefined()
expect(newBufferHandler).toHaveBeenCalledWith(editSession.buffer)
expect(newEditSessionHandler).toHaveBeenCalledWith editSession
describe "when given a relative path that hasn't been opened previously", ->
it "returns a new edit session for the given path (relative to the project root) and emits 'buffer-created' and 'edit-session-created' events", ->
editSession = project.open('a')
expect(editSession.buffer.getPath()).toBe absolutePath
expect(newBufferHandler).toHaveBeenCalledWith editSession.buffer
expect(newEditSessionHandler).toHaveBeenCalledWith editSession
describe "when passed the path to a buffer that has already been opened", ->
it "returns a new edit session containing previously opened buffer and emits a 'edit-session-created' event", ->
editSession = project.open(absolutePath)
newBufferHandler.reset()
expect(project.open(absolutePath).buffer).toBe editSession.buffer
expect(project.open('a').buffer).toBe editSession.buffer
expect(newBufferHandler).not.toHaveBeenCalled()
expect(newEditSessionHandler).toHaveBeenCalledWith editSession
describe "when not passed a path", ->
it "returns a new edit session and emits 'buffer-created' and 'edit-session-created' events", ->
editSession = project.open()
expect(editSession.buffer.getPath()).toBeUndefined()
expect(newBufferHandler).toHaveBeenCalledWith(editSession.buffer)
expect(newEditSessionHandler).toHaveBeenCalledWith editSession
describe "when passed a path that matches a custom opener", ->
it "returns the resource returned by the custom opener", ->
expect(project.open("a.foo", hey: "there")).toEqual { foo: "a.foo", options: {hey: "there"} }
expect(project.open("bar://baz")).toEqual { bar: "bar://baz" }
describe ".bufferForPath(path)", ->
describe "when opening a previously opened path", ->
@@ -84,12 +99,17 @@ describe "Project", ->
buffer = project.bufferForPath("a").retain().release()
expect(project.bufferForPath("a").retain().release()).not.toBe buffer
describe ".resolve(path)", ->
it "returns an absolute path based on the project's root", ->
absolutePath = fsUtils.resolveOnLoadPath('fixtures/dir/a')
expect(project.resolve('a')).toBe absolutePath
expect(project.resolve(absolutePath + '/../a')).toBe absolutePath
expect(project.resolve('a/../a')).toBe absolutePath
describe ".resolve(uri)", ->
describe "when passed an absolute or relative path", ->
it "returns an absolute path based on the project's root", ->
absolutePath = fsUtils.resolveOnLoadPath('fixtures/dir/a')
expect(project.resolve('a')).toBe absolutePath
expect(project.resolve(absolutePath + '/../a')).toBe absolutePath
expect(project.resolve('a/../a')).toBe absolutePath
describe "when passed a uri with a scheme", ->
it "does not modify uris that begin with a scheme", ->
expect(project.resolve('http://zombo.com')).toBe 'http://zombo.com'
describe ".relativize(path)", ->
it "returns an relative path based on the project's root", ->
@@ -160,8 +180,7 @@ describe "Project", ->
it "ignores ignored.txt file", ->
paths = null
config.get("core.ignoredNames").push("ignored.txt")
config.update()
config.pushAtKeyPath("core.ignoredNames", "ignored.txt")
waitsForPromise ->
project.getFilePaths().done (foundPaths) -> paths = foundPaths
+8
Ver Arquivo
@@ -33,6 +33,14 @@ describe "Range", ->
expect(new Range([2, 1], [3, 10]).union(new Range([2, 5], [3, 1]))).toEqual [[2, 1], [3, 10]]
expect(new Range([2, 5], [3, 1]).union(new Range([2, 1], [3, 10]))).toEqual [[2, 1], [3, 10]]
describe ".compare(otherRange)", ->
it "sorts earlier ranges first, and larger ranges first if both ranges start at the same place", ->
expect(new Range([1, 1], [2, 10]).compare(new Range([2, 1], [3, 10]))).toBe -1
expect(new Range([2, 1], [3, 10]).compare(new Range([1, 1], [2, 10]))).toBe 1
expect(new Range([1, 1], [3, 10]).compare(new Range([1, 1], [2, 10]))).toBe -1
expect(new Range([1, 1], [2, 10]).compare(new Range([1, 1], [3, 10]))).toBe 1
expect(new Range([1, 1], [3, 10]).compare(new Range([1, 1], [3, 10]))).toBe 0
describe ".translate(startPoint, endPoint)", ->
it "returns a range translates by the specified start and end points", ->
expect(new Range([1, 1], [2, 10]).translate([1])).toEqual [[2, 1], [3, 10]]
+3 -3
Ver Arquivo
@@ -46,10 +46,10 @@ describe "RootView", ->
pane2 = pane1.splitRight()
pane3 = pane2.splitRight()
pane4 = pane2.splitDown()
pane2.showItem(project.buildEditSession('b'))
pane3.showItem(project.buildEditSession('../sample.js'))
pane2.showItem(project.open('b'))
pane3.showItem(project.open('../sample.js'))
pane3.activeItem.setCursorScreenPosition([2, 4])
pane4.showItem(project.buildEditSession('../sample.txt'))
pane4.showItem(project.open('../sample.txt'))
pane4.activeItem.setCursorScreenPosition([0, 2])
pane2.focus()
+254
Ver Arquivo
@@ -0,0 +1,254 @@
RowMap = require 'row-map'
describe "RowMap", ->
map = null
beforeEach ->
map = new RowMap
describe "when no mappings have been recorded", ->
it "maps buffer rows to screen rows 1:1", ->
expect(map.screenRowRangeForBufferRow(0)).toEqual [0, 1]
expect(map.screenRowRangeForBufferRow(100)).toEqual [100, 101]
describe ".mapBufferRowRange(startBufferRow, endBufferRow, screenRows)", ->
describe "when mapping to a single screen row (like a visible fold)", ->
beforeEach ->
map.mapBufferRowRange(5, 10, 1)
map.mapBufferRowRange(15, 20, 1)
map.mapBufferRowRange(25, 30, 1)
it "accounts for the mapping when translating buffer rows to screen row ranges", ->
expect(map.screenRowRangeForBufferRow(0)).toEqual [0, 1]
expect(map.screenRowRangeForBufferRow(4)).toEqual [4, 5]
expect(map.screenRowRangeForBufferRow(5)).toEqual [5, 6]
expect(map.screenRowRangeForBufferRow(9)).toEqual [5, 6]
expect(map.screenRowRangeForBufferRow(10)).toEqual [6, 7]
expect(map.screenRowRangeForBufferRow(14)).toEqual [10, 11]
expect(map.screenRowRangeForBufferRow(15)).toEqual [11, 12]
expect(map.screenRowRangeForBufferRow(19)).toEqual [11, 12]
expect(map.screenRowRangeForBufferRow(20)).toEqual [12, 13]
expect(map.screenRowRangeForBufferRow(24)).toEqual [16, 17]
expect(map.screenRowRangeForBufferRow(25)).toEqual [17, 18]
expect(map.screenRowRangeForBufferRow(29)).toEqual [17, 18]
expect(map.screenRowRangeForBufferRow(30)).toEqual [18, 19]
it "accounts for the mapping when translating screen rows to buffer row ranges", ->
expect(map.bufferRowRangeForScreenRow(0)).toEqual [0, 1]
expect(map.bufferRowRangeForScreenRow(4)).toEqual [4, 5]
expect(map.bufferRowRangeForScreenRow(5)).toEqual [5, 10]
expect(map.bufferRowRangeForScreenRow(6)).toEqual [10, 11]
expect(map.bufferRowRangeForScreenRow(10)).toEqual [14, 15]
expect(map.bufferRowRangeForScreenRow(11)).toEqual [15, 20]
expect(map.bufferRowRangeForScreenRow(12)).toEqual [20, 21]
expect(map.bufferRowRangeForScreenRow(16)).toEqual [24, 25]
expect(map.bufferRowRangeForScreenRow(17)).toEqual [25, 30]
expect(map.bufferRowRangeForScreenRow(18)).toEqual [30, 31]
describe "when mapping to zero screen rows (like an invisible fold)", ->
beforeEach ->
map.mapBufferRowRange(5, 10, 0)
map.mapBufferRowRange(15, 20, 0)
map.mapBufferRowRange(25, 30, 0)
it "accounts for the mapping when translating buffer rows to screen row ranges", ->
expect(map.screenRowRangeForBufferRow(0)).toEqual [0, 1]
expect(map.screenRowRangeForBufferRow(4)).toEqual [4, 5]
expect(map.screenRowRangeForBufferRow(5)).toEqual [5, 5]
expect(map.screenRowRangeForBufferRow(9)).toEqual [5, 5]
expect(map.screenRowRangeForBufferRow(10)).toEqual [5, 6]
expect(map.screenRowRangeForBufferRow(14)).toEqual [9, 10]
expect(map.screenRowRangeForBufferRow(15)).toEqual [10, 10]
expect(map.screenRowRangeForBufferRow(19)).toEqual [10, 10]
expect(map.screenRowRangeForBufferRow(20)).toEqual [10, 11]
expect(map.screenRowRangeForBufferRow(24)).toEqual [14, 15]
expect(map.screenRowRangeForBufferRow(25)).toEqual [15, 15]
expect(map.screenRowRangeForBufferRow(29)).toEqual [15, 15]
expect(map.screenRowRangeForBufferRow(30)).toEqual [15, 16]
it "accounts for the mapping when translating screen rows to buffer row ranges", ->
expect(map.bufferRowRangeForScreenRow(0)).toEqual [0, 1]
expect(map.bufferRowRangeForScreenRow(4)).toEqual [4, 5]
expect(map.bufferRowRangeForScreenRow(5)).toEqual [10, 11]
expect(map.bufferRowRangeForScreenRow(9)).toEqual [14, 15]
expect(map.bufferRowRangeForScreenRow(10)).toEqual [20, 21]
expect(map.bufferRowRangeForScreenRow(14)).toEqual [24, 25]
expect(map.bufferRowRangeForScreenRow(15)).toEqual [30, 31]
describe "when mapping a single buffer row to multiple screen rows (like a wrapped line)", ->
beforeEach ->
map.mapBufferRowRange(5, 6, 3)
map.mapBufferRowRange(10, 11, 2)
map.mapBufferRowRange(20, 21, 5)
it "accounts for the mapping when translating buffer rows to screen row ranges", ->
expect(map.screenRowRangeForBufferRow(0)).toEqual [0, 1]
expect(map.screenRowRangeForBufferRow(4)).toEqual [4, 5]
expect(map.screenRowRangeForBufferRow(5)).toEqual [5, 8]
expect(map.screenRowRangeForBufferRow(6)).toEqual [8, 9]
expect(map.screenRowRangeForBufferRow(9)).toEqual [11, 12]
expect(map.screenRowRangeForBufferRow(10)).toEqual [12, 14]
expect(map.screenRowRangeForBufferRow(11)).toEqual [14, 15]
expect(map.screenRowRangeForBufferRow(19)).toEqual [22, 23]
expect(map.screenRowRangeForBufferRow(20)).toEqual [23, 28]
expect(map.screenRowRangeForBufferRow(21)).toEqual [28, 29]
it "accounts for the mapping when translating screen rows to buffer row ranges", ->
expect(map.bufferRowRangeForScreenRow(0)).toEqual [0, 1]
expect(map.bufferRowRangeForScreenRow(4)).toEqual [4, 5]
expect(map.bufferRowRangeForScreenRow(5)).toEqual [5, 6]
expect(map.bufferRowRangeForScreenRow(7)).toEqual [5, 6]
expect(map.bufferRowRangeForScreenRow(8)).toEqual [6, 7]
expect(map.bufferRowRangeForScreenRow(11)).toEqual [9, 10]
expect(map.bufferRowRangeForScreenRow(12)).toEqual [10, 11]
expect(map.bufferRowRangeForScreenRow(13)).toEqual [10, 11]
expect(map.bufferRowRangeForScreenRow(14)).toEqual [11, 12]
expect(map.bufferRowRangeForScreenRow(22)).toEqual [19, 20]
expect(map.bufferRowRangeForScreenRow(23)).toEqual [20, 21]
expect(map.bufferRowRangeForScreenRow(27)).toEqual [20, 21]
expect(map.bufferRowRangeForScreenRow(28)).toEqual [21, 22]
describe "after re-mapping a row range to a new number of screen rows", ->
beforeEach ->
map.applyScreenDelta(12, 2) # simulate adding 2 more soft wraps
map.mapBufferRowRange(10, 11, 4)
it "updates translation accordingly", ->
expect(map.screenRowRangeForBufferRow(4)).toEqual [4, 5]
expect(map.screenRowRangeForBufferRow(5)).toEqual [5, 8]
expect(map.screenRowRangeForBufferRow(6)).toEqual [8, 9]
expect(map.screenRowRangeForBufferRow(9)).toEqual [11, 12]
expect(map.screenRowRangeForBufferRow(10)).toEqual [12, 16]
expect(map.screenRowRangeForBufferRow(11)).toEqual [16, 17]
expect(map.screenRowRangeForBufferRow(19)).toEqual [24, 25]
expect(map.screenRowRangeForBufferRow(20)).toEqual [25, 30]
expect(map.screenRowRangeForBufferRow(21)).toEqual [30, 31]
describe "when the row range is inside an existing 1:1 region", ->
it "preserves the starting screen row of subsequent 1:N regions", ->
map.mapBufferRowRange(5, 10, 1)
map.mapBufferRowRange(25, 30, 1)
expect(map.bufferRowRangeForScreenRow(5)).toEqual [5, 10]
expect(map.bufferRowRangeForScreenRow(21)).toEqual [25, 30]
map.mapBufferRowRange(15, 20, 1)
expect(map.bufferRowRangeForScreenRow(11)).toEqual [15, 20]
expect(map.bufferRowRangeForScreenRow(5)).toEqual [5, 10]
expect(map.bufferRowRangeForScreenRow(21)).toEqual [25, 30]
describe "when the row range surrounds existing regions", ->
it "replaces the regions inside the given buffer row range with a single region", ->
map.mapBufferRowRange(5, 10, 1) # inner fold 1
map.mapBufferRowRange(11, 13, 1) # inner fold 2
map.mapBufferRowRange(15, 20, 1) # inner fold 3
map.mapBufferRowRange(22, 27, 1) # following fold
map.mapBufferRowRange(5, 20, 1)
expect(map.bufferRowRangeForScreenRow(5)).toEqual [5, 20]
expect(map.bufferRowRangeForScreenRow(6)).toEqual [20, 21]
expect(map.bufferRowRangeForScreenRow(7)).toEqual [21, 22]
expect(map.bufferRowRangeForScreenRow(8)).toEqual [22, 27]
describe "when the row range straddles existing regions", ->
it "splits the straddled regions and places the new region between them", ->
# filler region 0
map.mapBufferRowRange(4, 7, 1) # region 1
# filler region 2
map.mapBufferRowRange(13, 15, 1) # region 3
# create region straddling region 0 and region 2
map.mapBufferRowRange(2, 10, 1)
expect(map.regions[0]).toEqual(bufferRows: 2, screenRows: 2)
expect(map.regions[1]).toEqual(bufferRows: 8, screenRows: 1)
expect(map.regions[2]).toEqual(bufferRows: 3, screenRows: 8)
expect(map.regions[3]).toEqual(bufferRows: 2, screenRows: 1)
it "merges adjacent isomorphic mappings", ->
map.mapBufferRowRange(2, 4, 1)
map.mapBufferRowRange(4, 5, 2)
map.mapBufferRowRange(1, 4, 3)
expect(map.regions).toEqual [{bufferRows: 5, screenRows: 5}]
describe ".applyBufferDelta(startBufferRow, delta)", ->
describe "when applying a positive delta", ->
it "expands the region containing the given start row by the given delta", ->
map.mapBufferRowRange(4, 8, 1)
map.applyBufferDelta(5, 4)
expect(map.regions[0]).toEqual(bufferRows: 4, screenRows: 4)
expect(map.regions[1]).toEqual(bufferRows: 8, screenRows: 1)
describe "when applying a negative delta", ->
it "shrinks regions starting at the start row until the entire delta is consumed", ->
map.mapBufferRowRange(4, 8, 1)
map.mapBufferRowRange(10, 14, 1)
map.applyBufferDelta(3, -6)
expect(map.regions[0]).toEqual(bufferRows: 3, screenRows: 4)
expect(map.regions[1]).toEqual(bufferRows: 0, screenRows: 1)
expect(map.regions[2]).toEqual(bufferRows: 1, screenRows: 2)
expect(map.regions[3]).toEqual(bufferRows: 4, screenRows: 1)
describe ".applyScreenDelta(startScreenRow, delta)", ->
describe "when applying a positive delta", ->
it "can enlarge the screen side of existing regions", ->
map.mapBufferRowRange(5, 6, 3) # wrapped line
map.applyScreenDelta(5, 2) # wrap it twice more
expect(map.screenRowRangeForBufferRow(5)).toEqual [5, 10]
describe "when applying a negative delta", ->
it "can collapse the screen side of multiple regions to 0 until the entire delta has been applied", ->
map.mapBufferRowRange(5, 10, 1) # inner fold 1
map.mapBufferRowRange(11, 13, 1) # inner fold 2
map.mapBufferRowRange(15, 20, 1) # inner fold 3
map.mapBufferRowRange(22, 27, 1) # following fold
map.applyScreenDelta(6, -5)
expect(map.screenRowRangeForBufferRow(5)).toEqual [5, 6]
expect(map.screenRowRangeForBufferRow(9)).toEqual [5, 6]
expect(map.screenRowRangeForBufferRow(10)).toEqual [6, 6]
expect(map.screenRowRangeForBufferRow(19)).toEqual [6, 6]
expect(map.screenRowRangeForBufferRow(22)).toEqual [8, 9]
expect(map.screenRowRangeForBufferRow(26)).toEqual [8, 9]
it "starts collapsing the first region at the start row, not before", ->
map.mapBufferRowRange(5, 6, 4)
map.mapBufferRowRange(11, 13, 1)
map.applyScreenDelta(7, -5)
expect(map.regions[0]).toEqual(bufferRows: 5, screenRows: 5)
expect(map.regions[1]).toEqual(bufferRows: 1, screenRows: 2)
expect(map.regions[2]).toEqual(bufferRows: 5, screenRows: 2)
it "does not throw an exception when applying a delta beyond the last region", ->
map.mapBufferRowRange(5, 10, 1) # inner fold 1
map.applyScreenDelta(15, 10)
+1 -1
Ver Arquivo
@@ -71,4 +71,4 @@ describe "Selection", ->
it "destroys its marker", ->
selection.setBufferRange([[2, 0], [2, 10]])
selection.destroy()
expect(editSession.getMarkerBufferRange(selection.marker)).toBeUndefined()
expect(selection.marker.isDestroyed()).toBeTruthy()
+167 -115
Ver Arquivo
@@ -3,7 +3,7 @@ Buffer = require 'text-buffer'
fsUtils = require 'fs-utils'
_ = require 'underscore'
describe 'Buffer', ->
describe 'TextBuffer', ->
[filePath, fileContents, buffer] = []
beforeEach ->
@@ -781,24 +781,33 @@ describe 'Buffer', ->
expect(buffer.positionForCharacterIndex(20)).toEqual [3, 0]
describe "markers", ->
markerCreatedHandler = null
beforeEach ->
buffer.on('marker-created', markerCreatedHandler = jasmine.createSpy("markerCreatedHandler"))
describe "marker creation", ->
it "allows markers to be created with ranges and positions", ->
marker1 = buffer.markRange([[4, 20], [4, 23]])
expect(buffer.getMarkerRange(marker1)).toEqual [[4, 20], [4, 23]]
expect(buffer.getMarkerPosition(marker1)).toEqual [4, 23]
expect(buffer.getMarkerTailPosition(marker1)).toEqual [4, 20]
expect(marker1.getRange()).toEqual [[4, 20], [4, 23]]
expect(marker1.getHeadPosition()).toEqual [4, 23]
expect(marker1.getTailPosition()).toEqual [4, 20]
marker2 = buffer.markPosition([4, 20])
expect(buffer.getMarkerRange(marker2)).toEqual [[4, 20], [4, 20]]
expect(buffer.getMarkerPosition(marker2)).toEqual [4, 20]
expect(buffer.getMarkerTailPosition(marker2)).toEqual [4, 20]
expect(marker2.getRange()).toEqual [[4, 20], [4, 20]]
expect(marker2.getHeadPosition()).toEqual [4, 20]
expect(marker2.getTailPosition()).toEqual [4, 20]
it "allows markers to be created in a reversed orientation", ->
marker = buffer.markRange([[4, 20], [4, 23]], reverse: true)
expect(buffer.isMarkerReversed(marker)).toBeTruthy()
expect(buffer.getMarkerRange(marker)).toEqual [[4, 20], [4, 23]]
expect(buffer.getMarkerHeadPosition(marker)).toEqual [4, 20]
expect(buffer.getMarkerTailPosition(marker)).toEqual [4, 23]
expect(marker.isReversed()).toBeTruthy()
expect(marker.getRange()).toEqual [[4, 20], [4, 23]]
expect(marker.getHeadPosition()).toEqual [4, 20]
expect(marker.getTailPosition()).toEqual [4, 23]
it "emits the 'marker-created' event when markers are created", ->
marker = buffer.markRange([[4, 20], [4, 23]])
expect(markerCreatedHandler).toHaveBeenCalledWith(marker)
describe "marker manipulation", ->
marker = null
@@ -806,46 +815,45 @@ describe 'Buffer', ->
marker = buffer.markRange([[4, 20], [4, 23]])
it "allows a marker's head and tail positions to be changed", ->
buffer.setMarkerHeadPosition(marker, [5, 3])
expect(buffer.getMarkerRange(marker)).toEqual [[4, 20], [5, 3]]
marker.setHeadPosition([5, 3])
expect(marker.getRange()).toEqual [[4, 20], [5, 3]]
buffer.setMarkerTailPosition(marker, [6, 3])
expect(buffer.getMarkerRange(marker)).toEqual [[5, 3], [6, 3]]
expect(buffer.isMarkerReversed(marker)).toBeTruthy()
marker.setTailPosition([6, 3])
expect(marker.getRange()).toEqual [[5, 3], [6, 3]]
expect(marker.isReversed()).toBeTruthy()
it "clips head and tail positions to ensure they are in bounds", ->
buffer.setMarkerHeadPosition(marker, [-100, -5])
expect(buffer.getMarkerRange(marker)).toEqual([[0, 0], [4, 20]])
buffer.setMarkerTailPosition(marker, [Infinity, Infinity])
expect(buffer.getMarkerRange(marker)).toEqual([[0, 0], [12, 2]])
marker.setHeadPosition([-100, -5])
expect(marker.getRange()).toEqual([[0, 0], [4, 20]])
marker.setTailPosition([Infinity, Infinity])
expect(marker.getRange()).toEqual([[0, 0], [12, 2]])
it "allows a marker's tail to be placed and cleared", ->
buffer.clearMarkerTail(marker)
expect(buffer.getMarkerRange(marker)).toEqual [[4, 23], [4, 23]]
buffer.placeMarkerTail(marker)
buffer.setMarkerHeadPosition(marker, [2, 0])
expect(buffer.getMarkerRange(marker)).toEqual [[2, 0], [4, 23]]
expect(buffer.isMarkerReversed(marker)).toBeTruthy()
marker.clearTail()
expect(marker.getRange()).toEqual [[4, 23], [4, 23]]
marker.placeTail()
marker.setHeadPosition([2, 0])
expect(marker.getRange()).toEqual [[2, 0], [4, 23]]
expect(marker.isReversed()).toBeTruthy()
it "returns whether the position changed", ->
expect(buffer.setMarkerHeadPosition(marker, [5, 3])).toBeTruthy()
expect(buffer.setMarkerHeadPosition(marker, [5, 3])).toBeFalsy()
expect(marker.setHeadPosition([5, 3])).toBeTruthy()
expect(marker.setHeadPosition([5, 3])).toBeFalsy()
expect(buffer.setMarkerTailPosition(marker, [6, 3])).toBeTruthy()
expect(buffer.setMarkerTailPosition(marker, [6, 3])).toBeFalsy()
expect(marker.setTailPosition([6, 3])).toBeTruthy()
expect(marker.setTailPosition([6, 3])).toBeFalsy()
describe ".observeMarker(marker, callback)", ->
[observeHandler, marker, subscription] = []
describe "change events", ->
[changedHandler, marker] = []
beforeEach ->
observeHandler = jasmine.createSpy("observeHandler")
marker = buffer.markRange([[4, 20], [4, 23]])
subscription = buffer.observeMarker(marker, observeHandler)
marker.on 'changed', changedHandler = jasmine.createSpy("changedHandler")
it "calls the callback when the marker's head position changes", ->
buffer.setMarkerHeadPosition(marker, [6, 2])
expect(observeHandler).toHaveBeenCalled()
expect(observeHandler.argsForCall[0][0]).toEqual {
it "triggers 'changed' events when the marker's head position changes", ->
marker.setHeadPosition([6, 2])
expect(changedHandler).toHaveBeenCalled()
expect(changedHandler.argsForCall[0][0]).toEqual {
oldHeadPosition: [4, 23]
newHeadPosition: [6, 2]
oldTailPosition: [4, 20]
@@ -853,10 +861,10 @@ describe 'Buffer', ->
bufferChanged: false
valid: true
}
observeHandler.reset()
changedHandler.reset()
buffer.insert([6, 0], '...')
expect(observeHandler.argsForCall[0][0]).toEqual {
expect(changedHandler.argsForCall[0][0]).toEqual {
oldTailPosition: [4, 20]
newTailPosition: [4, 20]
oldHeadPosition: [6, 2]
@@ -866,9 +874,9 @@ describe 'Buffer', ->
}
it "calls the given callback when the marker's tail position changes", ->
buffer.setMarkerTailPosition(marker, [6, 2])
expect(observeHandler).toHaveBeenCalled()
expect(observeHandler.argsForCall[0][0]).toEqual {
marker.setTailPosition([6, 2])
expect(changedHandler).toHaveBeenCalled()
expect(changedHandler.argsForCall[0][0]).toEqual {
oldHeadPosition: [4, 23]
newHeadPosition: [4, 23]
oldTailPosition: [4, 20]
@@ -876,11 +884,11 @@ describe 'Buffer', ->
bufferChanged: false
valid: true
}
observeHandler.reset()
changedHandler.reset()
buffer.insert([6, 0], '...')
expect(observeHandler.argsForCall[0][0]).toEqual {
expect(changedHandler.argsForCall[0][0]).toEqual {
oldHeadPosition: [4, 23]
newHeadPosition: [4, 23]
oldTailPosition: [6, 2]
@@ -889,10 +897,10 @@ describe 'Buffer', ->
valid: true
}
it "calls the callback when the selection's tail is cleared", ->
buffer.clearMarkerTail(marker)
expect(observeHandler).toHaveBeenCalled()
expect(observeHandler.argsForCall[0][0]).toEqual {
it "triggers 'changed' events when the selection's tail is cleared", ->
marker.clearTail()
expect(changedHandler).toHaveBeenCalled()
expect(changedHandler.argsForCall[0][0]).toEqual {
oldHeadPosition: [4, 23]
newHeadPosition: [4, 23]
oldTailPosition: [4, 20]
@@ -901,10 +909,10 @@ describe 'Buffer', ->
valid: true
}
it "only calls the callback once when both the marker's head and tail positions change due to the same operation", ->
it "only triggers 'changed' events once when both the marker's head and tail positions change due to the same operation", ->
buffer.insert([4, 0], '...')
expect(observeHandler.callCount).toBe 1
expect(observeHandler.argsForCall[0][0]).toEqual {
expect(changedHandler.callCount).toBe 1
expect(changedHandler.argsForCall[0][0]).toEqual {
oldTailPosition: [4, 20]
newTailPosition: [4, 23]
oldHeadPosition: [4, 23]
@@ -912,11 +920,11 @@ describe 'Buffer', ->
bufferChanged: true
valid: true
}
observeHandler.reset()
changedHandler.reset()
buffer.setMarkerRange(marker, [[0, 0], [1, 1]])
expect(observeHandler.callCount).toBe 1
expect(observeHandler.argsForCall[0][0]).toEqual {
marker.setRange([[0, 0], [1, 1]])
expect(changedHandler.callCount).toBe 1
expect(changedHandler.argsForCall[0][0]).toEqual {
oldTailPosition: [4, 23]
newTailPosition: [0, 0]
oldHeadPosition: [4, 26]
@@ -925,10 +933,10 @@ describe 'Buffer', ->
valid: true
}
it "calls the callback with the valid flag set to false when the marker is invalidated", ->
it "triggers 'changed' events with the valid flag set to false when the marker is invalidated", ->
buffer.deleteRow(4)
expect(observeHandler.callCount).toBe 1
expect(observeHandler.argsForCall[0][0]).toEqual {
expect(changedHandler.callCount).toBe 1
expect(changedHandler.argsForCall[0][0]).toEqual {
oldTailPosition: [4, 20]
newTailPosition: [4, 20]
oldHeadPosition: [4, 23]
@@ -937,10 +945,10 @@ describe 'Buffer', ->
valid: false
}
observeHandler.reset()
changedHandler.reset()
buffer.undo()
expect(observeHandler.callCount).toBe 1
expect(observeHandler.argsForCall[0][0]).toEqual {
expect(changedHandler.callCount).toBe 1
expect(changedHandler.argsForCall[0][0]).toEqual {
oldTailPosition: [4, 20]
newTailPosition: [4, 20]
oldHeadPosition: [4, 23]
@@ -949,10 +957,22 @@ describe 'Buffer', ->
valid: true
}
it "allows the observation subscription to be cancelled", ->
subscription.cancel()
buffer.setMarkerHeadPosition(marker, [6, 2])
expect(observeHandler).not.toHaveBeenCalled()
describe ".findMarkers(attributes)", ->
[marker1, marker2, marker3, marker4] = []
beforeEach ->
marker1 = buffer.markRange([[0, 0], [3, 0]], class: 'a')
marker2 = buffer.markRange([[0, 0], [5, 0]], class: 'a')
marker3 = buffer.markRange([[6, 0], [7, 0]], class: 'a')
marker4 = buffer.markRange([[9, 0], [10, 0]], class: 'b')
it "returns the markers matching the given attributes, sorted by the buffer location and size of their ranges", ->
expect(buffer.findMarkers(class: 'a')).toEqual [marker2, marker1, marker3]
it "allows the startRow and endRow to be specified", ->
expect(buffer.findMarkers(class: 'a', startRow: 0)).toEqual [marker2, marker1]
expect(buffer.findMarkers(class: 'a', startRow: 0, endRow: 3)).toEqual [marker1]
expect(buffer.findMarkers(endRow: 10)).toEqual [marker4]
describe "marker destruction", ->
marker = null
@@ -961,22 +981,27 @@ describe 'Buffer', ->
marker = buffer.markRange([[4, 20], [4, 23]])
it "allows a marker to be destroyed", ->
buffer.destroyMarker(marker)
expect(buffer.getMarkerRange(marker)).toBeUndefined()
marker.destroy()
expect(buffer.getMarker(marker.id)).toBeUndefined()
it "does not restore invalidated markers that have been destroyed", ->
buffer.delete([[4, 15], [4, 25]])
expect(buffer.getMarkerRange(marker)).toBeUndefined()
buffer.destroyMarker(marker)
expect(buffer.getMarker(marker.id)).toBeUndefined()
marker.destroy()
buffer.undo()
expect(buffer.getMarkerRange(marker)).toBeUndefined()
expect(buffer.getMarker(marker.id)).toBeUndefined()
# even "invalidationStrategy: never" markers get destroyed properly
marker2 = buffer.markRange([[4, 20], [4, 23]], invalidationStrategy: 'never')
buffer.delete([[4, 15], [4, 25]])
buffer.destroyMarker(marker2)
marker2.destroy()
buffer.undo()
expect(buffer.getMarkerRange(marker2)).toBeUndefined()
expect(buffer.getMarker(marker2.id)).toBeUndefined()
it "emits 'destroyed' on the marker when it is destroyed", ->
marker.on 'destroyed', destroyedHandler = jasmine.createSpy("destroyedHandler")
marker.destroy()
expect(destroyedHandler).toHaveBeenCalled()
describe "marker updates due to buffer changes", ->
[marker1, marker2, marker3] = []
@@ -990,149 +1015,176 @@ describe 'Buffer', ->
describe "when the change precedes the marker range", ->
it "moves the marker", ->
buffer.insert([4, 5], '...')
expect(buffer.getMarkerRange(marker1)).toEqual [[4, 23], [4, 26]]
expect(marker1.getRange()).toEqual [[4, 23], [4, 26]]
buffer.delete([[4, 5], [4, 8]])
expect(buffer.getMarkerRange(marker1)).toEqual [[4, 20], [4, 23]]
expect(marker1.getRange()).toEqual [[4, 20], [4, 23]]
buffer.insert([0, 0], '\nhi\n')
expect(buffer.getMarkerRange(marker1)).toEqual [[6, 20], [6, 23]]
expect(marker1.getRange()).toEqual [[6, 20], [6, 23]]
# undo works
buffer.undo()
expect(buffer.getMarkerRange(marker1)).toEqual [[4, 20], [4, 23]]
expect(marker1.getRange()).toEqual [[4, 20], [4, 23]]
buffer.undo()
expect(buffer.getMarkerRange(marker1)).toEqual [[4, 23], [4, 26]]
expect(marker1.getRange()).toEqual [[4, 23], [4, 26]]
it "restores the marker range exactly on undo", ->
marker = buffer.markRange([[3, 0], [3, 62]])
buffer.delete([[2, 0], [3, 0]])
expect(marker.getRange()).toEqual [[2, 0], [2, 62]]
buffer.undo()
expect(marker.getRange()).toEqual [[3, 0], [3, 62]]
describe "when the change follows the marker range", ->
it "does not move the marker", ->
buffer.insert([6, 5], '...')
expect(buffer.getMarkerRange(marker1)).toEqual [[4, 20], [4, 23]]
expect(marker1.getRange()).toEqual [[4, 20], [4, 23]]
buffer.delete([[6, 5], [6, 8]])
expect(buffer.getMarkerRange(marker1)).toEqual [[4, 20], [4, 23]]
expect(marker1.getRange()).toEqual [[4, 20], [4, 23]]
buffer.insert([10, 0], '\nhi\n')
expect(buffer.getMarkerRange(marker1)).toEqual [[4, 20], [4, 23]]
expect(marker1.getRange()).toEqual [[4, 20], [4, 23]]
describe "when the change is an insertion at the start of the marker range", ->
it "does not move the start point, but does move the end point", ->
buffer.insert([4, 20], '...')
expect(buffer.getMarkerRange(marker1)).toEqual [[4, 20], [4, 26]]
expect(marker1.getRange()).toEqual [[4, 20], [4, 26]]
describe "when the invalidation strategy is 'between'", ->
it "invalidates the marker", ->
buffer.insert([4, 20], '...')
expect(buffer.getMarkerRange(marker3)).toBeUndefined()
expect(marker3.isValid()).toBeFalsy()
describe "when the change is an insertion at the end of the marker range", ->
it "moves the end point", ->
buffer.insert([4, 23], '...')
expect(buffer.getMarkerRange(marker1)).toEqual [[4, 20], [4, 26]]
expect(marker1.getRange()).toEqual [[4, 20], [4, 26]]
describe "when the invalidation strategy is 'between'", ->
it "invalidates the marker", ->
buffer.insert([4, 23], '...')
expect(buffer.getMarkerRange(marker3)).toBeUndefined()
expect(marker3.isValid()).toBeFalsy()
describe "when the change surrounds the marker range", ->
describe "when the marker's invalidation strategy is 'contains' (the default)", ->
it "invalidates the marker", ->
buffer.delete([[4, 15], [4, 25]])
expect(buffer.getMarkerRange(marker1)).toBeUndefined()
expect(marker1.isValid()).toBeFalsy()
buffer.undo()
expect(buffer.getMarkerRange(marker1)).toEqual [[4, 20], [4, 23]]
expect(marker1.isValid()).toBeTruthy()
expect(marker1.getRange()).toEqual [[4, 20], [4, 23]]
describe "when the marker's invalidation strategy is 'between'", ->
it "invalidates the marker", ->
buffer.delete([[4, 15], [4, 25]])
expect(buffer.getMarkerRange(marker3)).toBeUndefined()
expect(marker3.isValid()).toBeFalsy()
buffer.undo()
expect(buffer.getMarkerRange(marker3)).toEqual [[4, 20], [4, 23]]
expect(marker3.isValid()).toBeTruthy()
expect(marker3.getRange()).toEqual [[4, 20], [4, 23]]
describe "when the marker's invalidation strategy is 'never'", ->
it "does not invalidate the marker, but sets it to an empty range at the end of the change", ->
buffer.change([[4, 15], [4, 25]], "...")
expect(buffer.getMarkerRange(marker2)).toEqual [[4, 18], [4, 18]]
expect(marker2.isValid()).toBeTruthy()
expect(marker2.getRange()).toEqual [[4, 18], [4, 18]]
buffer.undo()
expect(buffer.getMarkerRange(marker2)).toEqual [[4, 20], [4, 23]]
expect(marker2.isValid()).toBeTruthy()
expect(marker2.getRange()).toEqual [[4, 20], [4, 23]]
describe "when the change straddles the start of the marker range", ->
describe "when the marker's invalidation strategy is 'contains' (the default)", ->
it "invalidates the marker", ->
buffer.delete([[4, 15], [4, 22]])
expect(buffer.getMarkerRange(marker1)).toBeUndefined()
expect(marker1.isValid()).toBeFalsy()
buffer.undo()
expect(buffer.getMarkerRange(marker1)).toEqual [[4, 20], [4, 23]]
expect(marker1.isValid()).toBeTruthy()
expect(marker1.getRange()).toEqual [[4, 20], [4, 23]]
describe "when the marker's invalidation strategy is 'between'", ->
it "invalidates the marker", ->
buffer.delete([[4, 15], [4, 22]])
expect(buffer.getMarkerRange(marker3)).toBeUndefined()
expect(marker3.isValid()).toBeFalsy()
buffer.undo()
expect(buffer.getMarkerRange(marker3)).toEqual [[4, 20], [4, 23]]
expect(marker3.isValid()).toBeTruthy()
expect(marker3.getRange()).toEqual [[4, 20], [4, 23]]
describe "when the marker's invalidation strategy is 'never'", ->
it "moves the start of the marker range to the end of the change", ->
buffer.delete([[4, 15], [4, 22]])
expect(buffer.getMarkerRange(marker2)).toEqual [[4, 15], [4, 16]]
expect(marker2.isValid()).toBeTruthy()
expect(marker2.getRange()).toEqual [[4, 15], [4, 16]]
buffer.undo()
expect(buffer.getMarkerRange(marker1)).toEqual [[4, 20], [4, 23]]
expect(marker2.isValid()).toBeTruthy()
expect(marker2.getRange()).toEqual [[4, 20], [4, 23]]
describe "when the change straddles the end of the marker range", ->
describe "when the marker's invalidation strategy is 'contains' (the default)", ->
it "invalidates the marker", ->
buffer.delete([[4, 22], [4, 25]])
expect(buffer.getMarkerRange(marker1)).toBeUndefined()
expect(marker1.isValid()).toBeFalsy()
buffer.undo()
expect(buffer.getMarkerRange(marker1)).toEqual [[4, 20], [4, 23]]
expect(marker1.isValid()).toBeTruthy()
expect(marker1.getRange()).toEqual [[4, 20], [4, 23]]
describe "when the marker's invalidation strategy is 'between'", ->
it "invalidates the marker", ->
buffer.delete([[4, 22], [4, 25]])
expect(buffer.getMarkerRange(marker3)).toBeUndefined()
expect(marker3.isValid()).toBeFalsy()
buffer.undo()
expect(buffer.getMarkerRange(marker3)).toEqual [[4, 20], [4, 23]]
expect(marker3.isValid()).toBeTruthy()
expect(marker3.getRange()).toEqual [[4, 20], [4, 23]]
describe "when the marker's invalidation strategy is 'never'", ->
it "moves the end of the marker range to the start of the change", ->
buffer.delete([[4, 22], [4, 25]])
expect(buffer.getMarkerRange(marker2)).toEqual [[4, 20], [4, 22]]
expect(marker2.isValid()).toBeTruthy()
expect(marker2.getRange()).toEqual [[4, 20], [4, 22]]
buffer.undo()
expect(buffer.getMarkerRange(marker1)).toEqual [[4, 20], [4, 23]]
expect(marker2.isValid()).toBeTruthy()
expect(marker2.getRange()).toEqual [[4, 20], [4, 23]]
describe "when the change is between the start and the end of the marker range", ->
describe "when the marker's invalidation strategy is 'contains' (the default)", ->
it "does not invalidate the marker", ->
buffer.insert([4, 21], 'x')
expect(buffer.getMarkerRange(marker1)).toEqual [[4, 20], [4, 24]]
expect(marker1.isValid()).toBeTruthy()
expect(marker1.getRange()).toEqual [[4, 20], [4, 24]]
buffer.undo()
expect(buffer.getMarkerRange(marker1)).toEqual [[4, 20], [4, 23]]
expect(marker1.isValid()).toBeTruthy()
expect(marker1.getRange()).toEqual [[4, 20], [4, 23]]
describe "when the marker's invalidation strategy is 'between'", ->
it "invalidates the marker", ->
buffer.insert([4, 21], 'x')
expect(buffer.getMarkerRange(marker3)).toBeUndefined()
expect(marker3.isValid()).toBeFalsy()
buffer.undo()
expect(buffer.getMarkerRange(marker3)).toEqual [[4, 20], [4, 23]]
expect(marker3.isValid()).toBeTruthy()
expect(marker3.getRange()).toEqual [[4, 20], [4, 23]]
describe "when the marker's invalidation strategy is 'never'", ->
it "moves the end of the marker range to the start of the change", ->
buffer.insert([4, 21], 'x')
expect(buffer.getMarkerRange(marker2)).toEqual [[4, 20], [4, 24]]
expect(marker2.isValid()).toBeTruthy()
expect(marker2.getRange()).toEqual [[4, 20], [4, 24]]
buffer.undo()
expect(buffer.getMarkerRange(marker1)).toEqual [[4, 20], [4, 23]]
expect(marker2.isValid()).toBeTruthy()
expect(marker2.getRange()).toEqual [[4, 20], [4, 23]]
describe "when the buffer changes due to the undo or redo of a previous operation", ->
it "restores invalidated markers when undoing/redoing in the other direction", ->
buffer.change([[4, 21], [4, 24]], "foo")
expect(buffer.getMarkerRange(marker1)).toBeUndefined()
expect(marker1.isValid()).toBeFalsy()
marker3 = buffer.markRange([[4, 20], [4, 23]])
buffer.undo()
expect(buffer.getMarkerRange(marker1)).toEqual [[4, 20], [4, 23]]
expect(buffer.getMarkerRange(marker3)).toBeUndefined()
expect(marker1.isValid()).toBeTruthy()
expect(marker1.getRange()).toEqual [[4, 20], [4, 23]]
expect(marker3.isValid()).toBeFalsy()
marker4 = buffer.markRange([[4, 20], [4, 23]])
buffer.redo()
expect(buffer.getMarkerRange(marker3)).toEqual [[4, 20], [4, 23]]
expect(buffer.getMarkerRange(marker4)).toBeUndefined()
expect(marker3.isValid()).toBeTruthy()
expect(marker3.getRange()).toEqual [[4, 20], [4, 23]]
expect(marker4.isValid()).toBeFalsy()
buffer.undo()
expect(buffer.getMarkerRange(marker4)).toEqual [[4, 20], [4, 23]]
expect(marker4.isValid()).toBeTruthy()
expect(marker4.getRange()).toEqual [[4, 20], [4, 23]]
describe ".markersForPosition(position)", ->
it "returns all markers that intersect the given position", ->
+7 -7
Ver Arquivo
@@ -198,11 +198,11 @@ describe "TextMateGrammar", ->
{tokens} = grammar.tokenizeLine('%Q+matz had some #{%Q-crazy ideas-} for ruby syntax+ # damn.')
expect(tokens[0]).toEqual value: '%Q+', scopes: ["source.ruby","string.quoted.other.literal.upper.ruby","punctuation.definition.string.begin.ruby"]
expect(tokens[1]).toEqual value: 'matz had some ', scopes: ["source.ruby","string.quoted.other.literal.upper.ruby"]
expect(tokens[2]).toEqual value: '#{', scopes: ["source.ruby","string.quoted.other.literal.upper.ruby","source.ruby.embedded.source","punctuation.section.embedded.ruby"]
expect(tokens[3]).toEqual value: '%Q-', scopes: ["source.ruby","string.quoted.other.literal.upper.ruby","source.ruby.embedded.source","string.quoted.other.literal.upper.ruby","punctuation.definition.string.begin.ruby"]
expect(tokens[4]).toEqual value: 'crazy ideas', scopes: ["source.ruby","string.quoted.other.literal.upper.ruby","source.ruby.embedded.source","string.quoted.other.literal.upper.ruby"]
expect(tokens[5]).toEqual value: '-', scopes: ["source.ruby","string.quoted.other.literal.upper.ruby","source.ruby.embedded.source","string.quoted.other.literal.upper.ruby","punctuation.definition.string.end.ruby"]
expect(tokens[6]).toEqual value: '}', scopes: ["source.ruby","string.quoted.other.literal.upper.ruby","source.ruby.embedded.source","punctuation.section.embedded.ruby"]
expect(tokens[2]).toEqual value: '#{', scopes: ["source.ruby","string.quoted.other.literal.upper.ruby","meta.embedded.line.ruby","punctuation.section.embedded.begin.ruby"]
expect(tokens[3]).toEqual value: '%Q-', scopes: ["source.ruby","string.quoted.other.literal.upper.ruby","meta.embedded.line.ruby","string.quoted.other.literal.upper.ruby","punctuation.definition.string.begin.ruby"]
expect(tokens[4]).toEqual value: 'crazy ideas', scopes: ["source.ruby","string.quoted.other.literal.upper.ruby","meta.embedded.line.ruby","string.quoted.other.literal.upper.ruby"]
expect(tokens[5]).toEqual value: '-', scopes: ["source.ruby","string.quoted.other.literal.upper.ruby","meta.embedded.line.ruby","string.quoted.other.literal.upper.ruby","punctuation.definition.string.end.ruby"]
expect(tokens[6]).toEqual value: '}', scopes: ["source.ruby","string.quoted.other.literal.upper.ruby","meta.embedded.line.ruby","punctuation.section.embedded.end.ruby", "source.ruby"]
expect(tokens[7]).toEqual value: ' for ruby syntax', scopes: ["source.ruby","string.quoted.other.literal.upper.ruby"]
expect(tokens[8]).toEqual value: '+', scopes: ["source.ruby","string.quoted.other.literal.upper.ruby","punctuation.definition.string.end.ruby"]
expect(tokens[9]).toEqual value: ' ', scopes: ["source.ruby"]
@@ -448,7 +448,7 @@ describe "TextMateGrammar", ->
describe "when the grammar is added", ->
it "retokenizes existing buffers that contain tokens that match the injection selector", ->
editSession = project.buildEditSession('sample.js')
editSession = project.open('sample.js')
editSession.setText("// http://github.com")
{tokens} = editSession.lineForScreenRow(0)
@@ -463,7 +463,7 @@ describe "TextMateGrammar", ->
describe "when the grammar is updated", ->
it "retokenizes existing buffers that contain tokens that match the injection selector", ->
editSession = project.buildEditSession('sample.js')
editSession = project.open('sample.js')
editSession.setText("// SELECT * FROM OCTOCATS")
{tokens} = editSession.lineForScreenRow(0)
+14
Ver Arquivo
@@ -343,3 +343,17 @@ describe "TokenizedBuffer", ->
fullyTokenize(tokenizedBuffer)
{tokens} = tokenizedBuffer.lineForScreenRow(0)
expect(tokens[0]).toEqual value: '<', scopes: ["text.html.ruby","meta.tag.block.any.html","punctuation.definition.tag.begin.html"]
describe ".tokenForPosition(position)", ->
afterEach ->
tokenizedBuffer.destroy()
buffer.release()
it "returns the correct token (regression)", ->
buffer = project.bufferForPath('sample.js')
tokenizedBuffer = new TokenizedBuffer(buffer)
tokenizedBuffer.setVisible(true)
fullyTokenize(tokenizedBuffer)
expect(tokenizedBuffer.tokenForPosition([1,0]).scopes).toEqual ["source.js"]
expect(tokenizedBuffer.tokenForPosition([1,1]).scopes).toEqual ["source.js"]
expect(tokenizedBuffer.tokenForPosition([1,2]).scopes).toEqual ["source.js", "storage.modifier.js"]
+39 -83
Ver Arquivo
@@ -64,103 +64,59 @@ describe "UndoManager", ->
expect(buffer.getText()).toContain 'qsport'
describe "transaction methods", ->
describe "transact([fn])", ->
describe "when called with a function", ->
it "causes changes performed within the function's dynamic extent to be undone simultaneously", ->
buffer.insert([0, 0], "foo")
describe "transact()", ->
beforeEach ->
buffer.setText('')
undoManager.transact ->
undoManager.transact ->
buffer.insert([1, 2], "111")
buffer.insert([1, 9], "222")
it "starts a transaction that can be committed later", ->
buffer.append('1')
undoManager.transact()
buffer.append('2')
buffer.append('3')
undoManager.commit()
buffer.append('4')
expect(buffer.lineForRow(1)).toBe ' 111var 222sort = function(items) {'
expect(buffer.getText()).toBe '1234'
undoManager.undo()
expect(buffer.getText()).toBe '123'
undoManager.undo()
expect(buffer.getText()).toBe '1'
undoManager.redo()
expect(buffer.getText()).toBe '123'
undoManager.undo()
expect(buffer.lineForRow(1)).toBe ' var sort = function(items) {'
expect(buffer.lineForRow(0)).toContain 'foo'
it "starts a transaction that can be aborted later", ->
buffer.append('1')
buffer.append('2')
undoManager.undo()
undoManager.transact()
expect(buffer.lineForRow(0)).not.toContain 'foo'
buffer.append('3')
buffer.append('4')
expect(buffer.getText()).toBe '1234'
undoManager.redo()
expect(buffer.lineForRow(0)).toContain 'foo'
undoManager.abort()
expect(buffer.getText()).toBe '12'
undoManager.redo()
expect(buffer.lineForRow(1)).toBe ' 111var 222sort = function(items) {'
undoManager.undo()
expect(buffer.getText()).toBe '1'
undoManager.undo()
expect(buffer.lineForRow(1)).toBe ' var sort = function(items) {'
undoManager.redo()
expect(buffer.getText()).toBe '12'
it "does not record empty transactions", ->
buffer.insert([0,0], "foo")
undoManager.transact ->
undoManager.undo()
expect(buffer.lineForRow(0)).not.toContain("foo")
it "undoes operations that occured prior to an exception when the transaction is undone", ->
buffer.setText("jumpstreet")
expect(->
undoManager.transact ->
buffer.insert([0,0], "3")
buffer.insert([0,0], "2")
throw new Error("problem")
buffer.insert([0,0], "2")
).toThrow('problem')
expect(buffer.lineForRow(0)).toBe "23jumpstreet"
undoManager.undo()
expect(buffer.lineForRow(0)).toBe "jumpstreet"
describe "when called without a function", ->
beforeEach ->
buffer.setText('')
it "returns a transaction object that can be committed later", ->
buffer.append('1')
undoManager.transact()
buffer.append('2')
buffer.append('3')
undoManager.commit()
buffer.append('4')
expect(buffer.getText()).toBe '1234'
undoManager.undo()
expect(buffer.getText()).toBe '123'
undoManager.undo()
expect(buffer.getText()).toBe '1'
undoManager.redo()
expect(buffer.getText()).toBe '123'
it "returns a transaction object that can be aborted later", ->
buffer.append('1')
buffer.append('2')
undoManager.transact()
buffer.append('3')
buffer.append('4')
expect(buffer.getText()).toBe '1234'
undoManager.abort()
expect(buffer.getText()).toBe '12'
undoManager.undo()
expect(buffer.getText()).toBe '1'
undoManager.redo()
expect(buffer.getText()).toBe '12'
undoManager.redo()
expect(buffer.getText()).toBe '12'
undoManager.redo()
expect(buffer.getText()).toBe '12'
describe "commit", ->
it "throws an exception if there is no current transaction", ->
expect(-> buffer.commit()).toThrow()
it "does not record empty transactions", ->
buffer.insert([0,0], "foo")
undoManager.transact()
undoManager.commit()
undoManager.undo()
expect(buffer.lineForRow(0)).not.toContain("foo")
describe "abort", ->
it "does not affect the undo stack when the current transaction is empty", ->
buffer.setText('')
+105 -21
Ver Arquivo
@@ -1,18 +1,20 @@
$ = require 'jquery'
{$$} = require 'space-pen'
fsUtils = require 'fs-utils'
{less} = require 'less'
WindowEventHandler = require 'window-event-handler'
describe "Window", ->
projectPath = null
[projectPath, windowEventHandler] = []
beforeEach ->
spyOn(atom, 'getPathToOpen').andReturn(project.getPath())
window.handleWindowEvents()
windowEventHandler = new WindowEventHandler()
window.deserializeEditorWindow()
projectPath = project.getPath()
afterEach ->
window.unloadEditorWindow()
windowEventHandler.unsubscribe()
$(window).off 'beforeunload'
describe "when the window is loaded", ->
@@ -150,28 +152,10 @@ describe "Window", ->
expect(buffer.subscriptionCount()).toBe 0
it "only serializes window state the first time it is called", ->
window.unloadEditorWindow()
window.unloadEditorWindow()
expect(atom.saveWindowState.callCount).toBe 1
describe ".installAtomCommand(commandPath)", ->
commandPath = '/tmp/installed-atom-command/atom'
afterEach ->
fsUtils.remove(commandPath) if fsUtils.exists(commandPath)
describe "when the command path doesn't exist", ->
it "copies atom.sh to the specified path", ->
expect(fsUtils.exists(commandPath)).toBeFalsy()
window.installAtomCommand(commandPath)
waitsFor ->
fsUtils.exists(commandPath)
runs ->
expect(fsUtils.read(commandPath).length).toBeGreaterThan 1
describe ".deserialize(state)", ->
class Foo
@deserialize: ({name}) -> new Foo(name)
@@ -230,3 +214,103 @@ describe "Window", ->
event = buildDragEvent("drop", [])
window.onDrop(event)
expect(atom.open).not.toHaveBeenCalled()
describe "when a link is clicked", ->
it "opens the http/https links in an external application", ->
ChildProcess = require 'child_process'
spyOn(ChildProcess, 'spawn')
$("<a href='http://github.com'>the website</a>").appendTo(document.body).click().remove()
expect(ChildProcess.spawn).toHaveBeenCalled()
expect(ChildProcess.spawn.argsForCall[0][1][0]).toBe "http://github.com"
ChildProcess.spawn.reset()
$("<a href='https://github.com'>the website</a>").appendTo(document.body).click().remove()
expect(ChildProcess.spawn).toHaveBeenCalled()
expect(ChildProcess.spawn.argsForCall[0][1][0]).toBe "https://github.com"
ChildProcess.spawn.reset()
$("<a href=''>the website</a>").appendTo(document.body).click().remove()
expect(ChildProcess.spawn).not.toHaveBeenCalled()
ChildProcess.spawn.reset()
$("<a href='#scroll-me'>link</a>").appendTo(document.body).click().remove()
expect(ChildProcess.spawn).not.toHaveBeenCalled()
describe "core:focus-next and core:focus-previous", ->
describe "when there is no currently focused element", ->
it "focuses the element with the lowest/highest tabindex", ->
elements = $$ ->
@div =>
@button tabindex: 2
@input tabindex: 1
elements.attachToDom()
elements.trigger "core:focus-next"
expect(elements.find("[tabindex=1]:focus")).toExist()
$(":focus").blur()
elements.trigger "core:focus-previous"
expect(elements.find("[tabindex=2]:focus")).toExist()
describe "when a tabindex is set on the currently focused element", ->
it "focuses the element with the next highest tabindex", ->
elements = $$ ->
@div =>
@input tabindex: 1
@button tabindex: 2
@button tabindex: 5
@input tabindex: -1
@input tabindex: 3
@button tabindex: 7
elements.attachToDom()
elements.find("[tabindex=1]").focus()
elements.trigger "core:focus-next"
expect(elements.find("[tabindex=2]:focus")).toExist()
elements.trigger "core:focus-next"
expect(elements.find("[tabindex=3]:focus")).toExist()
elements.focus().trigger "core:focus-next"
expect(elements.find("[tabindex=5]:focus")).toExist()
elements.focus().trigger "core:focus-next"
expect(elements.find("[tabindex=7]:focus")).toExist()
elements.focus().trigger "core:focus-next"
expect(elements.find("[tabindex=1]:focus")).toExist()
elements.trigger "core:focus-previous"
expect(elements.find("[tabindex=7]:focus")).toExist()
elements.trigger "core:focus-previous"
expect(elements.find("[tabindex=5]:focus")).toExist()
elements.focus().trigger "core:focus-previous"
expect(elements.find("[tabindex=3]:focus")).toExist()
elements.focus().trigger "core:focus-previous"
expect(elements.find("[tabindex=2]:focus")).toExist()
elements.focus().trigger "core:focus-previous"
expect(elements.find("[tabindex=1]:focus")).toExist()
it "skips disabled elements", ->
elements = $$ ->
@div =>
@input tabindex: 1
@button tabindex: 2, disabled: 'disabled'
@input tabindex: 3
elements.attachToDom()
elements.find("[tabindex=1]").focus()
elements.trigger "core:focus-next"
expect(elements.find("[tabindex=3]:focus")).toExist()
elements.trigger "core:focus-previous"
expect(elements.find("[tabindex=1]:focus")).toExist()
+18 -1
Ver Arquivo
@@ -1,3 +1,20 @@
## File.markdown
:cool:
:cool:
```ruby
def func
x = 1
end
```
```
function f(x) {
return x++;
}
```
```kombucha
drink-that-stuff:
tastes-weird~
```
+34
Ver Arquivo
@@ -0,0 +1,34 @@
fs = require 'fs'
fsUtils = require 'fs-utils'
installer = require 'command-installer'
describe "install(commandPath, callback)", ->
directory = '/tmp/install-atom-command/atom'
commandPath = "#{directory}/source"
destinationPath = "#{directory}/bin/source"
beforeEach ->
spyOn(installer, 'findInstallDirectory').andCallFake (callback) ->
callback(directory)
fsUtils.remove(directory) if fsUtils.exists(directory)
it "symlinks the command and makes it executable", ->
fsUtils.write(commandPath, 'test')
expect(fsUtils.isFile(commandPath)).toBeTruthy()
expect(fsUtils.isExecutable(commandPath)).toBeFalsy()
expect(fsUtils.isFile(destinationPath)).toBeFalsy()
installDone = false
installError = null
installer.install commandPath, (error) ->
installDone = true
installError = error
waitsFor -> installDone
runs ->
expect(installError).toBeNull()
expect(fsUtils.isFile(destinationPath)).toBeTruthy()
expect(fs.realpathSync(destinationPath)).toBe fs.realpathSync(commandPath)
expect(fsUtils.isExecutable(destinationPath)).toBeTruthy()
-90
Ver Arquivo
@@ -1,90 +0,0 @@
CSON = require 'cson'
describe "CSON", ->
describe "@stringify(object)", ->
describe "when the object is undefined", ->
it "throws an exception", ->
expect(-> CSON.stringify()).toThrow()
describe "when the object is a function", ->
it "throws an exception", ->
expect(-> CSON.stringify(-> 'function')).toThrow()
describe "when the object contains a function", ->
it "throws an exception", ->
expect(-> CSON.stringify(a: -> 'function')).toThrow()
describe "when formatting an undefined key", ->
it "does not include the key in the formatted CSON", ->
expect(CSON.stringify(b: 1, c: undefined)).toBe "'b': 1"
describe "when formatting a string", ->
it "returns formatted CSON", ->
expect(CSON.stringify(a: 'b')).toBe "'a': 'b'"
it "escapes single quotes", ->
expect(CSON.stringify(a: "'b'")).toBe "'a': '\\\'b\\\''"
it "doesn't escape double quotes", ->
expect(CSON.stringify(a: '"b"')).toBe "'a': '\"b\"'"
it "escapes newlines", ->
expect(CSON.stringify("a\nb")).toBe "'a\\nb'"
describe "when formatting a boolean", ->
it "returns formatted CSON", ->
expect(CSON.stringify(true)).toBe 'true'
expect(CSON.stringify(false)).toBe 'false'
expect(CSON.stringify(a: true)).toBe "'a': true"
expect(CSON.stringify(a: false)).toBe "'a': false"
describe "when formatting a number", ->
it "returns formatted CSON", ->
expect(CSON.stringify(54321.012345)).toBe '54321.012345'
expect(CSON.stringify(a: 14)).toBe "'a': 14"
expect(CSON.stringify(a: 1.23)).toBe "'a': 1.23"
describe "when formatting null", ->
it "returns formatted CSON", ->
expect(CSON.stringify(null)).toBe 'null'
expect(CSON.stringify(a: null)).toBe "'a': null"
describe "when formatting an array", ->
describe "when the array is empty", ->
it "puts the array on a single line", ->
expect(CSON.stringify([])).toBe "[]"
it "returns formatted CSON", ->
expect(CSON.stringify(a: ['b'])).toBe "'a': [\n 'b'\n]"
expect(CSON.stringify(a: ['b', 4])).toBe "'a': [\n 'b'\n 4\n]"
describe "when the array has an undefined value", ->
it "formats the undefined value as null", ->
expect(CSON.stringify(['a', undefined, 'b'])).toBe "[\n 'a'\n null\n 'b'\n]"
describe "when the array contains an object", ->
it "wraps the object in {}", ->
expect(CSON.stringify([{a:'b', a1: 'b1'}, {c: 'd'}])).toBe "[\n {\n 'a': 'b'\n 'a1': 'b1'\n }\n {\n 'c': 'd'\n }\n]"
describe "when formatting an object", ->
describe "when the object is empty", ->
it "returns {}", ->
expect(CSON.stringify({})).toBe "{}"
it "returns formatted CSON", ->
expect(CSON.stringify(a: {b: 'c'})).toBe "'a':\n 'b': 'c'"
describe "when converting back to an object", ->
it "produces the original object", ->
object =
showInvisibles: true
fontSize: 20
core:
themes: ['a', 'b']
whitespace:
ensureSingleTrailingNewline: true
cson = CSON.stringify(object)
CoffeeScript = require 'coffee-script'
evaledObject = CoffeeScript.eval(cson, bare: true)
expect(evaledObject).toEqual object
+14 -1
Ver Arquivo
@@ -1,9 +1,10 @@
Subscriber = require 'subscriber'
EventEmitter = require 'event-emitter'
_ = require 'underscore'
{$$} = require 'space-pen'
describe "Subscriber", ->
[emitter1, emitter2, event1Handler, event2Handler, subscriber] = []
[emitter1, emitter2, emitter3, event1Handler, event2Handler, event3Handler, subscriber] = []
class TestEventEmitter
_.extend TestEventEmitter.prototype, EventEmitter
@@ -14,11 +15,17 @@ describe "Subscriber", ->
beforeEach ->
emitter1 = new TestEventEmitter
emitter2 = new TestEventEmitter
emitter3 = $$ ->
@div =>
@a()
@span()
subscriber = new TestSubscriber
event1Handler = jasmine.createSpy("event1Handler")
event2Handler = jasmine.createSpy("event2Handler")
event3Handler = jasmine.createSpy("event3Handler")
subscriber.subscribe emitter1, 'event1', event1Handler
subscriber.subscribe emitter2, 'event2', event2Handler
subscriber.subscribe emitter3, 'event3', 'a', event3Handler
it "subscribes to events on the specified object", ->
emitter1.trigger 'event1', 'foo'
@@ -27,6 +34,12 @@ describe "Subscriber", ->
emitter2.trigger 'event2', 'bar'
expect(event2Handler).toHaveBeenCalledWith('bar')
emitter3.find('span').trigger 'event3'
expect(event3Handler).not.toHaveBeenCalledWith()
emitter3.find('a').trigger 'event3'
expect(event3Handler).toHaveBeenCalled()
it "allows an object to unsubscribe en-masse", ->
subscriber.unsubscribe()
emitter1.trigger 'event1', 'foo'
+4 -7
Ver Arquivo
@@ -3,12 +3,9 @@ Package = require 'package'
fsUtils = require 'fs-utils'
_ = require 'underscore'
$ = require 'jquery'
CSON = require 'cson'
CSON = require 'season'
###
# Internal: Loads and resolves packages. #
###
### Internal: Loads and resolves packages. ###
module.exports =
class AtomPackage extends Package
@@ -65,11 +62,11 @@ class AtomPackage extends Package
loadMetadata: ->
if metadataPath = fsUtils.resolveExtension(fsUtils.join(@path, 'package'), ['json', 'cson'])
@metadata = CSON.readObject(metadataPath)
@metadata = CSON.readFileSync(metadataPath)
@metadata ?= {}
loadKeymaps: ->
@keymaps = @getKeymapPaths().map (path) -> [path, CSON.readObject(path)]
@keymaps = @getKeymapPaths().map (path) -> [path, CSON.readFileSync(path)]
getKeymapPaths: ->
keymapsDirPath = fsUtils.join(@path, 'keymaps')
+2 -2
Ver Arquivo
@@ -5,13 +5,13 @@ Theme = require 'theme'
module.exports =
class AtomTheme extends Theme
# Internal: Given a path, this loads it as a stylesheet.
# Given a path, this loads it as a stylesheet.
#
# stylesheetPath - A {String} to a stylesheet
loadStylesheet: (stylesheetPath)->
@stylesheets[stylesheetPath] = window.loadStylesheet(stylesheetPath)
# Internal: Loads the stylesheets found in a `package.cson` file.
# Loads the stylesheets found in a `package.cson` file.
load: ->
if fsUtils.extension(@path) in ['.css', '.less']
@loadStylesheet(@path)
+1 -1
Ver Arquivo
@@ -71,7 +71,7 @@ _.extend atom,
loadPackage: (id, options) ->
if @isPackageDisabled(id)
return console.warn("Tried to load disabled packaged '#{id}'")
return console.warn("Tried to load disabled package '#{id}'")
if path = @resolvePackagePath(id)
return pack if pack = @getLoadedPackage(id)
+1 -3
Ver Arquivo
@@ -5,9 +5,7 @@ fsUtils = require 'fs-utils'
Specificity = require 'specificity'
PEG = require 'pegjs'
###
# Internal #
###
### Internal ###
module.exports =
class BindingSet
+29 -24
Ver Arquivo
@@ -1,9 +1,7 @@
Range = require 'range'
_ = require 'underscore'
###
# Internal #
###
### Internal ###
module.exports =
class BufferChangeOperation
@@ -19,26 +17,35 @@ class BufferChangeOperation
@options ?= {}
do: ->
@oldText = @buffer.getTextInRange(@oldRange)
@newRange = @calculateNewRange(@oldRange, @newText)
@buffer.pauseEvents()
@pauseMarkerObservation()
@markersToRestoreOnUndo = @invalidateMarkers(@oldRange)
@changeBuffer
oldRange: @oldRange
newRange: @newRange
oldText: @oldText
newText: @newText
redo: ->
@restoreMarkers(@markersToRestoreOnRedo)
if @oldRange?
@oldText = @buffer.getTextInRange(@oldRange)
@newRange = @calculateNewRange(@oldRange, @newText)
newRange = @changeBuffer
oldRange: @oldRange
newRange: @newRange
oldText: @oldText
newText: @newText
@restoreMarkers(@markersToRestoreOnRedo) if @markersToRestoreOnRedo
@buffer.resumeEvents()
@resumeMarkerObservation()
newRange
undo: ->
@buffer.pauseEvents()
@pauseMarkerObservation()
@markersToRestoreOnRedo = @invalidateMarkers(@newRange)
@changeBuffer
oldRange: @newRange
newRange: @oldRange
oldText: @newText
newText: @oldText
if @oldRange?
@changeBuffer
oldRange: @newRange
newRange: @oldRange
oldText: @newText
newText: @oldText
@restoreMarkers(@markersToRestoreOnUndo)
@buffer.resumeEvents()
@resumeMarkerObservation()
splitLines: (text) ->
lines = text.split('\n')
@@ -74,13 +81,10 @@ class BufferChangeOperation
@buffer.cachedMemoryContents = null
@buffer.conflict = false if @buffer.conflict and !@buffer.isModified()
@pauseMarkerObservation()
event = { oldRange, newRange, oldText, newText }
@updateMarkers(event)
@buffer.trigger 'changed', event
@buffer.scheduleModifiedEvents()
@resumeMarkerObservation()
@buffer.trigger 'markers-updated'
newRange
@@ -96,13 +100,14 @@ class BufferChangeOperation
newRange
invalidateMarkers: (oldRange) ->
_.compact(@buffer.getMarkers().map (marker) -> marker.tryToInvalidate(oldRange))
@buffer.getMarkers().map (marker) -> marker.tryToInvalidate(oldRange)
pauseMarkerObservation: ->
marker.pauseEvents() for marker in @buffer.getMarkers()
marker.pauseEvents() for marker in @buffer.getMarkers(includeInvalid: true)
resumeMarkerObservation: ->
marker.resumeEvents() for marker in @buffer.getMarkers()
marker.resumeEvents() for marker in @buffer.getMarkers(includeInvalid: true)
@buffer.trigger 'markers-updated' if @oldRange?
updateMarkers: (bufferChange) ->
marker.handleBufferChange(bufferChange) for marker in @buffer.getMarkers()
+82 -65
Ver Arquivo
@@ -10,23 +10,20 @@ class BufferMarker
suppressObserverNotification: false
invalidationStrategy: null
###
# Internal #
###
constructor: ({@id, @buffer, range, @invalidationStrategy, noTail, reverse}) ->
### Internal ###
constructor: ({@id, @buffer, range, @invalidationStrategy, @attributes, noTail, reverse}) ->
@invalidationStrategy ?= 'contains'
@setRange(range, {noTail, reverse})
###
# Public #
###
### Public ###
# Public: Sets the marker's range, potentialy modifying both its head and tail.
# Sets the marker's range, potentialy modifying both its head and tail.
#
# range - The new {Range} the marker should cover
# options - A hash of options with the following keys:
# :reverse - if `true`, the marker is reversed; that is, its tail is "above" the head
# :noTail - if `true`, the marker doesn't have a tail
# reverse: if `true`, the marker is reversed; that is, its tail is "above" the head
# noTail: if `true`, the marker doesn't have a tail
setRange: (range, options={}) ->
@consolidateObserverNotifications false, =>
range = Range.fromObject(range)
@@ -37,7 +34,7 @@ class BufferMarker
@setTailPosition(range.start) unless options.noTail
@setHeadPosition(range.end)
# Public: Identifies if the ending position of a marker is greater than the starting position.
# Identifies if the ending position of a marker is greater than the starting position.
#
# This can happen when, for example, you highlight text "up" in a {Buffer}.
#
@@ -45,37 +42,55 @@ class BufferMarker
isReversed: ->
@tailPosition? and @headPosition.isLessThan(@tailPosition)
# Public: Identifies if the marker's head position is equal to its tail.
# Checks that the marker's attributes match the given attributes
#
# Returns a {Boolean}.
matchesAttributes: (queryAttributes) ->
for key, value of queryAttributes
switch key
when 'startRow'
return false unless @getRange().start.row == value
when 'endRow'
return false unless @getRange().end.row == value
when 'containsRange'
return false unless @getRange().containsRange(value, exclusive: true)
when 'containsRow'
return false unless @getRange().containsRow(value)
else
return false unless _.isEqual(@attributes[key], value)
true
# Identifies if the marker's head position is equal to its tail.
#
# Returns a {Boolean}.
isRangeEmpty: ->
@getHeadPosition().isEqual(@getTailPosition())
# Public: Retrieves the {Range} between a marker's head and its tail.
#
# Retrieves the {Range} between a marker's head and its tail.
#
# Returns a {Range}.
getRange: ->
if @tailPosition
new Range(@tailPosition, @headPosition)
new Range(@getTailPosition(), @getHeadPosition())
else
new Range(@headPosition, @headPosition)
new Range(@getHeadPosition(), @getHeadPosition())
# Public: Retrieves the position of the marker's head.
# Retrieves the position of the marker's head.
#
# Returns a {Point}.
getHeadPosition: -> @headPosition
getHeadPosition: -> @headPosition?.copy()
# Public: Retrieves the position of the marker's tail.
# Retrieves the position of the marker's tail.
#
# Returns a {Point}.
getTailPosition: -> @tailPosition ? @getHeadPosition()
getTailPosition: -> @tailPosition?.copy() ? @getHeadPosition()
# Public: Sets the position of the marker's head.
# Sets the position of the marker's head.
#
# newHeadPosition - The new {Point} to place the head
# options - A hash with the following keys:
# :clip - if `true`, the point is [clipped]{Buffer.clipPosition}
# :bufferChanged - if `true`, indicates that the {Buffer} should trigger an event that it's changed
# clip: if `true`, the point is [clipped]{Buffer.clipPosition}
# bufferChanged: if `true`, indicates that the {Buffer} should trigger an event that it's changed
#
# Returns a {Point} representing the new head position.
setHeadPosition: (newHeadPosition, options={}) ->
@@ -88,12 +103,12 @@ class BufferMarker
@notifyObservers({oldHeadPosition, newHeadPosition, bufferChanged})
@headPosition
# Public: Sets the position of the marker's tail.
# Sets the position of the marker's tail.
#
# newHeadPosition - The new {Point} to place the tail
# options - A hash with the following keys:
# :clip - if `true`, the point is [clipped]{Buffer.clipPosition}
# :bufferChanged - if `true`, indicates that the {Buffer} should trigger an event that it's changed
# clip: if `true`, the point is [clipped]{Buffer.clipPosition}
# bufferChanged: if `true`, indicates that the {Buffer} should trigger an event that it's changed
#
# Returns a {Point} representing the new tail position.
setTailPosition: (newTailPosition, options={}) ->
@@ -106,19 +121,19 @@ class BufferMarker
@notifyObservers({oldTailPosition, newTailPosition, bufferChanged})
@tailPosition
# Public: Retrieves the starting position of the marker.
# Retrieves the starting position of the marker.
#
# Returns a {Point}.
getStartPosition: ->
@getRange().start
# Public: Retrieves the ending position of the marker.
# Retrieves the ending position of the marker.
#
# Returns a {Point}.
getEndPosition: ->
@getRange().end
# Public: Sets the marker's tail to the same position as the marker's head.
# Sets the marker's tail to the same position as the marker's head.
#
# This only works if there isn't already a tail position.
#
@@ -126,56 +141,58 @@ class BufferMarker
placeTail: ->
@setTailPosition(@getHeadPosition()) unless @tailPosition
# Public: Removes the tail from the marker.
# Removes the tail from the marker.
clearTail: ->
oldTailPosition = @getTailPosition()
@tailPosition = null
newTailPosition = @getTailPosition()
@notifyObservers({oldTailPosition, newTailPosition, bufferChanged: false})
# Public: Identifies if a {Point} is within the marker.
# Identifies if a {Point} is within the marker.
#
# Returns a {Boolean}.
containsPoint: (point) ->
@getRange().containsPoint(point)
# Public: Sets a callback to be fired whenever a marker is changed.
observe: (callback) ->
@on 'changed', callback
cancel: => @unobserve(callback)
# Destroys the marker
destroy: ->
@buffer.destroyMarker(@id)
@trigger 'destroyed'
# Public: Removes the fired callback whenever a marker changes.
unobserve: (callback) ->
@off 'changed', callback
# Returns a {Boolean} indicating whether the marker is valid. Markers can be
# invalidated when a region surrounding them in the buffer is changed.
isValid: ->
@buffer.getMarker(@id)?
###
# Internal #
###
# Returns a {Boolean} indicating whether the marker has been destroyed. A marker
# can be invalid without being destroyed, in which case undoing the invalidating
# operation would restore the marker. Once a marker is destroyed by calling
# {BufferMarker.destroy}, no undo/redo operation can ever bring it back.
isDestroyed: ->
not (@buffer.validMarkers[@id]? or @buffer.invalidMarkers[@id]?)
### Internal ###
tryToInvalidate: (changedRange) ->
betweenStartAndEnd = @getRange().containsRange(changedRange, exclusive: false)
containsStart = changedRange.containsPoint(@getStartPosition(), exclusive: true)
containsEnd = changedRange.containsPoint(@getEndPosition(), exclusive: true)
switch @invalidationStrategy
when 'between'
if betweenStartAndEnd or containsStart or containsEnd
@invalidate()
[@id]
when 'contains'
if containsStart or containsEnd
@invalidate()
[@id]
when 'never'
if containsStart or containsEnd
previousRange = @getRange()
if containsStart and containsEnd
@setRange([changedRange.end, changedRange.end])
else if containsStart
@setRange([changedRange.end, @getEndPosition()])
else
@setRange([@getStartPosition(), changedRange.start])
[@id, previousRange]
previousRange = @getRange()
if changedRange
betweenStartAndEnd = @getRange().containsRange(changedRange, exclusive: false)
containsStart = changedRange.containsPoint(@getStartPosition(), exclusive: true)
containsEnd = changedRange.containsPoint(@getEndPosition(), exclusive: true)
switch @invalidationStrategy
when 'between'
@invalidate() if betweenStartAndEnd or containsStart or containsEnd
when 'contains'
@invalidate() if containsStart or containsEnd
when 'never'
if containsStart or containsEnd
if containsStart and containsEnd
@setRange([changedRange.end, changedRange.end])
else if containsStart
@setRange([changedRange.end, @getEndPosition()])
else
@setRange([@getStartPosition(), changedRange.start])
[@id, previousRange]
handleBufferChange: (bufferChange) ->
@consolidateObserverNotifications true, =>
@@ -213,7 +230,7 @@ class BufferMarker
newHeadPosition ?= @getHeadPosition()
oldTailPosition ?= @getTailPosition()
newTailPosition ?= @getTailPosition()
valid = @buffer.validMarkers[@id]?
valid = @isValid()
@trigger 'changed', {oldHeadPosition, newHeadPosition, oldTailPosition, newTailPosition, bufferChanged, valid}
consolidateObserverNotifications: (bufferChanged, fn) ->
+21 -16
Ver Arquivo
@@ -17,22 +17,20 @@ class ConfigPanel extends View
input = $(input)
name = input.attr('id')
type = input.attr('type')
@observeConfig name, (value) ->
if type is 'checkbox'
input.attr('checked', value)
else
input.val(value) if value
input.on 'change', ->
input.on 'change', =>
value = input.val()
config.set name, switch type
when 'int'
parseInt(value)
when 'float'
parseFloat(value)
when 'checkbox'
!!input.attr('checked')
else
value
if type == 'checkbox'
value = !!input.attr('checked')
else
value = @parseValue(type, value)
config.set(name, value)
bindEditors: ->
for editor in @find('.editor[id]').views()
@@ -45,9 +43,16 @@ class ConfigPanel extends View
value ?= ""
editor.setText(value.toString())
editor.getBuffer().on 'contents-modified', ->
value = editor.getText()
if type == 'int' then value = parseInt(value) or 0
if type == 'float' then value = parseFloat(value) or 0
if value == "" then value = undefined
config.set name, value
editor.getBuffer().on 'contents-modified', =>
config.set(name, @parseValue(type, editor.getText()))
parseValue: (type, value) ->
switch type
when 'int'
intValue = parseInt(value)
value = intValue unless isNaN(intValue)
when 'float'
floatValue = parseFloat(value)
value = floatValue unless isNaN(floatValue)
value = undefined if value == ''
value
+79 -32
Ver Arquivo
@@ -1,7 +1,7 @@
fsUtils = require 'fs-utils'
_ = require 'underscore'
EventEmitter = require 'event-emitter'
CSON = require 'cson'
CSON = require 'season'
fs = require 'fs'
async = require 'async'
pathWatcher = require 'pathwatcher'
@@ -29,9 +29,7 @@ class Config
settings: null
configFileHasErrors: null
###
# Internal #
###
### Internal ###
constructor: ->
@defaultSettings =
@@ -46,7 +44,6 @@ class Config
fsUtils.makeDirectory(@configDirPath)
queue = async.queue ({sourcePath, destinationPath}, callback) =>
fsUtils.copy(sourcePath, destinationPath, callback)
queue.drain = done
@@ -73,7 +70,7 @@ class Config
loadUserConfig: ->
if fsUtils.exists(@configFilePath)
try
userConfig = CSON.readObject(@configFilePath)
userConfig = CSON.readFileSync(@configFilePath)
_.extend(@settings, userConfig)
@configFileHasErrors = false
@trigger 'updated'
@@ -90,29 +87,6 @@ class Config
@watchSubscription?.close()
@watchSubscription = null
# Public: Retrieves the setting for the given key.
#
# 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.
get: (keyPath) ->
_.valueForKeyPath(@settings, keyPath) ?
_.valueForKeyPath(@defaultSettings, keyPath)
# Public: 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
#
# Returns the `value`.
set: (keyPath, value) ->
_.setValueForKeyPath(@settings, keyPath, value)
@update()
value
setDefaults: (keyPath, defaults) ->
keys = keyPath.split('.')
hash = @defaultSettings
@@ -123,9 +97,80 @@ class Config
_.extend hash, defaults
@update()
# Public: Establishes an event listener for a given key.
### Public ###
# Retrieves the setting for the given key.
#
# Whenever the value of the key is changed, a callback is fired.
# 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.
get: (keyPath) ->
value = _.valueForKeyPath(@settings, keyPath) ? _.valueForKeyPath(@defaultSettings, keyPath)
_.deepClone(value)
# Retrieves the setting for the given key as an integer.
#
# 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, defaultValueWhenFalsy) ->
parseInt(@get(keyPath))
# 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
# positive
#
# Returns the value from Atom's default settings, the user's configuration file,
# or `defaultValue` if the key value isn't greater than zero.
getPositiveInt: (keyPath, defaultValue) ->
Math.max(@getInt(keyPath), 0) or defaultValue
# 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
#
# Returns the `value`.
set: (keyPath, value) ->
if @get(keyPath) != value
value = undefined if _.valueForKeyPath(@defaultSettings, keyPath) == value
_.setValueForKeyPath(@settings, keyPath, value)
@update()
value
# Push the value to the array at the key path.
#
# keyPath - The {String} key path.
# value - The value to push to the array.
#
# Returns the new array length of the setting.
pushAtKeyPath: (keyPath, value) ->
arrayValue = @get(keyPath) ? []
result = arrayValue.push(value)
@set(keyPath, arrayValue)
result
# Remove the value from the array at the key path.
#
# keyPath - The {String} key path.
# value - The value to remove from the array.
#
# Returns the new array value of the setting.
removeAtKeyPath: (keyPath, value) ->
arrayValue = @get(keyPath) ? []
result = _.remove(arrayValue, value)
@set(keyPath, arrayValue)
result
# Establishes an event listener for a given key.
#
# `callback` is fired immediately and whenever the value of the key is changed
#
# keyPath - The {String} name of the key to watch
# callback - The {Function} that fires when the. It is given a single argument, `value`,
@@ -144,12 +189,14 @@ class Config
callback(value)
subscription
### Internal ###
update: ->
return if @configFileHasErrors
@save()
@trigger 'updated'
save: ->
CSON.writeObject(@configFilePath, @settings)
CSON.writeFileSync(@configFilePath, @settings)
_.extend Config.prototype, EventEmitter
+1 -1
Ver Arquivo
@@ -3,7 +3,7 @@ Point = require 'point'
Range = require 'range'
_ = require 'underscore'
# Internal:
### Internal ###
module.exports =
class CursorView extends View
@content: ->
+57 -59
Ver Arquivo
@@ -14,13 +14,11 @@ class Cursor
visible: true
needsAutoscroll: null
###
# Internal #
###
### Internal ###
constructor: ({@editSession, @marker}) ->
@updateVisibility()
@editSession.observeMarker @marker, (e) =>
@marker.on 'changed', (e) =>
@updateVisibility()
{oldHeadScreenPosition, newHeadScreenPosition} = e
{oldHeadBufferPosition, newHeadBufferPosition} = e
@@ -42,7 +40,7 @@ class Cursor
destroy: ->
@destroyed = true
@editSession.destroyMarker(@marker)
@marker.destroy()
@editSession.removeCursor(this)
@trigger 'destroyed'
@@ -53,47 +51,45 @@ class Cursor
unless fn()
@trigger 'autoscrolled' if @needsAutoscroll
###
# Public #
###
### Public ###
# Public: Moves a cursor to a given screen position.
# 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 {EditSession} to wherever the cursor moves to
# autoscroll: A {Boolean} which, if `true`, scrolls the {EditSession} to wherever the cursor moves to
#
setScreenPosition: (screenPosition, options={}) ->
@changePosition options, =>
@editSession.setMarkerHeadScreenPosition(@marker, screenPosition, options)
@marker.setHeadScreenPosition(screenPosition, options)
# Public: Gets the screen position of the cursor.
# Gets the screen position of the cursor.
#
# Returns an {Array} of two numbers: the screen row, and the screen column.
getScreenPosition: ->
@editSession.getMarkerHeadScreenPosition(@marker)
@marker.getHeadScreenPosition()
# Public: Moves a cursor to a given buffer position.
# 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 {EditSession} to wherever the cursor moves to
# autoscroll: A {Boolean} which, if `true`, scrolls the {EditSession} to wherever the cursor moves to
#
setBufferPosition: (bufferPosition, options={}) ->
@changePosition options, =>
@editSession.setMarkerHeadBufferPosition(@marker, bufferPosition, options)
@marker.setHeadBufferPosition(bufferPosition, options)
# Public: Gets the current buffer position.
# Gets the current buffer position.
#
# Returns an {Array} of two numbers: the buffer row, and the buffer column.
getBufferPosition: ->
@editSession.getMarkerHeadBufferPosition(@marker)
@marker.getHeadBufferPosition()
# Public: If the marker range is empty, the cursor is marked as being visible.
# If the marker range is empty, the cursor is marked as being visible.
updateVisibility: ->
@setVisible(@editSession.isMarkerRangeEmpty(@marker))
@setVisible(@marker.getBufferRange().isEmpty())
# Public: Sets the visibility of the cursor.
# Sets the visibility of the cursor.
#
# visible - A {Boolean} indicating whether the cursor should be visible
setVisible: (visible) ->
@@ -102,25 +98,27 @@ class Cursor
@needsAutoscroll ?= true if @visible and @isLastCursor()
@trigger 'visibility-changed', @visible
# Public: Retrieves the visibility of the cursor.
# Retrieves the visibility of the cursor.
#
# Returns a {Boolean}.
isVisible: -> @visible
# Public: Identifies what the cursor considers a "word" RegExp.
# Identifies what the cursor considers a "word" RegExp.
#
# Returns a {RegExp}.
wordRegExp: ->
nonWordCharacters = config.get("editor.nonWordCharacters")
new RegExp("^[\t ]*$|[^\\s#{_.escapeRegExp(nonWordCharacters)}]+|[#{_.escapeRegExp(nonWordCharacters)}]+", "g")
# Public: Identifies if this cursor is the last in the {EditSession}.
# Identifies if this cursor is the last in the {EditSession}.
#
# "Last" is defined as the most recently added cursor.
#
# Returns a {Boolean}.
isLastCursor: ->
this == @editSession.getCursor()
# Public: Identifies if the cursor is surrounded by whitespace.
# Identifies if the cursor is surrounded by whitespace.
#
# "Surrounded" here means that all characters before and after the cursor is whitespace.
#
@@ -130,84 +128,84 @@ class Cursor
range = [[row, Math.min(0, column - 1)], [row, Math.max(0, column + 1)]]
/^\s+$/.test @editSession.getTextInBufferRange(range)
# Public: Removes the setting for auto-scroll.
# Removes the setting for auto-scroll.
clearAutoscroll: ->
@needsAutoscroll = null
# Public: Deselects whatever the cursor is selecting.
# Deselects whatever the cursor is selecting.
clearSelection: ->
if @selection
@selection.goalBufferRange = null
@selection.clear() unless @selection.retainSelection
# Public: Retrieves the cursor's screen row.
# Retrieves the cursor's screen row.
#
# Returns a {Number}.
getScreenRow: ->
@getScreenPosition().row
# Public: Retrieves the cursor's screen column.
# Retrieves the cursor's screen column.
#
# Returns a {Number}.
getScreenColumn: ->
@getScreenPosition().column
# Public: Retrieves the cursor's buffer row.
# Retrieves the cursor's buffer row.
#
# Returns a {Number}.
getBufferRow: ->
@getBufferPosition().row
# Public: Retrieves the cursor's buffer column.
# Retrieves the cursor's buffer column.
#
# Returns a {Number}.
getBufferColumn: ->
@getBufferPosition().column
# Public: Retrieves the cursor's buffer row text.
# Retrieves the cursor's buffer row text.
#
# Returns a {String}.
getCurrentBufferLine: ->
@editSession.lineForBufferRow(@getBufferRow())
# Public: Moves the cursor up one screen row.
# Moves the cursor up one screen row.
moveUp: (rowCount = 1) ->
{ row, column } = @getScreenPosition()
column = @goalColumn if @goalColumn?
@setScreenPosition({row: row - rowCount, column: column})
@goalColumn = column
# Public: Moves the cursor down one screen row.
# Moves the cursor down one screen row.
moveDown: (rowCount = 1) ->
{ row, column } = @getScreenPosition()
column = @goalColumn if @goalColumn?
@setScreenPosition({row: row + rowCount, column: column})
@goalColumn = column
# Public: Moves the cursor left one screen column.
# Moves the cursor left one screen column.
moveLeft: ->
{ row, column } = @getScreenPosition()
[row, column] = if column > 0 then [row, column - 1] else [row - 1, Infinity]
@setScreenPosition({row, column})
# Public: Moves the cursor right one screen column.
# Moves the cursor right one screen column.
moveRight: ->
{ row, column } = @getScreenPosition()
@setScreenPosition([row, column + 1], skipAtomicTokens: true, wrapBeyondNewlines: true, wrapAtSoftNewlines: true)
# Public: Moves the cursor to the top of the buffer.
# Moves the cursor to the top of the buffer.
moveToTop: ->
@setBufferPosition([0,0])
# Public: Moves the cursor to the bottom of the buffer.
# Moves the cursor to the bottom of the buffer.
moveToBottom: ->
@setBufferPosition(@editSession.getEofBufferPosition())
# Public: Moves the cursor to the beginning of the buffer line.
# Moves the cursor to the beginning of the buffer line.
moveToBeginningOfLine: ->
@setBufferPosition([@getBufferRow(), 0])
# Public: Moves the cursor to the beginning of the first character in the line.
# Moves the cursor to the beginning of the first character in the line.
moveToFirstCharacterOfLine: ->
position = @getBufferPosition()
scanRange = @getCurrentLineBufferRange()
@@ -218,7 +216,7 @@ class Cursor
newPosition = [position.row, 0] if newPosition.isEqual(position)
@setBufferPosition(newPosition)
# Public: Moves the cursor to the beginning of the buffer line, skipping all whitespace.
# Moves the cursor to the beginning of the buffer line, skipping all whitespace.
skipLeadingWhitespace: ->
position = @getBufferPosition()
scanRange = @getCurrentLineBufferRange()
@@ -228,28 +226,28 @@ class Cursor
@setBufferPosition(endOfLeadingWhitespace) if endOfLeadingWhitespace.isGreaterThan(position)
# Public: Moves the cursor to the end of the buffer line.
# Moves the cursor to the end of the buffer line.
moveToEndOfLine: ->
@setBufferPosition([@getBufferRow(), Infinity])
# Public: Moves the cursor to the beginning of the word.
# Moves the cursor to the beginning of the word.
moveToBeginningOfWord: ->
@setBufferPosition(@getBeginningOfCurrentWordBufferPosition())
# Public: Moves the cursor to the end of the word.
# Moves the cursor to the end of the word.
moveToEndOfWord: ->
if position = @getEndOfCurrentWordBufferPosition()
@setBufferPosition(position)
# Public: Moves the cursor to the beginning of the next word.
# Moves the cursor to the beginning of the next word.
moveToBeginningOfNextWord: ->
if position = @getBeginningOfNextWordBufferPosition()
@setBufferPosition(position)
# Public: Retrieves the buffer position of where the current word starts.
# Retrieves the buffer position of where the current word starts.
#
# options - A hash with one option:
# :wordRegex - A {RegExp} indicating what constitutes a "word" (default: {wordRegExp})
# wordRegex: A {RegExp} indicating what constitutes a "word" (default: {wordRegExp})
#
# Returns a {Range}.
getBeginningOfCurrentWordBufferPosition: (options = {}) ->
@@ -267,10 +265,10 @@ class Cursor
beginningOfWordPosition or currentBufferPosition
# Public: Retrieves the buffer position of where the current word ends.
# Retrieves the buffer position of where the current word ends.
#
# options - A hash with one option:
# :wordRegex - A {RegExp} indicating what constitutes a "word" (default: {wordRegExp})
# wordRegex: A {RegExp} indicating what constitutes a "word" (default: {wordRegExp})
#
# Returns a {Range}.
getEndOfCurrentWordBufferPosition: (options = {}) ->
@@ -287,10 +285,10 @@ class Cursor
endOfWordPosition ? currentBufferPosition
# Public: Retrieves the buffer position of where the next word starts.
# Retrieves the buffer position of where the next word starts.
#
# options - A hash with one option:
# :wordRegex - A {RegExp} indicating what constitutes a "word" (default: {wordRegExp})
# wordRegex: A {RegExp} indicating what constitutes a "word" (default: {wordRegExp})
#
# Returns a {Range}.
getBeginningOfNextWordBufferPosition: (options = {}) ->
@@ -305,7 +303,7 @@ class Cursor
beginningOfNextWordPosition or currentBufferPosition
# Public: Gets the word located under the cursor.
# Gets the word located under the cursor.
#
# options - An object with properties based on {.getBeginningOfCurrentWordBufferPosition}.
#
@@ -315,7 +313,7 @@ class Cursor
endOptions = _.extend(_.clone(options), allowNext: false)
new Range(@getBeginningOfCurrentWordBufferPosition(startOptions), @getEndOfCurrentWordBufferPosition(endOptions))
# Public: Retrieves the range for the current line.
# Retrieves the range for the current line.
#
# options - A hash with the same keys as {EditSession.bufferRangeForBufferRow}
#
@@ -323,7 +321,7 @@ class Cursor
getCurrentLineBufferRange: (options) ->
@editSession.bufferRangeForBufferRow(@getBufferRow(), options)
# Public: Retrieves the range for the current paragraph.
# Retrieves the range for the current paragraph.
#
# A paragraph is defined as a block of text surrounded by empty lines.
#
@@ -345,19 +343,19 @@ class Cursor
new Range([startRow, 0], [endRow, @editSession.lineLengthForBufferRow(endRow)])
# Public: Retrieves the characters that constitute a word preceeding the current cursor position.
# Retrieves the characters that constitute a word preceeding the current cursor position.
#
# Returns a {String}.
getCurrentWordPrefix: ->
@editSession.getTextInBufferRange([@getBeginningOfCurrentWordBufferPosition(), @getBufferPosition()])
# Public: Identifies if the cursor is at the start of a line.
# Identifies if the cursor is at the start of a line.
#
# Returns a {Boolean}.
isAtBeginningOfLine: ->
@getBufferPosition().column == 0
# Public: Retrieves the indentation level of the current line.
# Retrieves the indentation level of the current line.
#
# Returns a {Number}.
getIndentLevel: ->
@@ -366,13 +364,13 @@ class Cursor
else
@getBufferColumn()
# Public: Identifies if the cursor is at the end of a line.
# Identifies if the cursor is at the end of a line.
#
# Returns a {Boolean}.
isAtEndOfLine: ->
@getBufferPosition().isEqual(@getCurrentLineBufferRange().end)
# Public: Retrieves the grammar's token scopes for the line.
# Retrieves the grammar's token scopes for the line.
#
# Returns an {Array} of {String}s.
getScopes: ->
+7 -7
Ver Arquivo
@@ -12,24 +12,26 @@ module.exports =
class Directory
path: null
# Public: Creates a new directory.
### Public ###
# Creates a new directory.
#
# path - A {String} representing the file directory
# symlink - A {Boolean} indicating if the path is a symlink (default: false)
constructor: (@path, @symlink=false) ->
# Public: Retrieves the basename of the directory.
# Retrieves the basename of the directory.
#
# Returns a {String}.
getBaseName: ->
fsUtils.base(@path)
# Public: Retrieves the directory's path.
# Retrieves the directory's path.
#
# Returns a {String}.
getPath: -> @path
# Public: Retrieves the file entries in the directory.
# Retrieves the file entries in the directory.
#
# This does follow symlinks.
#
@@ -51,9 +53,7 @@ class Directory
directories.concat(files)
###
# Internal #
###
### Internal ###
afterSubscribe: ->
@subscribeToNativeChangeEvents() if @subscriptionCount() == 1
+72 -65
Ver Arquivo
@@ -9,50 +9,47 @@ class DisplayBufferMarker
tailScreenPosition: null
valid: true
###
# Internal #
###
### Internal ###
constructor: ({@id, @displayBuffer}) ->
@buffer = @displayBuffer.buffer
constructor: ({@bufferMarker, @displayBuffer}) ->
@id = @bufferMarker.id
@observeBufferMarker()
###
# Public #
###
### Public ###
# Public: Gets the screen range of the display marker.
# Gets the screen range of the display marker.
#
# Returns a {Range}.
getScreenRange: ->
@displayBuffer.screenRangeForBufferRange(@getBufferRange(), wrapAtSoftNewlines: true)
# Public: Modifies the screen range of the display marker.
# Modifies the screen range of the display marker.
#
# screenRange - The new {Range} to use
# options - A hash of options matching those found in {BufferMarker.setRange}
setScreenRange: (screenRange, options) ->
@setBufferRange(@displayBuffer.bufferRangeForScreenRange(screenRange), options)
# Public: Gets the buffer range of the display marker.
# Gets the buffer range of the display marker.
#
# Returns a {Range}.
getBufferRange: ->
@buffer.getMarkerRange(@id)
@bufferMarker.getRange()
# Public: Modifies the buffer range of the display marker.
# Modifies the buffer range of the display marker.
#
# screenRange - The new {Range} to use
# options - A hash of options matching those found in {BufferMarker.setRange}
setBufferRange: (bufferRange, options) ->
@buffer.setMarkerRange(@id, bufferRange, options)
@bufferMarker.setRange(bufferRange, options)
# Public: Retrieves the screen position of the marker's head.
# Retrieves the screen position of the marker's head.
#
# Returns a {Point}.
getHeadScreenPosition: ->
@headScreenPosition ?= @displayBuffer.screenPositionForBufferPosition(@getHeadBufferPosition(), wrapAtSoftNewlines: true)
# Public: Sets the screen position of the marker's head.
# Sets the screen position of the marker's head.
#
# screenRange - The new {Point} to use
# options - A hash of options matching those found in {DisplayBuffer.bufferPositionForScreenPosition}
@@ -60,26 +57,26 @@ class DisplayBufferMarker
screenPosition = @displayBuffer.clipScreenPosition(screenPosition, options)
@setHeadBufferPosition(@displayBuffer.bufferPositionForScreenPosition(screenPosition, options))
# Public: Retrieves the buffer position of the marker's head.
# Retrieves the buffer position of the marker's head.
#
# Returns a {Point}.
getHeadBufferPosition: ->
@buffer.getMarkerHeadPosition(@id)
@bufferMarker.getHeadPosition()
# Public: Sets the buffer position of the marker's head.
# Sets the buffer position of the marker's head.
#
# screenRange - The new {Point} to use
# options - A hash of options matching those found in {DisplayBuffer.bufferPositionForScreenPosition}
setHeadBufferPosition: (bufferPosition) ->
@buffer.setMarkerHeadPosition(@id, bufferPosition)
@bufferMarker.setHeadPosition(bufferPosition)
# Public: Retrieves the screen position of the marker's tail.
# Retrieves the screen position of the marker's tail.
#
# Returns a {Point}.
getTailScreenPosition: ->
@tailScreenPosition ?= @displayBuffer.screenPositionForBufferPosition(@getTailBufferPosition(), wrapAtSoftNewlines: true)
# Public: Sets the screen position of the marker's tail.
# Sets the screen position of the marker's tail.
#
# screenRange - The new {Point} to use
# options - A hash of options matching those found in {DisplayBuffer.bufferPositionForScreenPosition}
@@ -87,71 +84,81 @@ class DisplayBufferMarker
screenPosition = @displayBuffer.clipScreenPosition(screenPosition, options)
@setTailBufferPosition(@displayBuffer.bufferPositionForScreenPosition(screenPosition, options))
# Public: Retrieves the buffer position of the marker's tail.
# Retrieves the buffer position of the marker's tail.
#
# Returns a {Point}.
getTailBufferPosition: ->
@buffer.getMarkerTailPosition(@id)
# Public: Sets the buffer position of the marker's tail.
@bufferMarker.getTailPosition()
# Sets the buffer position of the marker's tail.
#
# screenRange - The new {Point} to use
# options - A hash of options matching those found in {DisplayBuffer.bufferPositionForScreenPosition}
setTailBufferPosition: (bufferPosition) ->
@buffer.setMarkerTailPosition(@id, bufferPosition)
@bufferMarker.setTailPosition(bufferPosition)
# Public: Sets the marker's tail to the same position as the marker's head.
# Sets the marker's tail to the same position as the marker's head.
#
# This only works if there isn't already a tail position.
#
# Returns a {Point} representing the new tail position.
placeTail: ->
@buffer.placeMarkerTail(@id)
@bufferMarker.placeTail()
# Public: Removes the tail from the marker.
# Removes the tail from the marker.
clearTail: ->
@buffer.clearMarkerTail(@id)
@bufferMarker.clearTail()
# Public: Sets a callback to be fired whenever the marker is changed.
#
# callback - A {Function} to execute
observe: (callback) ->
@observeBufferMarkerIfNeeded()
@on 'changed', callback
cancel: => @unobserve(callback)
# Returns whether the head precedes the tail in the buffer
isReversed: ->
@bufferMarker.isReversed()
# Public: Removes the callback that's fired whenever the marker changes.
#
# callback - A {Function} to remove
unobserve: (callback) ->
@off 'changed', callback
@unobserveBufferMarkerIfNeeded()
# Returns a {Boolean} indicating whether the marker is valid. Markers can be
# invalidated when a region surrounding them in the buffer is changed.
isValid: ->
@bufferMarker.isValid()
###
# Internal #
###
# Returns a {Boolean} indicating whether the marker has been destroyed. A marker
# can be invalid without being destroyed, in which case undoing the invalidating
# operation would restore the marker. Once a marker is destroyed by calling
# {BufferMarker.destroy}, no undo/redo operation can ever bring it back.
isDestroyed: ->
@bufferMarker.isDestroyed()
matchesAttributes: (attributes) ->
@bufferMarker.matchesAttributes(attributes)
# Destroys the marker
destroy: ->
@bufferMarker.destroy()
# Returns a {String} representation of the marker
inspect: ->
"DisplayBufferMarker(id: #{@id}, bufferRange: #{@getBufferRange().inspect()})"
### Internal ###
destroyed: ->
delete @displayBuffer.markers[@id]
@trigger 'destroyed'
observeBufferMarker: ->
@bufferMarker.on 'destroyed', => @destroyed()
observeBufferMarkerIfNeeded: ->
return if @subscriptionCount()
@getHeadScreenPosition() # memoize current value
@getTailScreenPosition() # memoize current value
@bufferMarkerSubscription =
@buffer.observeMarker @id, ({oldHeadPosition, newHeadPosition, oldTailPosition, newTailPosition, bufferChanged, valid}) =>
@notifyObservers
oldHeadBufferPosition: oldHeadPosition
newHeadBufferPosition: newHeadPosition
oldTailBufferPosition: oldTailPosition
newTailBufferPosition: newTailPosition
bufferChanged: bufferChanged
valid: valid
@displayBuffer.markers[@id] = this
unobserveBufferMarkerIfNeeded: ->
return if @subscriptionCount()
@bufferMarkerSubscription.cancel()
delete @displayBuffer.markers[@id]
@bufferMarker.on 'changed', ({oldHeadPosition, newHeadPosition, oldTailPosition, newTailPosition, bufferChanged, valid}) =>
@notifyObservers
oldHeadBufferPosition: oldHeadPosition
newHeadBufferPosition: newHeadPosition
oldTailBufferPosition: oldTailPosition
newTailBufferPosition: newTailPosition
bufferChanged: bufferChanged
valid: valid
notifyObservers: ({oldHeadBufferPosition, oldTailBufferPosition, bufferChanged, valid} = {}) ->
return unless @valid or @isValid()
oldHeadScreenPosition = @getHeadScreenPosition()
newHeadScreenPosition = oldHeadScreenPosition
oldTailScreenPosition = @getTailScreenPosition()
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
@@ -15,7 +15,7 @@ class EditorConfigPanel extends ConfigPanel
@div class: 'control-group', =>
@label class: 'control-label', "Font Size:"
@div class: 'controls', =>
@subview "fontSizeEditor", new Editor(mini: true, attributes: {id: 'editor.fontSize', type: 'int', style: 'width: 40px'})
@subview "fontSizeEditor", new Editor(mini: true, attributes: {id: 'editor.fontSize', type: 'int', style: 'width: 4em'})
@div class: 'control-group', =>
@label class: 'control-label', "Font Family:"
@@ -56,7 +56,7 @@ class EditorConfigPanel extends ConfigPanel
@div class: 'control-group', =>
@label class: 'control-label', for: 'editor.preferredLineLength', "Preferred Line Length:"
@div class: 'controls', =>
@subview "preferredLineLengthEditor", new Editor(mini: true, attributes: {id: 'editor.preferredLineLength', type: 'int', style: 'width: 40px'})
@subview "preferredLineLengthEditor", new Editor(mini: true, attributes: {id: 'editor.preferredLineLength', type: 'int', style: 'width: 4em'})
@div class: 'control-group', =>
@label class: 'control-label', for: 'editor.nonWordCharacters', "Non-Word Characters:"
+345 -532
Ver Arquivo
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
+7 -7
Ver Arquivo
@@ -4,7 +4,7 @@ _ = require 'underscore'
#
# Each event can have more than one handler; that is, an event can trigger multiple functions.
module.exports =
# Public: Associates an event name with a function to perform.
# Associates an event name with a function to perform.
#
# This is called endlessly, until the event is turned {.off}.
#
@@ -27,7 +27,7 @@ module.exports =
@afterSubscribe?()
# Public: Associates an event name with a function to perform only once.
# Associates an event name with a function to perform only once.
#
# eventName - A {String} name identifying an event
# handler - A {Function} that's executed when the event is triggered
@@ -38,7 +38,7 @@ module.exports =
@on eventName, oneShotHandler
# Public: Triggers a registered event.
# Triggers a registered event.
#
# eventName - A {String} name identifying an event
# args - Any additional arguments to pass over to the event `handler`
@@ -55,7 +55,7 @@ module.exports =
if handlers = @eventHandlersByEventName?[eventName]
handlers.forEach (handler) -> handler(args...)
# Public: Stops executing handlers for a registered event.
# Stops executing handlers for a registered event.
#
# eventNames - A {String} containing one or more space-separated events.
# handler - The {Function} to remove from the event. If not provided, all handlers are removed.
@@ -90,20 +90,20 @@ module.exports =
@eventHandlersByNamespace = {}
@afterUnsubscribe?() if @subscriptionCount() < subscriptionCountBefore
# Public: When called, stops triggering any events.
# When called, stops triggering any events.
pauseEvents: ->
@pauseCount ?= 0
if @pauseCount++ == 0
@queuedEvents ?= []
# Public: When called, resumes triggering events.
# When called, resumes triggering events.
resumeEvents: ->
if --@pauseCount == 0
queuedEvents = @queuedEvents
@queuedEvents = null
@trigger(event...) for event in queuedEvents
# Public: Identifies how many events are registered.
# Identifies how many events are registered.
#
# Returns a {Number}.
subscriptionCount: ->
+10 -12
Ver Arquivo
@@ -7,7 +7,7 @@ _ = require 'underscore'
# Public: Represents an individual file in the editor.
#
# The entry point for this class is in two locations:
# The entry point for this class is in two locations:
# * {Buffer}, which associates text contents with a file
# * {Directory}, which associcates the children of a directory as files
module.exports =
@@ -15,7 +15,7 @@ class File
path: null
cachedContents: null
# Public: Creates a new file.
# Creates a new file.
#
# path - A {String} representing the file path
# symlink - A {Boolean} indicating if the path is a symlink (default: false)
@@ -24,23 +24,23 @@ class File
if fs.statSync(@path).isDirectory()
throw new Error("#{@path} is a directory")
# Public: Sets the path for the file.
# Sets the path for the file.
#
# path - A {String} representing the new file path
setPath: (@path) ->
# Public: Retrieves the path for the file.
# Retrieves the path for the file.
#
# Returns a {String}.
getPath: -> @path
# Public: Gets the file's basename--that is, the file without any directory information.
# Gets the file's basename--that is, the file without any directory information.
#
# Returns a {String}.
getBaseName: ->
fsUtils.base(@path)
# Public: Writes (and saves) new contents to the file.
# Writes (and saves) new contents to the file.
#
# text - A {String} representing the new contents.
write: (text) ->
@@ -49,7 +49,7 @@ class File
fsUtils.write(@getPath(), text)
@subscribeToNativeChangeEvents() if not previouslyExisted and @subscriptionCount() > 0
# Public: Reads the file.
# Reads the file.
#
# flushCache - A {Boolean} indicating if the cache should be erased--_i.e._, a force read is performed
#
@@ -62,19 +62,17 @@ class File
else
@cachedContents
# Public: Checks to see if a file exists.
# Checks to see if a file exists.
#
# Returns a {Boolean}.
exists: ->
fsUtils.exists(@getPath())
###
# Internal #
###
### Internal ###
afterSubscribe: ->
@subscribeToNativeChangeEvents() if @exists() and @subscriptionCount() == 1
afterUnsubscribe: ->
@unsubscribeFromNativeChangeEvents() if @subscriptionCount() == 0
+43 -49
Ver Arquivo
@@ -1,73 +1,69 @@
Range = require 'range'
Point = require 'point'
# Public: Represents a fold that's hiding text from the screen.
# Public: Represents a fold that collapses multiple buffer lines into a single
# line on the screen.
#
# Folds are the primary reason that screen ranges and buffer ranges vary. Their
# creation is managed by the {DisplayBuffer}.
# Their creation is managed by the {DisplayBuffer}.
module.exports =
class Fold
@idCounter: 1
id: null
displayBuffer: null
startRow: null
endRow: null
marker: null
###
# Internal #
###
### Internal ###
constructor: (@displayBuffer, @startRow, @endRow) ->
@id = @constructor.idCounter++
constructor: (@displayBuffer, @marker) ->
@id = @marker.id
@displayBuffer.foldsByMarkerId[@marker.id] = this
@updateDisplayBuffer()
@marker.on 'destroyed', => @destroyed()
# Returns whether this fold is contained within another fold
isInsideLargerFold: ->
@displayBuffer.findMarker(class: 'fold', containsBufferRange: @getBufferRange())?
# Destroys this fold
destroy: ->
@displayBuffer.destroyFold(this)
@marker.destroy()
inspect: ->
"Fold(#{@startRow}, #{@endRow})"
# Public: Retrieves the buffer row range that a fold occupies.
# Returns the fold's {Range} in buffer coordinates
#
# includeNewline - A {Boolean} which, if `true`, includes the trailing newline
#
# Returns a {Range}.
getBufferRange: ({includeNewline}={}) ->
range = @marker.getRange()
if includeNewline
end = [@endRow + 1, 0]
else
end = [@endRow, Infinity]
range.end.row++
range.end.column = 0
range
new Range([@startRow, 0], end)
# Returns the fold's start row as a {Number}.
getStartRow: ->
@getBufferRange().start.row
# Public: Retrieves the number of buffer rows a fold occupies.
# Retrieves the number of buffer rows a fold occupies.
#
# Returns a {Number}.
getBufferRowCount: ->
@endRow - @startRow + 1
handleBufferChange: (event) ->
oldStartRow = @startRow
# Returns the fold's end row as a {Number}.
getEndRow: ->
@getBufferRange().end.row
if @isContainedByRange(event.oldRange)
@displayBuffer.unregisterFold(@startRow, this)
return
# Returns a {String} representation of the fold.
inspect: ->
"Fold(#{@getStartRow()}, #{@getEndRow()})"
@startRow += @getRowDelta(event, @startRow)
@endRow += @getRowDelta(event, @endRow)
if @startRow != oldStartRow
@displayBuffer.unregisterFold(oldStartRow, this)
@displayBuffer.registerFold(this)
# Public: Identifies if a {Range} occurs within a fold.
# Retrieves the number of buffer rows spanned by the fold.
#
# range - A {Range} to check
#
# Returns a {Boolean}.
isContainedByRange: (range) ->
range.start.row <= @startRow and @endRow <= range.end.row
# Returns a {Number}.
getBufferRowCount: ->
@getEndRow() - @getStartRow() + 1
# Public: Identifies if a fold is nested within a fold.
# Identifies if a fold is nested within a fold.
#
# fold - A {Fold} to check
#
@@ -75,12 +71,10 @@ class Fold
isContainedByFold: (fold) ->
@isContainedByRange(fold.getBufferRange())
getRowDelta: (event, row) ->
{ newRange, oldRange } = event
updateDisplayBuffer: ->
unless @isInsideLargerFold()
@displayBuffer.updateScreenLines(@getStartRow(), @getEndRow() + 1, 0, updateMarkers: true)
if oldRange.end.row <= row
newRange.end.row - oldRange.end.row
else if newRange.end.row < row
newRange.end.row - row
else
0
destroyed: ->
delete @displayBuffer.foldsByMarkerId[@marker.id]
@updateDisplayBuffer()
+38 -44
Ver Arquivo
@@ -11,33 +11,17 @@ GitUtils = require 'git-utils'
module.exports =
class Git
# Public: Creates a new `Git` instance.
#
# path - The git repository to open
# options - A hash with one key:
# :refreshOnWindowFocus - A {Boolean} that identifies if the windows should refresh
#
# Returns a new {Git} object.
@open: (path, options) ->
return null unless path
try
new Git(path, options)
catch e
null
statuses: null
upstream: null
statusTask: null
###
# Internal #
###
### Internal ###
# Internal: Creates a new `Git` object.
# Creates a new `Git` object.
#
# path - The {String} representing the path to your git working directory
# options - A hash with the following keys:
# :refreshOnWindowFocus - If `true`, {#refreshIndex} and {#refreshStatus} are called on focus
# refreshOnWindowFocus: If `true`, {#refreshIndex} and {#refreshStatus} are called on focus
constructor: (path, options={}) ->
@repo = GitUtils.open(path)
unless @repo?
@@ -72,11 +56,23 @@ class Git
@unsubscribe()
###
# Public #
###
### Public ###
# Public: Retrieves the git repository.
# Creates a new `Git` instance.
#
# path - The git repository to open
# options - A hash with one key:
# refreshOnWindowFocus: A {Boolean} that identifies if the windows should refresh
#
# Returns a new {Git} object.
@open: (path, options) ->
return null unless path
try
new Git(path, options)
catch e
null
# Retrieves the git repository.
#
# Returns a new `Repository`.
getRepo: ->
@@ -84,22 +80,22 @@ class Git
throw new Error("Repository has been destroyed")
@repo
# Public: Reread the index to update any values that have changed since the last time the index was read.
# Reread the index to update any values that have changed since the last time the index was read.
refreshIndex: -> @getRepo().refreshIndex()
# Public: Retrieves the path of the repository.
# Retrieves the path of the repository.
#
# Returns a {String}.
getPath: ->
@path ?= fsUtils.absolute(@getRepo().getPath())
# Public: Retrieves the working directory of the repository.
# Retrieves the working directory of the repository.
#
# Returns a {String}.
getWorkingDirectory: ->
@getRepo().getWorkingDirectory()
# Public: Retrieves the reference or SHA-1 that `HEAD` points to.
# Retrieves the reference or SHA-1 that `HEAD` points to.
#
# This can be `refs/heads/master`, or a full SHA-1 if the repository is in a detached `HEAD` state.
#
@@ -107,7 +103,7 @@ class Git
getHead: ->
@getRepo().getHead() ? ''
# Public: Retrieves the status of a single path in the repository.
# Retrieves the status of a single path in the repository.
#
# path - An {String} defining a relative path
#
@@ -123,7 +119,7 @@ class Git
@trigger 'status-changed', path, pathStatus
pathStatus
# Public: Identifies if a path is ignored.
# Identifies if a path is ignored.
#
# path - The {String} path to check
#
@@ -131,7 +127,7 @@ class Git
isPathIgnored: (path) ->
@getRepo().isIgnored(@relativize(path))
# Public: Identifies if a value represents a status code.
# Identifies if a value represents a status code.
#
# status - The code {Number} to check
#
@@ -139,7 +135,7 @@ class Git
isStatusModified: (status) ->
@getRepo().isStatusModified(status)
# Public: Identifies if a path was modified.
# Identifies if a path was modified.
#
# path - The {String} path to check
#
@@ -147,7 +143,7 @@ class Git
isPathModified: (path) ->
@isStatusModified(@getPathStatus(path))
# Public: Identifies if a status code represents a new path.
# Identifies if a status code represents a new path.
#
# status - The code {Number} to check
#
@@ -155,7 +151,7 @@ class Git
isStatusNew: (status) ->
@getRepo().isStatusNew(status)
# Public: Identifies if a path is new.
# Identifies if a path is new.
#
# path - The {String} path to check
#
@@ -163,7 +159,7 @@ class Git
isPathNew: (path) ->
@isStatusNew(@getPathStatus(path))
# Public: Makes a path relative to the repository's working directory.
# Makes a path relative to the repository's working directory.
#
# path - The {String} path to convert
#
@@ -171,7 +167,7 @@ class Git
relativize: (path) ->
@getRepo().relativize(path)
# Public: Retrieves a shortened version of {.getHead}.
# Retrieves a shortened version of {.getHead}.
#
# This removes the leading segments of `refs/heads`, `refs/tags`, or `refs/remotes`.
# It also shortenes the SHA-1 of a detached `HEAD` to 7 characters.
@@ -180,7 +176,7 @@ class Git
getShortHead: ->
@getRepo().getShortHead()
# Public: Restore the contents of a path in the working directory and index to the version at `HEAD`.
# Restore the contents of a path in the working directory and index to the version at `HEAD`.
#
# This is essentially the same as running:
# ```
@@ -196,7 +192,7 @@ class Git
@getPathStatus(path) if headCheckedOut
headCheckedOut
# Public: Retrieves the number of lines added and removed to a path.
# Retrieves the number of lines added and removed to a path.
#
# This compares the working directory contents of the path to the `HEAD` version.
#
@@ -206,7 +202,7 @@ class Git
getDiffStats: (path) ->
@getRepo().getDiffStats(@relativize(path))
# Public: Identifies if a path is a submodule.
# Identifies if a path is a submodule.
#
# path - The {String} path to check
#
@@ -214,7 +210,7 @@ class Git
isSubmodule: (path) ->
@getRepo().isSubmodule(@relativize(path))
# Public: Retrieves the status of a directory.
# Retrieves the status of a directory.
#
# path - The {String} path to check
#
@@ -226,7 +222,7 @@ class Git
directoryStatus |= status if path.indexOf(directoryPath) is 0
directoryStatus
# Public: Retrieves the number of commits the `HEAD` branch is ahead/behind the remote branch it is tracking.
# Retrieves the number of commits the `HEAD` branch is ahead/behind the remote branch it is tracking.
#
# This is similar to the commit numbers reported by `git status` when a remote tracking branch exists.
#
@@ -234,7 +230,7 @@ class Git
getAheadBehindCounts: ->
@getRepo().getAheadBehindCount()
# Public: Retrieves the line diffs comparing the `HEAD` version of the given path and the given text.
# Retrieves the line diffs comparing the `HEAD` version of the given path and the given text.
#
# This is similar to the commit numbers reported by `git status` when a remote tracking branch exists.
#
@@ -245,9 +241,7 @@ class Git
getLineDiffs: (path, text) ->
@getRepo().getLineDiffs(@relativize(path), text)
###
# Internal #
###
### Internal ###
refreshStatus: ->
if @statusTask?
+7 -13
Ver Arquivo
@@ -9,9 +9,7 @@ _ = require 'underscore'
module.exports =
class Gutter extends View
###
# Internal #
###
### Internal ###
@content: ->
@div class: 'gutter', =>
@@ -50,25 +48,21 @@ class Gutter extends View
$(document).on "mousemove.gutter-#{@getEditor().id}", moveHandler
$(document).one "mouseup.gutter-#{@getEditor().id}", => $(document).off 'mousemove', moveHandler
###
# Public #
###
### Public ###
# Public: Retrieves the containing {Editor}.
# Retrieves the containing {Editor}.
#
# Returns an {Editor}.
getEditor: ->
@parentView
# Public: Defines whether to show the gutter or not.
# 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()
###
# Internal #
###
### Internal ###
updateLineNumbers: (changes, renderFrom, renderTo) ->
if renderFrom < @firstScreenRow or renderTo > @lastScreenRow
@@ -77,12 +71,12 @@ class Gutter extends View
performUpdate = true
else
for change in changes
if change.delta != 0 or (change.bufferDelta? and change.bufferDelta != 0)
if change.screenDelta or change.bufferDelta
performUpdate = true
break
@renderLineNumbers(renderFrom, renderTo) if performUpdate
renderLineNumbers: (startScreenRow, endScreenRow) ->
editor = @getEditor()
maxDigits = editor.getLineCount().toString().length
+17 -22
Ver Arquivo
@@ -8,26 +8,19 @@ module.exports=
class ImageEditSession
registerDeserializer(this)
# Public: Identifies if a path can be opened by the image viewer.
#
# path - The {String} name of the path to check
#
# Returns a {Boolean}.
@canOpen: (path) ->
_.indexOf([
'.gif'
'.jpeg'
'.jpg'
'.png'
], fsUtils.extension(path), true) >= 0
# Files with these extensions will be opened as images
@imageExtensions: ['.gif', '.jpeg', '.jpg', '.png']
### Internal ###
Project = require 'project'
Project.registerOpener (path) =>
new ImageEditSession(path) if _.include(@imageExtensions, fsUtils.extension(path))
###
# Internal #
###
@deserialize: (state) ->
if fsUtils.exists(state.path)
project.buildEditSession(state.path)
project.open(state.path)
else
console.warn "Could not build edit session for path '#{state.path}' because that file no longer exists"
@@ -40,10 +33,12 @@ class ImageEditSession
getViewClass: ->
require 'image-view'
# Public: Retrieves the filename of the open file.
### Public ###
# Retrieves the filename of the open file.
#
# This is `'untitled'` if the file is new and not saved to the disk.
#
#
# Returns a {String}.
getTitle: ->
if path = @getPath()
@@ -51,17 +46,17 @@ class ImageEditSession
else
'untitled'
# Public: Retrieves the URI of the current image.
# Retrieves the URI of the current image.
#
# Returns a {String}.
getUri: -> @path
# Public: Retrieves the path of the current image.
# Retrieves the path of the current image.
#
# Returns a {String}.
getPath: -> @path
# Public: Compares two `ImageEditSession`s to determine equality.
# Compares two `ImageEditSession`s to determine equality.
#
# Equality is based on the condition that the two URIs are the same.
#
+12 -13
Ver Arquivo
@@ -6,12 +6,12 @@ $ = require 'jquery'
module.exports =
class ImageView extends ScrollView
# Internal:
### Internal ###
@content: ->
@div class: 'image-view', tabindex: -1, =>
@img outlet: 'image'
# Internal:
initialize: (imageEditSession) ->
super
@@ -29,7 +29,6 @@ class ImageView extends ScrollView
@command 'image-view:zoom-out', => @zoomOut()
@command 'image-view:reset-zoom', => @resetZoom()
# Internal:
afterAttach: (onDom) ->
return unless onDom
@@ -40,7 +39,9 @@ class ImageView extends ScrollView
@active = @is(pane.activeView)
@centerImage() if @active and not wasActive
# Public: Places the image in the center of the {Editor}.
### Public ###
# Places the image in the center of the {Editor}.
centerImage: ->
return unless @loaded and @isVisible()
@@ -49,7 +50,7 @@ class ImageView extends ScrollView
'left': Math.max((@width() - @image.outerWidth()) / 2, 0)
@image.show()
# Public: Indicates the path of the image.
# Indicates the path of the image.
#
# path - A {String} for the new image path.
setPath: (path) ->
@@ -60,25 +61,25 @@ class ImageView extends ScrollView
else
@image.hide()
# Public: Retrieve's the {Editor}'s pane.
# Retrieve's the {Editor}'s pane.
#
# Returns a {Pane}.
getPane: ->
@parent('.item-views').parent('.pane').view()
# Public: Zooms the image out.
# Zooms the image out.
#
# This is done by a factor of `0.9`.
zoomOut: ->
@adjustSize(0.9)
# Public: Zooms the image in.
# Zooms the image in.
#
# This is done by a factor of `1.1`.
zoomIn: ->
@adjustSize(1.1)
# Public: Zooms the image to its normal width and height.
# Zooms the image to its normal width and height.
resetZoom: ->
return unless @loaded and @isVisible()
@@ -86,9 +87,7 @@ class ImageView extends ScrollView
@image.height(@originalHeight)
@centerImage()
###
# Internal #
###
### Internal ###
adjustSize: (factor) ->
return unless @loaded and @isVisible()
@@ -100,4 +99,4 @@ class ImageView extends ScrollView
@centerImage()
setModel: (imageEditSession) ->
@setPath(imageEditSession?.getPath())
@setPath(imageEditSession?.getPath())
+2 -3
Ver Arquivo
@@ -1,8 +1,7 @@
$ = require 'jquery'
_ = require 'underscore'
fsUtils = require 'fs-utils'
CSON = require 'cson'
CSON = require 'season'
BindingSet = require 'binding-set'
# Internal: Associates keymaps with actions.
@@ -46,7 +45,7 @@ class Keymap
@load(filePath) for filePath in fsUtils.list(directoryPath, ['.cson', '.json'])
load: (path) ->
@add(path, CSON.readObject(path))
@add(path, CSON.readFileSync(path))
add: (args...) ->
name = args.shift() if args.length > 1
+6 -2
Ver Arquivo
@@ -67,10 +67,10 @@
# allow standard input fields to work correctly
'input:not(.hidden-input)':
'tab': 'core:focus-next'
'shift-tab': 'core:focus-previous'
'left': 'native!'
'right': 'native!'
'tab': 'native!'
'shift-tab': 'native!'
'shift-left': 'native!'
'shift-right': 'native!'
'backspace': 'native!'
@@ -81,3 +81,7 @@
'meta-x': 'native!'
'meta-c': 'native!'
'meta-v': 'native!'
'button':
'tab': 'core:focus-next'
'shift-tab': 'core:focus-previous'
+26 -40
Ver Arquivo
@@ -5,9 +5,7 @@ require 'underscore-extensions'
EventEmitter = require 'event-emitter'
Subscriber = require 'subscriber'
###
# Internal #
###
### Internal ###
module.exports =
class LanguageMode
@@ -16,18 +14,20 @@ class LanguageMode
editSession: null
currentGrammarScore: null
# Public: Sets up a `LanguageMode` for the given {EditSession}.
### Internal ###
destroy: ->
@unsubscribe()
### Public ###
# Sets up a `LanguageMode` for the given {EditSession}.
#
# editSession - The {EditSession} to associate with
constructor: (@editSession) ->
@buffer = @editSession.buffer
# Internal:
destroy: ->
@unsubscribe()
# Public: Wraps the lines between two rows in comments.
# Wraps the lines between two rows in comments.
#
# If the language doesn't have comment, nothing happens.
#
@@ -74,7 +74,7 @@ class LanguageMode
for row in [start..end]
buffer.insert([row, 0], commentStartString)
# Public: Folds all the foldable lines in the buffer.
# Folds all the foldable lines in the buffer.
foldAll: ->
for currentRow in [0..@buffer.getLastRow()]
[startRow, endRow] = @rowRangeForFoldAtBufferRow(currentRow) ? []
@@ -82,11 +82,16 @@ class LanguageMode
@editSession.createFold(startRow, endRow)
# Public: Unfolds all the foldable lines in the buffer.
# Unfolds all the foldable lines in the buffer.
unfoldAll: ->
for row in [@buffer.getLastRow()..0]
fold.destroy() for fold in @editSession.displayBuffer.foldsStartingAtBufferRow(row)
# Given a buffer row, creates a fold at it.
#
# bufferRow - A {Number} indicating the buffer row
#
# Returns the new {Fold}.
foldBufferRow: (bufferRow) ->
for currentRow in [bufferRow..0]
rowRange = @rowRangeForCommentAtBufferRow(currentRow)
@@ -96,7 +101,7 @@ class LanguageMode
fold = @editSession.displayBuffer.largestFoldStartingAtBufferRow(startRow)
return @editSession.createFold(startRow, endRow) unless fold
# Public: Given a buffer row, this unfolds it.
# Given a buffer row, this unfolds it.
#
# bufferRow - A {Number} indicating the buffer row
unfoldBufferRow: (bufferRow) ->
@@ -140,7 +145,7 @@ class LanguageMode
endRow = currentRow
return [startRow, endRow] if startRow isnt endRow
# Public: Given a buffer row, this returns a suggested indentation level.
# Given a buffer row, this returns a suggested indentation level.
#
# The indentation level provided is based on the current {LanguageMode}.
#
@@ -157,48 +162,29 @@ class LanguageMode
return currentIndentLevel unless precedingRow?
precedingLine = @buffer.lineForRow(precedingRow)
desiredIndentLevel = @editSession.indentationForBufferRow(precedingRow)
desiredIndentLevel += 1 if increaseIndentRegex.test(precedingLine)
desiredIndentLevel += 1 if increaseIndentRegex.test(precedingLine) and not @editSession.isBufferRowCommented(precedingRow)
return desiredIndentLevel unless decreaseIndentRegex = @decreaseIndentRegexForScopes(scopes)
desiredIndentLevel -= 1 if decreaseIndentRegex.test(currentLine)
Math.max(desiredIndentLevel, currentIndentLevel)
desiredIndentLevel
# Public: Indents all the rows between two buffer row numbers.
# Indents all the rows between two buffer row numbers.
#
# startRow - The row {Number} to start at
# endRow - The row {Number} to end at
autoIndentBufferRows: (startRow, endRow) ->
@autoIndentBufferRow(row) for row in [startRow..endRow]
# Public: Given a buffer row, this indents it.
# Given a buffer row, this indents it.
#
# bufferRow - The row {Number}
autoIndentBufferRow: (bufferRow) ->
@autoIncreaseIndentForBufferRow(bufferRow)
@autoDecreaseIndentForBufferRow(bufferRow)
indentLevel = @suggestedIndentForBufferRow(bufferRow)
@editSession.setIndentationForBufferRow(bufferRow, indentLevel)
# Public: Given a buffer row, this increases the indentation.
#
# bufferRow - The row {Number}
autoIncreaseIndentForBufferRow: (bufferRow) ->
precedingRow = @buffer.previousNonBlankRow(bufferRow)
return unless precedingRow?
precedingLine = @editSession.lineForBufferRow(precedingRow)
scopes = @editSession.scopesForBufferPosition([precedingRow, Infinity])
increaseIndentRegex = @increaseIndentRegexForScopes(scopes)
return unless increaseIndentRegex
currentIndentLevel = @editSession.indentationForBufferRow(bufferRow)
desiredIndentLevel = @editSession.indentationForBufferRow(precedingRow)
desiredIndentLevel += 1 if increaseIndentRegex.test(precedingLine)
if desiredIndentLevel > currentIndentLevel
@editSession.setIndentationForBufferRow(bufferRow, desiredIndentLevel)
# Public: Given a buffer row, this decreases the indentation.
# Given a buffer row, this decreases the indentation.
#
# bufferRow - The row {Number}
autoDecreaseIndentForBufferRow: (bufferRow) ->
-192
Ver Arquivo
@@ -1,192 +0,0 @@
Point = require 'point'
Range = require 'range'
_ = require 'underscore'
# Internal: Responsible for doing the translations between screen positions and buffer positions.
module.exports =
class LineMap
maxScreenLineLength: 0
constructor: ->
@screenLines = []
insertAtScreenRow: (bufferRow, screenLines) ->
@spliceAtScreenRow(bufferRow, 0, screenLines)
replaceScreenRows: (start, end, screenLines) ->
@spliceAtScreenRow(start, end - start + 1, screenLines)
spliceAtScreenRow: (startRow, rowCount, screenLines) ->
maxLengthCandidates = screenLines
for screenLine in @screenLines[startRow...startRow+rowCount]
if screenLine.text.length == @maxScreenLineLength
@maxScreenLineLength = 0
maxLengthCandidates = @screenLines
_.spliceWithArray(@screenLines, startRow, rowCount, screenLines)
for screenLine in maxLengthCandidates
@maxScreenLineLength = Math.max(@maxScreenLineLength, screenLine.text.length)
# Public: Gets the line for the given screen row.
#
# screenRow - A {Number} indicating the screen row.
#
# Returns a {ScreenLine}.
lineForScreenRow: (row) ->
@screenLines[row]
# Public: Gets the lines for the given screen row boundaries.
#
# start - A {Number} indicating the beginning screen row.
# end - A {Number} indicating the ending screen row.
#
# Returns an {Array} of {ScreenLine}s.
linesForScreenRows: (startRow, endRow) ->
@screenLines[startRow..endRow]
# Public: Given starting and ending screen rows, this returns an array of the
# buffer rows corresponding to every screen row in the range
#
# startRow - The screen row {Number} to start at
# endRow - The screen row {Integer} to end at (default: {.lastScreenRow})
#
# Returns an {Array} of buffer rows as {Integers}s.
bufferRowsForScreenRows: (startRow, endRow=@lastScreenRow()) ->
bufferRows = []
bufferRow = 0
for screenLine, screenRow in @screenLines
break if screenRow > endRow
bufferRows.push(bufferRow) if screenRow >= startRow
bufferRow += screenLine.bufferRows
bufferRows
getScreenLineCount: ->
@screenLines.length
# Retrieves the last screen row in the buffer.
#
# Returns an {Integer}.
lastScreenRow: ->
@getScreenLineCount() - 1
clipScreenPosition: (screenPosition, options={}) ->
{ wrapBeyondNewlines, wrapAtSoftNewlines } = options
{ row, column } = Point.fromObject(screenPosition)
if row < 0
row = 0
column = 0
else if row > @lastScreenRow()
row = @lastScreenRow()
column = Infinity
else if column < 0
column = 0
screenLine = options.screenLine ? @lineForScreenRow(row)
maxScreenColumn = screenLine.getMaxScreenColumn()
if screenLine.isSoftWrapped() and column >= maxScreenColumn
if wrapAtSoftNewlines
row++
column = 0
else
column = screenLine.clipScreenColumn(maxScreenColumn - 1)
else if wrapBeyondNewlines and column > maxScreenColumn and row < @lastScreenRow()
row++
column = 0
else
column = screenLine.clipScreenColumn(column, options)
new Point(row, column)
# Public: Given a buffer position, this converts it into a screen position.
#
# bufferPosition - An object that represents a buffer position. It can be either
# an {Object} (`{row, column}`), {Array} (`[row, column]`), or {Point}
# options - The same options available to {.clipScreenPosition}.
#
# Returns a {Point}.
screenPositionForBufferPosition: (bufferPosition, options={}) ->
{ row, column } = Point.fromObject(bufferPosition)
[screenRow, screenLines] = @screenRowAndScreenLinesForBufferRow(row)
for screenLine in screenLines
maxBufferColumn = screenLine.getMaxBufferColumn()
if screenLine.isSoftWrapped() and column > maxBufferColumn
screenRow++
else
if column <= maxBufferColumn
screenColumn = screenLine.screenColumnForBufferColumn(column)
else
screenColumn = Infinity
break
options.screenLine = screenLine
@clipScreenPosition([screenRow, screenColumn], options)
screenRowAndScreenLinesForBufferRow: (bufferRow) ->
screenLines = []
screenRow = 0
currentBufferRow = 0
for screenLine in @screenLines
nextBufferRow = currentBufferRow + screenLine.bufferRows
if currentBufferRow > bufferRow
break
else if currentBufferRow == bufferRow or currentBufferRow <= bufferRow < nextBufferRow
screenLines.push(screenLine)
else
screenRow++
currentBufferRow = nextBufferRow
[screenRow, screenLines]
# Public: Given a buffer range, this converts it into a screen position.
#
# screenPosition - An object that represents a buffer position. It can be either
# an {Object} (`{row, column}`), {Array} (`[row, column]`), or {Point}
# options - The same options available to {.clipScreenPosition}.
#
# Returns a {Point}.
bufferPositionForScreenPosition: (screenPosition, options) ->
{ row, column } = @clipScreenPosition(Point.fromObject(screenPosition), options)
[bufferRow, screenLine] = @bufferRowAndScreenLineForScreenRow(row)
bufferColumn = screenLine.bufferColumnForScreenColumn(column)
new Point(bufferRow, bufferColumn)
bufferRowAndScreenLineForScreenRow: (screenRow) ->
bufferRow = 0
for screenLine, currentScreenRow in @screenLines
if currentScreenRow == screenRow
break
else
bufferRow += screenLine.bufferRows
[bufferRow, screenLine]
# Public: Given a buffer range, this converts it into a screen position.
#
# bufferRange - The {Range} to convert
#
# Returns a {Range}.
screenRangeForBufferRange: (bufferRange) ->
bufferRange = Range.fromObject(bufferRange)
start = @screenPositionForBufferPosition(bufferRange.start)
end = @screenPositionForBufferPosition(bufferRange.end)
new Range(start, end)
# Public: Given a screen range, this converts it into a buffer position.
#
# screenRange - The {Range} to convert
#
# Returns a {Range}.
bufferRangeForScreenRange: (screenRange) ->
screenRange = Range.fromObject(screenRange)
start = @bufferPositionForScreenPosition(screenRange.start)
end = @bufferPositionForScreenPosition(screenRange.end)
new Range(start, end)
# Internal:
logLines: (start=0, end=@screenLineCount() - 1)->
for row in [start..end]
line = @lineForScreenRow(row).text
console.log row, line, line.length
+7 -3
Ver Arquivo
@@ -2,9 +2,7 @@ Token = require 'token'
EventEmitter = require 'event-emitter'
_ = require 'underscore'
###
# Internal #
###
### Internal ###
module.exports =
class NullGrammar
name: 'Null Grammar'
@@ -15,6 +13,12 @@ class NullGrammar
tokenizeLine: (line) ->
{ tokens: [new Token(value: line, scopes: ['null-grammar.text.plain'])] }
tokenizeLines: (text) ->
lines = text.split('\n')
for line, i in lines
{tokens} = @tokenizeLine(line)
tokens
grammarUpdated: -> # noop
_.extend NullGrammar.prototype, EventEmitter
+2 -3
Ver Arquivo
@@ -30,10 +30,9 @@ class PackageConfigPanel extends ConfigPanel
checkbox = $(e.target)
name = checkbox.closest('tr').attr('name')
if checkbox.attr('checked')
_.remove(config.get('core.disabledPackages'), name)
config.removeAtKeyPath('core.disabledPackages', name)
else
config.get('core.disabledPackages').push(name)
config.update()
config.pushAtKeyPath('core.disabledPackages', name)
@observeConfig 'core.disabledPackages', (disabledPackages) =>
@packageTableBody.find("input[type='checkbox']").attr('checked', true)
+1 -3
Ver Arquivo
@@ -1,8 +1,6 @@
fsUtils = require 'fs-utils'
###
# Internal #
###
### Internal ###
module.exports =
class Package
@build: (path) ->
+2 -6
Ver Arquivo
@@ -6,9 +6,7 @@ module.exports =
class PaneContainer extends View
registerDeserializer(this)
###
# Internal #
###
### Internal ###
@deserialize: ({root}) ->
container = new PaneContainer
@@ -26,9 +24,7 @@ class PaneContainer extends View
deserializer: 'PaneContainer'
root: @getRoot()?.serialize()
###
# Public #
###
### Public ###
focusNextPane: ->
panes = @getPanes()
+1 -3
Ver Arquivo
@@ -2,9 +2,7 @@ $ = require 'jquery'
_ = require 'underscore'
PaneAxis = require 'pane-axis'
###
# Internal #
###
### Internal ###
module.exports =
class PaneRow extends PaneAxis
+3 -7
Ver Arquivo
@@ -7,9 +7,7 @@ PaneColumn = require 'pane-column'
module.exports =
class Pane extends View
###
# Internal #
###
### Internal ###
@content: (wrappedView) ->
@div class: 'pane', =>
@@ -65,9 +63,7 @@ class Pane extends View
@attached = true
@trigger 'pane:attached', [this]
###
# Public #
###
### Public ###
makeActive: ->
for pane in @getContainer().getPanes() when pane isnt this
@@ -186,7 +182,7 @@ class Pane extends View
saveItem: (item, nextAction) ->
if item.getUri?()
item.save()
item.save?()
nextAction?()
else
@saveItemAs(item, nextAction)
+1 -1
Ver Arquivo
@@ -5,7 +5,7 @@ module.exports =
class Pasteboard
signatureForMetadata: null
# Internal: Creates an `md5` hash of some text.
# Creates an `md5` hash of some text.
#
# text - A {String} to encrypt.
#
+19 -21
Ver Arquivo
@@ -6,7 +6,7 @@ _ = require 'underscore'
module.exports =
class Point
# Public: Constructs a `Point` from a given object.
# Constructs a `Point` from a given object.
#
# object - This can be an {Array} (`[startRow, startColumn, endRow, endColumn]`) or an object `{row, column}`
#
@@ -22,7 +22,7 @@ class Point
new Point(row, column)
# Public: Identifies which `Point` is smaller.
# Identifies which `Point` is smaller.
#
# "Smaller" means that both the `row` and `column` values of one `Point` are less than or equal
# to the other.
@@ -39,7 +39,7 @@ class Point
else
point2
# Public: Creates a new `Point` object.
# Creates a new `Point` object.
#
# row - A {Number} indicating the row (default: 0)
# column - A {Number} indicating the column (default: 0)
@@ -47,13 +47,13 @@ class Point
# Returns a {Point},
constructor: (@row=0, @column=0) ->
# Public: Creates an identical copy of the `Point`.
# Creates an identical copy of the `Point`.
#
# Returns a duplicate {Point}.
copy: ->
new Point(@row, @column)
# Public: Adds the `column`s of two `Point`s together.
# Adds the `column`s of two `Point`s together.
#
# other - The {Point} to add with
#
@@ -68,7 +68,7 @@ class Point
new Point(row, column)
# Public: Moves a `Point`.
# Moves a `Point`.
#
# In other words, the `row` values and `column` values are added to each other.
#
@@ -79,7 +79,7 @@ class Point
other = Point.fromObject(other)
new Point(@row + other.row, @column + other.column)
# Public: Creates two new `Point`s, split down a `column` value.
# Creates two new `Point`s, split down a `column` value.
#
# In other words, given a point, this creates `Point(0, column)` and `Point(row, column)`.
#
@@ -94,7 +94,7 @@ class Point
[new Point(0, column), new Point(@row, rightColumn)]
# Internal: Compares two `Point`s.
# Compares two `Point`s.
#
# other - The {Point} to compare against
#
@@ -103,7 +103,7 @@ class Point
# * If the first `row` is less than `other.row`, returns `-1`.
# * If the first `column` is greater than `other.column`, returns `1`.
# * If the first `column` is less than `other.column`, returns `-1`.
#
#
# Otherwise, returns `0`.
compare: (other) ->
if @row > other.row
@@ -118,7 +118,7 @@ class Point
else
0
# Public: Identifies if two `Point`s are equal.
# Identifies if two `Point`s are equal.
#
# other - The {Point} to compare against
#
@@ -128,7 +128,7 @@ class Point
other = Point.fromObject(other)
@row == other.row and @column == other.column
# Public: Identifies if one `Point` is less than another.
# Identifies if one `Point` is less than another.
#
# other - The {Point} to compare against
#
@@ -136,7 +136,7 @@ class Point
isLessThan: (other) ->
@compare(other) < 0
# Public: Identifies if one `Point` is less than or equal to another.
# Identifies if one `Point` is less than or equal to another.
#
# other - The {Point} to compare against
#
@@ -144,7 +144,7 @@ class Point
isLessThanOrEqual: (other) ->
@compare(other) <= 0
# Public: Identifies if one `Point` is greater than another.
# Identifies if one `Point` is greater than another.
#
# other - The {Point} to compare against
#
@@ -152,7 +152,7 @@ class Point
isGreaterThan: (other) ->
@compare(other) > 0
# Public: Identifies if one `Point` is greater than or equal to another.
# Identifies if one `Point` is greater than or equal to another.
#
# other - The {Point} to compare against
#
@@ -160,25 +160,23 @@ class Point
isGreaterThanOrEqual: (other) ->
@compare(other) >= 0
# Public: Converts the {Point} to a String.
# Converts the {Point} to a String.
#
# Returns a {String}.
toString: ->
"#{@row},#{@column}"
# Public: Converts the {Point} to an Array.
# Converts the {Point} to an Array.
#
# Returns an {Array}.
toArray: ->
[@row, @column]
###
# Internal #
###
### Internal ###
inspect: ->
"(#{@row}, #{@column})"
# Internal:
serialize: ->
@toArray()
+85 -80
Ver Arquivo
@@ -4,7 +4,6 @@ $ = require 'jquery'
Range = require 'range'
Buffer = require 'text-buffer'
EditSession = require 'edit-session'
ImageEditSession = require 'image-edit-session'
EventEmitter = require 'event-emitter'
Directory = require 'directory'
BufferedProcess = require 'buffered-process'
@@ -17,6 +16,14 @@ module.exports =
class Project
registerDeserializer(this)
@openers: []
@registerOpener: (opener) ->
@openers.push(opener)
@unregisterOpener: (opener) ->
_.remove(@openers, opener)
tabLength: 2
softTabs: true
softWrap: false
@@ -24,17 +31,7 @@ class Project
editSessions: null
ignoredPathRegexes: null
# Public: Establishes a new project at a given path.
#
# path - The {String} name of the path
constructor: (path) ->
@setPath(path)
@editSessions = []
@buffers = []
###
# Internal #
###
### Internal ###
serialize: ->
deserializer: 'Project'
@@ -46,17 +43,23 @@ class Project
destroy: ->
editSession.destroy() for editSession in @getEditSessions()
###
# Public #
###
### Public ###
# Public: Retrieves the project path.
# Establishes a new project at a given path.
#
# path - The {String} name of the path
constructor: (path) ->
@setPath(path)
@editSessions = []
@buffers = []
# Retrieves the project path.
#
# Returns a {String}.
getPath: ->
@rootDirectory?.path
# Public: Sets the project path.
# Sets the project path.
#
# path - A {String} representing the new path
setPath: (path) ->
@@ -70,13 +73,13 @@ class Project
@trigger "path-changed"
# Public: Retrieves the name of the root directory.
# Retrieves the name of the root directory.
#
# Returns a {String}.
getRootDirectory: ->
@rootDirectory
# Public: Retrieves the names of every file (that's not `git ignore`d) in the project.
# Retrieves the names of every file (that's not `git ignore`d) in the project.
#
# Returns an {Array} of {String}s.
getFilePaths: ->
@@ -88,7 +91,7 @@ class Project
deferred.resolve(paths)
deferred.promise()
# Public: Identifies if a path is ignored.
# Identifies if a path is ignored.
#
# path - The {String} name of the path to check
#
@@ -100,7 +103,7 @@ class Project
@ignoreRepositoryPath(path)
# Public: Identifies if a path is ignored.
# Identifies if a path is ignored.
#
# path - The {String} name of the path to check
#
@@ -108,16 +111,20 @@ class Project
ignoreRepositoryPath: (path) ->
config.get("core.hideGitIgnoredFiles") and git?.isPathIgnored(fsUtils.join(@getPath(), path))
# Public: Given a path, this resolves it relative to the project directory.
# Given a uri, this resolves it relative to the project directory. If the path
# is already absolute or if it is prefixed with a scheme, it is returned unchanged.
#
# filePath - The {String} name of the path to convert
# uri - The {String} name of the path to convert
#
# Returns a {String}.
resolve: (filePath) ->
filePath = fsUtils.join(@getPath(), filePath) unless filePath[0] == '/'
fsUtils.absolute filePath
resolve: (uri) ->
if uri?.match(/[A-Za-z0-9+-.]+:\/\//) # leave path alone if it has a scheme
uri
else
uri = fsUtils.join(@getPath(), uri) unless uri[0] == '/'
fsUtils.absolute uri
# Public: Given a path, this makes it relative to the project directory.
# Given a path, this makes it relative to the project directory.
#
# filePath - The {String} name of the path to convert
#
@@ -126,87 +133,53 @@ class Project
return fullPath unless fullPath.lastIndexOf(@getPath()) is 0
fullPath.replace(@getPath(), "").replace(/^\//, '')
# Public: Identifies if the project is using soft tabs.
# Identifies if the project is using soft tabs.
#
# Returns a {Boolean}.
getSoftTabs: -> @softTabs
# Public: Sets the project to use soft tabs.
# Sets the project to use soft tabs.
#
# softTabs - A {Boolean} which, if `true`, sets soft tabs
setSoftTabs: (@softTabs) ->
# Public: Identifies if the project is using soft wrapping.
# Identifies if the project is using soft wrapping.
#
# Returns a {Boolean}.
getSoftWrap: -> @softWrap
# Public: Sets the project to use soft wrapping.
# Sets the project to use soft wrapping.
#
# softTabs - A {Boolean} which, if `true`, sets soft wrapping
setSoftWrap: (@softWrap) ->
# Public: Given a path to a file, this constructs and associates a new `EditSession`, showing the file.
# Given a path to a file, this constructs and associates a new `EditSession`, showing the file.
#
# filePath - The {String} path of the file to associate with
# editSessionOptions - Options that you can pass to the `EditSession` constructor
#
# Returns either an {EditSession} (for text) or {ImageEditSession} (for images).
buildEditSession: (filePath, editSessionOptions={}) ->
if ImageEditSession.canOpen(filePath)
new ImageEditSession(filePath)
else
@buildEditSessionForBuffer(@bufferForPath(filePath), editSessionOptions)
open: (filePath, options={}) ->
for opener in @constructor.openers
return resource if resource = opener(filePath, options)
# Public: Retrieves all the {EditSession}s in the project; that is, the `EditSession`s for all open files.
@buildEditSessionForBuffer(@bufferForPath(filePath), options)
# Retrieves all the {EditSession}s in the project; that is, the `EditSession`s for all open files.
#
# Returns an {Array} of {EditSession}s.
getEditSessions: ->
new Array(@editSessions...)
###
# Internal #
###
### Public ###
buildEditSessionForBuffer: (buffer, editSessionOptions) ->
options = _.extend(@defaultEditSessionOptions(), editSessionOptions)
options.project = this
options.buffer = buffer
editSession = new EditSession(options)
@editSessions.push editSession
@trigger 'edit-session-created', editSession
editSession
defaultEditSessionOptions: ->
tabLength: @tabLength
softTabs: @getSoftTabs()
softWrap: @getSoftWrap()
eachEditSession: (callback) ->
callback(editSession) for editSession in @getEditSessions()
@on 'edit-session-created', (editSession) -> callback(editSession)
eachBuffer: (args...) ->
subscriber = args.shift() if args.length > 1
callback = args.shift()
callback(buffer) for buffer in @getBuffers()
if subscriber
subscriber.subscribe this, 'buffer-created', (buffer) -> callback(buffer)
else
@on 'buffer-created', (buffer) -> callback(buffer)
###
# Public #
###
# Public: Removes an {EditSession} association from the project.
# Removes an {EditSession} association from the project.
#
# Returns the removed {EditSession}.
removeEditSession: (editSession) ->
_.remove(@editSessions, editSession)
# Public: Retrieves all the {Buffer}s in the project; that is, the buffers for all open files.
# Retrieves all the {Buffer}s in the project; that is, the buffers for all open files.
#
# Returns an {Array} of {Buffer}s.
getBuffers: ->
@@ -215,7 +188,7 @@ class Project
buffers.push editSession.buffer
buffers
# Public: Given a file path, this retrieves or creates a new {Buffer}.
# Given a file path, this retrieves or creates a new {Buffer}.
#
# If the `filePath` already has a `buffer`, that value is used instead. Otherwise,
# `text` is used as the contents of the new buffer.
@@ -233,7 +206,7 @@ class Project
else
@buildBuffer(null, text)
# Public: Given a file path, this sets its {Buffer}.
# Given a file path, this sets its {Buffer}.
#
# filePath - A {String} representing a path
# text - The {String} text to use as a buffer
@@ -245,13 +218,13 @@ class Project
@trigger 'buffer-created', buffer
buffer
# Public: Removes a {Buffer} association from the project.
# Removes a {Buffer} association from the project.
#
# Returns the removed {Buffer}.
removeBuffer: (buffer) ->
_.remove(@buffers, buffer)
# Public: Performs a search across all the files in the project.
# Performs a search across all the files in the project.
#
# regex - A {RegExp} to search with
# iterator - A {Function} callback on each file found
@@ -307,4 +280,36 @@ class Project
new BufferedProcess({command, args, stdout, exit})
deferred
### Internal ###
buildEditSessionForBuffer: (buffer, editSessionOptions) ->
options = _.extend(@defaultEditSessionOptions(), editSessionOptions)
options.project = this
options.buffer = buffer
editSession = new EditSession(options)
@editSessions.push editSession
@trigger 'edit-session-created', editSession
editSession
defaultEditSessionOptions: ->
tabLength: @tabLength
softTabs: @getSoftTabs()
softWrap: @getSoftWrap()
eachEditSession: (callback) ->
callback(editSession) for editSession in @getEditSessions()
@on 'edit-session-created', (editSession) -> callback(editSession)
eachBuffer: (args...) ->
subscriber = args.shift() if args.length > 1
callback = args.shift()
callback(buffer) for buffer in @getBuffers()
if subscriber
subscriber.subscribe this, 'buffer-created', (buffer) -> callback(buffer)
else
@on 'buffer-created', (buffer) -> callback(buffer)
_.extend Project.prototype, EventEmitter
require 'image-edit-session'
+47 -23
Ver Arquivo
@@ -11,7 +11,7 @@ _ = require 'underscore'
module.exports =
class Range
# Public: Constructs a `Range` from a given object.
# Constructs a `Range` from a given object.
#
# object - This can be an {Array} (`[startRow, startColumn, endRow, endColumn]`) or an object `{start: Point, end: Point}`
#
@@ -24,7 +24,7 @@ class Range
else
new Range(object.start, object.end)
# Public: Constructs a `Range` from a {Point}, and the delta values beyond that point.
# Constructs a `Range` from a {Point}, and the delta values beyond that point.
#
# point - A {Point} to start with
# rowDelta - A {Number} indicating how far from the starting {Point} the range's row should be
@@ -36,7 +36,7 @@ class Range
pointB = new Point(point.row + rowDelta, point.column + columnDelta)
new Range(pointA, pointB)
# Public: Creates a new `Range` object based on two {Point}s.
# Creates a new `Range` object based on two {Point}s.
#
# pointA - The first {Point} (default: `0, 0`)
# pointB - The second {Point} (default: `0, 0`)
@@ -51,13 +51,13 @@ class Range
@start = pointB
@end = pointA
# Public: Creates an identical copy of the `Range`.
# Creates an identical copy of the `Range`.
#
# Returns a duplicate {Range}.
copy: ->
new Range(@start.copy(), @end.copy())
# Public: Identifies if two `Range`s are equal.
# Identifies if two `Range`s are equal.
#
# All four points (`start.row`, `start.column`, `end.row`, `end.column`) must be
# equal for this method to return `true`.
@@ -71,7 +71,24 @@ class Range
other.start.isEqual(@start) and other.end.isEqual(@end)
# Public: Identifies if the `Range` is on the same line.
# Returns an integer (-1, 0, 1) indicating whether this range is less than, equal,
# or greater than the given range when sorting.
#
# Ranges that start earlier are considered "less than" ranges that start later.
# If ranges start at the same location, the larger range sorts before the smaller
# range.
#
# other - A {Range} to compare against.
#
# Returns a {Number}, either -1, 0, or 1.
compare: (other) ->
other = Range.fromObject(other)
if value = @start.compare(other.start)
value
else
other.end.compare(@end)
# Identifies if the `Range` is on the same line.
#
# In other words, if `start.row` is equal to `end.row`.
#
@@ -79,7 +96,7 @@ class Range
isSingleLine: ->
@start.row == @end.row
# Public: Identifies if two `Range`s are on the same line.
# Identifies if two `Range`s are on the same line.
#
# other - A different {Range} to check against
#
@@ -87,11 +104,7 @@ class Range
coversSameRows: (other) ->
@start.row == other.start.row && @end.row == other.end.row
# Internal:
inspect: ->
"[#{@start.inspect()} - #{@end.inspect()}]"
# Public: Adds a new point to the `Range`s `start` and `end`.
# Adds a new point to the `Range`s `start` and `end`.
#
# point - A new {Point} to add
#
@@ -99,7 +112,7 @@ class Range
add: (point) ->
new Range(@start.add(point), @end.add(point))
# Public: Moves a `Range`.
# Moves a `Range`.
#
# In other words, the starting and ending `row` values, and the starting and ending
# `column` values, are added to each other.
@@ -111,7 +124,7 @@ class Range
translate: (startPoint, endPoint=startPoint) ->
new Range(@start.translate(startPoint), @end.translate(endPoint))
# Public: Identifies if two `Range`s intersect each other.
# Identifies if two `Range`s intersect each other.
#
# otherRange - A different {Range} to check against
#
@@ -122,22 +135,22 @@ class Range
else
otherRange.intersectsWith(this)
# Public: Identifies if a second `Range` is contained within a first.
# Identifies if a second `Range` is contained within a first.
#
# otherRange - A different {Range} to check against
# options - A hash with a single option:
# :exclusive - A {Boolean} which, if `true`, indicates that no {Point}s in the `Range` can be equal
# exclusive: A {Boolean} which, if `true`, indicates that no {Point}s in the `Range` can be equal
#
# Returns a {Boolean}.
containsRange: (otherRange, {exclusive} = {}) ->
{ start, end } = Range.fromObject(otherRange)
@containsPoint(start, {exclusive}) and @containsPoint(end, {exclusive})
# Public: Identifies if a `Range` contains a {Point}.
# Identifies if a `Range` contains a {Point}.
#
# point - A {Point} to check against
# options - A hash with a single option:
# :exclusive - A {Boolean} which, if `true`, indicates that no {Point}s in the `Range` can be equal
# exclusive: A {Boolean} which, if `true`, indicates that no {Point}s in the `Range` can be equal
#
# Returns a {Boolean}.
containsPoint: (point, {exclusive} = {}) ->
@@ -147,7 +160,7 @@ class Range
else
point.isGreaterThanOrEqual(@start) and point.isLessThanOrEqual(@end)
# Public: Identifies if a `Range` contains a row.
# Identifies if a `Range` contains a row.
#
# row - A row {Number} to check against
# options - A hash with a single option:
@@ -156,7 +169,7 @@ class Range
containsRow: (row) ->
@start.row <= row <= @end.row
# Public: Constructs a union between two `Range`s.
# Constructs a union between two `Range`s.
#
# otherRange - A different {Range} to unionize with
#
@@ -166,7 +179,7 @@ class Range
end = if @end.isGreaterThan(otherRange.end) then @end else otherRange.end
new Range(start, end)
# Public: Identifies if a `Range` is empty.
# Identifies if a `Range` is empty.
#
# A `Range` is empty if its start {Point} matches its end.
#
@@ -174,7 +187,7 @@ class Range
isEmpty: ->
@start.isEqual(@end)
# Public: Calculates the difference between a `Range`s `start` and `end` points.
# Calculates the difference between a `Range`s `start` and `end` points.
#
# Returns a {Point}.
toDelta: ->
@@ -185,8 +198,19 @@ class Range
columns = @end.column
new Point(rows, columns)
# Public: Calculates the number of rows a `Range`s contains.
# Calculates the number of rows a `Range`s contains.
#
# Returns a {Number}.
getRowCount: ->
@end.row - @start.row + 1
# Returns an array of all rows in a `Range`
#
# Returns an {Array}
getRows: ->
[@start.row..@end.row]
### Internal ###
inspect: ->
"[#{@start.inspect()} - #{@end.inspect()}]"
+27 -29
Ver Arquivo
@@ -25,9 +25,7 @@ class RootView extends View
disabledPackages: []
themes: ['atom-dark-ui', 'atom-dark-syntax']
###
# Internal:
###
### Internal ###
@content: ({panes}={}) ->
@div id: 'root-view', =>
@@ -98,32 +96,30 @@ class RootView extends View
afterAttach: (onDom) ->
@focus() if onDom
###
# Public #
###
### Public ###
# Public: Shows a dialog asking if the pane was _really_ meant to be closed.
# Shows a dialog asking if the pane was _really_ meant to be closed.
confirmClose: ->
@panes.confirmClose()
# Public: Given a filepath, this opens it in Atom.
# Given a filepath, this opens it in Atom.
#
# Returns the `EditSession` for the file URI.
open: (path, options = {}) ->
changeFocus = options.changeFocus ? true
path = project.resolve(path) if path?
if activePane = @getActivePane()
editSession = activePane.itemForUri(path) ? project.buildEditSession(path)
editSession = activePane.itemForUri(path) ? project.open(path)
activePane.showItem(editSession)
else
editSession = project.buildEditSession(path)
editSession = project.open(path)
activePane = new Pane(editSession)
@panes.append(activePane)
activePane.focus() if changeFocus
editSession
# Public: Updates the application's title, based on whichever file is open.
# Updates the application's title, based on whichever file is open.
updateTitle: ->
if projectPath = project.getPath()
if item = @getActivePaneItem()
@@ -133,19 +129,19 @@ class RootView extends View
else
@setTitle('untitled')
# Public: Sets the application's title.
# Sets the application's title.
#
# Returns a {String}.
setTitle: (title) ->
document.title = title
# Public: Retrieves all of the application's {Editor}s.
# Retrieves all of the application's {Editor}s.
#
# Returns an {Array} of {Editor}s.
getEditors: ->
@panes.find('.pane > .item-views > .editor').map(-> $(this).view()).toArray()
# Public: Retrieves all of the modified buffers that are open and unsaved.
# Retrieves all of the modified buffers that are open and unsaved.
#
# Returns an {Array} of {Buffer}s.
getModifiedBuffers: ->
@@ -155,13 +151,13 @@ class RootView extends View
modifiedBuffers.push item.buffer if item.buffer.isModified()
modifiedBuffers
# Public: Retrieves all of the paths to open files.
# Retrieves all of the paths to open files.
#
# Returns an {Array} of {String}s.
getOpenBufferPaths: ->
_.uniq(_.flatten(@getEditors().map (editor) -> editor.getOpenBufferPaths()))
# Public: Retrieves the pane that's currently open.
# Retrieves the pane that's currently open.
#
# Returns an {Pane}.
getActivePane: ->
@@ -177,29 +173,23 @@ class RootView extends View
focusNextPane: -> @panes.focusNextPane()
getFocusedPane: -> @panes.getFocusedPane()
# Internal: Destroys everything.
remove: ->
editor.remove() for editor in @getEditors()
project.destroy()
super
# Public: Saves all of the open buffers.
# Saves all of the open buffers.
saveAll: ->
@panes.saveAll()
# Public: Fires a callback on each open {Pane}.
# Fires a callback on each open {Pane}.
#
# callback - A {Function} to call
eachPane: (callback) ->
@panes.eachPane(callback)
# Public: Retrieves all of the open {Pane}s.
# Retrieves all of the open {Pane}s.
#
# Returns an {Array} of {Pane}.
getPanes: ->
@panes.getPanes()
# Public: Given a {Pane}, this fetches its ID.
# Given a {Pane}, this fetches its ID.
#
# pane - An open {Pane}
#
@@ -207,21 +197,29 @@ class RootView extends View
indexOfPane: (pane) ->
@panes.indexOfPane(pane)
# Public: Fires a callback on each open {Editor}.
# Fires a callback on each open {Editor}.
#
# callback - A {Function} to call
eachEditor: (callback) ->
callback(editor) for editor in @getEditors()
@on 'editor:attached', (e, editor) -> callback(editor)
# Public: Fires a callback on each open {EditSession}.
# Fires a callback on each open {EditSession}.
#
# callback - A {Function} to call
eachEditSession: (callback) ->
project.eachEditSession(callback)
# Public: Fires a callback on each open {Buffer}.
# Fires a callback on each open {Buffer}.
#
# callback - A {Function} to call
eachBuffer: (callback) ->
project.eachBuffer(callback)
### Internal ###
# Destroys everything.
remove: ->
editor.remove() for editor in @getEditors()
project.destroy()
super
+176
Ver Arquivo
@@ -0,0 +1,176 @@
## Internal ##
# Facilitates the mapping of screen rows to buffer rows and vice versa. All row
# ranges dealt with by this class are end-row exclusive. For example, a fold of
# rows 4 through 8 would be expressed as `mapBufferRowRange(4, 9, 1)`, which maps
# the region from 4 to 9 in the buffer to a single screen row. Conversely, a
# soft-wrapped screen line means there are multiple screen rows corresponding to
# a single buffer row, as follows: `mapBufferRowRange(4, 5, 3)`. That says that
# buffer row 4 maps to 3 rows on screen.
#
# The RowMap revolves around the `@regions` array. Each region describes a number
# of rows in both the screen and buffer coordinate spaces. So if you inserted a
# single fold from 5-10, the regions array would look like this:
#
# ```
# [{bufferRows: 5, screenRows: 5}, {bufferRows: 5, screenRows: 1}]
# ```
#
# The first region expresses an iso-mapping, a region in which one buffer row
# is equivalent to one screen row. The second region expresses the fold, with
# 5 buffer rows mapping to a single screen row. Position translation functions
# by traversing through these regions and summing the number of rows traversed
# in both the screen and the buffer.
module.exports =
class RowMap
constructor: ->
@regions = []
screenRowRangeForBufferRow: (targetBufferRow) ->
{ region, screenRow, bufferRow } = @traverseToBufferRow(targetBufferRow)
if region and region.bufferRows != region.screenRows # 1:n region
[screenRow, screenRow + region.screenRows]
else # 1:1 region
screenRow += targetBufferRow - bufferRow
[screenRow, screenRow + 1]
# This will return just the given buffer row if it is part of an iso region,
# but if it is part of a fold it will return the range of the entire fold. This
# helps the DisplayBuffer always start processing at the beginning of a fold
# for changes that occur inside the fold.
bufferRowRangeForBufferRow: (targetBufferRow) ->
{ region, screenRow, bufferRow } = @traverseToBufferRow(targetBufferRow)
if region and region.bufferRows != region.screenRows # 1:n region
[bufferRow, bufferRow + region.bufferRows]
else # 1:1 region
[targetBufferRow, targetBufferRow + 1]
bufferRowRangeForScreenRow: (targetScreenRow) ->
{ region, screenRow, bufferRow } = @traverseToScreenRow(targetScreenRow)
if region and region.bufferRows != region.screenRows # 1:n region
[bufferRow, bufferRow + region.bufferRows]
else # 1:1 region
bufferRow += targetScreenRow - screenRow
[bufferRow, bufferRow + 1]
# This method is used to create new regions, storing a mapping between a range
# of buffer rows to a certain number of screen rows. It will never add or remove
# rows in either coordinate space, meaning that it never changes the position
# of subsequent regions. It will overwrite or split existing regions that overlap
# with the region being stored however.
mapBufferRowRange: (startBufferRow, endBufferRow, screenRows) ->
{ index, bufferRow, screenRow } = @traverseToBufferRow(startBufferRow)
overlapStartIndex = index
overlapStartBufferRow = bufferRow
preRows = startBufferRow - overlapStartBufferRow
endScreenRow = screenRow + preRows + screenRows
overlapEndIndex = index
overlapEndBufferRow = bufferRow
overlapEndScreenRow = screenRow
# determine regions that the new region overlaps. they will need replacement.
while overlapEndIndex < @regions.length
region = @regions[overlapEndIndex]
overlapEndBufferRow += region.bufferRows
overlapEndScreenRow += region.screenRows
break if overlapEndBufferRow >= endBufferRow and overlapEndScreenRow >= endScreenRow
overlapEndIndex++
# we will replace overlapStartIndex..overlapEndIndex with these regions
newRegions = []
# if we straddle the first overlapping region, push a smaller region representing
# the portion before the new region
if preRows > 0
newRegions.push(bufferRows: preRows, screenRows: preRows)
# push the new region
newRegions.push(bufferRows: endBufferRow - startBufferRow, screenRows: screenRows)
# if we straddle the last overlapping region, push a smaller region representing
# the portion after the new region
if overlapEndBufferRow > endBufferRow
newRegions.push(bufferRows: overlapEndBufferRow - endBufferRow, screenRows: overlapEndScreenRow - endScreenRow)
@regions[overlapStartIndex..overlapEndIndex] = newRegions
@mergeIsomorphicRegions(Math.max(0, overlapStartIndex - 1), Math.min(@regions.length - 1, overlapEndIndex + 1))
mergeIsomorphicRegions: (startIndex, endIndex) ->
return if startIndex == endIndex
region = @regions[startIndex]
nextRegion = @regions[startIndex + 1]
if region.bufferRows == region.screenRows and nextRegion.bufferRows == nextRegion.screenRows
@regions[startIndex..startIndex + 1] =
bufferRows: region.bufferRows + nextRegion.bufferRows
screenRows: region.screenRows + nextRegion.screenRows
@mergeIsomorphicRegions(startIndex, endIndex - 1)
else
@mergeIsomorphicRegions(startIndex + 1, endIndex)
# This method records insertion or removal of rows in the buffer, adjusting the
# buffer dimension of regions following the start row accordingly.
applyBufferDelta: (startBufferRow, delta) ->
return if delta is 0
{ index, bufferRow } = @traverseToBufferRow(startBufferRow)
if delta > 0 and index < @regions.length
{ bufferRows, screenRows } = @regions[index]
bufferRows += delta
@regions[index] = { bufferRows, screenRows }
else
delta = -delta
while delta > 0 and index < @regions.length
{ bufferRows, screenRows } = @regions[index]
regionStartBufferRow = bufferRow
regionEndBufferRow = bufferRow + bufferRows
maxDelta = regionEndBufferRow - Math.max(regionStartBufferRow, startBufferRow)
regionDelta = Math.min(delta, maxDelta)
bufferRows -= regionDelta
@regions[index] = { bufferRows, screenRows }
delta -= regionDelta
bufferRow += bufferRows
index++
# This method records insertion or removal of rows on the screen, adjusting the
# screen dimension of regions following the start row accordingly.
applyScreenDelta: (startScreenRow, delta) ->
return if delta is 0
{ index, screenRow } = @traverseToScreenRow(startScreenRow)
if delta > 0 and index < @regions.length
{ bufferRows, screenRows } = @regions[index]
screenRows += delta
@regions[index] = { bufferRows, screenRows }
else
delta = -delta
while delta > 0 and index < @regions.length
{ bufferRows, screenRows } = @regions[index]
regionStartScreenRow = screenRow
regionEndScreenRow = screenRow + screenRows
maxDelta = regionEndScreenRow - Math.max(regionStartScreenRow, startScreenRow)
regionDelta = Math.min(delta, maxDelta)
screenRows -= regionDelta
@regions[index] = { bufferRows, screenRows }
delta -= regionDelta
screenRow += screenRows
index++
traverseToBufferRow: (targetBufferRow) ->
bufferRow = 0
screenRow = 0
for region, index in @regions
if (bufferRow + region.bufferRows) > targetBufferRow
return { region, index, screenRow, bufferRow }
bufferRow += region.bufferRows
screenRow += region.screenRows
{ index, screenRow, bufferRow }
traverseToScreenRow: (targetScreenRow) ->
bufferRow = 0
screenRow = 0
for region, index in @regions
if (screenRow + region.screenRows) > targetScreenRow
return { region, index, screenRow, bufferRow }
bufferRow += region.bufferRows
screenRow += region.screenRows
{ index, screenRow, bufferRow }
+13 -10
Ver Arquivo
@@ -6,15 +6,15 @@ fuzzyFilter = require 'fuzzy-filter'
module.exports =
class SelectList extends View
###
# Internal #
###
### Internal ###
@content: ->
@div class: @viewClass(), =>
@subview 'miniEditor', new Editor(mini: true)
@div class: 'error', outlet: 'error'
@div class: 'loading', outlet: 'loading'
@div class: 'loading', outlet: 'loadingArea', =>
@span class: 'loading-message', outlet: 'loading'
@span class: 'badge', outlet: 'loadingBadge'
@ol outlet: 'list'
@viewClass: -> 'select-list'
@@ -60,8 +60,8 @@ class SelectList extends View
@populateList()
@setLoading()
setError: (message) ->
if not message or message.length == ""
setError: (message='') ->
if message.length is 0
@error.text("").hide()
@removeClass("error")
else
@@ -69,12 +69,15 @@ class SelectList extends View
@error.text(message).show()
@addClass("error")
setLoading: (message) ->
if not message or message.length == ""
@loading.text("").hide()
setLoading: (message='') ->
if message.length is 0
@loading.text("")
@loadingBadge.text("")
@loadingArea.hide()
else
@setError()
@loading.text(message).show()
@loading.text(message)
@loadingArea.show()
populateList: ->
return unless @array?
+1 -1
Ver Arquivo
@@ -2,10 +2,10 @@ Point = require 'point'
Range = require 'range'
{View, $$} = require 'space-pen'
# Internal:
module.exports =
class SelectionView extends View
# Internal: Establishes the DOM for the selection view.
@content: ->
@div class: 'selection'
+122 -135
Ver Arquivo
@@ -13,13 +13,11 @@ class Selection
wordwise: false
needsAutoscroll: null
###
# Internal #
###
### Internal ###
constructor: ({@cursor, @marker, @editSession, @goalBufferRange}) ->
@cursor.selection = this
@editSession.observeMarker @marker, => @screenRangeChanged()
@marker.on 'changed', => @screenRangeChanged()
@cursor.on 'destroyed.selection', =>
@cursor = null
@destroy()
@@ -39,54 +37,54 @@ class Selection
clearAutoscroll: ->
@needsAutoscroll = null
###
# Public #
###
# Public: Identifies if the selection is highlighting anything.
### Public ###
# Identifies if the selection is highlighting anything.
#
# Returns a {Boolean}.
isEmpty: ->
@getBufferRange().isEmpty()
# Public: Identifies if the selection is reversed, that is, it is highlighting "up."
# Identifies if the ending position of a marker is greater than the starting position.
#
# This can happen when, for example, you highlight text "up" in a {Buffer}.
#
# Returns a {Boolean}.
isReversed: ->
@editSession.isMarkerReversed(@marker)
@marker.isReversed()
# Public: Identifies if the selection is a single line.
# Identifies if the selection is a single line.
#
# Returns a {Boolean}.
isSingleScreenLine: ->
@getScreenRange().isSingleLine()
# Public: Retrieves the screen range for the selection.
# Retrieves the screen range for the selection.
#
# Returns a {Range}.
getScreenRange: ->
@editSession.getMarkerScreenRange(@marker)
@marker.getScreenRange()
# Public: Modifies the screen range for the selection.
# Modifies the screen range for the selection.
#
# screenRange - The new {Range} to use
# options - A hash of options matching those found in {.setBufferRange}
setScreenRange: (screenRange, options) ->
@setBufferRange(@editSession.bufferRangeForScreenRange(screenRange), options)
# Public: Retrieves the buffer range for the selection.
# Retrieves the buffer range for the selection.
#
# Returns a {Range}.
getBufferRange: ->
@editSession.getMarkerBufferRange(@marker)
@marker.getBufferRange()
# Public: Modifies the buffer range for the selection.
# Modifies the buffer range for the selection.
#
# screenRange - The new {Range} to select
# options - A hash of options with the following keys:
# :preserveFolds - if `true`, the fold settings are preserved after the selection moves
# :autoscroll - if `true`, the {EditSession} scrolls to the new selection
# preserveFolds: if `true`, the fold settings are preserved after the selection moves
# autoscroll: if `true`, the {EditSession} scrolls to the new selection
setBufferRange: (bufferRange, options={}) ->
bufferRange = Range.fromObject(bufferRange)
@needsAutoscroll = options.autoscroll
@@ -94,9 +92,9 @@ class Selection
@editSession.destroyFoldsIntersectingBufferRange(bufferRange) unless options.preserveFolds
@modifySelection =>
@cursor.needsAutoscroll = false if options.autoscroll?
@editSession.setMarkerBufferRange(@marker, bufferRange, options)
@marker.setBufferRange(bufferRange, options)
# Public: Retrieves the starting and ending buffer rows the selection is highlighting.
# Retrieves the starting and ending buffer rows the selection is highlighting.
#
# Returns an {Array} of two {Number}s: the starting row, and the ending row.
getBufferRowRange: ->
@@ -106,22 +104,17 @@ class Selection
end = Math.max(start, end - 1) if range.end.column == 0
[start, end]
# Internal:
screenRangeChanged: ->
screenRange = @getScreenRange()
@trigger 'screen-range-changed', screenRange
# Public: Retrieves the text in the selection.
# Retrieves the text in the selection.
#
# Returns a {String}.
getText: ->
@editSession.buffer.getTextInRange(@getBufferRange())
# Public: Clears the selection, moving the marker to move to the head.
# Clears the selection, moving the marker to move to the head.
clear: ->
@editSession.clearMarkerTail(@marker)
@marker.clearTail()
# Public: Modifies the selection to mark the current word.
# Modifies the selection to mark the current word.
#
# Returns a {Range}.
selectWord: ->
@@ -135,7 +128,7 @@ class Selection
expandOverWord: ->
@setBufferRange(@getBufferRange().union(@cursor.getCurrentWordBufferRange()))
# Public: Selects an entire line in the {Buffer}.
# Selects an entire line in the {Buffer}.
#
# row - The line {Number} to select (default: the row of the cursor)
selectLine: (row=@cursor.getBufferPosition().row) ->
@@ -149,16 +142,16 @@ class Selection
range = @getBufferRange().union(@cursor.getCurrentLineBufferRange(includeNewline: true))
@setBufferRange(range)
# Public: Selects the text from the current cursor position to a given screen position.
# Selects the text from the current cursor position to a given screen position.
#
# position - An instance of {Point}, with a given `row` and `column`.
selectToScreenPosition: (position) ->
@modifySelection =>
if @initialScreenRange
if position.isLessThan(@initialScreenRange.start)
@editSession.setMarkerScreenRange(@marker, [position, @initialScreenRange.end], reverse: true)
@marker.setScreenRange([position, @initialScreenRange.end], reverse: true)
else
@editSession.setMarkerScreenRange(@marker, [@initialScreenRange.start, position])
@marker.setScreenRange([@initialScreenRange.start, position])
else
@cursor.setScreenPosition(position)
@@ -167,61 +160,61 @@ class Selection
else if @wordwise
@expandOverWord()
# Public: Selects the text from the current cursor position to a given buffer position.
# Selects the text from the current cursor position to a given buffer position.
#
# 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.
# Selects the text one position right of the cursor.
selectRight: ->
@modifySelection => @cursor.moveRight()
# Public: Selects the text one position left of the cursor.
# Selects the text one position left of the cursor.
selectLeft: ->
@modifySelection => @cursor.moveLeft()
# Public: Selects all the text one position above the cursor.
# Selects all the text one position above the cursor.
selectUp: ->
@modifySelection => @cursor.moveUp()
# Public: Selects all the text one position below the cursor.
# Selects all the text one position below the cursor.
selectDown: ->
@modifySelection => @cursor.moveDown()
# Public: Selects all the text from the current cursor position to the top of the buffer.
# Selects all the text from the current cursor position to the top of the buffer.
selectToTop: ->
@modifySelection => @cursor.moveToTop()
# Public: Selects all the text from the current cursor position to the bottom of the buffer.
# Selects all the text from the current cursor position to the bottom of the buffer.
selectToBottom: ->
@modifySelection => @cursor.moveToBottom()
# Public: Selects all the text in the buffer.
# Selects all the text in the buffer.
selectAll: ->
@setBufferRange(@editSession.buffer.getRange(), autoscroll: false)
# Public: Selects all the text from the current cursor position to the beginning of the line.
# Selects all the text from the current cursor position to the beginning of the line.
selectToBeginningOfLine: ->
@modifySelection => @cursor.moveToBeginningOfLine()
# Public: Selects all the text from the current cursor position to the end of the line.
# Selects all the text from the current cursor position to the end of the line.
selectToEndOfLine: ->
@modifySelection => @cursor.moveToEndOfLine()
# Public: Selects all the text from the current cursor position to the beginning of the word.
# Selects all the text from the current cursor position to the beginning of the word.
selectToBeginningOfWord: ->
@modifySelection => @cursor.moveToBeginningOfWord()
# Public: Selects all the text from the current cursor position to the end of the word.
# Selects all the text from the current cursor position to the end of the word.
selectToEndOfWord: ->
@modifySelection => @cursor.moveToEndOfWord()
# Public: Selects all the text from the current cursor position to the beginning of the next word.
# Selects all the text from the current cursor position to the beginning of the next word.
selectToBeginningOfNextWord: ->
@modifySelection => @cursor.moveToBeginningOfNextWord()
# Public: Moves the selection down one row.
# Moves the selection down one row.
addSelectionBelow: ->
range = (@goalBufferRange ? @getBufferRange()).copy()
nextRow = range.end.row + 1
@@ -239,7 +232,7 @@ class Selection
@editSession.addSelectionForBufferRange(range, goalBufferRange: range, suppressMerge: true)
break
# Public: Moves the selection up one row.
# Moves the selection up one row.
addSelectionAbove: ->
range = (@goalBufferRange ? @getBufferRange()).copy()
previousRow = range.end.row - 1
@@ -257,20 +250,25 @@ class Selection
@editSession.addSelectionForBufferRange(range, goalBufferRange: range, suppressMerge: true)
break
# Public: Replaces text at the current selection.
# Replaces text at the current selection.
#
# text - A {String} representing the text to add
# options - A hash containing the following options:
# :normalizeIndent - TODO
# :select - if `true`, selects the newly added text
# :autoIndent - if `true`, indents the newly added text appropriately
# 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)
insertText: (text, options={}) ->
oldBufferRange = @getBufferRange()
@editSession.destroyFoldsContainingBufferRow(oldBufferRange.end.row)
wasReversed = @isReversed()
text = @normalizeIndent(text, options) if options.normalizeIndent
@clear()
@cursor.needsAutoscroll = @cursor.isLastCursor()
if options.indentBasis? and not options.autoIndent
text = @normalizeIndents(text, options.indentBasis)
newBufferRange = @editSession.buffer.change(oldBufferRange, text)
if options.select
@setBufferRange(newBufferRange, reverse: wasReversed)
@@ -278,17 +276,43 @@ class Selection
@cursor.setBufferPosition(newBufferRange.end, skipAtomicTokens: true) if wasReversed
if options.autoIndent
if text == '\n'
@editSession.autoIndentBufferRow(newBufferRange.end.row)
else if /\S/.test(text)
@editSession.autoDecreaseIndentForRow(newBufferRange.start.row)
@editSession.autoIndentBufferRow(row) for row in newBufferRange.getRows()
else if options.autoIndentNewline and text == '\n'
@editSession.autoIndentBufferRow(newBufferRange.end.row)
else if options.autoDecreaseIndent and /\S/.test text
@editSession.autoDecreaseIndentForBufferRow(newBufferRange.start.row)
newBufferRange
# Public: Indents the selection.
normalizeIndents: (text, indentBasis) ->
textPrecedingCursor = @cursor.getCurrentBufferLine()[0...@cursor.getBufferColumn()]
isCursorInsideExistingLine = /\S/.test(textPrecedingCursor)
lines = text.split('\n')
firstLineIndentLevel = @editSession.indentLevelForLine(lines[0])
if isCursorInsideExistingLine
minimumIndentLevel = @editSession.indentationForBufferRow(@cursor.getBufferRow())
else
minimumIndentLevel = @cursor.getIndentLevel() + firstLineIndentLevel
normalizedLines = []
for line, i in lines
if i == 0
indentLevel = firstLineIndentLevel
else if /$^/.test line # remove all indentation from empty lines
indentLevel = 0
else
lineIndentLevel = @editSession.indentLevelForLine(lines[i])
indentLevel = minimumIndentLevel + (lineIndentLevel - indentBasis)
normalizedLines.push(@setIndentationForLine(line, indentLevel))
normalizedLines.join('\n')
# Indents the selection.
#
# options - A hash with one key, `autoIndent`. If `true`, the indentation is
# performed appropriately. Otherwise, {EditSession#getTabText} is used
# options - A hash with one key, `autoIndent`. If `true`, the indentation is
# performed appropriately. Otherwise, {EditSession.getTabText} is used
indent: ({ autoIndent }={})->
{ row, column } = @cursor.getBufferPosition()
@@ -304,68 +328,28 @@ class Selection
else
@indentSelectedRows()
# Public: If the selection spans multiple rows, indents all of them.
# If the selection spans multiple rows, indents all of them.
indentSelectedRows: ->
[start, end] = @getBufferRowRange()
for row in [start..end]
@editSession.buffer.insert([row, 0], @editSession.getTabText()) unless @editSession.buffer.lineLengthForRow(row) == 0
normalizeIndent: (text, options) ->
return text unless /\n/.test(text)
currentBufferRow = @cursor.getBufferRow()
currentBufferColumn = @cursor.getBufferColumn()
lines = text.split('\n')
currentBasis = options.indentBasis ? @editSession.indentLevelForLine(lines[0])
lines[0] = lines[0].replace(/^\s*/, '') # strip leading space from first line
normalizedLines = []
textPrecedingCursor = @editSession.buffer.getTextInRange([[currentBufferRow, 0], [currentBufferRow, currentBufferColumn]])
insideExistingLine = textPrecedingCursor.match(/\S/)
if insideExistingLine
desiredBasis = @editSession.indentationForBufferRow(currentBufferRow)
else if options.autoIndent
desiredBasis = @editSession.suggestedIndentForBufferRow(currentBufferRow)
else
desiredBasis = @cursor.getIndentLevel()
for line, i in lines
if i == 0
if insideExistingLine
delta = 0
else
delta = desiredBasis - @cursor.getIndentLevel()
else
delta = desiredBasis - currentBasis
normalizedLines.push(@adjustIndentationForLine(line, delta))
normalizedLines.join('\n')
adjustIndentationForLine: (line, delta) ->
currentIndentLevel = @editSession.indentLevelForLine(line)
desiredIndentLevel = Math.max(0, currentIndentLevel + delta)
setIndentationForLine: (line, indentLevel) ->
desiredIndentLevel = Math.max(0, indentLevel)
desiredIndentString = @editSession.buildIndentString(desiredIndentLevel)
line.replace(/^[\t ]*/, desiredIndentString)
# Public: Performs a backspace, removing the character found behind the selection.
# Performs a backspace, removing the character found behind the selection.
backspace: ->
if @isEmpty() and not @editSession.isFoldedAtScreenRow(@cursor.getScreenRow())
if @cursor.isAtBeginningOfLine() and @editSession.isFoldedAtScreenRow(@cursor.getScreenRow() - 1)
@selectToBufferPosition([@cursor.getBufferRow() - 1, Infinity])
else
@selectLeft()
@selectLeft() if @isEmpty() and not @editSession.isFoldedAtScreenRow(@cursor.getScreenRow())
@deleteSelectedText()
# Public: Performs a backspace to the beginning of the current word, removing characters found there.
# Performs a backspace to the beginning of the current word, removing characters found there.
backspaceToBeginningOfWord: ->
@selectToBeginningOfWord() if @isEmpty()
@deleteSelectedText()
# Public: Performs a backspace to the beginning of the current line, removing characters found there.
# Performs a backspace to the beginning of the current line, removing characters found there.
backspaceToBeginningOfLine: ->
if @isEmpty() and @cursor.isAtBeginningOfLine()
@selectLeft()
@@ -373,7 +357,7 @@ class Selection
@selectToBeginningOfLine()
@deleteSelectedText()
# Public: Performs a delete, removing the character found ahead of the cursor position.
# Performs a delete, removing the character found ahead of the cursor position.
delete: ->
if @isEmpty()
if @cursor.isAtEndOfLine() and fold = @editSession.largestFoldStartingAtScreenRow(@cursor.getScreenRow() + 1)
@@ -382,22 +366,20 @@ class Selection
@selectRight()
@deleteSelectedText()
# Public: Performs a delete to the end of the current word, removing characters found there.
# Performs a delete to the end of the current word, removing characters found there.
deleteToEndOfWord: ->
@selectToEndOfWord() if @isEmpty()
@deleteSelectedText()
# Public: Deletes the selected text.
# Deletes the selected text.
deleteSelectedText: ->
bufferRange = @getBufferRange()
if fold = @editSession.largestFoldContainingBufferRow(bufferRange.end.row)
includeNewline = bufferRange.start.column == 0 or bufferRange.start.row >= fold.startRow
bufferRange = bufferRange.union(fold.getBufferRange({ includeNewline }))
if bufferRange.isEmpty() and fold = @editSession.largestFoldContainingBufferRow(bufferRange.start.row)
bufferRange = bufferRange.union(fold.getBufferRange(includeNewline: true))
@editSession.buffer.delete(bufferRange) unless bufferRange.isEmpty()
@cursor?.setBufferPosition(bufferRange.start)
# Public: Deletes the line.
# Deletes the line.
deleteLine: ->
if @isEmpty()
start = @cursor.getScreenRow()
@@ -414,7 +396,7 @@ class Selection
end--
@editSession.buffer.deleteRows(start, end)
# Public: Joins the current line with the one below it.
# Joins the current line with the one below it.
#
# If there selection spans more than one line, all the lines are joined together.
joinLine: ->
@@ -438,9 +420,9 @@ class Selection
@deleteSelectedText()
if joinMarker?
newSelectedRange = @editSession.getMarkerBufferRange(joinMarker)
newSelectedRange = joinMarker.getBufferRange()
@setBufferRange(newSelectedRange)
@editSession.destroyMarker(joinMarker)
joinMarker.destroy()
outdentSelectedRows: ->
[start, end] = @getBufferRowRange()
@@ -454,27 +436,27 @@ class Selection
[start, end] = @getBufferRowRange()
@editSession.autoIndentBufferRows(start, end)
# Public: Wraps the selected lines in comments.
# Wraps the selected lines in comments.
#
# Returns an {Array} of the commented {Ranges}.
toggleLineComments: ->
@editSession.toggleLineCommentsForBufferRows(@getBufferRowRange()...)
# Public: Performs a cut operation on the selection, until the end of the line.
# Performs a cut operation on the selection, until the end of the line.
#
# maintainPasteboard - A {Boolean} indicating TODO
cutToEndOfLine: (maintainPasteboard) ->
@selectToEndOfLine() if @isEmpty()
@cut(maintainPasteboard)
# Public: Performs a cut operation on the selection.
# Performs a cut operation on the selection.
#
# maintainPasteboard - A {Boolean} indicating TODO
cut: (maintainPasteboard=false) ->
@copy(maintainPasteboard)
@delete()
# Public: Performs a copy operation on the selection.
# Performs a copy operation on the selection.
#
# maintainPasteboard - A {Boolean} indicating TODO
copy: (maintainPasteboard=false) ->
@@ -488,28 +470,27 @@ class Selection
pasteboard.write(text, metadata)
# Public: Folds the selection.
# Folds the selection.
fold: ->
range = @getBufferRange()
@editSession.createFold(range.start.row, range.end.row)
@cursor.setBufferPosition([range.end.row + 1, 0])
autoIndentText: (text) ->
@editSession.autoIndentTextAfterBufferPosition(text, @cursor.getBufferPosition())
autoOutdent: ->
@editSession.autoOutdentBufferRow(@cursor.getBufferRow())
modifySelection: (fn) ->
@retainSelection = true
@placeTail()
fn()
@retainSelection = false
# Sets the marker's tail to the same position as the marker's head.
#
# This only works if there isn't already a tail position.
#
# Returns a {Point} representing the new tail position.
placeTail: ->
@editSession.placeMarkerTail(@marker)
@marker.placeTail()
# Public: Identifies if a selection intersects with a given buffer range.
# Identifies if a selection intersects with a given buffer range.
#
# bufferRange - A {Range} to check against
#
@@ -517,7 +498,7 @@ class Selection
intersectsBufferRange: (bufferRange) ->
@getBufferRange().intersectsWith(bufferRange)
# Public: Identifies if a selection intersects with another selection.
# Identifies if a selection intersects with another selection.
#
# otherSelection - A `Selection` to check against
#
@@ -525,7 +506,7 @@ class Selection
intersectsWith: (otherSelection) ->
@getBufferRange().intersectsWith(otherSelection.getBufferRange())
# Public: Merges two selections together.
# Merges two selections together.
#
# otherSelection - A `Selection` to merge with
# options - A hash of options matching those found in {.setBufferRange}
@@ -537,4 +518,10 @@ class Selection
@goalBufferRange = otherSelection.goalBufferRange
otherSelection.destroy()
### Internal ###
screenRangeChanged: ->
screenRange = @getScreenRange()
@trigger 'screen-range-changed', screenRange
_.extend Selection.prototype, EventEmitter
+1 -3
Ver Arquivo
@@ -6,9 +6,7 @@ fsUtils = require 'fs-utils'
EventEmitter = require 'event-emitter'
NullGrammar = require 'null-grammar'
###
# Internal #
###
### Internal ###
module.exports =
class Syntax
+144 -204
Ver Arquivo
@@ -29,7 +29,7 @@ class Buffer
invalidMarkers: null
refcount: 0
# Public: Creates a new buffer.
# Creates a new buffer.
#
# path - A {String} representing the file path
# initialText - A {String} setting the starting text
@@ -55,9 +55,7 @@ class Buffer
@undoManager = new UndoManager(this)
###
# Internal #
###
### Internal ###
destroy: ->
throw new Error("Destroying buffer twice with path '#{@getPath()}'") if @destroyed
@@ -97,19 +95,17 @@ class Buffer
@file.on "moved", =>
@trigger "path-changed", this
###
# Public #
###
# Public: Identifies if the buffer belongs to multiple editors.
### Public ###
# Identifies if the buffer belongs to multiple editors.
#
# For example, if the {Editor} was split.
#
# Returns a {Boolean}.
# Returns a {Boolean}.
hasMultipleEditors: -> @refcount > 1
# Public: Reloads a file in the {EditSession}.
# Reloads a file in the {EditSession}.
#
# Essentially, this performs a force read of the file.
reload: ->
@@ -119,25 +115,25 @@ class Buffer
@triggerModifiedStatusChanged(false)
@trigger 'reloaded'
# Public: Rereads the contents of the file, and stores them in the cache.
# Rereads the contents of the file, and stores them in the cache.
#
# Essentially, this performs a force read of the file on disk.
updateCachedDiskContents: ->
@cachedDiskContents = @file.read()
# Public: Gets the file's basename--that is, the file without any directory information.
# Gets the file's basename--that is, the file without any directory information.
#
# Returns a {String}.
getBaseName: ->
@file?.getBaseName()
# Public: Retrieves the path for the file.
# Retrieves the path for the file.
#
# Returns a {String}.
getPath: ->
@file?.getPath()
# Public: Sets the path for the file.
# Sets the path for the file.
#
# path - A {String} representing the new file path
setPath: (path) ->
@@ -150,7 +146,7 @@ class Buffer
@trigger "path-changed", this
# Public: Retrieves the current buffer's file extension.
# Retrieves the current buffer's file extension.
#
# Returns a {String}.
getExtension: ->
@@ -159,25 +155,25 @@ class Buffer
else
null
# Public: Retrieves the cached buffer contents.
# Retrieves the cached buffer contents.
#
# Returns a {String}.
getText: ->
@cachedMemoryContents ?= @getTextInRange(@getRange())
# Public: Replaces the current buffer contents.
# Replaces the current buffer contents.
#
# text - A {String} containing the new buffer contents.
setText: (text) ->
@change(@getRange(), text, normalizeLineEndings: false)
# Public: Gets the range of the buffer contents.
# Gets the range of the buffer contents.
#
# Returns a new {Range}, from `[0, 0]` to the end of the buffer.
getRange: ->
new Range([0, 0], [@getLastRow(), @getLastLine().length])
# Public: Given a range, returns the lines of text within it.
# Given a range, returns the lines of text within it.
#
# range - A {Range} object specifying your points of interest
#
@@ -197,13 +193,13 @@ class Buffer
return multipleLines.join ''
# Public: Gets all the lines in a file.
# Gets all the lines in a file.
#
# Returns an {Array} of {String}s.
getLines: ->
@lines
# Public: Given a row, returns the line of text.
# Given a row, returns the line of text.
#
# row - A {Number} indicating the row.
#
@@ -211,13 +207,18 @@ class Buffer
lineForRow: (row) ->
@lines[row]
# Given a row, returns its line ending.
#
# row - A {Number} indicating the row.
#
# Returns a {String}, or `undefined` if `row` is the final row.
lineEndingForRow: (row) ->
@lineEndings[row] unless row is @getLastRow()
suggestedLineEndingForRow: (row) ->
@lineEndingForRow(row) ? @lineEndingForRow(row - 1)
# Public: Given a row, returns the length of the line of text.
# Given a row, returns the length of the line of text.
#
# row - A {Number} indicating the row.
#
@@ -225,13 +226,18 @@ class Buffer
lineLengthForRow: (row) ->
@lines[row].length
# Given a row, returns the length of the line ending
#
# row - A {Number} indicating the row.
#
# Returns a {Number}.
lineEndingLengthForRow: (row) ->
(@lineEndingForRow(row) ? '').length
# Public: Given a buffer row, this retrieves the range for that line.
# Given a buffer row, this retrieves the range for that line.
#
# row - A {Number} identifying the row
# options - A hash with one key, `includeNewline`, which specifies whether you
# options - A hash with one key, `includeNewline`, which specifies whether you
# want to include the trailing newline
#
# Returns a {Range}.
@@ -241,25 +247,25 @@ class Buffer
else
new Range([row, 0], [row, @lineLengthForRow(row)])
# Public: Gets the number of lines in a file.
# Gets the number of lines in a file.
#
# Returns a {Number}.
getLineCount: ->
@getLines().length
# Public: Gets the row number of the last line.
# Gets the row number of the last line.
#
# Returns a {Number}.
getLastRow: ->
@getLines().length - 1
# Public: Finds the last line in the current buffer.
# Finds the last line in the current buffer.
#
# Returns a {String}.
getLastLine: ->
@lineForRow(@getLastRow())
# Public: Finds the last point in the current buffer.
# Finds the last point in the current buffer.
#
# Returns a {Point} representing the last position.
getEofPosition: ->
@@ -282,13 +288,13 @@ class Buffer
new Point(row, index)
# Public: Given a row, this deletes it from the buffer.
# Given a row, this deletes it from the buffer.
#
# row - A {Number} representing the row to delete
deleteRow: (row) ->
@deleteRows(row, row)
# Public: Deletes a range of rows from the buffer.
# Deletes a range of rows from the buffer.
#
# start - A {Number} representing the starting row
# end - A {Number} representing the ending row
@@ -307,36 +313,29 @@ class Buffer
@delete(new Range(startPoint, endPoint))
# Public: Adds text to the end of the buffer.
# Adds text to the end of the buffer.
#
# text - A {String} of text to add
append: (text) ->
@insert(@getEofPosition(), text)
# Public: Adds text to a specific point in the buffer
# Adds text to a specific point in the buffer
#
# point - A {Point} in the buffer to insert into
# text - A {String} of text to add
insert: (point, text) ->
@change(new Range(point, point), text)
# Public: Deletes text from the buffer
# Deletes text from the buffer
#
# range - A {Range} whose text to delete
delete: (range) ->
@change(range, '')
# Internal:
change: (oldRange, newText, options) ->
oldRange = Range.fromObject(oldRange)
operation = new BufferChangeOperation({buffer: this, oldRange, newText, options})
range = @pushOperation(operation)
range
# Public: Given a position, this clips it to a real position.
# Given a position, this clips it to a real position.
#
# For example, if `position`'s row exceeds the row count of the buffer,
# or if its column goes beyond a line's length, this "sanitizes" the value
# or if its column goes beyond a line's length, this "sanitizes" the value
# to a real position.
#
# Returns the new, clipped {Point}. Note that this could be the same as `position` if no clipping was performed.
@@ -350,11 +349,11 @@ class Buffer
column = Math.max(position.column, 0)
column = Math.min(@lineLengthForRow(row), column)
new Point(row, column)
# Public: Given a range, this clips it to a real range.
# Given a range, this clips it to a real range.
#
# For example, if `range`'s row exceeds the row count of the buffer,
# or if its column goes beyond a line's length, this "sanitizes" the value
# or if its column goes beyond a line's length, this "sanitizes" the value
# to a real range.
#
# range - The {Point} to clip
@@ -368,31 +367,21 @@ class Buffer
prefix: @lines[range.start.row][0...range.start.column]
suffix: @lines[range.end.row][range.end.column..]
# Internal:
pushOperation: (operation, editSession) ->
if @undoManager
@undoManager.pushOperation(operation, editSession)
else
operation.do()
# Internal:
transact: (fn) -> @undoManager.transact(fn)
# Public: Undos the last operation.
# Undos the last operation.
#
# editSession - The {EditSession} associated with the buffer.
undo: (editSession) -> @undoManager.undo(editSession)
# Public: Redos the last operation.
# Redos the last operation.
#
# editSession - The {EditSession} associated with the buffer.
redo: (editSession) -> @undoManager.redo(editSession)
commit: -> @undoManager.commit()
abort: -> @undoManager.abort()
# Public: Saves the buffer.
# Saves the buffer.
save: ->
@saveAs(@getPath()) if @isModified()
# Public: Saves the buffer at a specific path.
# Saves the buffer at a specific path.
#
# path - The path to save at.
saveAs: (path) ->
@@ -405,7 +394,7 @@ class Buffer
@triggerModifiedStatusChanged(false)
@trigger 'saved'
# Public: Identifies if the buffer was modified.
# Identifies if the buffer was modified.
#
# Returns a {Boolean}.
isModified: ->
@@ -414,41 +403,78 @@ class Buffer
else
not @isEmpty()
# Public: Identifies if a buffer is in a git conflict with `HEAD`.
# Identifies if a buffer is in a git conflict with `HEAD`.
#
# Returns a {Boolean}.
isInConflict: -> @conflict
# Public: Identifies if a buffer is empty.
# Identifies if a buffer is empty.
#
# Returns a {Boolean}.
isEmpty: -> @lines.length is 1 and @lines[0].length is 0
getMarkers: ->
_.values(@validMarkers)
# Returns all valid {BufferMarker}s on the buffer.
getMarkers: ({includeInvalid} = {}) ->
markers = _.values(@validMarkers)
if includeInvalid
markers.concat(_.values(@invalidMarkers))
else
markers
# Public: Retrieves the quantity of markers in a buffer.
# Returns the {BufferMarker} with the given id.
getMarker: (id) ->
@validMarkers[id]
# Public: Finds the first marker satisfying the given attributes
#
# Returns a {String} marker-identifier
findMarker: (attributes) ->
@findMarkers(attributes)[0]
# Public: Finds all markers satisfying the given attributes
#
# attributes - The attributes against which to compare the markers' attributes
# There are some reserved keys that match against derived marker properties:
# startRow - The row at which the marker starts
# endRow - The row at which the marker ends
#
# Returns an {Array} of {BufferMarker}s
findMarkers: (attributes) ->
markers = @getMarkers().filter (marker) -> marker.matchesAttributes(attributes)
markers.sort (a, b) -> a.getRange().compare(b.getRange())
# Retrieves the quantity of markers in a buffer.
#
# Returns a {Number}.
getMarkerCount: ->
_.size(@validMarkers)
# Public: Constructs a new marker at a given range.
# Constructs a new marker at a given range.
#
# range - The marker {Range} (representing the distance between the head and tail)
# options - Options to pass to the {BufferMarker} constructor
# attributes - An optional hash of serializable attributes
# Any attributes you pass will be associated with the marker and can be retrieved
# or used in marker queries.
# The following attribute keys reserved, and control the marker's initial range
# reverse - if `true`, the marker is reversed; that is, its head precedes the tail
# noTail - if `true`, the marker is created without a tail
#
# Returns a {Number} representing the new marker's ID.
markRange: (range, options={}) ->
markRange: (range, attributes={}) ->
optionKeys = ['invalidationStrategy', 'noTail', 'reverse']
options = _.pick(attributes, optionKeys)
attributes = _.omit(attributes, optionKeys)
marker = new BufferMarker(_.defaults({
id: (@nextMarkerId++).toString()
buffer: this
range
attributes
}, options))
@validMarkers[marker.id] = marker
marker.id
@trigger 'marker-created', marker
marker
# Public: Constructs a new marker at a given position.
# Constructs a new marker at a given position.
#
# position - The marker {Point}; there won't be a tail
# options - Options to pass to the {BufferMarker} constructor
@@ -457,131 +483,16 @@ class Buffer
markPosition: (position, options) ->
@markRange([position, position], _.defaults({noTail: true}, options))
# Public: Removes the marker with the given id.
#
# id - The {Number} of the ID to remove
destroyMarker: (id) ->
delete @validMarkers[id]
delete @invalidMarkers[id]
getMarkerPosition: (args...) ->
@getMarkerHeadPosition(args...)
setMarkerPosition: (args...) ->
@setMarkerHeadPosition(args...)
# Public: Retrieves the position of the marker's head.
#
# id - A {Number} representing the marker to check
#
# Returns a {Point}, or `null` if the marker does not exist.
getMarkerHeadPosition: (id) ->
@validMarkers[id]?.getHeadPosition()
# Public: Sets the position of the marker's head.
#
# id - A {Number} representing the marker to change
# position - The new {Point} to place the head
# options - A hash with the following keys:
# :clip - if `true`, the point is [clipped]{Buffer.clipPosition}
# :bufferChanged - if `true`, indicates that the {Buffer} should trigger an event that it's changed
#
# Returns a {Point} representing the new head position.
setMarkerHeadPosition: (id, position, options) ->
@validMarkers[id]?.setHeadPosition(position)
# Public: Retrieves the position of the marker's tail.
#
# id - A {Number} representing the marker to check
#
# Returns a {Point}, or `null` if the marker does not exist.
getMarkerTailPosition: (id) ->
@validMarkers[id]?.getTailPosition()
# Public: Sets the position of the marker's tail.
#
# id - A {Number} representing the marker to change
# position - The new {Point} to place the tail
# options - A hash with the following keys:
# :clip - if `true`, the point is [clipped]{Buffer.clipPosition}
# :bufferChanged - if `true`, indicates that the {Buffer} should trigger an event that it's changed
#
# Returns a {Point} representing the new tail position.
setMarkerTailPosition: (id, position, options) ->
@validMarkers[id]?.setTailPosition(position)
# Public: Retrieves the {Range} between a marker's head and its tail.
#
# id - A {Number} representing the marker to check
#
# Returns a {Range}.
getMarkerRange: (id) ->
@validMarkers[id]?.getRange()
# Public: Sets the marker's range, potentialy modifying both its head and tail.
#
# id - A {Number} representing the marker to change
# range - The new {Range} the marker should cover
# options - A hash of options with the following keys:
# :reverse - if `true`, the marker is reversed; that is, its tail is "above" the head
# :noTail - if `true`, the marker doesn't have a tail
setMarkerRange: (id, range, options) ->
@validMarkers[id]?.setRange(range, options)
# Public: Sets the marker's tail to the same position as the marker's head.
#
# This only works if there isn't already a tail position.
#
# id - A {Number} representing the marker to change
#
# Returns a {Point} representing the new tail position.
placeMarkerTail: (id) ->
@validMarkers[id]?.placeTail()
# Public: Removes the tail from the marker.
#
# id - A {Number} representing the marker to change
clearMarkerTail: (id) ->
@validMarkers[id]?.clearTail()
# Public: Identifies if the ending position of a marker is greater than the starting position.
#
# This can happen when, for example, you highlight text "up" in a {Buffer}.
#
# id - A {Number} representing the marker to check
#
# Returns a {Boolean}.
isMarkerReversed: (id) ->
@validMarkers[id]?.isReversed()
# Public: Identifies if the marker's head position is equal to its tail.
#
# id - A {Number} representing the marker to check
#
# Returns a {Boolean}.
isMarkerRangeEmpty: (id) ->
@validMarkers[id]?.isRangeEmpty()
# Public: Sets a callback to be fired whenever a marker is changed.
#
# id - A {Number} representing the marker to watch
# callback - A {Function} to execute
observeMarker: (id, callback) ->
@validMarkers[id]?.observe(callback)
# Public: Given a buffer position, this finds all markers that contain the position.
# Given a buffer position, this finds all markers that contain the position.
#
# bufferPosition - A {Point} to check
#
# Returns an {Array} of {Numbers}, representing marker IDs containing `bufferPosition`.
markersForPosition: (bufferPosition) ->
bufferPosition = Point.fromObject(bufferPosition)
ids = []
for id, marker of @validMarkers
ids.push(id) if marker.containsPoint(bufferPosition)
ids
markersForPosition: (position) ->
position = Point.fromObject(position)
@getMarkers().filter (marker) -> marker.containsPoint(position)
# Public: Identifies if a character sequence is within a certain range.
# Identifies if a character sequence is within a certain range.
#
# regex - The {RegExp} to check
# startIndex - The starting row {Number}
@@ -611,14 +522,14 @@ class Buffer
matches
# Public: Scans for text in the buffer, calling a function on each match.
# Scans for text in the buffer, calling a function on each match.
#
# regex - A {RegExp} representing the text to find
# iterator - A {Function} that's called on each match
scan: (regex, iterator) ->
@scanInRange(regex, @getRange(), iterator)
# Public: Scans for text in a given range, calling a function on each match.
# Scans for text in a given range, calling a function on each match.
#
# regex - A {RegExp} representing the text to find
# range - A {Range} in the buffer to search within
@@ -661,7 +572,7 @@ class Buffer
break unless global and keepLooping
# Public: Scans for text in a given range _backwards_, calling a function on each match.
# Scans for text in a given range _backwards_, calling a function on each match.
#
# regex - A {RegExp} representing the text to find
# range - A {Range} in the buffer to search within
@@ -669,7 +580,7 @@ class Buffer
backwardsScanInRange: (regex, range, iterator) ->
@scanInRange regex, range, iterator, true
# Public: Given a row, identifies if it is blank.
# Given a row, identifies if it is blank.
#
# row - A row {Number} to check
#
@@ -677,7 +588,7 @@ class Buffer
isRowBlank: (row) ->
not /\S/.test @lineForRow(row)
# Public: Given a row, this finds the next row above it that's empty.
# Given a row, this finds the next row above it that's empty.
#
# startRow - A {Number} identifying the row to start checking at
#
@@ -691,7 +602,7 @@ class Buffer
return row unless @isRowBlank(row)
null
# Public: Given a row, this finds the next row that's blank.
# Given a row, this finds the next row that's blank.
#
# startRow - A row {Number} to check
#
@@ -704,7 +615,7 @@ class Buffer
return row unless @isRowBlank(row)
null
# Public: Identifies if the buffer has soft tabs anywhere.
# Identifies if the buffer has soft tabs anywhere.
#
# Returns a {Boolean},
usesSoftTabs: ->
@@ -713,22 +624,51 @@ class Buffer
return match[0][0] != '\t'
undefined
# Public: Checks out the current `HEAD` revision of the file.
# Checks out the current `HEAD` revision of the file.
checkoutHead: ->
path = @getPath()
return unless path
git?.checkoutHead(path)
# Public: Checks to see if a file exists.
# Checks to see if a file exists.
#
# Returns a {Boolean}.
fileExists: ->
@file? && @file.exists()
### Internal ###
###
# Internal #
###
pushOperation: (operation, editSession) ->
if @undoManager
@undoManager.pushOperation(operation, editSession)
else
operation.do()
transact: (fn) ->
if isNewTransaction = @undoManager.transact()
@pushOperation(new BufferChangeOperation(buffer: this)) # restores markers on undo
if fn
try
fn()
finally
@commit() if isNewTransaction
commit: ->
@pushOperation(new BufferChangeOperation(buffer: this)) # restores markers on redo
@undoManager.commit()
abort: -> @undoManager.abort()
change: (oldRange, newText, options) ->
oldRange = Range.fromObject(oldRange)
operation = new BufferChangeOperation({buffer: this, oldRange, newText, options})
range = @pushOperation(operation)
range
destroyMarker: (id) ->
if marker = @validMarkers[id] ? @invalidMarkers[id]
delete @validMarkers[id]
delete @invalidMarkers[id]
scheduleModifiedEvents: ->
clearTimeout(@stoppedChangingTimeout) if @stoppedChangingTimeout
+2 -6
Ver Arquivo
@@ -8,9 +8,7 @@ EventEmitter = require 'event-emitter'
pathSplitRegex = new RegExp("[#{nodePath.sep}.]")
TextMateScopeSelector = require 'text-mate-scope-selector'
###
# Internal #
###
### Internal ###
module.exports =
class TextMateGrammar
@@ -502,9 +500,7 @@ class Pattern
tokens
###
# Internal #
###
### Internal ###
shiftCapture = (captureIndices) ->
[captureIndices.shift(), captureIndices.shift(), captureIndices.shift()]

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