Comparar commits

...

579 Commits

Autor SHA1 Mensagem Data
Nathan Sobo d14d4aaae1 Remove subscriptions in editor view that are handled in presenter 2015-02-04 15:12:21 -07:00
Nathan Sobo 77599c799c Rename TextEditorPresenter::height to ::explicitHeight
This clarifies that the height is being assigned externally rather than
derived from the content.
2015-02-04 15:10:58 -07:00
Nathan Sobo b94576dc09 Pass view measurements to model via presenter
Someday, we won’t need to pass measurements to the model anymore.
2015-02-04 14:48:17 -07:00
Nathan Sobo 9bdb961b3f Update vertical scroll state when contentFrameWidth changes
A wider content frame can mean the horizontal scrollbar gets hidden,
which could in turn mean we need to adjust the scrollTop because the
clientHeight changed.
2015-02-04 11:51:46 -07:00
Nathan Sobo 59d96c9f1a Measure scrollbars immediately when editor becomes visible
This ensures all required measurements are present so assignment of
subsequent measurements such as backgroundColor have an effect.
2015-02-04 11:48:47 -07:00
Nathan Sobo 6196882b9d Include scrollbar dimensions in required measurements in presenter 2015-02-04 11:35:07 -07:00
Nathan Sobo 3973939de1 Don’t constrain scrollTop/Left until required measurements are assigned
This commit also adds to the list of required measurements and updates
the spec with a buildPresenter helper to more easily supply default
values for required measurements in each spec when they aren’t relevant
to that spec’s content.
2015-02-04 11:13:24 -07:00
Nathan Sobo b792190693 Constrain scrollLeft based on computed clientWith and scrollWidth 2015-02-04 10:22:29 -07:00
Nathan Sobo 1ae25ed85d Make randomized presenter spec failures easier to reproduce 2015-02-04 10:05:47 -07:00
Nathan Sobo 5bb3095ffa Constrain scrollTop based on clientHeight and scrollHeight 2015-02-04 09:38:00 -07:00
Nathan Sobo 3656d4cca6 Pass options in DisplayBuffer::screenRangeForBufferRange 2015-02-04 08:42:34 -07:00
Nathan Sobo 3e6669cf3e Log code for repeatable randomized presenter spec failures 2015-02-04 08:42:06 -07:00
Nathan Sobo 6977660699 Store ‘isOnlyWhitespace’ in line presenter state 2015-02-04 07:59:34 -07:00
Nathan Sobo ba6d11e24e Merge branch 'master' into ns-editor-presenters 2015-02-04 07:57:04 -07:00
Nathan Sobo 86991bbec2 Merge branch 'driskell-bugs/ident_guide_whitespace_only' 2015-02-03 21:26:25 -07:00
Nathan Sobo 973d7ebf13 Add spec coverage for indent guides + invisibles on blank lines 2015-02-03 21:26:01 -07:00
Nathan Sobo a685f3dc37 Prevent updates for off-screen line decoration markers on change
When there were lots of off-screen markers, we were performing lots of
redundant updates when off-screen markers changed. Now we only perform
updates if they intersect the visible row range.

@maxbrunsfeld this should improve the situation for folding/unfolding
when there are lots of others folds. Let me know.
2015-02-03 21:02:33 -07:00
Nathan Sobo 20a95269c9 Preserve line number for mouseWheelScreenRow in a sane way
What we were doing before made no sense.

Signed-off-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
2015-02-03 20:41:59 -07:00
Kevin Sawicki c7315e2be6 Merge pull request #5342 from paulcbetts/deltas
Delta Packages for Windows + Update Squirrel.Windows
2015-02-03 17:14:36 -08:00
Kevin Sawicki a4976c32ae Remove release notes from event payload
The release notes are provided by Squirrel on Mac but not by Squirrel for
Windows and the release notes package pulls them down manually anyway
so this field is no longer needed.

Checking for the presence of the release notes previously was preventing the
event from firing on Windows which would cause old release notes to show in
the package.

Closes #3757
2015-02-03 14:57:33 -08:00
Nathan Sobo 78b8039384 Don’t require sync update before measuring when autoHeight changes
Signed-off-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
2015-02-03 15:05:45 -07:00
Nathan Sobo cd77870286 In presenter, handle the first line being soft-wrapped
Signed-off-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
2015-02-03 15:05:44 -07:00
Nathan Sobo 75652e36d9 Remove ? guard from ::oldState and ::newState references 2015-02-03 15:05:44 -07:00
Nathan Sobo 9991cd73c7 Reference scrollingVertically in the proper location in presenter state 2015-02-03 15:05:44 -07:00
Nathan Sobo ac215e11b4 Fix velocity scrolling upward
There’s no need to check that the mouseWheelScreenRow is on screen
before preserving the corresponding line.
2015-02-03 15:05:44 -07:00
Nathan Sobo 20ce35c017 Emit update events in TextEditorPresenter when highlights are removed 2015-02-03 15:05:44 -07:00
Nathan Sobo edd595a72f Access mouseWheelScreenRow property instead of calling removed method 2015-02-03 15:05:43 -07:00
Nathan Sobo 0a6116a785 Fix undefined method errors in overlay manager
Access properties directly now
2015-02-03 15:05:43 -07:00
Nathan Sobo 2dc29a60ef Avoid full decorations query when decorations are added 2015-02-03 15:05:43 -07:00
Nathan Sobo b78522d8e6 Avoid full decoration query when line decorations’ markers change 2015-02-03 15:05:43 -07:00
Nathan Sobo c6f23c2edb Dispose of decoration subscriptions when it’s destroyed 2015-02-03 15:05:43 -07:00
Nathan Sobo 342903567e Avoid full decoration query when line decorations are destroyed 2015-02-03 15:05:43 -07:00
Nathan Sobo 648c58d41e Hide line decorations for invalid markers again 2015-02-03 15:05:42 -07:00
Nathan Sobo 7deb411e84 Fix randomized spec failures 2015-02-03 15:05:42 -07:00
Nathan Sobo cf50ec1861 Fix action logging in random spec 2015-02-03 15:05:42 -07:00
Nathan Sobo 4a74d4adba Avoid decoration queries when updating individual highlight decorations 2015-02-03 15:05:42 -07:00
Nathan Sobo f337553a70 Handle highlight state updates in ::updateDecorations 2015-02-03 15:05:42 -07:00
Nathan Sobo 0e85efdd28 🐎 Cache line/line-number decorations to avoid per-line queries 2015-02-03 15:05:34 -07:00
Nathan Sobo 3884a30f39 Unify decoration observation 2015-02-03 15:05:33 -07:00
Nathan Sobo f99b85a299 Use properties directly instead of getters
This will emphasize a design where everything is updated when the model
and view measurements change rather than recomputed as needed.
2015-02-03 15:05:33 -07:00
Nathan Sobo de5c1fc28d Add randomized fuzz test for TextEditorPresenter
This test performs random operations on the editor and assigns random
measurements from the view. After each operation, the state of a
pre-existing presenter is compared with that of a new presenter created
with the same parameters.

Since it’s easier to reason about building fresh state than it is to
reason about state updates, I hope this will catch any bugs in our
update logic as we optimize it and explore every corner case.
2015-02-03 15:05:33 -07:00
Nathan Sobo 89344c6cfd Update scrollbars state when editor content changes 2015-02-03 15:05:33 -07:00
Nathan Sobo 74e4756ef0 🐎 Don’t update presenter state unless measurements change 2015-02-03 15:05:33 -07:00
Nathan Sobo 510520d2c7 🎨 rename ::getStart/EndRow to ::computeStart/EndRow 2015-02-03 15:05:32 -07:00
Nathan Sobo 4eb39b1be2 Remove unnecessary onDidChangeSoftWrapped subscription 2015-02-03 15:05:32 -07:00
Nathan Sobo 9d507ea692 Break out state updates explicitly when editor content changes 2015-02-03 15:05:32 -07:00
Nathan Sobo f005b2005f Add specs for updating scrollWidth when the longest line changes 2015-02-03 15:05:32 -07:00
Nathan Sobo b521e8dc97 Handle auto-height in TextEditorPresenter 2015-02-03 15:05:32 -07:00
Nathan Sobo bbc1a264b5 Make each section of presenter state self-contained
This means we have some duplicated values in different parts of the
tree, but it’s cleaner in the view since each component only consumes
a single object. Seems like the presenter should convey the correct
data to the correct locations and minimize the logic in the view. A
few duplicated integers is a reasonable trade-off.
2015-02-03 15:05:31 -07:00
Nathan Sobo da4b3a47ef Don’t observe decorations in text editor view 2015-02-03 15:05:31 -07:00
Nathan Sobo 0e27bebbb3 Fix indentation 2015-02-03 15:05:31 -07:00
Nathan Sobo efdba9fc24 Use presenter state for scrollbars and scrollbar corner 2015-02-03 15:05:31 -07:00
Nathan Sobo 0910e86357 Add .right and .bottom to presenter scrollbar states 2015-02-03 15:05:31 -07:00
Nathan Sobo 14776e3f0a Give each scrollbar its own state object and track visibility 2015-02-03 15:05:30 -07:00
Nathan Sobo 42ab02d7d2 Rename ::clientHeight/Width to ::height and ::contentFrameWidth
The term “client” was actually a misnomer here because it typically
means the height excluding the scrollbars, which wasn’t how we were
using it here.
2015-02-03 15:05:30 -07:00
Nathan Sobo 1b5be9aef8 Track horizontal/vertical scrollbar height/width in presenter 2015-02-03 15:05:30 -07:00
Nathan Sobo 9de8ab949f Drop TextEditorComponent::getRenderedRowRange 2015-02-03 15:05:30 -07:00
Nathan Sobo 6108c04f40 Add spec coverage for deprecated ‘cursor:moved’ event
So we don’t accidentally drop it during this transition
2015-02-03 15:05:30 -07:00
Nathan Sobo d8cafb1fc6 Don’t observe screen line changes in editor component 2015-02-03 15:05:29 -07:00
Nathan Sobo 76241fb779 Handle line and line number preservation in presenter
The target of mousewheel events needs to be preserved when scrolling.
It used to be dealt with in the view, but now we can do it in the
presenter for a simpler view implementation.
2015-02-03 15:05:29 -07:00
Nathan Sobo d9a5d141eb Clear ::mouseWheelScreenRow after delay even if we don’t actually scroll 2015-02-03 15:05:29 -07:00
Nathan Sobo 37a040a620 Preserve line numbers in presenter based on ::mouseWheelScreenRow 2015-02-03 15:05:29 -07:00
Nathan Sobo 35d3690088 Preserve lines in TextEditorPresenter based on ::mouseWheelScreenRow 2015-02-03 15:05:29 -07:00
Nathan Sobo 578a76ba6b Make “mouse wheel” 2 words in presenter API
Signed-off-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
2015-02-03 15:05:28 -07:00
Nathan Sobo da5ee3fa86 Add TextEditorPresenter::state.mousewheelScreenRow 2015-02-03 15:05:28 -07:00
Nathan Sobo a88486e950 Use maxLineNumberDigits from presenter state in GutterComponent 2015-02-03 15:05:28 -07:00
Nathan Sobo 1a22952eda Only store backgroundColor and gutterBackgroundColor on presenter 2015-02-03 15:05:28 -07:00
Nathan Sobo fd50a0db6c Construct TextEditorPresenter before component mounts
This allows us to use the presenter for all stages of the component
lifecycle rather than needing to wait until it is created.

Signed-off-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
2015-02-03 15:05:28 -07:00
Nathan Sobo ac25596002 Wait for required measurements before building some presenter state
Signed-off-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
2015-02-03 15:05:27 -07:00
Max Brunsfeld 9cc7ecb1be Add maxLineNumberDigits to presenter state 2015-02-03 15:05:27 -07:00
Max Brunsfeld 60fca8d8b2 Use presenter state for gutter background color
Signed-off-by: Nathan Sobo <nathan@github.com>
2015-02-03 15:05:27 -07:00
Max Brunsfeld fd4f28911d Use presenter state for placeholder text
Signed-off-by: Nathan Sobo <nathan@github.com>
2015-02-03 15:05:27 -07:00
Nathan Sobo 1a61133def Remove unused local var
Signed-off-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
2015-02-03 15:05:26 -07:00
Nathan Sobo f5fa3b837e Set content.indentGuidesVisible in presenter if editor is mini 2015-02-03 15:05:26 -07:00
Nathan Sobo b21b9c3402 Use presenter’s scrollingVertically property to defer line measurement 2015-02-03 15:05:26 -07:00
Nathan Sobo d4517b1ab0 Add TextEditorPresenter::state.scrollingVertically
This is used by the view to defer measuring new lines until we stop
scrolling.
2015-02-03 15:05:26 -07:00
Nathan Sobo 837b9eefbb Use backgroundColor from presenter in LinesComponent 2015-02-03 15:05:26 -07:00
Nathan Sobo 71a27de7ac Add TextEditorPresenter::state.content.backgroundColor 2015-02-03 15:05:25 -07:00
Nathan Sobo 96ba4cc6bd Remove unused React props and methods that build them 2015-02-03 15:05:25 -07:00
Nathan Sobo 9d7285d04a Use presenter for rendering overlay decorations
We’re still doing some sync DOM reads and computation in the view that
should eventually be made async and moved into the presenter, but I’m
going to leave it alone for now.
2015-02-03 15:05:25 -07:00
Nathan Sobo e4c95d8ac3 Add overlay decorations to TextEditorPresenter::state 2015-02-03 15:05:25 -07:00
Nathan Sobo a5580a704d Remove unused properties in EditorComponent and children 2015-02-03 15:05:25 -07:00
Nathan Sobo c4b5a0f411 Honor the ‘onlyHead’ option for line and line-number decorations 2015-02-03 15:05:24 -07:00
Nathan Sobo af8c38ad80 🎨 2015-02-03 15:05:24 -07:00
Nathan Sobo 0802b9bdd1 Don’t decorate soft-wrapped lines/line numbers unless spanned by marker 2015-02-03 15:05:24 -07:00
Nathan Sobo b1fe567ce8 Build line numbers based on presenter state 2015-02-03 15:05:24 -07:00
Nathan Sobo 20838accc1 Key line numbers by buffer row and soft-wrap count
…instead of an array. This will make things simpler to diff in the
view.
2015-02-03 15:05:24 -07:00
Nathan Sobo 182531a010 Use presenter for gutter scrollHeight and scrollTop
Signed-off-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
2015-02-03 15:05:23 -07:00
Max Brunsfeld f218e985cf Update presenter scrollHeight when clientHeight changes
Signed-off-by: Nathan Sobo <nathan@github.com>
2015-02-03 15:05:23 -07:00
Max Brunsfeld 33081cefda Move .lineNumbers onto .gutter property of presenter state
Signed-off-by: Nathan Sobo <nathan@github.com>
2015-02-03 15:05:23 -07:00
Nathan Sobo d26e8a2df1 Move vertical scroll state to root of presenter state object
Signed-off-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
2015-02-03 15:05:23 -07:00
Nathan Sobo 1ff0b20cea Add ‘foldable’ to line number presenter state 2015-02-03 15:05:23 -07:00
Nathan Sobo 66c35d6e3e Reflect changes to line number decorations in presenter state
Signed-off-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
2015-02-03 15:05:22 -07:00
Nathan Sobo 5d2efc0469 Deprecate TextEditor::getGutterDecorations
Forgot to rename it when we renamed decorations of type ‘gutter’ to
‘line-number’. It’s not used in core or any packages at the time of
this commit.

Signed-off-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
2015-02-03 15:05:22 -07:00
Nathan Sobo 970936f96d Start on TextEditorPresenter::state.lineNumbers
Signed-off-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
2015-02-03 15:05:22 -07:00
Nathan Sobo 49bf3bb14e Use presenter to render flashes
Signed-off-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
2015-02-03 15:05:22 -07:00
Nathan Sobo 8ebd057b0c Use presenter to render highlights
Signed-off-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
2015-02-03 15:05:22 -07:00
Nathan Sobo 2f526c59c5 Add highlights state to TextEditorPresenter
Signed-off-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
2015-02-03 15:05:21 -07:00
Nathan Sobo 764139c25e Fix spec organization
Signed-off-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
2015-02-03 15:05:21 -07:00
Nathan Sobo 4b0536ab6a Remove unused props
Signed-off-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
2015-02-03 15:05:21 -07:00
Nathan Sobo 500f992d32 Remove shouldComponentUpdate hook
Will be replaced by manual update logic anyway.

Signed-off-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
2015-02-03 15:05:21 -07:00
Nathan Sobo 3b93f3d71b Blink cursors based on presenter state
Signed-off-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
2015-02-03 15:05:21 -07:00
Nathan Sobo b412c2642d Add TextEditorPresenter::onDidUpdateState
And ensure observers are invoked whenever the state changes in spec.
2015-02-03 15:05:20 -07:00
Nathan Sobo 3c6c385ec8 Add TextEditorPresenter::state.content.blinkCursorsOff 2015-02-03 15:05:20 -07:00
Nathan Sobo 06b5eba17c Fix 0-width cursors in presenter instead of view 2015-02-03 15:05:20 -07:00
Nathan Sobo 4ed07bb66d Use presenter state in CursorsComponent 2015-02-03 15:05:20 -07:00
Nathan Sobo c8b58761ba Add TextEditorPresenter::state.content.cursors 2015-02-03 15:05:20 -07:00
Nathan Sobo b7b0ec067c 🎨 2015-02-03 15:05:19 -07:00
Nathan Sobo add3972477 Use presenter’s scrollTop/scrollLeft in LinesComponent 2015-02-03 15:05:19 -07:00
Nathan Sobo f479e9d029 Add ::state.content.scrollTop/Left to TextEditorPresenter 2015-02-03 15:05:19 -07:00
Nathan Sobo 78d87d8f7c Use presenter to supply scrollHeight to lines component 2015-02-03 15:05:19 -07:00
Nathan Sobo 2c5888e25a Add ::state.content.scrollHeight to TextEditorPresenter 2015-02-03 15:05:19 -07:00
Nathan Sobo de0b5c4c62 Reorganize specs on TextEditorPresenter to mirror structure of state 2015-02-03 15:05:18 -07:00
Nathan Sobo 59b109654e Fix spec 2015-02-03 15:05:18 -07:00
Nathan Sobo e9d6e36b6a Remove unused code 2015-02-03 15:05:18 -07:00
Nathan Sobo fe5ee524a8 Show/hide line decorations when TextEditor::mini changes 2015-02-03 15:05:18 -07:00
Nathan Sobo 9a496e62cb Don’t apply line decorations to mini editors 2015-02-03 15:05:18 -07:00
Nathan Sobo a513cf260c Don’t apply line decorations to last line if it ends at column 0 2015-02-03 15:05:17 -07:00
Nathan Sobo 62a1210604 Honor the ‘onlyEmpty’ and ‘onlyNonEmpty’ line decoration options 2015-02-03 15:05:17 -07:00
Nathan Sobo 773482467e Handle updates to line decorations in TextEditorPresenter
This isn’t a super efficient approach, but it is simple and should be
correct. Once we move all state to the presenter we can perform a more
efficient synchronous update when markers change.
2015-02-03 15:05:17 -07:00
Nathan Sobo 5d8f831136 Instantiate presenter with minimal parameters in specs 2015-02-03 15:05:17 -07:00
Nathan Sobo 9c1efb6ba0 Simplify assertions 2015-02-03 15:05:17 -07:00
Nathan Sobo 568b9f6999 Add lineStateForScreenRow helper
The access pattern is pretty noisy in the specs
2015-02-03 15:05:16 -07:00
Nathan Sobo 06ef0792ce Add .decorationClasses to line state on initial render 2015-02-03 15:05:16 -07:00
Nathan Sobo bf9428aa19 Read scrollWidth from the presenter state when rendering 2015-02-03 15:05:16 -07:00
Nathan Sobo 64ef8add71 Remove unused argument 2015-02-03 15:05:16 -07:00
Nathan Sobo 32a1854b7c Use TextEditorPresenter::state.content.indentGuidesVisible 2015-02-03 15:05:16 -07:00
Nathan Sobo 590391a0ce Update .content.indentGuidesVisible when editor’s grammar changes 2015-02-03 15:05:15 -07:00
Nathan Sobo d0b52538b2 Add content.indentGuidesVisible to TextEditorPresenter::state 2015-02-03 15:05:15 -07:00
Nathan Sobo 22942ae1bd Remove unused local vars 2015-02-03 15:05:15 -07:00
Nathan Sobo 0a9f7586ae Add top-level .content object to presenter state
It contains the .scrollWidth and then all the lines in a nested .lines
object. The .width has been removed from each line and replaced with
.content.scrollWidth.
2015-02-03 15:05:15 -07:00
Nathan Sobo 3ec4b632ba Update TextEditorPresenter with scoped character widths in component
Signed-off-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
2015-02-03 15:05:15 -07:00
Nathan Sobo 115d764725 Handle scoped character widths in TextEditorPresenter
Signed-off-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
2015-02-03 15:05:14 -07:00
Nathan Sobo 20bb14da81 Start using TextEditorPresenter in LinesComponent
Removed shouldComponentUpdate because we will always update the
component manually once this is done, but I don’t want to accidentally
prevent the component from updating during the conversion process.

This commit has a failing spec due to the presenter not accounting for
individual character widths.
2015-02-03 15:05:14 -07:00
Nathan Sobo a3fb8b3aaa Disable spec until presenter approach stabilizes 2015-02-03 15:05:14 -07:00
Nathan Sobo 9e6aa8f873 Include more fields in line state 2015-02-03 15:05:14 -07:00
Nathan Sobo e2693da225 Fix endRow calculation 2015-02-03 15:05:14 -07:00
Nathan Sobo 9a070e7f6d Include endOfLineInvisibles in presenter state 2015-02-03 15:05:14 -07:00
Nathan Sobo db5059626f Use getters in TextEditorPresenter internally for consistency
::clientHeight is conditionally computed in the getter, so lets use them
everywhere for consistency.
2015-02-03 15:05:13 -07:00
Nathan Sobo 7095ccd32b Make all lines visible if no external client height is assigned 2015-02-03 15:05:13 -07:00
Nathan Sobo ac463143dd Add 1 pixel to scrollWidth to account for cursor if not soft-wrapped 2015-02-03 15:05:13 -07:00
Nathan Sobo 2296d2d378 Account for overdrawMargin of startRow when computing the endRow 2015-02-03 15:05:13 -07:00
Nathan Sobo 143183aa25 🎨 2015-02-03 15:05:13 -07:00
Nathan Sobo 0f4bcac8d4 Add 1 to the last row to ensure it’s visible 2015-02-03 15:05:12 -07:00
Nathan Sobo b09b54800f Include line text in presenter state 2015-02-03 15:05:12 -07:00
Nathan Sobo 2120c3c298 Instantiate presenter in TextEditorComponent and update measurements 2015-02-03 15:05:12 -07:00
Nathan Sobo f4d8ef8315 Add width to lines state based on the computed scrollWidth
This is based on the ::baseCharacterWidth property for now. To be fully
correct, we need to base the scrollWidth on the actual width of
individual characters.
2015-02-03 15:05:12 -07:00
Nathan Sobo a1c2e1bb66 Update TextEditorPresenter when the editor’s content changes 2015-02-03 15:05:12 -07:00
Nathan Sobo 5a2bbc945b Handle changing ::lineHeight in TextEditorPresenter 2015-02-03 15:05:12 -07:00
Nathan Sobo 9c2ed478cd Handle changing ::clientHeight in TextEditorPresenter 2015-02-03 15:05:11 -07:00
Nathan Sobo 880e1ce1f0 Handle changing ::scrollTop in TextEditorPresenter 2015-02-03 15:05:11 -07:00
Nathan Sobo f0920bf63b Start on TextEditorPresenter with lines state 2015-02-03 15:05:11 -07:00
Kevin Sawicki 37a85bcdd0 ⬆️ language-css@0.27 2015-02-03 13:50:45 -08:00
Kevin Sawicki 47bd093d68 🐎 Load 6to5 version directly from package.json
This prevents the entire library from being required just to check the
cache so when 6to5 is being used and all files are cached it should shave
~250ms off startup.
2015-02-03 13:36:26 -08:00
Kevin Sawicki a788a7e9b7 ⬆️ language-xml@0.28 2015-02-03 10:53:29 -08:00
Kevin Sawicki 9101da11ce ⬆️ update-package-dependencies@0.8 2015-02-03 10:41:26 -08:00
Max Brunsfeld f7159181ac 📝 Tweak docs for new service APIs 2015-02-03 10:21:23 -08:00
Kevin Sawicki 1784a7f726 ⬆️ Upgrade one themes 2015-02-03 10:20:23 -08:00
Kevin Sawicki a28a8447a7 ⬆️ language-json@0.12 2015-02-03 10:18:25 -08:00
Kevin Sawicki 197e74f18d Prepare 0.178 2015-02-03 09:43:05 -08:00
Kevin Sawicki 5cae6f20c4 Merge pull request #5368 from atom/ld-jasmine-ref-fix
Fix link to wrong version of Jasmine
2015-02-03 09:06:11 -08:00
Kevin Sawicki 6ad704c1e4 Defer requiring 6to5 until it is used
It seems to have a require time of ~250ms
2015-02-03 09:05:34 -08:00
Ben Ogle 2b0cb11df3 Merge pull request #5374 from atom/ld-doc-composed-commands
Add section on "composed" commands to Advanced Keymap document
2015-02-03 09:04:29 -08:00
Lee Dohm 9aee7d4b38 📝 Add section about composed commands
Fix #5357

This is intended to address the commonly-asked question about making a keybinding execute multiple commands.
2015-02-03 04:50:38 -08:00
Lee Dohm b6c86ea217 📝 Update command example for CommandRegistry 2015-02-03 04:31:23 -08:00
Lee Dohm 0d169f0516 Fix link to wrong version of Jasmine
Applies to #5354
2015-02-02 20:26:35 -08:00
Kevin Sawicki 5dea6e7d12 Link to all guides
Closes #5332
2015-02-02 18:20:53 -08:00
Kevin Sawicki 312e4e0e8a Be graceful about pathsToOpen string value
Gently convert it to an array of strings before opening

Closes #5340
2015-02-02 18:17:15 -08:00
Kevin Sawicki 853ad9cee3 Re-throw stylesheet compile errors
This makes it consistent with other read errors. Previously a Notification
was returned in the error case causing errors downstream where the package's
stylesheets array was assumed to be a path/content tuple.

Closes atom/deprecation-cop#22
2015-02-02 17:55:36 -08:00
Max Brunsfeld a5ccfa6299 Merge pull request #5346 from atom/ld-doc-updates
Some documentation updates
2015-02-02 17:26:08 -08:00
Lee Dohm 800ca5a37a 📝 Add a missing comma 2015-02-02 17:22:53 -08:00
Lee Dohm 23b8b15261 📝 Fix broken json-schema.org link 2015-02-02 17:22:29 -08:00
Lee Dohm 959aa08d62 📝 Atom 1.0 API is now frozen 2015-02-02 17:22:29 -08:00
Lee Dohm 6eed4e461f 📝 Give stronger recommendation on deactivate
Many packages don't clean up properly on update, causing strange
behavior until restart. Simply stating that the `deactivate` method is
optional is enough to let people know that it isn't required for the
common case.
2015-02-02 17:22:29 -08:00
Lee Dohm 00b79e69f1 📝 activationEvents are now activationCommands 2015-02-02 17:22:29 -08:00
Max Brunsfeld 3739995ddb Merge pull request #5277 from atom/mb-package-json-services
Allow packages to specify services in their package.json files
2015-02-02 17:07:41 -08:00
Max Brunsfeld f942bafe54 📝 Fix typo in 'Creating Package' docs 2015-02-02 16:47:33 -08:00
Kevin Sawicki 5fa230e71a ⬆️ apm@0.134 2015-02-02 14:17:19 -08:00
Kevin Sawicki 9858916c31 🎨 2015-02-02 14:10:53 -08:00
Kevin Sawicki 18ed76f111 Add transpiler name dir segment 2015-02-02 14:09:49 -08:00
Kevin Sawicki feb35e710c Make 6to5 first cache segment
Makes it easier to blow away if needed.
2015-02-02 14:08:50 -08:00
Kevin Sawicki 2e989b502a Remove unneeded try/catch 2015-02-02 14:08:35 -08:00
Kevin Sawicki 434c9e60a8 Remove console logging 2015-02-02 14:08:00 -08:00
Kevin Sawicki 84453d5441 Ignore errors writing to the cache 2015-02-02 14:07:39 -08:00
Kevin Sawicki 9e11914b8b Add helper to cache without requiring 2015-02-02 14:07:11 -08:00
Kevin Sawicki f9f7cf6d34 Add more 6to5 specs 2015-02-02 13:29:20 -08:00
Kevin Sawicki c7b206f5ca Add core-js license override 2015-02-02 13:22:08 -08:00
Kevin Sawicki 3b8b569d0c 🎨 2015-02-02 13:13:39 -08:00
Kevin Sawicki 8365ccb064 Merge pull request #5299 from bolinfest/6to5
Transpile all .js files beginning with the "use 6to5"; pragma with 6to5.
2015-02-02 13:11:33 -08:00
Kevin Sawicki fee8e4a75a Merge pull request #5335 from xfq/semicolon
📝 Minor punctuation fix.
2015-02-02 11:29:48 -08:00
Max Brunsfeld edd8714bbf 📝 Document provision and consumption of services 2015-02-02 11:18:52 -08:00
Kevin Sawicki 56adf8cc32 ⬆️ markdown-preview@0.132 2015-02-02 10:31:59 -08:00
Kevin Sawicki d3956da8e5 ⬆️ event-kit@1.0.2 2015-02-02 10:10:55 -08:00
Max Brunsfeld b24b338b2c Add deprecated shim for atom.services global 2015-02-02 10:01:57 -08:00
Max Brunsfeld 14969c0522 Avoid using private serviceHub in package manager test
Signed-off-by: Nathan Sobo <nathan@github.com>
2015-02-02 10:01:56 -08:00
Max Brunsfeld 65f2ffa55e Rename service keys in package.json
* serviceProvisions -> providedServices
* serviceDependencies -> consumedServices

Signed-off-by: Nathan Sobo <nathan@github.com>
2015-02-02 10:01:50 -08:00
Max Brunsfeld 5638c7fb6a Remove atom.services global
Signed-off-by: Nathan Sobo <nathan@github.com>
2015-02-02 10:01:34 -08:00
Max Brunsfeld cb8b254502 Use version-ranges in fixture package with service deps 2015-02-02 10:00:53 -08:00
Max Brunsfeld 027ebc78c4 Respect service{Provisions,Dependencies} in packages' metadata 2015-02-02 10:00:53 -08:00
Max Brunsfeld d9fb54ad6f Reset atom.services between specs 2015-02-02 10:00:53 -08:00
Jason Woods 2e72790e72 🐛 Fix ident guides missing on whitespace only lines with invis chars 2015-02-01 18:13:39 +00:00
Paul Betts 8fcd71f207 Publish delta packages 2015-02-01 01:51:49 -08:00
Paul Betts 776eedd473 Set us up to download the current release so we can create deltas 2015-02-01 01:51:48 -08:00
Paul Betts 62c515e4e7 ⬆️ grunt-atom-shell-installer@0.21.0 2015-02-01 01:51:42 -08:00
Xue Fuqiao 2170e917bd 📝 Minor punctuation fix. 2015-01-31 23:59:35 +08:00
Ivan Žužak 7add4af585 Merge pull request #5334 from xfq/https
Change HTTP to HTTPS
2015-01-31 11:42:56 +01:00
Ivan Žužak 5076e2c8b3 Merge pull request #5333 from xfq/devtools-link
Update the URI of DevTools tutorial in docs
2015-01-31 11:41:52 +01:00
Ivan Žužak b56d73cd74 Merge pull request #5330 from xfq/broken-link
📝 Fix a broken link.
2015-01-31 11:40:37 +01:00
Ivan Žužak 6c5a0e3670 Merge pull request #5329 from xfq/install-theme
📝 Fix theme docs.
2015-01-31 11:39:42 +01:00
Ivan Žužak 791f89216b Merge pull request #5327 from xfq/osx-fix
Change Mac OS X to OS X.
2015-01-31 11:38:53 +01:00
Xue Fuqiao f6067cb629 📝 Fix previous change. 2015-01-31 18:08:13 +08:00
Xue Fuqiao 22158031bf Change HTTP to HTTPS.
Change HTTP to HTTPS to avoid plaintext HTTP connections.
2015-01-31 17:59:45 +08:00
Xue Fuqiao 8ac3fada4e 📝 Update the URI of DevTools tutorial. 2015-01-31 17:11:09 +08:00
Xue Fuqiao a03d5bc288 📝 Fix a broken link.
Fix a broken link in `docs/creating-a-theme.md`.
2015-01-31 14:10:39 +08:00
Xue Fuqiao cc489e5663 📝 Fix theme docs. 2015-01-31 13:51:40 +08:00
Xue Fuqiao cd3f7f73a0 Change Mac OS X to OS X.
Apple has renamed Mac OS X to OS X.
2015-01-31 12:36:35 +08:00
Ben Ogle 9cb67a6def 📝 Fix doc reference in config 2015-01-30 16:52:19 -08:00
Kevin Sawicki 786e8c558a Merge pull request #5314 from eestrada/master
Make rpm install package a little more flexible
2015-01-30 11:12:50 -08:00
Ethan Estrada bfeeddea4d Make rpm package installs relocatable 2015-01-29 22:55:41 -07:00
Ethan Estrada 1589b8b192 Make apm symlink relative for rpm spec 2015-01-29 22:35:11 -07:00
Ben Ogle b02d0e3df3 Merge pull request #5280 from atom/bo-schema-on-load
Reset all user config values when the schema changes
2015-01-29 18:06:18 -08:00
Ben Ogle d3f29c4580 🎨 Fix wording 2015-01-29 17:54:41 -08:00
Ben Ogle 7263ca8faa 🎨 Fix spec title 2015-01-29 17:54:41 -08:00
Ben Ogle cd812e7a65 Transact on resetting the settings when a schema changes 2015-01-29 17:54:41 -08:00
Ben Ogle 4e6e636b8b 🎨 Reorder args to be consistent with other methods 2015-01-29 17:54:41 -08:00
Ben Ogle 083bafdb33 Handle unschema’d items in objects.
This is required for packages that still use configDefaults
2015-01-29 17:54:41 -08:00
Ben Ogle adbe151c5d Fix up the linter error 2015-01-29 17:54:41 -08:00
Ben Ogle 26b450fe49 🎨 Clean up spec naming 2015-01-29 17:54:41 -08:00
Ben Ogle 5d273d0ee6 Add a bit more coverage to the specs 2015-01-29 17:54:41 -08:00
Ben Ogle b9b2b4bca2 Adhere to the schemas when loading the user’s config 2015-01-29 17:54:41 -08:00
Ben Ogle 79094ee889 Reset all user config values when the schema changes. 2015-01-29 17:54:40 -08:00
Michael Bolin 52f2c0ec69 rename esnext to 6to5 2015-01-29 17:29:02 -08:00
Kevin Sawicki d2f485a1ab ⬆️ deprecation-cop@0.34 2015-01-29 16:44:33 -08:00
Kevin Sawicki c12002a0ba ⬆️ bracket-matcher@0.71 2015-01-29 16:41:22 -08:00
Michael Bolin fed2bd6ab8 fixed a bug in updateDigestForJsonValue with how null was handled 2015-01-29 12:33:14 -08:00
Michael Bolin d29b5b20cd Prefer Object.defineProperty() 2015-01-29 12:27:31 -08:00
Michael Bolin a0bb9d88e0 remove parens 2015-01-29 12:21:59 -08:00
Michael Bolin 32b3c4076f More fixes for @kevinsawicki 2015-01-29 12:15:10 -08:00
Michael Bolin acd1d31480 Address @kevinsawicki's comments. 2015-01-29 12:10:25 -08:00
Michael Bolin 242fce3d79 Transpile all .js files with 6to5.
In the spirit of supporting JavaScript development for Atom packages,
this adds default support for es.next transpilation support in the way
that Atom already has default support for CoffeeScript transpilation.
There are many new features in ES6+ that make JavaScript development
easier and more enjoyable, particularly in terms of support for async code.

For reference, this was a much faster way to iterate on this than running `./script/build`
each time:

```
cp /Users/mbolin/src/atom/static/index.js /Applications/Atom.app/Contents/Resources/app/static/index.js
coffee --output /Applications/Atom.app/Contents/Resources/app/src --compile /Users/mbolin/src/atom/src/esnext.coffee
```

Run the following in the console to see how warm the cache was after startup:

```
global.require('../src/esnext/').getCacheHits()
global.require('../src/esnext/').getCacheMisses()
```
2015-01-29 11:56:32 -08:00
Kevin Sawicki e0bac77fa5 ⬆️ season@5.1.2
Closes atom/snippets#101
2015-01-29 11:31:11 -08:00
Kevin Sawicki c4c13375e4 ⬆️ language-todo@0.16 2015-01-29 09:41:06 -08:00
Kevin Sawicki 83f0104c46 Merge pull request #5305 from mnquintana/patch-3
Fix broken npm package.json link in creating-a-package.md
2015-01-29 09:15:11 -08:00
Machiste N. Quintana a0a3c93b1d 📝 Fix broken npm package.json link
In creating-a-package.md
Fixes #5304
2015-01-29 10:52:46 -05:00
Nathan Sobo 98a874808e ⬆️ react-atom-fork for @bolinfest's changes 2015-01-29 08:24:00 -07:00
Ben Ogle e7b790c5b9 Merge pull request #5300 from nextPrime/patch-1
http://github.com -> https://github.com
2015-01-28 18:45:27 -08:00
nextPrime 4988293400 http://github.com -> https://github.com
Change http://github.com to https://github.com to avoid plaintext HTTP connections.
2015-01-28 21:17:29 -05:00
Kevin Sawicki 0d51c3b871 ⬆️ apm@0.133 2015-01-28 16:42:13 -08:00
Kevin Sawicki 8316183fa0 ⬇️ apm@0.131 2015-01-28 16:37:43 -08:00
simurai 8e617ff4e2 📝 Update Shadow DOM selector 2015-01-29 09:27:17 +09:00
Kevin Sawicki 3ca5495690 Merge pull request #5279 from paulcbetts/patch-1
Enable DirectWrite
2015-01-28 16:11:15 -08:00
Kevin Sawicki 5c47ae0cbc ⬆️ apm@0.132 2015-01-28 16:05:31 -08:00
Cheng Zhao 6a86a00c66 Merge pull request #5282 from atom/chrome40
Upgrade to Chrome40
2015-01-28 15:52:20 -08:00
Cheng Zhao 1abe64e73e styleguide@0.43.0 2015-01-28 14:35:45 -08:00
Cheng Zhao bb9150340d ⬆️ markdown-preview@0.131.0 2015-01-28 14:32:55 -08:00
Cheng Zhao c3f5c43694 ⬆️ link@0.30.0 2015-01-28 14:24:29 -08:00
Cheng Zhao 1678016ae4 ⬆️ image-view@0.48.0 2015-01-28 14:22:43 -08:00
Cheng Zhao f50e402de3 ⬆️ bracket-matcher@0.70.0 2015-01-28 14:20:08 -08:00
Cheng Zhao e6363150dd ⬆️ pathwatcher@3.1.0 2015-01-28 14:16:16 -08:00
Cheng Zhao 5f024bfd69 ⬆️ markdown-preview@0.130.0 2015-01-28 14:06:29 -08:00
Cheng Zhao 76adb58fea ⬆️ scandal@2.0.0 2015-01-28 14:06:15 -08:00
Cheng Zhao 53d5e61b23 ⬆️ atom-keymap@3.1.0 2015-01-28 14:06:15 -08:00
Cheng Zhao 499c09fc25 ⬆️ first-mate@3.0.0 2015-01-28 14:06:15 -08:00
Cheng Zhao 7891595fed ⬆️ text-buffer@4.0.0 2015-01-28 14:06:15 -08:00
Cheng Zhao 604f2a951f ⬆️ scroller-style@2.0.0 2015-01-28 14:06:15 -08:00
Cheng Zhao c1b6b716b1 ⬆️ archive-view@0.46.0 2015-01-28 14:06:15 -08:00
Cheng Zhao 4d861a68d1 ⬆️ dev-live-reload@0.41.0 2015-01-28 14:06:15 -08:00
Cheng Zhao a831688ce1 ⬆️ snippets@0.72.0 2015-01-28 14:06:15 -08:00
Cheng Zhao 1cbac24cee ⬆️ tree-view@0.154.0 2015-01-28 14:06:15 -08:00
Cheng Zhao afa5f73094 ⬆️ spell-check@0.54.0 2015-01-28 14:06:15 -08:00
Cheng Zhao a142e23f49 ⬆️ nslog@2.0.0 2015-01-28 14:06:14 -08:00
Cheng Zhao f1b7536d06 ⬆️ oniguruma@4.0.0 2015-01-28 14:06:14 -08:00
Cheng Zhao 397e61a0d8 ⬆️ git-utils@3.0.0 2015-01-28 14:06:14 -08:00
Cheng Zhao af22e45f70 ⬆️ runas@2.0.0 2015-01-28 14:06:14 -08:00
Cheng Zhao e6a12530a1 ⬆️ symbols-view@0.81.0 2015-01-28 14:06:14 -08:00
Cheng Zhao 896c98b7a0 ⬆️ pathwatcher@3.0.0 2015-01-28 14:06:14 -08:00
Cheng Zhao 3201a05672 ⬆️ apm@0.131.0 2015-01-28 14:06:14 -08:00
Cheng Zhao 93181d8a54 Array::find is undefined in Chrome 40 2015-01-28 14:06:14 -08:00
Cheng Zhao 661f1f822a ⬆️ atom-keymap@3.0.0 2015-01-28 14:06:14 -08:00
Cheng Zhao 678306317d ⬆️ text-buffer@3.11.0 2015-01-28 14:06:14 -08:00
Cheng Zhao 8dd5f17609 ⬆️ scrollbar-style@1.1.0 2015-01-28 14:06:14 -08:00
Cheng Zhao 13e069b45d ⬆️ scandal@1.1.0 2015-01-28 14:06:14 -08:00
Cheng Zhao e61cc5eb13 ⬆️ tree-view@0.152.0 2015-01-28 14:06:14 -08:00
Cheng Zhao 9e92952ad0 ⬆️ symbols-view@0.80.0 2015-01-28 14:06:14 -08:00
Cheng Zhao a8d778781c ⬆️ snippets@v0.71.0 2015-01-28 14:06:14 -08:00
Cheng Zhao da17679ec7 ⬆️ dev-live-preview@0.40.0 2015-01-28 14:06:14 -08:00
Cheng Zhao 1cf663714e ⬆️ archive-view@0.45.0 2015-01-28 14:06:14 -08:00
Cheng Zhao 05963c4e57 ⬆️ spell-check@0.53.0 2015-01-28 14:06:14 -08:00
Cheng Zhao 965a18f7e2 ⬆️ nslog@1.1.0 2015-01-28 14:06:14 -08:00
Cheng Zhao 58c45f87e6 ⬆️ oniguruma@3.1.0 2015-01-28 14:06:13 -08:00
Cheng Zhao 21e609ffac ⬆️ pathwatcher@2.7.0 2015-01-28 14:06:13 -08:00
Cheng Zhao 93befc986f ⬆️ git-utils@2.3.0 2015-01-28 14:06:13 -08:00
Cheng Zhao 3321d77264 ⬆️ runas@1.2.0 2015-01-28 14:06:13 -08:00
Cheng Zhao 84761563f0 ⬆️ apm@0.129.0 2015-01-28 14:06:13 -08:00
Cheng Zhao 014cf19723 ⬆️ atom-shell@0.21.0 2015-01-28 14:06:13 -08:00
Cheng Zhao 2a42ed4aaa ⬆️ markdown-preview@0.129.0 2015-01-28 14:05:17 -08:00
Kevin Sawicki 34b1615782 Lower limit to 175 2015-01-28 12:09:43 -08:00
Kevin Sawicki 87db2760b8 ⬆️ autocomplete@0.44 2015-01-28 09:40:42 -08:00
Max Brunsfeld b4dd0f4f73 ⬆️ language-c 2015-01-28 09:35:20 -08:00
Max Brunsfeld 9d655fd642 ⬆️ language-javascript 2015-01-28 09:12:38 -08:00
Max Brunsfeld 4700edf439 ⬆️ markdown-preview 2015-01-28 08:50:32 -08:00
Cheng Zhao 87c2f4496f 🐧 Set window icon, fixes #5281 2015-01-27 20:34:36 -08:00
Ben Ogle 2741445d95 ⬆️ background-tips@0.22.0 2015-01-27 18:32:32 -08:00
Kevin Sawicki ed90c5e6c2 ⬆️ less-cache@0.21 2015-01-27 17:32:13 -08:00
Kevin Sawicki 0bf2a3f480 ⬆️ apm@0.130 2015-01-27 17:24:03 -08:00
Paul Betts a743e8d058 Enable DirectWrite
Chrome 39 proper now has DirectWrite as the default renderer, which makes a huge difference in text clarity, especially on High DPI monitors.
2015-01-27 14:38:50 -08:00
Kevin Sawicki ddc607c760 ⬆️ tree-view@0.151 2015-01-27 14:30:28 -08:00
Kevin Sawicki cbc8ad1467 Merge pull request #5201 from wyand/master
🍎 try ~/Applications before using mdfind
2015-01-27 14:09:52 -08:00
Kevin Sawicki 1a1eb4380a ⬆️ tabs@0.65 2015-01-27 14:06:28 -08:00
Kevin Sawicki 535b49f60c ⬆️ tree-view@0.150 2015-01-27 13:43:09 -08:00
Kevin Sawicki b5e60f7aa0 ⬆️ spell-check@0.52 2015-01-27 13:33:04 -08:00
Kevin Sawicki ce29060d2d ⬆️ first-mate@2.2.5 2015-01-27 09:30:24 -08:00
Ben Ogle 7d1c23b561 ⬆️ git-diff@0.50.0 2015-01-26 17:39:30 -08:00
Ben Ogle 9e2799db27 ⬆️ status-bar@0.58.0 2015-01-26 15:56:50 -08:00
Daniel Hengeveld 040f6deb1e Merge pull request #5263 from atom/bump-tree-view
⬆️ bump tree view for new sort option
2015-01-26 14:31:53 -08:00
Kevin Sawicki 33d2debf5f Prepare 0.177 2015-01-26 13:59:57 -08:00
Daniel Hengeveld 6de40e21c7 bump tree view to include version with new sort option 2015-01-26 13:05:37 -08:00
Cheng Zhao 6169caa1fb Merge pull request #5235 from atom/atom-shell-v0.20.7
Upgrade to atom-shell v0.20.7
2015-01-26 12:58:06 -08:00
Cheng Zhao b4e25137a2 "screen" is now a browser side module 2015-01-26 11:28:00 -08:00
Cheng Zhao 05aaace387 Do not set window icon, fixes #4811 2015-01-26 11:28:00 -08:00
Cheng Zhao a32daecf5f ⬆️ atom-shell@0.20.7 2015-01-26 11:28:00 -08:00
Kevin Sawicki 6db43d85c4 Remove CI node_modules cleaning 2015-01-26 11:01:42 -08:00
Kevin Sawicki 53e06b8680 Clean out node_modules on Linux CI 2015-01-26 10:44:06 -08:00
Kevin Sawicki 9ab2500970 Clean out node_modules on Windows CI 2015-01-26 10:15:37 -08:00
Kevin Sawicki 697e70d47c ⬆️ apm@0.128 2015-01-26 09:43:42 -08:00
Daniel Hengeveld ffd1f8520c Merge pull request #5240 from mnquintana/less-rename
📝 Rename LESS to Less
2015-01-25 11:48:12 -08:00
Machiste Quintana 9132859757 📝 LESS -> Less 2015-01-24 09:49:53 -05:00
Kevin Sawicki 6af2e1649c ⬆️ legal-eagle@0.9 2015-01-23 13:33:25 -08:00
Kevin Sawicki 0962918a82 ⬆️ timecop@0.28 2015-01-23 11:51:41 -08:00
Kevin Sawicki 6b963a562f Track coffee cache hits correctly 2015-01-23 11:48:19 -08:00
Ben Ogle db41b022c2 Merge pull request #5228 from atom/bo-fix-wait-promise
Make waitsForPromise() work with es6 promises as well as Q promises
2015-01-23 10:33:20 -08:00
Kevin Sawicki d4084b305b ⬆️ season@5.1.1 2015-01-23 09:57:48 -08:00
Kevin Sawicki b81c1f408f Track coffee cache hits/misses 2015-01-23 09:41:26 -08:00
Kevin Sawicki 97bdb84122 ⬆️ season@5.1 2015-01-23 09:41:26 -08:00
Kevin Sawicki 87e8720269 ⬆️ less-cache@0.20 2015-01-23 09:41:26 -08:00
Max Brunsfeld 160c32384c ⬆️ markdown-preview@0.126.0 2015-01-23 08:33:44 -08:00
Ben Ogle 33299ec774 ⬆️ notifications@0.26.0 2015-01-22 18:15:50 -08:00
Max Brunsfeld d4cc549a01 Add jasmine.useRealClock
This is the only way to unmock the _.now function uses
by _.debounce and _.throttle, since package specs don't
necessarily have access to core's copy of underscore-plus

Signed-off-by: Nathan Sobo <nathan@github.com>
2015-01-22 17:59:18 -08:00
Ben Ogle 76a5da8be8 Make waitsForPromise() work with es6 promises as well as Q promises. 2015-01-22 17:26:15 -08:00
Ben Ogle 0b0cbe11dc 📝 Fix BufferedProcess docs 2015-01-22 17:24:56 -08:00
Nathan Sobo 86d730a3a9 Always forceUpdate when editor component is visible
Fixes #5187. Giving up after 90 minutes on producing a failing test case
because this is all changing soon anyway and getting simpler. I’m
willing to risk this regressing to solve it now and move on.
2015-01-22 17:28:02 -07:00
Kevin Sawicki 99ff482afe ⬆️ apm@0.127 2015-01-22 15:36:04 -08:00
Kevin Sawicki 84be87324e Always deepClone since it supports colors now 2015-01-22 14:41:55 -08:00
Kevin Sawicki d1ecafc69f ⬆️ apm@0.125 2015-01-22 14:12:58 -08:00
Kevin Sawicki 235f602002 Merge pull request #5220 from atom/ks-bundle-one-themes
Bundle the one themes
2015-01-22 14:11:37 -08:00
Kevin Sawicki 1ee4240b7e ⬆️ one-dark-ui@0.2 2015-01-22 13:49:42 -08:00
Kevin Sawicki a46c4ed30e ⬆️ one-light-syntax@0.2 2015-01-22 13:49:42 -08:00
Kevin Sawicki 023b9d18e1 ⬆️ one-dark-syntax@0.2 2015-01-22 13:49:42 -08:00
Kevin Sawicki 5b992d1397 Warm cache with one themes 2015-01-22 13:49:42 -08:00
Kevin Sawicki 1ad25f4a56 Bundle one themes 2015-01-22 13:49:42 -08:00
Kevin Sawicki 4baea35c23 ⬆️ apm@0.124 2015-01-22 11:33:57 -08:00
Kevin Sawicki 63335f6b60 Return Color object when in cloned objects
This adds a custom deepClone that clones any Color objects
correctly.
2015-01-22 10:48:07 -08:00
Nathan Sobo f691c20d68 Revert "Add license override for emoji-images"
This reverts commit 36d28235fa.
2015-01-22 10:58:51 -07:00
Max Brunsfeld 45ca1c6762 Merge pull request #5189 from atom/mb-simpler-auto-indent-on-paste
Preserve relative indentation of pasted lines when auto-indenting
2015-01-22 08:44:18 -08:00
Max Brunsfeld 528267b7d7 Preserve relative indentation of pasted lines when auto-indenting 2015-01-22 08:34:20 -08:00
Max Brunsfeld 5cdeb7bc18 Fix comment for Selection::adjustIndent 2015-01-22 08:34:20 -08:00
Nathan Sobo 36d28235fa Add license override for emoji-images 2015-01-22 08:40:53 -07:00
Dan Wyand 3b13e4f502 🍎 try ~/Applications before using mdfind 2015-01-21 23:01:37 -05:00
Kevin Sawicki f7ecc3e2a3 Don't log npm install warnings
Closes #5194
2015-01-21 17:10:21 -08:00
Kevin Sawicki 89a5469151 📝 Mention apm install --check
Refs atom/apm#265
2015-01-21 15:33:19 -08:00
Kevin Sawicki 77e8f5c9ac ⬆️ markdown-preview@0.125 2015-01-21 13:41:37 -08:00
Kevin Sawicki 1fc597ca22 🎨 2015-01-21 11:23:28 -08:00
Kevin Sawicki 5c62eb0253 ⬆️ wrap-guide@0.31 2015-01-21 11:22:29 -08:00
Kevin Sawicki c378ef0649 Merge pull request #5044 from russlescai/rl-scoped-file-encoding
Allow scoped default File Encoding
2015-01-21 11:22:17 -08:00
Nathan Sobo 12982027f2 ⬆️ release-notes 2015-01-21 12:19:37 -07:00
Kevin Sawicki 36a9e3f76e ⬆️ deprecation-cop@0.33 2015-01-21 11:11:42 -08:00
Kevin Sawicki a9d215970f Remove fs patches that are now fixed in Atom Shell 2015-01-21 10:39:08 -08:00
Kevin Sawicki 0daee2c2c2 Prepare 0.176 2015-01-21 10:12:57 -08:00
Kevin Sawicki f9fe317298 ⬆️ apm@0.123 2015-01-21 09:53:34 -08:00
Max Brunsfeld ce46991600 ⬆️ language-javascript
Fixes #5180
2015-01-21 09:52:17 -08:00
Nathan Sobo e1d9838a28 ⬆️ markdown-preview 2015-01-21 10:46:19 -07:00
Kevin Sawicki 0c057dbbd5 ⬆️ encoding-selector@0.17 2015-01-21 09:23:51 -08:00
Kevin Sawicki aad393618e Merge pull request #5175 from atom/ks-shorten-apm-paths
Shorten apm paths in bundle
2015-01-21 09:23:09 -08:00
Kevin Sawicki ccaffb1c28 Add fall back apm path for dev mode 2015-01-21 09:07:26 -08:00
Kevin Sawicki 47b1d3e90f Use new apm.cmd path 2015-01-21 09:07:26 -08:00
Kevin Sawicki 9df7ea91db Use new apm path on all platforms 2015-01-21 09:07:26 -08:00
Kevin Sawicki 5bf0e53c01 Shorten apm paths on Windows 2015-01-21 09:07:26 -08:00
Nathan Sobo c052b03793 ⬆️ markdown-preview 2015-01-21 10:05:43 -07:00
Nathan Sobo 22cf142473 Add license override for cheerio@0.15.0
Newer versions of the library have the license added, but this is easier
than upgrading for now.
2015-01-21 09:02:25 -07:00
Max Brunsfeld 1d6db875ff ⬆️ text-buffer
To fix exception contributing to #5169
2015-01-21 07:20:50 -08:00
Nathan Sobo b2a71ecd36 ⬆️ packages to fix deprecations 2015-01-21 07:47:29 -07:00
Kevin Sawicki 432b531c07 Include fallback vars when warming less cache 2015-01-20 18:45:14 -08:00
Kevin Sawicki 3190f7578f ⬆️ keybinding-resolve@0.27 2015-01-20 18:02:34 -08:00
Ben Ogle db49d4da31 Merge pull request #5157 from postcasio/da-handle-erofs
Handle EROFS errors when saving
2015-01-20 15:39:19 -08:00
Ben Ogle 9fb1edbc54 Fix inconsistent keybindings in first package guide
Closes #3863
2015-01-20 15:11:39 -08:00
Kevin Sawicki 5a2365cead ⬆️ apm@0.122 2015-01-20 10:14:35 -08:00
Kevin Sawicki 3619e6acaf ⬆️ language-sql@0.14 2015-01-20 10:14:23 -08:00
Kevin Sawicki 936ab1c898 Merge pull request #5118 from nullstyle/fix-addOpener-doc-typo
Fix typo in addOpener doc
2015-01-20 10:03:31 -08:00
Nathan Sobo b961ed416e Merge pull request #5152 from atom/sm-docs-upgrading-syntax-theme
Update atom-text-editor selector example
2015-01-20 09:54:13 -07:00
postcasio 867f920329 Handle EROFS errors when saving 2015-01-20 09:56:00 +00:00
simurai 6f4107fe5d 📝 Update text-editor example 2015-01-20 16:03:47 +09:00
Ben Ogle 323ce940f9 Merge pull request #5143 from avrahamruderman/master
typo
2015-01-19 10:49:34 -08:00
Avi 1fe452776d typo
the the
2015-01-19 22:11:31 +11:00
Scott Fleckenstein d263205a9a 📝 Fix typo in addOpener doc 2015-01-16 18:39:54 -08:00
Nathan Sobo 60970d6cec ⬆️ markdown-preview to debug flaky spec 2015-01-16 16:15:47 -07:00
Kevin Sawicki 32f6ab717e Merge pull request #5117 from rachelmyers/patch-1
Replace the lipstick emoji for formatting changes
2015-01-16 14:16:35 -08:00
Rachel Myers 4f871f1331 s/💄/🎨 as emoji for formatting changes
Fixes #5111
2015-01-16 14:08:33 -08:00
Max Brunsfeld c2107fa9b3 ⬆️ settings-view
For more deprecation fixes
2015-01-16 12:25:17 -08:00
Max Brunsfeld 96d4c1c41b Save config when deprerecated Config::set signature is used 2015-01-16 12:24:32 -08:00
Ben Ogle bac10d60c4 Merge pull request #5114 from atom/bo-readonly
Allow opening of readonly files
2015-01-16 11:41:30 -08:00
Ben Ogle 828b841f17 Roll two whens into one 2015-01-16 11:24:24 -08:00
Ben Ogle 11bda1b47a Add error handling for EBUSY as well 2015-01-16 11:07:32 -08:00
Ben Ogle 2577843e51 Explicitly check if the path to be deserialized is a dir 2015-01-16 10:53:35 -08:00
Max Brunsfeld e0e821e7a4 ⬆️ service-hub@0.2.0 2015-01-16 10:50:47 -08:00
Ben Ogle 93f109fbba Add a better message for EPERM errors. 2015-01-16 10:45:48 -08:00
Ben Ogle 97a55ba8c0 Only check read permission on read 2015-01-16 10:24:19 -08:00
Max Brunsfeld 0a59d13d56 Fix Config::unset with no scope-selector
Closes #5095
2015-01-16 10:23:12 -08:00
Kevin Sawicki 20dbc4cfd3 ⬆️ apm@0.121 2015-01-16 09:47:43 -08:00
Kevin Sawicki 6efaf91ff9 Merge pull request #5086 from atom/ks-deserialize-buffer-error
Check if files are accessible during deserialization
2015-01-16 09:47:01 -08:00
Kevin Sawicki b6b90c0270 💄 2015-01-16 09:33:42 -08:00
Kevin Sawicki d736ebff38 Don't deserialize buffers with inaccessible paths 2015-01-16 09:33:42 -08:00
Kevin Sawicki 0cf180804c 💄 2015-01-16 09:33:42 -08:00
Kevin Sawicki c0c5f46097 📝 Tweak catch comment 2015-01-16 09:33:42 -08:00
Kevin Sawicki 16cc9f76c4 📝 Tweak spec description 2015-01-16 09:33:42 -08:00
Kevin Sawicki 6ad8aa7e5c Ignore buffer errors when the path is a directory
This would previously cause Atom to fail to launch if a buffer's
serialized path was now a directory on launch.
2015-01-16 09:33:42 -08:00
Kevin Sawicki 67fc2b9af5 ⬆️ pathwatcher@2.6.1 2015-01-16 09:33:42 -08:00
Nathan Sobo b8b58b25da ⬆️ dev-live-reload 2015-01-16 09:38:14 -07:00
simurai 696c795b50 🐛 Fix overflow of mini editors
Issue #4400
2015-01-16 20:12:53 +09:00
Kevin Sawicki 97d697f195 Guard against no cwd when resolving
Refs #5074
2015-01-15 17:02:23 -08:00
Kevin Sawicki 57d020ff65 Ensure resolving paths to open uses strings
Refs #5074
2015-01-15 16:58:08 -08:00
Kevin Sawicki 3e56822968 ⬆️ oniguruma@3.0.6 2015-01-15 16:48:05 -08:00
Max Brunsfeld d46d797f87 ⬆️ snippets@0.70 2015-01-15 16:26:02 -08:00
Max Brunsfeld fa6a826a37 ⬆️ language-gfm 2015-01-15 14:16:41 -08:00
Ben Ogle e598856db5 Fix the version number in the upgrade guide 2015-01-15 14:00:37 -08:00
Ben Ogle 380fee33ef ⬆️ metrics@0.41.0 2015-01-15 14:00:37 -08:00
Kevin Sawicki d524d25a43 Prepare 0.175 2015-01-15 12:56:02 -08:00
Nathan Sobo 2f765974f8 Merge pull request #4169 from atom/upgrading-docs
Add package upgrade guide
2015-01-15 13:07:54 -07:00
Nathan Sobo 28868fdb8c ⬆️ deprecation-cop 2015-01-15 13:04:12 -07:00
Max Brunsfeld 4ce785d12c ⬆️ bookmarks and git-diff
For updated decoration type name
2015-01-15 11:35:46 -08:00
Cheng Zhao 7f8f5203bc ⬆️ grunt-atom-shell-installer@0.20.0 2015-01-15 11:09:14 -08:00
Kevin Sawicki 77fb8ba15b Prepare 0.174 2015-01-15 11:06:45 -08:00
Nathan Sobo 058005afdf ⬆️ markdown-preview 2015-01-15 11:28:41 -07:00
Kevin Sawicki 062072ad87 Add linter emoji 2015-01-15 09:57:38 -08:00
Kevin Sawicki a6a9a9e94b Use skinny arrow to remove lint warning 2015-01-15 09:53:47 -08:00
simurai c024abcc5f 🐛 Fix line-height of the indent-guide
Issue #4279
2015-01-15 20:14:57 +09:00
Nathan Sobo c69f4baa04 Merge pull request #5068 from atom/ns-line-number-decorations
Rename 'gutter' decorations to 'line-number'
2015-01-14 20:04:02 -07:00
Nathan Sobo 5a4cf01083 Move getFirst/LastVisibleScreenRow to the view 2015-01-14 20:02:49 -07:00
Nathan Sobo 49699bddf2 Fix styleSheets manifest key in package.json of fixtures 2015-01-14 19:52:26 -07:00
Nathan Sobo 542ed631e8 Emit deprecation warnings for decorations of type ‘gutter’ 2015-01-14 19:49:46 -07:00
Nathan Sobo 8c574bfd30 Rename 'gutter' decorations to 'line-number' decorations 2015-01-14 19:49:46 -07:00
Nathan Sobo f570a417e6 Allow for styles/ directory in themes in prebuild-less task 2015-01-14 19:49:07 -07:00
Nathan Sobo ae9e1b0416 ⬆️ ui themes to fix imports 2015-01-14 19:36:28 -07:00
Nathan Sobo 08ab5ff650 ⬆️ themes to rename stylesheets directories 2015-01-14 19:26:55 -07:00
Max Brunsfeld c33288f9bb ⬆️ symbols-view 2015-01-14 18:06:46 -08:00
Nathan Sobo 073f9f2a3e ⬆️ spell-check for deprecation fix 2015-01-14 19:06:15 -07:00
Nathan Sobo b6fc7ff1ac ⬆️ dev-live-reload for build error fix 2015-01-14 19:03:04 -07:00
Nathan Sobo 163ee97c73 ⬆️ packages for deprecation fixes 2015-01-14 18:58:56 -07:00
Max Brunsfeld 788b55ee2a ⬆️ git-diff 2015-01-14 17:56:04 -08:00
Max Brunsfeld 3a3a4cd3b2 ⬆️ grammar-selector 2015-01-14 17:52:02 -08:00
Max Brunsfeld 9d01795335 ⬆️ image-view 2015-01-14 17:49:06 -08:00
Nathan Sobo aa0a767e34 ⬆️ packages for deprecation fixes 2015-01-14 18:48:27 -07:00
Nathan Sobo fb4956dde6 Fix deprecated styleSheets manifest key 2015-01-14 18:48:27 -07:00
Nathan Sobo 94cee03335 Provide package name for styles metadata deprecations 2015-01-14 18:48:26 -07:00
Max Brunsfeld 15581c9750 ⬆️ incompatible-packages 2015-01-14 17:45:16 -08:00
Max Brunsfeld 55c8c920f9 ⬆️ keybinding-resolver 2015-01-14 17:43:00 -08:00
Max Brunsfeld 0db20c7cbb ⬆️ notifications@0.25.0 2015-01-14 17:37:50 -08:00
Max Brunsfeld 57e618dec9 ⬆️ package-generator 2015-01-14 17:34:03 -08:00
Max Brunsfeld 206810d5db ⬆️ release-notes 2015-01-14 17:31:04 -08:00
Nathan Sobo 85a2698f67 ⬆️ packages for deprecation fixes 2015-01-14 18:28:08 -07:00
Max Brunsfeld 04a8a6f784 ⬆️ settings-view 2015-01-14 17:26:41 -08:00
Nathan Sobo 5d40916b6c ⬆️ themes for deprecation fixes 2015-01-14 18:20:47 -07:00
Max Brunsfeld 9f73a42cf1 ⬆️ deprecation-cop@0.31.0 2015-01-14 17:19:14 -08:00
Nathan Sobo 75b43b4fa3 ⬆️ archive-view 2015-01-14 18:07:28 -07:00
Max Brunsfeld 19f105e9e7 ⬆️ snippets@0.69.0 2015-01-14 17:07:16 -08:00
Max Brunsfeld f097f6b4f8 ⬆️ deprecation-cop@0.30 2015-01-14 17:06:52 -08:00
Ben Ogle 9fa571bf39 Merge pull request #4906 from atom/bo-better-errors
Better errors
2015-01-14 16:25:01 -08:00
Ben Ogle 5cb18fc7e5 this. -> @ 2015-01-14 16:15:54 -08:00
Ben Ogle 274ae6cd57 Use buffer.getPath not @getPath in project
😬
2015-01-14 16:15:53 -08:00
Ben Ogle 7d3fe78eed Remove Pane require from workspace view specs 2015-01-14 16:15:53 -08:00
Ben Ogle 4ba7182bbf 💄 Inline fileName cause it's used once 2015-01-14 16:15:53 -08:00
Ben Ogle 5fe4476114 ⬆️ text-buffer@3.10.0 2015-01-14 16:15:53 -08:00
Ben Ogle 3dc908c5ff Use eventType from pathwatcher's watch errors in messages 2015-01-14 16:15:53 -08:00
Ben Ogle 2355862101 ⬆️ pathwatcher@2.6.0 2015-01-14 16:15:53 -08:00
Ben Ogle e51e859631 Clean up error messages for keymap.cson
Use the computed path as well
2015-01-14 16:15:53 -08:00
Ben Ogle 6211f7330f Use path name in config error messsages 2015-01-14 16:15:53 -08:00
Ben Ogle 62eac3f8a5 Fix error string when there is a user config error 2015-01-14 16:15:53 -08:00
Ben Ogle ba40706265 Add a notification when the init script can’t be loaded 2015-01-14 16:15:53 -08:00
Ben Ogle 683203a9a1 Attempt to fix theme manager specs
They work for me!
2015-01-14 16:15:53 -08:00
Ben Ogle 3ec3c2b69d Fix theme manager specs 2015-01-14 16:15:53 -08:00
Ben Ogle 1bc2248fc2 Fix config specs 2015-01-14 16:15:53 -08:00
Ben Ogle 4be793f465 Post notifications when the user’s sheet cannot be loaded / parsed 2015-01-14 16:15:53 -08:00
Ben Ogle 3454249b58 Allow ENOENT errors in project.open 2015-01-14 16:15:52 -08:00
Ben Ogle cbd42ac20c Don’t need the pane in workspace.open 2015-01-14 16:15:52 -08:00
Ben Ogle 02e4482def :up_arrow: command-palette@0.33.0 2015-01-14 16:15:52 -08:00
Ben Ogle 2306f16b30 :up_arrow: snippets for better errors 2015-01-14 16:15:52 -08:00
Ben Ogle 9bb6a18d41 Support empty paths 2015-01-14 16:15:52 -08:00
Ben Ogle 85844f03f7 Display better error on unable to watch keycap.cson 2015-01-14 16:15:52 -08:00
Ben Ogle 4b8e98af0c Display a better message when there are config watch errors on startup 2015-01-14 16:15:52 -08:00
Ben Ogle f30992c5f2 Upgrade pathwatcher and text-buffer for error catching 2015-01-14 16:15:52 -08:00
Ben Ogle 4af007dce3 💄 2015-01-14 16:15:52 -08:00
Ben Ogle 1e7da34346 Use code rather than name for custom error 2015-01-14 16:15:52 -08:00
Ben Ogle b8efbedee1 Create a warning notification when buffer has a watch error 2015-01-14 16:15:52 -08:00
Ben Ogle 8435826e8a Remove linter errors 2015-01-14 16:15:52 -08:00
Ben Ogle 16468eb65d Move workspace specs to workspace. 2015-01-14 16:15:52 -08:00
Ben Ogle d5e04e883e Use the error.code and path in the error 2015-01-14 16:15:52 -08:00
Ben Ogle 2f3ce50875 Add large file issue link to large file error 2015-01-14 16:15:51 -08:00
Ben Ogle c20d3a8182 Throw an error when the error is unhandled 2015-01-14 16:15:51 -08:00
Ben Ogle ca1f66d197 Post a notification when the user cannot access a file 2015-01-14 16:15:51 -08:00
Ben Ogle 4138b95146 Remove unnecessary < chars. wtf 2015-01-14 16:15:51 -08:00
Ben Ogle 11d4222c9f Handle error when opening a file that doesn’t exist 2015-01-14 16:15:51 -08:00
Ben Ogle bc454f14e0 Add a warning notification for oversize file open 2015-01-14 16:15:51 -08:00
Kevin Sawicki bc65322da0 Merge pull request #5066 from adrianlee44/patch-1
Fix getOriginUrl calling itself
2015-01-14 15:52:25 -08:00
Adrian Lee 63ad6b1a66 Fix getOriginUrl calling itself 2015-01-14 15:44:59 -08:00
Nathan Sobo 4dd0bf7dab ⬆️ tabs 2015-01-14 16:14:26 -07:00
Nathan Sobo aa7482ac28 ⬆️ deprecation-cop 2015-01-14 16:07:51 -07:00
Nathan Sobo ad1d92d6d9 ⬆️ markdown-preview 2015-01-14 16:02:59 -07:00
Kevin Sawicki 131e7a021a ⬆️ spell-check@0.50 2015-01-14 15:00:53 -08:00
Max Brunsfeld 38e0c39c4a ⬆️ styleguide@0.42.0
For deprecation fixes
2015-01-14 14:27:40 -08:00
Russell Lescai c538857cfa Removed unnecessary braces. 2015-01-15 08:57:25 +10:30
Max Brunsfeld c964e718b7 ⬆️ symbols-view@0.78.0
For deprecation fixes
2015-01-14 14:25:42 -08:00
Max Brunsfeld 2738da2829 ⬆️ tabs@0.63.0
For deprecation fixes
2015-01-14 14:20:58 -08:00
Max Brunsfeld fe36f5f9de ⬆️ timecop@0.27.0
For deprecation fixes
2015-01-14 14:05:40 -08:00
Max Brunsfeld c179e562a7 ⬆️ tree-view@0.148.0
For deprecation fixes
2015-01-14 13:58:28 -08:00
Max Brunsfeld e8ad1aa074 ⬆️ wrap-guide@0.30.0
For deprecation fixes
2015-01-14 13:57:59 -08:00
Nathan Sobo 3f190d67da ⬆️ markdown-preview to fix deprecations 2015-01-14 14:23:55 -07:00
Nathan Sobo e60a9e45f7 Display package name correctly for ::getUri deprecation warnings
We capture the package name during the call to ::addOpener and use it
if any open items have the deprecated ::getUri method.

Signed-off-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
2015-01-14 14:23:17 -07:00
Max Brunsfeld 114b938edf Merge pull request #5060 from atom/mb-fix-folds-with-splits
Fix folds with splits
2015-01-14 13:13:52 -08:00
Nathan Sobo 3093a35554 ⬆️ status-bar for deprecation fixes 2015-01-14 11:45:15 -07:00
Kevin Sawicki 3839432654 Merge pull request #5059 from atom/ks-squirrel-zero-point-eight
Upgrade to Squirrel for Windows 0.8
2015-01-14 10:42:00 -08:00
Nathan Sobo 0a66e9d21e Centralize deprecation warnings about getUri in Pane::addItem 2015-01-14 11:36:28 -07:00
Kevin Sawicki 3dbdfe54ad Use Atom icon in AtomSetup.exe 2015-01-14 10:32:50 -08:00
Kevin Sawicki 485fc62d22 ⬆️ grunt-atom-shell-installer@0.19 2015-01-14 10:32:50 -08:00
Kevin Sawicki a0f8405457 ⬆️ grunt-atom-shell-installer@0.18 2015-01-14 10:32:49 -08:00
Nathan Sobo 56da4f49d4 Avoid spurious deprecation warnings when editor URIs are undefined
Signed-off-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
2015-01-14 11:21:08 -07:00
Max Brunsfeld 0d5a707ffb Fix folding with splits
Fixes #4099

Signed-off-by: Nathan Sobo <nathan@github.com>
2015-01-14 09:43:20 -08:00
Kevin Sawicki 76b7a8cec9 Merge pull request #5051 from tony612/fix-marker-doc
📝 fix setHead & setTail docs in Marker
2015-01-14 09:10:26 -08:00
Nathan Sobo de987d3fd2 ⬆️ deprecation-cop 2015-01-14 10:04:03 -07:00
Tony Han 1a09ec6b92 📝 fix setHead & setTail docs in Marker
Change range -> position
2015-01-14 20:42:13 +08:00
Russell Lescai 6716e544f0 Fix spec it function. 2015-01-14 15:07:42 +10:30
Russell Lescai 2f3a9c9e35 Updated js scope selector. 2015-01-14 14:08:23 +10:30
Russell Lescai 3d1baaf3f3 Added spec. Changed argument order for config get/set. 2015-01-14 13:46:21 +10:30
Russell Lescai b819f681aa Moved setEncoding to TextEditor constructor. 2015-01-14 13:05:20 +10:30
simurai 149cfdf0c2 Let headers inherit the theme font-family
Issue https://github.com/atom/settings-view/issues/340
2015-01-14 10:37:06 +09:00
Kevin Sawicki d522438876 📝 💄 2015-01-13 17:23:15 -08:00
Kevin Sawicki 8fe72cd469 📝 Remove extra s in essential 2015-01-13 17:23:15 -08:00
Russell Lescai e30b2f1c73 Allow scoped default File Encoding 2015-01-14 11:51:21 +10:30
Nathan Sobo 51a950c419 ⬆️ deprecation-cop 2015-01-13 18:03:38 -07:00
Kevin Sawicki e693254913 Prevent form submits from changing the window URL
Refs atom/settings-view#341
2015-01-13 16:01:24 -08:00
Kevin Sawicki 170a71f416 ⬆️ apm@0.120 2015-01-13 14:52:11 -08:00
Kevin Sawicki e8c19300b5 Use access token when downloading atom shell 2015-01-13 14:37:21 -08:00
Kevin Sawicki 045850ba06 Prepare 0.173 2015-01-13 14:29:26 -08:00
Ben Ogle ab9cc75f8b Add bit about the engines field 2015-01-07 14:04:08 -08:00
Ben Ogle 9fcac1ab1c Add a bit about contributing to the guide 2015-01-07 14:04:08 -08:00
Ben Ogle c172882c15 Package upgrade guide 💄 2015-01-07 14:04:08 -08:00
Ben Ogle bb6446f7d0 Update wording in tldr of guide 2015-01-07 14:04:08 -08:00
Ben Ogle e2609ddd06 New version of atom-space-pen-views 2015-01-07 14:04:07 -08:00
Ben Ogle 7f3e0287eb Add bit about removal of subscribe() in upgrading your View section 2015-01-07 14:04:07 -08:00
Ben Ogle 1bba80c2b2 Add an example of an arg with the emitter 2015-01-07 14:04:07 -08:00
Ben Ogle 0845610595 editorView -> editorElement again 2015-01-07 14:04:07 -08:00
Ben Ogle 2c09321315 💄 2015-01-07 14:04:07 -08:00
Ben Ogle f2d495eab2 Update the guide to have a section on using the model layer. 2015-01-07 14:04:07 -08:00
Ben Ogle 67c193c5ce Update the guide's selectListView upgrade text 2015-01-07 14:04:07 -08:00
Ben Ogle e62485195a Update the docs on TextEditorView 2015-01-07 14:04:07 -08:00
Ben Ogle 1eba8cff39 Update text about attached and detached semantics 2015-01-07 14:04:07 -08:00
Ben Ogle 40b465b0b6 Update deprecation cop summary 2015-01-07 14:04:07 -08:00
Ben Ogle 909ec375b1 Remove unnecessary tos 2015-01-07 14:04:07 -08:00
Ben Ogle a17b504bdb Add missing line to upgrade guide 2015-01-07 14:04:07 -08:00
Ben Ogle 75857bec01 Update items to equal an array 2015-01-07 14:04:07 -08:00
Ben Ogle b8fa3a2127 Cleanup based on @kevinsawicki feedback 2015-01-07 14:04:07 -08:00
Ben Ogle 979fad966d 💄 2015-01-07 14:04:07 -08:00
Ben Ogle d523f9e1ec Add images of deprecations 2015-01-07 14:04:06 -08:00
Ben Ogle f10453ed04 Reword parts of the guide for clarity 2015-01-07 14:04:06 -08:00
Ben Ogle 2743c0ab6f Upgrade versions of atom-space-pen-views 2015-01-07 14:04:06 -08:00
Ben Ogle 2cf5df858f Be more clear about the export from atom-space-pen-views 2015-01-07 14:04:06 -08:00
Ben Ogle dfbb50385d Comment 💄 2015-01-07 14:04:06 -08:00
Ben Ogle e0f6642a9b with -> by 2015-01-07 14:04:06 -08:00
Ben Ogle 87c217c3f6 Fix don’t 2015-01-07 14:04:06 -08:00
Ben Ogle 77aa539e70 Update a bit of the guide 2015-01-07 14:04:06 -08:00
Ben Ogle 13fa424ed5 Add upgrading selectors section to package upgrade guide 2015-01-07 14:04:06 -08:00
Ben Ogle 0ea2a9dc9c Add guide section on subscribing to commands 2015-01-07 14:04:06 -08:00
Ben Ogle e3eadc310d Add guide section about events and the emitter. 2015-01-07 14:04:06 -08:00
Ben Ogle 6520587ba8 Add section about updating specs 2015-01-07 14:04:06 -08:00
Ben Ogle 3bb62b6651 Update converting view section 2015-01-07 14:04:06 -08:00
Ben Ogle e8ab37c207 Update the View Changes section 2015-01-07 14:04:06 -08:00
Ben Ogle 612e972ac6 Add tl;dr section 2015-01-07 14:04:05 -08:00
Ben Ogle 14a20147c6 Add an example 2015-01-07 14:04:05 -08:00
Ben Ogle ed9c62f883 Add a section on converting a SelectListView 2015-01-07 14:04:05 -08:00
Ben Ogle 0239c7d386 Update structure of the docs a bit 2015-01-07 14:04:05 -08:00
Ben Ogle 7a0b8c31d4 Add initial draft of converting your package 2015-01-07 14:04:05 -08:00
112 arquivos alterados com 5578 adições e 1354 exclusões
+3 -2
Ver Arquivo
@@ -36,7 +36,7 @@ many packages and themes that are stored in other repos under the
[tabs](https://github.com/atom/tabs),
[find-and-replace](https://github.com/atom/find-and-replace),
[language-javascript](https://github.com/atom/language-javascript), and
[atom-light-ui](http://github.com/atom/atom-light-ui).
[atom-light-ui](https://github.com/atom/atom-light-ui).
For more information on how to work with Atom's official packages, see
[Contributing to Atom Packages](https://atom.io/docs/latest/contributing-to-packages.html)
@@ -74,7 +74,7 @@ For more information on how to work with Atom's official packages, see
* Limit the first line to 72 characters or less
* Reference issues and pull requests liberally
* Consider starting the commit message with an applicable emoji:
* :lipstick: `:lipstick:` when improving the format/structure of the code
* :art: `:art:` when improving the format/structure of the code
* :racehorse: `:racehorse:` when improving performance
* :non-potable_water: `:non-potable_water:` when plugging memory leaks
* :memo: `:memo:` when writing docs
@@ -88,6 +88,7 @@ For more information on how to work with Atom's official packages, see
* :lock: `:lock:` when dealing with security
* :arrow_up: `:arrow_up:` when upgrading dependencies
* :arrow_down: `:arrow_down:` when downgrading dependencies
* :shirt: `:shirt:` when removing linter warnings
## CoffeeScript Styleguide
+2 -2
Ver Arquivo
@@ -1,6 +1,6 @@
![Atom](https://cloud.githubusercontent.com/assets/72919/2874231/3af1db48-d3dd-11e3-98dc-6066f8bc766f.png)
Atom is a hackable text editor for the 21st century, built on [atom-shell](http://github.com/atom/atom-shell), and based on everything we love about our favorite editors. We designed it to be deeply customizable, but still approachable using the default configuration.
Atom is a hackable text editor for the 21st century, built on [atom-shell](https://github.com/atom/atom-shell), and based on everything we love about our favorite editors. We designed it to be deeply customizable, but still approachable using the default configuration.
Visit [atom.io](https://atom.io) to learn more or visit the [Atom forum](https://discuss.atom.io).
@@ -9,7 +9,7 @@ about the Atom 1.0 roadmap.
## Installing
### Mac OS X
### OS X
Download the latest [Atom release](https://github.com/atom/atom/releases/latest).
+1 -1
Ver Arquivo
@@ -6,6 +6,6 @@
"url": "https://github.com/atom/atom.git"
},
"dependencies": {
"atom-package-manager": "0.119.0"
"atom-package-manager": "0.134.0"
}
}
+15 -9
Ver Arquivo
@@ -45,18 +45,24 @@ if [ $REDIRECT_STDERR ]; then
fi
if [ $OS == 'Mac' ]; then
ATOM_PATH="${ATOM_PATH:-/Applications}" # Set ATOM_PATH unless it is already set
ATOM_APP_NAME=Atom.app
# If ATOM_PATH isn't a executable file, use spotlight to search for Atom
if [ ! -x "$ATOM_PATH/$ATOM_APP_NAME" ]; then
ATOM_PATH="$(mdfind "kMDItemCFBundleIdentifier == 'com.github.atom'" | grep -v ShipIt | head -1 | xargs -0 dirname)"
fi
if [ -z "${ATOM_PATH}" ]; then
# If ATOM_PATH isnt set, check /Applications and then ~/Applications for Atom.app
if [ -x "/Applications/$ATOM_APP_NAME" ]; then
ATOM_PATH="/Applications"
elif [ -x "$HOME/Applications/$ATOM_APP_NAME" ]; then
ATOM_PATH="$HOME/Applications"
else
# We havent found an Atom.app, use spotlight to search for Atom
ATOM_PATH="$(mdfind "kMDItemCFBundleIdentifier == 'com.github.atom'" | grep -v ShipIt | head -1 | xargs -0 dirname)"
# Exit if Atom can't be found
if [ -z "$ATOM_PATH" ]; then
echo "Cannot locate Atom.app, it is usually located in /Applications. Set the ATOM_PATH environment variable to the directory containing Atom.app."
exit 1
# Exit if Atom can't be found
if [ ! -x "$ATOM_PATH/$ATOM_APP_NAME" ]; then
echo "Cannot locate Atom.app, it is usually located in /Applications. Set the ATOM_PATH environment variable to the directory containing Atom.app."
exit 1
fi
fi
fi
if [ $EXPECT_OUTPUT ]; then
+3
Ver Arquivo
@@ -198,6 +198,7 @@ module.exports = (grunt) ->
outputDir: 'atom-shell'
downloadDir: atomShellDownloadDir
rebuild: true # rebuild native modules after atom-shell is updated
token: process.env.ATOM_ACCESS_TOKEN
'create-windows-installer':
appDirectory: shellAppDir
@@ -205,6 +206,8 @@ module.exports = (grunt) ->
authors: 'GitHub Inc.'
loadingGif: path.resolve(__dirname, '..', 'resources', 'win', 'loading.gif')
iconUrl: 'https://raw.githubusercontent.com/atom/atom/master/resources/win/atom.ico'
setupIcon: path.resolve(__dirname, '..', 'resources', 'win', 'atom.ico')
remoteReleases: 'https://atom.io/api/updates'
shell:
'kill-atom':
+3 -3
Ver Arquivo
@@ -1,13 +1,13 @@
# VERSION: 0.1
# DESCRIPTION: Create the atom editor in a container
# DESCRIPTION: Create the atom editor in a container
# AUTHOR: Jessica Frazelle <jessie@docker.com>
# COMMENTS:
# This file describes how to build the atom editor
# This file describes how to build the atom editor
# in a container with all dependencies installed.
# Tested on Debian Jessie.
# USAGE:
# # Download atom Dockerfile
# wget http://raw.githubusercontent.com/atom/atom/master/Dockerfile
# wget https://raw.githubusercontent.com/atom/atom/master/Dockerfile
#
# # Build atom image
# docker build -t atom .
+3 -3
Ver Arquivo
@@ -12,19 +12,19 @@
"fs-plus": "2.x",
"github-releases": "~0.2.0",
"grunt": "~0.4.1",
"grunt-atom-shell-installer": "^0.16.0",
"grunt-atom-shell-installer": "^0.21.0",
"grunt-cli": "~0.1.9",
"grunt-coffeelint": "git+https://github.com/atom/grunt-coffeelint.git#cfb99aa99811d52687969532bd5a98011ed95bfe",
"grunt-contrib-coffee": "~0.12.0",
"grunt-contrib-csslint": "~0.1.2",
"grunt-contrib-less": "~0.8.0",
"grunt-cson": "0.14.0",
"grunt-download-atom-shell": "~0.10.0",
"grunt-download-atom-shell": "~0.11.0",
"grunt-lesslint": "0.13.0",
"grunt-peg": "~1.1.0",
"grunt-shell": "~0.3.1",
"harmony-collections": "~0.3.8",
"legal-eagle": "~0.8.0",
"legal-eagle": "~0.9.0",
"minidump": "~0.8",
"npm": "~1.4.5",
"rcedit": "~0.3.0",
+4 -1
Ver Arquivo
@@ -144,7 +144,10 @@ module.exports = (grunt) ->
cp 'spec', path.join(appDir, 'spec')
cp 'src', path.join(appDir, 'src'), filter: /.+\.(cson|coffee)$/
cp 'static', path.join(appDir, 'static')
cp 'apm', path.join(appDir, 'apm'), filter: filterNodeModule
cp path.join('apm', 'node_modules', 'atom-package-manager'), path.join(appDir, 'apm'), filter: filterNodeModule
if process.platform isnt 'win32'
fs.symlinkSync(path.join('..', '..', 'bin', 'apm'), path.join(appDir, 'apm', 'node_modules', '.bin', 'apm'))
if process.platform is 'darwin'
grunt.file.recurse path.join('resources', 'mac'), (sourcePath, rootDirectory, subDirectory='', filename) ->
+7
Ver Arquivo
@@ -35,6 +35,10 @@ module.exports =
MIT/X11
"""
'cheerio@0.15.0':
repository: "https://github.com/cheeriojs/cheerio"
license: 'MIT'
source: 'https://github.com/cheeriojs/cheerio/blob/master/package.json'
'specificity@0.1.3':
repository: 'https://github.com/keeganstreet/specificity'
license: 'MIT'
@@ -75,3 +79,6 @@ module.exports =
LGPL
"""
'core-js@0.4.10':
license: 'MIT'
source: 'http://rock.mit-license.org linked in source files and bower.json says MIT'
+1 -1
Ver Arquivo
@@ -10,7 +10,7 @@ module.exports = (grunt) ->
fullPath = path.join(relativePath, fileName)
else
fullPath = fileName
longPaths.push(fullPath) if fullPath.length >= 200
longPaths.push(fullPath) if fullPath.length >= 175
longPaths.sort (longPath1, longPath2) -> longPath2.length - longPath1.length
+36 -3
Ver Arquivo
@@ -1,4 +1,5 @@
path = require 'path'
fs = require 'fs'
LessCache = require 'less-cache'
@@ -7,14 +8,35 @@ module.exports = (grunt) ->
prebuiltConfigurations = [
['atom-dark-ui', 'atom-dark-syntax']
['atom-dark-ui', 'atom-light-syntax']
['atom-dark-ui', 'one-dark-syntax']
['atom-dark-ui', 'one-light-syntax']
['atom-dark-ui', 'solarized-dark-syntax']
['atom-dark-ui', 'base16-tomorrow-dark-theme']
['atom-dark-ui', 'base16-tomorrow-light-theme']
['atom-light-ui', 'atom-light-syntax']
['atom-light-ui', 'atom-dark-syntax']
['atom-light-ui', 'one-dark-syntax']
['atom-light-ui', 'one-light-syntax']
['atom-light-ui', 'solarized-dark-syntax']
['atom-light-ui', 'base16-tomorrow-dark-theme']
['atom-light-ui', 'base16-tomorrow-light-theme']
['one-dark-ui', 'one-dark-syntax']
['one-dark-ui', 'one-light-syntax']
['one-dark-ui', 'atom-dark-syntax']
['one-dark-ui', 'atom-light-syntax']
['one-dark-ui', 'solarized-dark-syntax']
['one-dark-ui', 'base16-tomorrow-dark-theme']
['one-dark-ui', 'base16-tomorrow-light-theme']
['one-light-ui', 'one-light-syntax']
['one-light-ui', 'one-dark-syntax']
['one-light-ui', 'atom-light-syntax']
['one-light-ui', 'atom-dark-syntax']
['one-light-ui', 'solarized-dark-syntax']
['one-light-ui', 'base16-tomorrow-dark-theme']
['one-light-ui', 'base16-tomorrow-light-theme']
]
directory = path.join(grunt.config.get('atom.appDir'), 'less-compile-cache')
@@ -25,7 +47,10 @@ module.exports = (grunt) ->
for theme in configuration
# TODO Use AtomPackage class once it runs outside of an Atom context
themePath = path.resolve('node_modules', theme)
stylesheetsDir = path.join(themePath, 'stylesheets')
if fs.existsSync(path.join(themePath, 'stylesheets'))
stylesheetsDir = path.join(themePath, 'stylesheets')
else
stylesheetsDir = path.join(themePath, 'styles')
{main} = grunt.file.readJSON(path.join(themePath, 'package.json'))
main ?= 'index.less'
mainPath = path.join(themePath, main)
@@ -38,10 +63,18 @@ module.exports = (grunt) ->
resourcePath: path.resolve('.')
importPaths: importPaths
cssForFile = (file) ->
baseVarImports = """
@import "variables/ui-variables";
@import "variables/syntax-variables";
"""
less = fs.readFileSync(file, 'utf8')
lessCache.cssForFile(file, [baseVarImports, less].join('\n'))
for file in @filesSrc
grunt.verbose.writeln("File #{file.cyan} created in cache.")
lessCache.readFileSync(file)
cssForFile(file)
for file in themeMains
grunt.verbose.writeln("File #{file.cyan} created in cache.")
lessCache.readFileSync(file)
cssForFile(file)
+1 -1
Ver Arquivo
@@ -68,7 +68,7 @@ getAssets = ->
]
when 'win32'
assets = [{assetName: 'atom-windows.zip', sourcePath: 'Atom'}]
for squirrelAsset in ['AtomSetup.exe', 'RELEASES', "atom-#{version}-full.nupkg"]
for squirrelAsset in ['AtomSetup.exe', 'RELEASES', "atom-#{version}-full.nupkg", "atom-#{version}-delta.nupkg"]
cp path.join(buildDir, 'installer', squirrelAsset), path.join(buildDir, squirrelAsset)
assets.push({assetName: squirrelAsset, sourcePath: assetName})
assets
+31 -9
Ver Arquivo
@@ -49,19 +49,17 @@ can be expressed as keystroke patterns separated by spaces.
Commands are custom DOM events that are triggered when a keystroke matches a
binding. This allows user interface code to listen for named commands without
specifying the specific keybinding that triggers it. For example, the following
code sets up {EditorView} to listen for commands to move the cursor to the first
character of the current line:
code creates a command to insert the current date in an editor:
```coffee
class EditorView
listenForEvents: ->
@command 'editor:move-to-first-character-of-line', =>
@editor.moveToFirstCharacterOfLine()
atom.commands.add 'atom-text-editor',
'user:insert-date': (event) ->
editor = @getModel()
editor.insertText(new Date().toLocaleString())
```
The `::command` method is basically an enhanced version of jQuery's `::on`
method that listens for a custom DOM event and adds some metadata to the DOM,
which is read by the command palette.
`atom.commands` refers to the global {CommandRegistry} instance where all commands
are set and consequently picked up by the command palette.
When you are looking to bind new keys, it is often useful to use the command
palette (`ctrl-shift-p`) to discover what commands are being listened for in a
@@ -69,6 +67,30 @@ given focus context. Commands are "humanized" following a simple algorithm, so a
command like `editor:fold-current-row` would appear as "Editor: Fold Current
Row".
### "Composed" Commands
A common question is, "How do I make a single keybinding execute two or more
commands?" There isn't any direct support for this in Atom, but it can be
achieved by creating a custom command that performs the multiple actions
you desire and then creating a keybinding for that command. For example, let's
say I want to create a "composed" command that performs a Select Line followed
by Cut. You could add the following to your `init.coffee`:
```coffee
atom.commands.add 'atom-text-editor', 'custom:cut-line', ->
editor = atom.workspace.getActiveTextEditor()
editor.selectLinesContainingCursors()
editor.cutSelectedText()
```
Then let's say we want to map this custom command to `alt-ctrl-z`, you could
add the following to your keymap:
```coffee
'atom-text-editor':
'alt-ctrl-z': 'custom:cut-line'
```
### Specificity and Cascade Order
As is the case with CSS applying styles, when multiple bindings match for a
+1 -1
Ver Arquivo
@@ -8,7 +8,7 @@ Ubuntu LTS 12.04 64-bit is the recommended platform.
* C++ toolchain
* [Git](http://git-scm.com/)
* [Node.js](http://nodejs.org/download/) v0.10.x
* [npm](http://www.npmjs.org/) v1.4.x (bundled with Node.js)
* [npm](https://www.npmjs.com/) v1.4.x (bundled with Node.js)
* `npm -v` to check the version.
* `npm config set python /usr/bin/python2 -g` to ensure that gyp uses python2.
* You might need to run this command as `sudo`, depending on how you have set up [npm](https://github.com/joyent/node/wiki/Installing-Node.js-via-package-manager#ubuntu-mint-elementary-os).
+2 -2
Ver Arquivo
@@ -9,7 +9,7 @@
* For 64-bit builds of node and native modules you **must** have the
[Windows 7 64-bit SDK](http://www.microsoft.com/en-us/download/details.aspx?id=8279).
You may also need the [compiler update for the Windows SDK 7.1](http://www.microsoft.com/en-us/download/details.aspx?id=4422)
* [Python](http://www.python.org/download/) v2.7.
* [Python](https://www.python.org/downloads/) v2.7.
* The python.exe must be available at `%SystemDrive%\Python27\python.exe`.
If it is installed elsewhere, you can create a symbolic link to the
directory containing the python.exe using:
@@ -19,7 +19,7 @@
### On Windows 8
* [Visual Studio Express 2013 for Windows Desktop](http://www.visualstudio.com/en-us/downloads/download-visual-studio-vs#DownloadFamilies_2)
* [node.js](http://nodejs.org/download/) v0.10.x
* [Python](http://www.python.org/download/) v2.7.x (required by [node-gyp](https://github.com/TooTallNate/node-gyp))
* [Python](https://www.python.org/downloads/) v2.7.x (required by [node-gyp](https://github.com/TooTallNate/node-gyp))
* [GitHub for Windows](http://windows.github.com/)
## Instructions
+4 -4
Ver Arquivo
@@ -44,9 +44,9 @@ the editor to see it in action!
on publishing the package you just created to [atom.io][atomio].
[atomio]: https://atom.io
[CSS]: http://en.wikipedia.org/wiki/Cascading_Style_Sheets
[LESS]: http://lesscss.org
[plist]: http://en.wikipedia.org/wiki/Property_list
[R]: http://en.wikipedia.org/wiki/R_(programming_language)
[CSS]: https://en.wikipedia.org/wiki/Cascading_Style_Sheets
[Less]: http://lesscss.org
[plist]: https://en.wikipedia.org/wiki/Property_list
[R]: https://en.wikipedia.org/wiki/R_(programming_language)
[TextMate]: http://macromates.com
[TextMateOrg]: https://github.com/textmate
+4 -4
Ver Arquivo
@@ -6,7 +6,7 @@ theme.
### Differences
TextMate themes use [plist][plist] files while Atom themes use [CSS][CSS] or
[LESS][LESS] to style the UI and syntax in the editor.
[Less][Less] to style the UI and syntax in the editor.
The utility that converts the theme first parses the theme's plist file and
then creates comparable CSS rules and properties that will style Atom similarly.
@@ -61,8 +61,8 @@ __Syntax Theme__ dropdown menu to enable your new theme.
on publishing the theme you just created to [atom.io][atomio].
[atomio]: https://atom.io
[CSS]: http://en.wikipedia.org/wiki/Cascading_Style_Sheets
[LESS]: http://lesscss.org
[plist]: http://en.wikipedia.org/wiki/Property_list
[CSS]: https://en.wikipedia.org/wiki/Cascading_Style_Sheets
[Less]: http://lesscss.org
[plist]: https://en.wikipedia.org/wiki/Property_list
[TextMate]: http://macromates.com
[TextMateThemes]: http://wiki.macromates.com/Themes/UserSubmittedThemes
+104 -15
Ver Arquivo
@@ -51,9 +51,24 @@ in the _menus_ 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
- `activationCommands` (**Optional**): an Array of Strings identifying commands that
trigger your package's activation. You can delay the loading of your package
until one of these events is triggered.
- `providedServices` (**Optional**): an Object describing the services that your
package provides, which can be used by other packages. The keys of this object
are the names of the services, and the values are Objects with the following
keys:
- `description` (**Optional**) a String describing the service
- `versions` (**Required**) an Object whose keys are Semver version strings,
and whose values are names of methods in your package's top-level module
that return a value implementing the service.
- `consumedServices` (**Optional**): an Object describing the services that your
package uses, which can be provided by other packages. The keys of this object
are the names of the services, and the values are Objects with the following
keys:
- `versions` (**Required**) an Object whose keys are Semver version ranges
and whose values are names of methods in your package's top-level module
that are called with values implementing the service.
## Source Code
@@ -83,9 +98,9 @@ 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.
down, or when your package is being updated or disabled. If your package is
watching any files, holding external resources, providing commands or subscribing
to events, release them here.
### Simple Package Code
@@ -112,19 +127,18 @@ module.exports =
serialize: -> # ...
```
Beyond this simple contract, your package has access to Atom's API. 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.
Beyond this simple contract, your package has access to [Atom's API][api]. Be aware
that the Atom 1.0 API is mostly frozen. Refer to the API documentation for what
is public. That said, 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.
## Style Sheets
Style sheets for your package should be placed in the _styles_ directory.
Any style sheets in this directory will be loaded and attached to the DOM when
your package is activated. Style sheets can be written as CSS or [LESS], but
LESS is recommended.
your package is activated. Style sheets can be written as CSS or [Less], but
Less is recommended.
Ideally, you won't need much in the way of styling. We've provided a standard
set of components which define both the colors and UI elements for any package
@@ -352,6 +366,79 @@ to indicate the type your value should be, its default, etc.
See the [Config API Docs](https://atom.io/docs/api/latest/Config) for more
details specifying your configuration.
## Interacting With Other Packages Via Services
Atom packages can interact with each other through versioned APIs called
*services*. To provide a service, in your `package.json`, specify one or more
version numbers, each paired with the name of a method on your package's main module:
```json
{
"providedServices": {
"my-service": {
"description": "Does a useful thing",
"versions": {
"1.2.3": "provideMyServiceV1",
"2.3.4": "provideMyServiceV2",
}
}
}
}
```
In your package's main module, implement the methods named above. These methods
will be called any time a package is activated that consumes their corresponding
service. They should return a value that implements the service's API.
```coffeescript
module.exports =
activate: -> # ...
provideMyServiceV1: ->
adaptToLegacyAPI(myService)
provideMyServiceV2: ->
myService
```
Similarly, to consume a service, specify one or more [version *ranges*][version-ranges],
each paired with the name of a method on the package's main module:
```json
{
"consumedServices": {
"another-service": {
"versions": {
"^1.2.3": "consumeAnotherServiceV1",
">=2.3.4 <2.5": "consumeAnotherServiceV2",
}
}
}
}
```
These methods will be called any time a package is activated that *provides* their
corresponding service. They will receive the service object as an argument. You
will usually need to perform some kind of cleanup in the event that the package
providing the service is deactivated. To do this, return a `Disposable` from
your service-consuming method:
```coffeescript
{Disposable} = require 'atom'
module.exports =
activate: -> # ...
consumeAnotherServiceV1: (service) ->
useService(adaptServiceFromLegacyAPI(service))
new Disposable -> stopUsingService(service)
consumeAnotherServiceV2: (service) ->
useService(service)
new Disposable -> stopUsingService(service)
```
## Bundle External Resources
It's common to ship external resources like images and fonts in the package, to
@@ -402,11 +489,12 @@ registry.
Run `apm help publish` to see all the available options and `apm help` to see
all the other available commands.
[api]: https://atom.io/docs/api/latest
[file-tree]: https://github.com/atom/tree-view
[status-bar]: https://github.com/atom/status-bar
[cs-syntax]: https://github.com/atom/language-coffee-script
[npm]: http://en.wikipedia.org/wiki/Npm_(software)
[npm-keys]: https://npmjs.org/doc/json.html
[npm]: https://en.wikipedia.org/wiki/Npm_(software)
[npm-keys]: https://docs.npmjs.com/files/package.json
[git-tag]: http://git-scm.com/book/en/Git-Basics-Tagging
[wrap-guide]: https://github.com/atom/wrap-guide/
[keymaps]: advanced/keymaps.md
@@ -418,9 +506,10 @@ all the other available commands.
[underscore]: http://underscorejs.org/
[jasmine]: http://jasmine.github.io
[cson]: https://github.com/atom/season
[LESS]: http://lesscss.org
[Less]: http://lesscss.org
[ui-variables]: https://github.com/atom/atom-dark-ui/blob/master/styles/ui-variables.less
[first-package]: your-first-package.html
[convert-bundle]: converting-a-text-mate-bundle.html
[convert-theme]: converting-a-text-mate-theme.html
[json-schema]: http://json-schema.org/
[version-ranges]: https://docs.npmjs.com/misc/semver#ranges
+10 -9
Ver Arquivo
@@ -1,7 +1,7 @@
# Creating a Theme
Atom's interface is rendered using HTML, and it's styled via [LESS] which is a
superset of CSS. Don't worry if you haven't heard of LESS before; it's just like
Atom's interface is rendered using HTML, and it's styled via [Less] which is a
superset of CSS. Don't worry if you haven't heard of Less before; it's just like
CSS, but with a few handy extensions.
Atom supports two types of themes: _UI_ and _syntax_. UI themes style
@@ -9,22 +9,23 @@ elements such as the tree view, the tabs, drop-down lists, and the status bar.
Syntax themes style the code inside the editor.
Themes can be installed and changed from the settings view which you can open
by selecting the _Atom > Preferences..._ menu and navigating to the _Themes_
section on the left hand side.
by selecting the _Atom > Preferences..._ menu and navigating to the _Install_
section and the _Themes_ section on the left hand side.
## Getting Started
Themes are pretty straightforward but it's still helpful to be familiar with
a few things before starting:
* LESS is a superset of CSS, but it has some really handy features like
* Less is a superset of CSS, but it has some really handy features like
variables. If you aren't familiar with its syntax, take a few minutes
to [familiarize yourself][less-tutorial].
* You may also want to review the concept of a _[package.json]_, too. This file
is used to help distribute your theme to Atom users.
* Your theme's _package.json_ must contain a `"theme"` key with a value
of `"ui"` or `"syntax"` for Atom to recognize and load it as a theme.
* You can find existing themes to install or fork on [atom.io](atomio).
* You can find existing themes to install or fork on
[atom.io][atomio-themes].
## Creating a Syntax Theme
@@ -130,13 +131,13 @@ _styleguide_, or use the shortcut `cmd-ctrl-shift-g`.
![styleguide-img]
[atomio]: http://atom.io/packages
[LESS]: http://lesscss.org/
[atomio-themes]: https://atom.io/themes
[Less]: http://lesscss.org/
[git]: http://git-scm.com/
[atom]: https://atom.io/
[package.json]: ./creating-a-package.html#package-json
[less-tutorial]: https://speakerdeck.com/danmatthews/less-css
[devtools-tutorial]: https://developers.google.com/chrome-developer-tools/docs/elements
[devtools-tutorial]: https://developer.chrome.com/devtools/docs/dom-and-styles
[ui-variables]: ./theme-variables.html
[livereload]: https://github.com/atom/dev-live-reload
[styleguide]: https://github.com/atom/styleguide
+3 -3
Ver Arquivo
@@ -169,17 +169,17 @@ For example, to change the color of the cursor, you could add the following
rule to your _~/.atom/styles.less_ file:
```less
atom-text-editor.is-focused .cursor {
atom-text-editor::shadow .cursor {
border-color: pink;
}
```
Unfamiliar with LESS? Read more about it [here][LESS].
Unfamiliar with Less? Read more about it [here][Less].
This file can also be named _styles.css_ and contain CSS.
[creating-a-package]: creating-a-package.md
[create-theme]: creating-a-theme.md
[LESS]: http://www.lesscss.org
[Less]: http://www.lesscss.org
[CSON]: https://github.com/atom/season
[CoffeeScript]: http://coffeescript.org/
+15
Ver Arquivo
@@ -100,6 +100,21 @@ When an error is thrown in Atom, the developer tools are automatically shown wit
If you can reproduce the error, use this approach to get the full stack trace. The stack trace might point to a problem in your [Init script][init script or stylesheet] or a specific package you installed, which you can then disable and report an issue on its GitHub repository.
## Check that you have a build toolchain installed
If you are having issues installing a package using `apm install`, this could be
because the package has dependencies on libraries that contain native code
and so you will need to have a C++ compiler and Python installed to be able to
install it.
You can run `apm install --check` to see if [apm][apm] can build native code on
your machine.
Check out the pre-requisites in the [build instructions][build-instructions] for
your platform for more details.
[apm]: https://github.com/atom/apm
[build-instructions]: https://github.com/atom/atom/tree/master/docs/build-instructions
[submitting issues]: https://github.com/atom/atom/blob/master/CONTRIBUTING.md#submitting-issues
[building atom]: https://github.com/atom/atom#building
[atom releases]: https://github.com/atom/atom/releases
+1 -1
Ver Arquivo
@@ -34,7 +34,7 @@ files. If you are using Git you can use `cmd-shift-b` to search the list of
files modified and untracked in your project's repository.
You can also use the tree view to navigate to a file. To open and focus the
the tree view, press `ctrl-0`. The tree view can be toggled open and closed with
tree view, press `ctrl-0`. The tree view can be toggled open and closed with
`cmd-\`.
#### Adding, Moving, Deleting Files
+3
Ver Arquivo
@@ -11,6 +11,7 @@
* [Contributing](contributing.md)
* [Contributing to Core Packages](contributing-to-packages.md)
* [Debugging](debugging.md)
* [Your First Package](your-first-package.md)
### Advanced Topics
@@ -19,6 +20,8 @@
* [Keymaps](advanced/keymaps.md)
* [Serialization](advanced/serialization.md)
* [Scopes and Scope Descriptors](advanced/scopes-and-scope-descriptors.md)
* [Theme Variables](theme-variables.md)
* [apm REST API](apm-rest-api.md)
### Upgrading to 1.0 APIs
+3 -3
Ver Arquivo
@@ -47,7 +47,7 @@ If not, there are a few things you should check before publishing:
Before you publish a package it is a good idea to check ahead of time if
a package with the same name has already been published to atom.io. You can do
that by visiting `http://atom.io/packages/my-package` to see if the package
that by visiting `https://atom.io/packages/my-package` to see if the package
already exists. If it does, update your package's name to something that is
available before proceeding.
@@ -73,7 +73,7 @@ and you only need to enter this information the first time you publish. The
credentials are stored securely in your [keychain][keychain] once you login.
:tada: Your package is now published and available on atom.io. Head on over to
`http://atom.io/packages/my-package` to see your package's page.
`https://atom.io/packages/my-package` to see your package's page.
With `apm publish`, you can bump the version and publish by using
```sh
@@ -107,7 +107,7 @@ Use `patch` when you make a small change like a bug fix that does not add or rem
[atomio]: https://atom.io
[github]: https://github.com
[git-tag]: http://git-scm.com/book/en/Git-Basics-Tagging
[keychain]: http://en.wikipedia.org/wiki/Keychain_(Apple)
[keychain]: https://en.wikipedia.org/wiki/Keychain_(Apple)
[repo-guide]: http://guides.github.com/overviews/desktop
[semver]: http://semver.org
[your-first-package]: your-first-package.html
+1 -1
Ver Arquivo
@@ -24,7 +24,7 @@ Here's an example `.less` file that a package can define using theme variables:
```css
@import "ui-variables";
.my-selector{
.my-selector {
background-color: @base-background-color;
padding: @component-padding;
}
+625
Ver Arquivo
@@ -0,0 +1,625 @@
# Upgrading your package to 1.0 APIs
Atom is rapidly approaching 1.0. Much of the effort leading up to the 1.0 has been cleaning up APIs in an attempt to future proof, and make a more pleasant experience developing packages.
This document will guide you through the large bits of upgrading your package to work with 1.0 APIs.
## TL;DR
We've set deprecation messages and errors in strategic places to help make sure you don't miss anything. You should be able to get 95% of the way to an updated package just by fixing errors and deprecations. There are a couple of things you can do to get the full effect of all the errors and deprecations.
### Use atom-space-pen-views
If you use any class from `require 'atom'` with a `$` or `View` in the name, add the `atom-space-pen-views` module to your package's `package.json` file's dependencies:
```js
{
"dependencies": {
"atom-space-pen-views": "^2.0.3"
}
}
```
Then run `apm install` in your package directory.
### Require views from atom-space-pen-views
Anywhere you are requiring one of the following from `atom` you need to require them from `atom-space-pen-views` instead.
```coffee
# require these from 'atom-space-pen-views' rather than 'atom'
$
$$
$$$
View
TextEditorView
ScrollView
SelectListView
```
So this:
```coffee
# Old way
{$, TextEditorView, View, GitRepository} = require 'atom'
```
Would be replaced by this:
```coffee
# New way
{GitRepository} = require 'atom'
{$, TextEditorView, View} = require 'atom-space-pen-views'
```
### Run specs and test your package
You wrote specs, right!? Here's where they shine. Run them with `cmd-shift-P`, and search for `run package specs`. It will show all the deprecation messages and errors.
### Update the engines field in package.json
When you are deprecation free and all done converting, upgrade the `engines` field in your package.json:
```json
{
"engines": {
"atom": ">=0.174.0, <2.0.0"
}
}
```
### Examples
We have upgraded all the core packages. Please see [this issue](https://github.com/atom/atom/issues/4011) for a link to all the upgrade PRs.
## Deprecations
All of the methods in Atom core that have changes will emit deprecation messages when called. These messages are shown in two places: your **package specs**, and in **Deprecation Cop**.
### Specs
Just run your specs, and all the deprecations will be displayed in yellow.
![spec-deps](https://cloud.githubusercontent.com/assets/69169/5637943/b85114ba-95b5-11e4-8681-b81ea8f556d7.png)
### Deprecation Cop
Run an atom window in dev mode (`atom -d`) with your package loaded, and open Deprecation Cop (search for `deprecation` in the command palette). Deprecated methods will be appear in Deprecation Cop only after they have been called.
![dep-cop](https://cloud.githubusercontent.com/assets/69169/5637914/6e702fa2-95b5-11e4-92cc-a236ddacee21.png)
When deprecation cop is open, and deprecated methods are called, a `Refresh` button will appear in the top right of the Deprecation Cop interface. So exercise your package, then come back to Deprecation Cop and click the `Refresh` button.
## Upgrading your Views
Previous to 1.0, views were baked into Atom core. These views were based on jQuery and `space-pen`. They looked something like this:
```coffee
# The old way: getting views from atom
{$, TextEditorView, View} = require 'atom'
module.exports =
class SomeView extends View
@content: ->
@div class: 'find-and-replace', =>
@div class: 'block', =>
@subview 'myEditor', new TextEditorView(mini: true)
#...
```
### The New
`require 'atom'` no longer provides view helpers or jQuery. Atom core is now 'view agnostic'. The preexisting view system is available from a new npm package: `atom-space-pen-views`.
`atom-space-pen-views` now provides jQuery, `space-pen` views, and Atom specific views:
```coffee
# These are now provided by atom-space-pen-views
$
$$
$$$
View
TextEditorView
ScrollView
SelectListView
```
### Adding the module dependencies
To use the new views, you need to specify the `atom-space-pen-views` module in your package's `package.json` file's dependencies:
```js
{
"dependencies": {
"atom-space-pen-views": "^2.0.3"
}
}
```
`space-pen` bundles jQuery. If you do not need `space-pen` or any of the views, you can require jQuery directly.
```js
{
"dependencies": {
"jquery": "^2"
}
}
```
### Converting your views
Sometimes it is as simple as converting the requires at the top of each view page. I assume you read the 'TL;DR' section and have updated all of your requires.
### Upgrading classes extending any space-pen View
#### `afterAttach` and `beforeRemove` updated
The `afterAttach` and `beforeRemove` hooks have been replaced with
`attached` and `detached` and the semantics have changed.
`afterAttach` was called whenever the node was attached to another DOM node, even if that parent node wasn't present in the DOM. `afterAttach` also was called with a boolean indicating whether or not the element and its parents were on the DOM. Now the `attached` hook is _only_ called when the node and all of its parents are actually on the DOM, and is not called with a boolean.
`beforeRemove` was only called when `$.fn.remove` was called, which was typically used when the node was completely removed from the DOM. The new `detached` hook is called whenever the DOM node is _detached_, which could happen if the node is being detached for reattachment later. In short, if `beforeRemove` is called the node is never coming back. With `detached` it might be attached again later.
```coffee
# Old way
{View} = require 'atom'
class MyView extends View
afterAttach: (onDom) ->
#...
beforeRemove: ->
#...
```
```coffee
# New way
{View} = require 'atom-space-pen-views'
class MyView extends View
attached: ->
# Always called with the equivalent of @afterAttach(true)!
#...
detached: ->
#...
```
#### `subscribe` and `subscribeToCommand` methods removed
The `subscribe` and `subscribeToCommand` methods have been removed. See the Eventing and Disposables section for more info.
### Upgrading to the new TextEditorView
All of the atom-specific methods available on the `TextEditorView` have been moved to the `TextEditor`, available via `TextEditorView::getModel`. See the [`TextEditorView` docs][TextEditorView] and [`TextEditor` docs][TextEditor] for more info.
### Upgrading classes extending ScrollView
The `ScrollView` has very minor changes.
You can no longer use `@off` to remove default behavior for `core:move-up`, `core:move-down`, etc.
```coffee
# Old way to turn off default behavior
class ResultsView extends ScrollView
initialize: (@model) ->
super()
# turn off default scrolling behavior from ScrollView
@off 'core:move-up'
@off 'core:move-down'
@off 'core:move-left'
@off 'core:move-right'
```
```coffee
# New way to turn off default behavior
class ResultsView extends ScrollView
initialize: (@model) ->
disposable = super()
# turn off default scrolling behavior from ScrollView
disposable.dispose()
```
* Check out [an example](https://github.com/atom/find-and-replace/pull/311/files#diff-9) from find-and-replace.
* See the [docs][ScrollView] for all the options.
### Upgrading classes extending SelectListView
Your SelectListView might look something like this:
```coffee
# Old!
class CommandPaletteView extends SelectListView
initialize: ->
super()
@addClass('command-palette overlay from-top')
atom.workspaceView.command 'command-palette:toggle', => @toggle()
confirmed: ({name, jQuery}) ->
@cancel()
# do something with the result
toggle: ->
if @hasParent()
@cancel()
else
@attach()
attach: ->
@storeFocusedElement()
items = [] # TODO: build items
@setItems(items)
atom.workspaceView.append(this)
@focusFilterEditor()
confirmed: ({name, jQuery}) ->
@cancel()
```
This attaches and detaches itself from the dom when toggled, canceling magically detaches it from the DOM, and it uses the classes `overlay` and `from-top`.
The new SelectListView no longer automatically detaches itself from the DOM when cancelled. It's up to you to implement whatever cancel beahavior you want. Using the new APIs to mimic the sematics of the old class, it should look like this:
```coffee
# New!
class CommandPaletteView extends SelectListView
initialize: ->
super()
# no more need for the `overlay` and `from-top` classes
@addClass('command-palette')
atom.commands.add 'atom-workspace', 'command-palette:toggle', => @toggle()
# You need to implement the `cancelled` method and hide.
cancelled: ->
@hide()
confirmed: ({name, jQuery}) ->
@cancel()
# do something with the result
toggle: ->
# Toggling now checks panel visibility,
# and hides / shows rather than attaching to / detaching from the DOM.
if @panel?.isVisible()
@cancel()
else
@show()
show: ->
# Now you will add your select list as a modal panel to the workspace
@panel ?= atom.workspace.addModalPanel(item: this)
@panel.show()
@storeFocusedElement()
items = [] # TODO: build items
@setItems(items)
@focusFilterEditor()
hide: ->
@panel?.hide()
```
* And check out the [conversion of CommandPaletteView][selectlistview-example] as a real-world example.
* See the [SelectListView docs][SelectListView] for all options.
## Using the model layer rather than the view layer
The API no longer exposes any specialized view objects or view classes. `atom.workspaceView`, and all the view classes: `WorkspaceView`, `EditorView`, `PaneView`, etc. have been globally deprecated.
Nearly all of the atom-specific actions performed by the old view objects can now be managed via the model layer. For example, here's adding a panel to the interface using the `atom.workspace` model instead of the `workspaceView`:
```coffee
# Old!
div = document.createElement('div')
atom.workspaceView.appendToTop(div)
```
```coffee
# New!
div = document.createElement('div')
atom.workspace.addTopPanel(item: div)
```
For actions that still require the view, such as dispatching commands or munging css classes, you'll access the view via the `atom.views.getView()` method. This will return a subclass of `HTMLElement` rather than a jQuery object or an instance of a deprecated view class (e.g. `WorkspaceView`).
```coffee
# Old!
workspaceView = atom.workspaceView
editorView = workspaceView.getActiveEditorView()
paneView = editorView.getPaneView()
```
```coffee
# New!
# Generally, just use the models
workspace = atom.workspace
editor = workspace.getActiveTextEditor()
pane = editor.getPane()
# If you need views, get them with `getView`
workspaceElement = atom.views.getView(atom.workspace)
editorElement = atom.views.getView(editor)
paneElement = atom.views.getView(pane)
```
## Updating Specs
`atom.workspaceView`, the `WorkspaceView` class and the `EditorView` class have been deprecated. These two objects are used heavily throughout specs, mostly to dispatch events and commands. This section will explain how to remove them while still retaining the ability to dispatch events and commands.
### Removing WorkspaceView references
`WorkspaceView` has been deprecated. Everything you could do on the view, you can now do on the `Workspace` model.
Requiring `WorkspaceView` from `atom` and accessing any methods on it will throw a deprecation warning. Many specs lean heavily on `WorkspaceView` to trigger commands and fetch `EditorView` objects.
Your specs might contain something like this:
```coffee
# Old!
{WorkspaceView} = require 'atom'
describe 'FindView', ->
beforeEach ->
atom.workspaceView = new WorkspaceView()
```
Instead, we will use the `atom.views.getView()` method. This will return a plain `HTMLElement`, not a `WorkspaceView` or jQuery object.
```coffee
# New!
describe 'FindView', ->
workspaceElement = null
beforeEach ->
workspaceElement = atom.views.getView(atom.workspace)
```
### Attaching the workspace to the DOM
The workspace needs to be attached to the DOM in some cases. For example, view hooks only work (`attached()` on `View`, `attachedCallback()` on custom elements) when there is a descendant attached to the DOM.
You might see this in your specs:
```coffee
# Old!
atom.workspaceView.attachToDom()
```
Change it to:
```coffee
# New!
jasmine.attachToDOM(workspaceElement)
```
### Removing EditorView references
Like `WorkspaceView`, `EditorView` has been deprecated. Everything you needed to do on the view you are now able to do on the `TextEditor` model.
In many cases, you will not even need to get the editor's view anymore. Any of those instances should be updated to use the `TextEditor` instance instead. You should really only need the editor's view when you plan on triggering a command on the view in a spec.
Your specs might contain something like this:
```coffee
# Old!
describe 'Something', ->
[editorView] = []
beforeEach ->
editorView = atom.workspaceView.getActiveView()
```
We're going to use `atom.views.getView()` again to get the editor element. As in the case of the `workspaceElement`, `getView` will return a subclass of `HTMLElement` rather than an `EditorView` or jQuery object.
```coffee
# New!
describe 'Something', ->
[editor, editorElement] = []
beforeEach ->
editor = atom.workspace.getActiveTextEditor()
editorElement = atom.views.getView(editor)
```
### Dispatching commands
Since the `editorElement` objects are no longer `jQuery` objects, they no longer support `trigger()`. Additionally, Atom has a new command dispatcher, `atom.commands`, that we use rather than commandeering jQuery's `trigger` method.
From this:
```coffee
# Old!
workspaceView.trigger 'a-package:toggle'
editorView.trigger 'find-and-replace:show'
```
To this:
```coffee
# New!
atom.commands.dispatch workspaceElement, 'a-package:toggle'
atom.commands.dispatch editorElement, 'find-and-replace:show'
```
## Eventing and Disposables
A couple large things changed with respect to events:
1. All model events are now exposed as event subscription methods that return [`Disposable`][disposable] objects
1. The `subscribe()` method is no longer available on `space-pen` `View` objects
1. An Emitter is now provided from `require 'atom'`
### Consuming Events
All events from the Atom API are now methods that return a [`Disposable`][disposable] object, on which you can call `dispose()` to unsubscribe.
```coffee
# Old!
editor.on 'changed', ->
```
```coffee
# New!
disposable = editor.onDidChange ->
# You can unsubscribe at some point in the future via `dispose()`
disposable.dispose()
```
Deprecation warnings will guide you toward the correct methods.
#### Using a CompositeDisposable
You can group multiple disposables into a single disposable with a `CompositeDisposable`.
```coffee
{CompositeDisposable} = require 'atom'
class Something
constructor: ->
editor = atom.workspace.getActiveTextEditor()
@disposables = new CompositeDisposable
@disposables.add editor.onDidChange ->
@disposables.add editor.onDidChangePath ->
destroy: ->
@disposables.dispose()
```
### Removing View::subscribe and Subscriber::subscribe calls
There were a couple permutations of `subscribe()`. In these examples, a `CompositeDisposable` is used as it will commonly be useful where conversion is necessary.
#### subscribe(unsubscribable)
This one is very straight forward.
```coffee
# Old!
@subscribe editor.on 'changed', ->
```
```coffee
# New!
disposables = new CompositeDisposable
disposables.add editor.onDidChange ->
```
#### subscribe(modelObject, event, method)
When the modelObject is an Atom model object, the change is very simple. Just use the correct event method, and add it to your CompositeDisposable.
```coffee
# Old!
@subscribe editor, 'changed', ->
```
```coffee
# New!
disposables = new CompositeDisposable
disposables.add editor.onDidChange ->
```
#### subscribe(jQueryObject, selector(optional), event, method)
Things are a little more complicated when subscribing to a DOM or jQuery element. Atom no longer provides helpers for subscribing to elements. You can use jQuery or the native DOM APIs, whichever you prefer.
```coffee
# Old!
@subscribe $(window), 'focus', ->
```
```coffee
# New!
{Disposable, CompositeDisposable} = require 'atom'
disposables = new CompositeDisposable
# New with jQuery
focusCallback = ->
$(window).on 'focus', focusCallback
disposables.add new Disposable ->
$(window).off 'focus', focusCallback
# New with native APIs
focusCallback = ->
window.addEventListener 'focus', focusCallback
disposables.add new Disposable ->
window.removeEventListener 'focus', focusCallback
```
### Providing Events: Using the Emitter
You no longer need to require `emissary` to get an emitter. We now provide an `Emitter` class from `require 'atom'`. We have a specific pattern for use of the `Emitter`. Rather than mixing it in, we instantiate a member variable, and create explicit subscription methods. For more information see the [`Emitter` docs][emitter].
```coffee
# New!
{Emitter} = require 'atom'
class Something
constructor: ->
@emitter = new Emitter
destroy: ->
@emitter.dispose()
onDidChange: (callback) ->
@emitter.on 'did-change', callback
methodThatFiresAChange: ->
@emitter.emit 'did-change', {data: 2}
# Using the evented class
something = new Something
something.onDidChange (eventObject) ->
console.log eventObject.data # => 2
something.methodThatFiresAChange()
```
## Subscribing To Commands
`$.fn.command` and `View::subscribeToCommand` are no longer available. Now we use `atom.commands.add`, and collect the results in a `CompositeDisposable`. See [the docs][commands-add] for more info.
```coffee
# Old!
atom.workspaceView.command 'core:close core:cancel', ->
# When inside a View class, you might see this
@subscribeToCommand 'core:close core:cancel', ->
```
```coffee
# New!
@disposables.add atom.commands.add 'atom-workspace',
'core:close': ->
'core:cancel': ->
# You can register commands directly on individual DOM elements in addition to
# using selectors. When in a View class, you should have a `@element` object
# available. `@element` is a plain HTMLElement object
@disposables.add atom.commands.add @element,
'core:close': ->
'core:cancel': ->
```
## Upgrading your stylesheet's selectors
Many selectors have changed, and we have introduced the [Shadow DOM][shadowdom] to the editor. See [Upgrading Your Package Selectors guide][upgrading-selectors] for more information in upgrading your package stylesheets.
## Help us improve this guide!
Did you hit something painful that wasn't in here? Want to reword some bit of it? Find something incorrect? Please edit [this file][guide], and send a pull request. Contributions are greatly appreciated.
[texteditorview]:https://github.com/atom/atom-space-pen-views#texteditorview
[scrollview]:https://github.com/atom/atom-space-pen-views#scrollview
[selectlistview]:https://github.com/atom/atom-space-pen-views#selectlistview
[selectlistview-example]:https://github.com/atom/command-palette/pull/19/files
[emitter]:https://atom.io/docs/api/latest/Emitter
[texteditor]:https://atom.io/docs/api/latest/TextEditor
[disposable]:https://atom.io/docs/api/latest/Disposable
[commands-add]:https://atom.io/docs/api/latest/CommandRegistry#instance-add
[upgrading-selectors]:upgrading-your-ui-theme
[shadowdom]:http://blog.atom.io/2014/11/18/avoiding-style-pollution-with-the-shadow-dom.html
[guide]:https://github.com/atom/atom/blob/master/docs/upgrading/upgrading-your-package.md
+2 -5
Ver Arquivo
@@ -6,19 +6,16 @@ Syntax themes are specifically intended to style only text editor content, so th
When theme style sheets are loaded into the text editor's shadow DOM, selectors intended to target the editor from the *outside* no longer make sense. Styles targeting the `.editor` and `.editor-colors` classes instead need to target the `:host` pseudo-element, which matches against the containing `atom-text-editor` node. Check out the [Shadow DOM 201][host-pseudo-element] article for more information about the `:host` pseudo-element.
Here's an example from Atom's light syntax theme. Note that the previous selectors intended to target the editor from the outside have been retained to allow the theme to keep working during the transition phase when it is possible to disable the shadow DOM.
Here's an example from Atom's light syntax theme. Note that the `atom-text-editor` selector intended to target the editor from the outside has been retained to allow the theme to keep working during the transition phase when it is possible to disable the shadow DOM.
```css
.editor-colors, :host { /* :host added */
atom-text-editor, :host { /* :host added */
background-color: @syntax-background-color;
color: @syntax-text-color;
}
.editor, :host { /* :host added */
.invisible-character {
color: @syntax-invisible-character-color;
}
/* more nested selectors... */
}
```
+2 -2
Ver Arquivo
@@ -1,6 +1,6 @@
# Writing specs
Atom uses [Jasmine](http://jasmine.github.io/2.0/introduction.html) as its spec framework. Any new functionality should have specs to guard against regressions.
Atom uses [Jasmine](http://jasmine.github.io/1.3/introduction.html) as its spec framework. Any new functionality should have specs to guard against regressions.
## Create a new spec
@@ -12,7 +12,7 @@ Atom uses [Jasmine](http://jasmine.github.io/2.0/introduction.html) as its spec
0. Add one or more `describe` methods
The `describe` method takes two arguments, a description and a function. If the description explains a behavior it typically begins with `when` if it is more like a unit test it begins with the method name.
The `describe` method takes two arguments, a description and a function. If the description explains a behavior it typically begins with `when`; if it is more like a unit test it begins with the method name.
```coffee
describe "when a test is written", ->
+8 -8
Ver Arquivo
@@ -1,7 +1,7 @@
# Create Your First Package
This tutorial will guide you though creating a simple command that replaces the
selected text with [ascii art](http://en.wikipedia.org/wiki/ASCII_art). When you
selected text with [ascii art](https://en.wikipedia.org/wiki/ASCII_art). When you
run our new command with the word "cool" selected, it will be replaced with:
```
@@ -72,12 +72,12 @@ command palette or by pressing `ctrl-alt-cmd-l`.
Now open the command panel and search for the `ascii-art:convert` command. But
it's not there! To fix this, open _package.json_ and find the property called
`activationEvents`. Activation Events speed up load time by allowing Atom to
`activationCommands`. Activation Events speed up load time by allowing Atom to
delay a package's activation until it's needed. So remove the existing command
and add `ascii-art:convert` to the `activationEvents` array:
and add `ascii-art:convert` to the `activationCommands` array:
```json
"activationEvents": ["ascii-art:convert"],
"activationCommands": ["ascii-art:convert"],
```
First, reload the window by running the command `window:reload`. Now when you
@@ -88,17 +88,17 @@ run the `ascii-art:convert` command it will output 'Hello, World!'
Now let's add a key binding to trigger the `ascii-art:convert` command. Open
_keymaps/ascii-art.cson_ and add a key binding linking `ctrl-alt-a` to the
`ascii-art:convert` command. You can delete the pre-existing key binding since
you don't need it anymore. When finished, the file will look like this:
you don't need it anymore. When finished, the file will have this:
```coffeescript
'atom-text-editor':
'cmd-alt-a': 'ascii-art:convert'
'ctrl-alt-a': 'ascii-art:convert'
```
Notice `atom-text-editor` on the first line. Just like CSS, keymap selectors
*scope* key bindings so they only apply to specific elements. In this case, our
binding is only active for elements matching the `atom-text-editor` selector. If
the Tree View has focus, pressing `cmd-alt-a` won't trigger the
the Tree View has focus, pressing `ctrl-alt-a` won't trigger the
`ascii-art:convert` command. But if the editor has focus, the
`ascii-art:convert` method *will* be triggered. More information on key bindings
can be found in the [keymaps](advanced/keymaps.html) documentation.
@@ -142,7 +142,7 @@ convert: ->
selection.insertText("\n#{asciiArt}\n")
```
Select some text in an editor window and hit `cmd-alt-a`. :tada: You're now an
Select some text in an editor window and hit `ctrl-alt-a`. :tada: You're now an
ASCII art professional!
## Further reading
+74 -68
Ver Arquivo
@@ -1,7 +1,7 @@
{
"name": "atom",
"productName": "Atom",
"version": "0.172.0",
"version": "0.178.0",
"description": "A hackable text editor for the 21st Century.",
"main": "./src/browser/main.js",
"repository": {
@@ -17,10 +17,11 @@
"url": "http://github.com/atom/atom/raw/master/LICENSE.md"
}
],
"atomShellVersion": "0.20.5",
"atomShellVersion": "0.21.0",
"dependencies": {
"6to5-core": "^3.0.14",
"async": "0.2.6",
"atom-keymap": "^2.5.2",
"atom-keymap": "^3.1.0",
"bootstrap": "git+https://github.com/atom/bootstrap.git#6af81906189f1747fd6c93479e3d998ebe041372",
"clear-cut": "0.4.0",
"coffee-script": "1.8.0",
@@ -28,106 +29,111 @@
"color": "^0.7.3",
"delegato": "^1",
"emissary": "^1.3.1",
"event-kit": "^1.0.1",
"first-mate": "^2.2.4",
"event-kit": "^1.0.2",
"first-mate": "^3.0.0",
"fs-plus": "^2.3.2",
"fstream": "0.1.24",
"fuzzaldrin": "^2.1",
"git-utils": "^2.2",
"git-utils": "^3.0.0",
"grim": "1.1.0",
"guid": "0.0.10",
"jasmine-json": "~0.0",
"jasmine-tagged": "^1.1.2",
"jquery": "^2.1.1",
"less-cache": "0.19.0",
"less-cache": "0.21",
"marked": "^0.3",
"mixto": "^1",
"mkdirp": "0.3.5",
"nslog": "^1.0.1",
"oniguruma": "^3.0.4",
"nslog": "^2.0.0",
"oniguruma": "^4.0.0",
"optimist": "0.4.0",
"pathwatcher": "^2.5.0",
"pathwatcher": "^3.1.0",
"property-accessors": "^1",
"q": "^1.0.1",
"random-words": "0.0.1",
"react-atom-fork": "^0.11.4",
"react-atom-fork": "^0.11.5",
"reactionary-atom-fork": "^1.0.0",
"runas": "1.0.1",
"scandal": "1.0.3",
"runas": "2.0.0",
"scandal": "2.0.0",
"scoped-property-store": "^0.16.2",
"scrollbar-style": "^1.0.2",
"season": "^5.0.5",
"scrollbar-style": "^2.0.0",
"season": "^5.1.2",
"semver": "2.2.1",
"serializable": "^1",
"service-hub": "^0.1.0",
"service-hub": "^0.2.0",
"space-pen": "3.8.2",
"stacktrace-parser": "0.1.1",
"temp": "0.7.0",
"text-buffer": "^3.8.4",
"text-buffer": "^4.0.0",
"theorist": "^1.0.2",
"underscore-plus": "^1.6.6",
"vm-compatibility-layer": "0.1.0"
},
"packageDependencies": {
"atom-dark-syntax": "0.23.0",
"atom-dark-ui": "0.43.0",
"atom-light-syntax": "0.24.0",
"atom-light-ui": "0.37.0",
"base16-tomorrow-dark-theme": "0.23.0",
"base16-tomorrow-light-theme": "0.6.0",
"solarized-dark-syntax": "0.30.0",
"solarized-light-syntax": "0.17.0",
"archive-view": "0.43.0",
"autocomplete": "0.42.0",
"autoflow": "0.20.0",
"autosave": "0.19.0",
"background-tips": "0.20.0",
"bookmarks": "0.33.0",
"bracket-matcher": "0.67.0",
"command-palette": "0.32.0",
"deprecation-cop": "0.26.0",
"dev-live-reload": "0.36.0",
"encoding-selector": "0.14.0",
"atom-dark-syntax": "0.26.0",
"atom-dark-ui": "0.46.0",
"atom-light-syntax": "0.26.0",
"atom-light-ui": "0.40.0",
"base16-tomorrow-dark-theme": "0.25.0",
"base16-tomorrow-light-theme": "0.8.0",
"one-dark-ui": "0.3.0",
"one-dark-syntax": "0.3.0",
"one-light-syntax": "0.3.0",
"one-light-ui": "0.2.0",
"solarized-dark-syntax": "0.32.0",
"solarized-light-syntax": "0.19.0",
"archive-view": "0.46.0",
"autocomplete": "0.44.0",
"autoflow": "0.21.0",
"autosave": "0.20.0",
"background-tips": "0.22.0",
"bookmarks": "0.35.0",
"bracket-matcher": "0.71.0",
"command-palette": "0.34.0",
"deprecation-cop": "0.34.0",
"dev-live-reload": "0.41.0",
"encoding-selector": "0.17.0",
"exception-reporting": "0.21.0",
"find-and-replace": "0.155.0",
"fuzzy-finder": "0.64.0",
"git-diff": "0.47.0",
"find-and-replace": "0.156.0",
"fuzzy-finder": "0.65.0",
"git-diff": "0.50.0",
"go-to-line": "0.30.0",
"grammar-selector": "0.42.0",
"image-view": "0.46.0",
"incompatible-packages": "0.19.0",
"keybinding-resolver": "0.25.0",
"link": "0.29.0",
"markdown-preview": "0.117.0",
"metrics": "0.40.0",
"notifications": "0.24.0",
"grammar-selector": "0.44.0",
"image-view": "0.48.0",
"incompatible-packages": "0.21.0",
"keybinding-resolver": "0.27.0",
"link": "0.30.0",
"markdown-preview": "0.132.0",
"metrics": "0.41.0",
"notifications": "0.26.0",
"open-on-github": "0.32.0",
"package-generator": "0.36.0",
"release-notes": "0.45.0",
"settings-view": "0.172.0",
"snippets": "0.67.0",
"spell-check": "0.49.0",
"status-bar": "0.56.0",
"styleguide": "0.41.0",
"symbols-view": "0.77.0",
"tabs": "0.62.0",
"timecop": "0.26.0",
"tree-view": "0.147.0",
"update-package-dependencies": "0.7.0",
"package-generator": "0.37.0",
"release-notes": "0.47.0",
"settings-view": "0.174.0",
"snippets": "0.72.0",
"spell-check": "0.54.0",
"status-bar": "0.57.0",
"styleguide": "0.43.0",
"symbols-view": "0.81.0",
"tabs": "0.64.0",
"timecop": "0.28.0",
"tree-view": "0.154.0",
"update-package-dependencies": "0.8.0",
"welcome": "0.21.0",
"whitespace": "0.28.0",
"wrap-guide": "0.29.0",
"language-c": "0.37.0",
"wrap-guide": "0.31.0",
"language-c": "0.38.0",
"language-clojure": "0.10.0",
"language-coffee-script": "0.39.0",
"language-css": "0.26.0",
"language-gfm": "0.62.0",
"language-css": "0.27.0",
"language-gfm": "0.63.0",
"language-git": "0.10.0",
"language-go": "0.21.0",
"language-html": "0.28.0",
"language-hyperlink": "0.12.2",
"language-java": "0.14.0",
"language-javascript": "0.54.0",
"language-json": "0.11.0",
"language-javascript": "0.56.0",
"language-json": "0.12.0",
"language-less": "0.24.0",
"language-make": "0.13.0",
"language-mustache": "0.11.0",
@@ -141,11 +147,11 @@
"language-sass": "0.31.0",
"language-shellscript": "0.12.0",
"language-source": "0.9.0",
"language-sql": "0.13.0",
"language-sql": "0.14.0",
"language-text": "0.6.0",
"language-todo": "0.15.0",
"language-todo": "0.16.0",
"language-toml": "0.15.0",
"language-xml": "0.27.0",
"language-xml": "0.28.0",
"language-yaml": "0.22.0"
},
"private": true,
+2 -1
Ver Arquivo
@@ -5,6 +5,7 @@ Summary: Atom is a hackable text editor for the 21st century
License: MIT
URL: https://atom.io/
AutoReqProv: no # Avoid libchromiumcontent.so missing dependency
Prefix: /usr/local
%description
<%= description %>
@@ -13,7 +14,7 @@ AutoReqProv: no # Avoid libchromiumcontent.so missing dependency
mkdir -p %{buildroot}/usr/local/share/atom
cp -r /tmp/atom-build/Atom/* %{buildroot}/usr/local/share/atom
mkdir -p %{buildroot}/usr/local/bin/
ln -sf /usr/local/share/atom/resources/app/apm/node_modules/.bin/apm %{buildroot}/usr/local/bin/apm
ln -sf ../share/atom/resources/app/apm/node_modules/.bin/apm %{buildroot}/usr/local/bin/apm
cp atom.sh %{buildroot}/usr/local/bin/atom
chmod 755 atom.sh
mkdir -p %{buildroot}/usr/local/share/applications/
+1 -1
Ver Arquivo
@@ -1,3 +1,3 @@
#!/bin/sh
"$0/../../app/apm/node_modules/atom-package-manager/bin/node.exe" "$0/../../app/apm/node_modules/atom-package-manager/lib/cli.js" "$@"
"$0/../../app/apm/bin/node.exe" "$0/../../app/apm/lib/cli.js" "$@"
+1 -1
Ver Arquivo
@@ -18,5 +18,5 @@ FOR %%a IN (%*) DO (
IF "%EXPECT_OUTPUT%"=="YES" (
"%~dp0\..\..\atom.exe" %*
) ELSE (
"%~dp0\..\app\apm\node_modules\atom-package-manager\bin\node.exe" "%~dp0\atom.js" %*
"%~dp0\..\app\apm\bin\node.exe" "%~dp0\atom.js" %*
)
+1 -1
Ver Arquivo
@@ -18,5 +18,5 @@ done
if [ $EXPECT_OUTPUT ]; then
"$0/../../../atom.exe" "$@"
else
"$0/../../app/apm/node_modules/atom-package-manager/bin/node.exe" "$0/../atom.js" "$@"
"$0/../../app/apm/bin/node.exe" "$0/../atom.js" "$@"
fi
+3 -3
Ver Arquivo
@@ -48,9 +48,9 @@ function bootstrap() {
var dedupeNpmCommand = npmPath + npmFlags + 'dedupe';
if (process.argv.indexOf('--no-quiet') === -1) {
buildInstallCommand += ' --quiet';
apmInstallCommand += ' --quiet';
moduleInstallCommand += ' --quiet';
buildInstallCommand += ' --loglevel error';
apmInstallCommand += ' --loglevel error';
moduleInstallCommand += ' --loglevel error';
dedupeApmCommand += ' --quiet';
dedupeNpmCommand += ' --quiet';
buildInstallOptions.ignoreStdout = true;
+41
Ver Arquivo
@@ -0,0 +1,41 @@
to5 = require '../src/6to5'
crypto = require 'crypto'
describe "6to5 transpiler support", ->
describe "::create6to5VersionAndOptionsDigest", ->
it "returns a digest for the library version and specified options", ->
defaultOptions =
blacklist: [
'useStrict'
]
experimental: true
optional: [
'asyncToGenerator'
]
reactCompat: true
sourceMap: 'inline'
version = '3.0.14'
shasum = crypto.createHash('sha1')
shasum.update('6to5-core', 'utf8')
shasum.update('\0', 'utf8')
shasum.update(version, 'utf8')
shasum.update('\0', 'utf8')
shasum.update('{"blacklist": ["useStrict",],"experimental": true,"optional": ["asyncToGenerator",],"reactCompat": true,"sourceMap": "inline",}')
expectedDigest = shasum.digest('hex')
observedDigest = to5.create6to5VersionAndOptionsDigest(version, defaultOptions)
expect(observedDigest).toEqual expectedDigest
describe "when a .js file starts with 'use 6to5';", ->
it "transpiles it using 6to5", ->
transpiled = require('./fixtures/6to5/single-quotes.js')
expect(transpiled(3)).toBe 4
describe 'when a .js file starts with "use 6to5";', ->
it "transpiles it using 6to5", ->
transpiled = require('./fixtures/6to5/double-quotes.js')
expect(transpiled(3)).toBe 4
describe "when a .js file does not start with 'use 6to6';", ->
it "does not transpile it using 6to5", ->
expect(-> require('./fixtures/6to5/invalid.js')).toThrow()
+1 -2
Ver Arquivo
@@ -47,9 +47,8 @@ describe "the `atom` global", ->
updateAvailableHandler.callCount > 0
runs ->
{releaseVersion, releaseNotes} = updateAvailableHandler.mostRecentCall.args[0]
{releaseVersion} = updateAvailableHandler.mostRecentCall.args[0]
expect(releaseVersion).toBe 'version'
expect(releaseNotes).toBe 'notes'
describe "loading default config", ->
it 'loads the default core config', ->
+127 -13
Ver Arquivo
@@ -226,14 +226,44 @@ describe "Config", ->
advanceClock(500)
expect(atom.config.save.callCount).toBe 1
describe "when a 'source' and no 'scopeSelector' is given", ->
it "removes all scoped settings with the given source", ->
atom.config.set("foo.bar.baz", 1, scopeSelector: ".a", source: "source-a")
atom.config.set("foo.bar.quux", 2, scopeSelector: ".b", source: "source-a")
expect(atom.config.get("foo.bar", scope: [".a.b"])).toEqual(baz: 1, quux: 2)
describe "when no 'scopeSelector' is given", ->
describe "when a 'source' but no key-path is given", ->
it "removes all scoped settings with the given source", ->
atom.config.set("foo.bar.baz", 1, scopeSelector: ".a", source: "source-a")
atom.config.set("foo.bar.quux", 2, scopeSelector: ".b", source: "source-a")
expect(atom.config.get("foo.bar", scope: [".a.b"])).toEqual(baz: 1, quux: 2)
atom.config.unset(null, source: "source-a")
expect(atom.config.get("foo.bar", scope: [".a"])).toEqual(baz: 0, ok: 0)
atom.config.unset(null, source: "source-a")
expect(atom.config.get("foo.bar", scope: [".a"])).toEqual(baz: 0, ok: 0)
describe "when a 'source' and a key-path is given", ->
it "removes all scoped settings with the given source and key-path", ->
atom.config.set("foo.bar.baz", 1)
atom.config.set("foo.bar.baz", 2, scopeSelector: ".a", source: "source-a")
atom.config.set("foo.bar.baz", 3, scopeSelector: ".a.b", source: "source-b")
expect(atom.config.get("foo.bar.baz", scope: [".a.b"])).toEqual(3)
atom.config.unset("foo.bar.baz", source: "source-b")
expect(atom.config.get("foo.bar.baz", scope: [".a.b"])).toEqual(2)
expect(atom.config.get("foo.bar.baz")).toEqual(1)
describe "when no 'source' is given", ->
it "removes all scoped and unscoped properties for that key-path", ->
atom.config.setDefaults("foo.bar", baz: 100)
atom.config.set("foo.bar", { baz: 1, ok: 2 }, scopeSelector: ".a")
atom.config.set("foo.bar", { baz: 11, ok: 12 }, scopeSelector: ".b")
atom.config.set("foo.bar", { baz: 21, ok: 22 })
atom.config.unset("foo.bar.baz")
expect(atom.config.get("foo.bar.baz", scope: [".a"])).toBe 100
expect(atom.config.get("foo.bar.baz", scope: [".b"])).toBe 100
expect(atom.config.get("foo.bar.baz")).toBe 100
expect(atom.config.get("foo.bar.ok", scope: [".a"])).toBe 2
expect(atom.config.get("foo.bar.ok", scope: [".b"])).toBe 12
expect(atom.config.get("foo.bar.ok")).toBe 22
describe "when a 'scopeSelector' is given", ->
it "restores the global default when no scoped default set", ->
@@ -669,6 +699,26 @@ describe "Config", ->
expect(atom.config.get("foo.bar")).toBe 'baz'
expect(atom.config.get("foo.bar", scope: ['.source.ruby'])).toBe 'more-specific'
describe "when the config file does not conform to the schema", ->
beforeEach ->
fs.writeFileSync atom.config.configFilePath, """
'*':
foo:
bar: 'omg'
int: 'baz'
'.source.ruby':
foo:
bar: 'scoped'
int: 'nope'
"""
it "validates and does not load the incorrect values", ->
atom.config.loadUserConfig()
expect(atom.config.get("foo.int")).toBe 12
expect(atom.config.get("foo.bar")).toBe 'omg'
expect(atom.config.get("foo.int", scope: ['.source.ruby'])).toBe 12
expect(atom.config.get("foo.bar", scope: ['.source.ruby'])).toBe 'scoped'
describe "when the config file contains valid cson", ->
beforeEach ->
fs.writeFileSync(atom.config.configFilePath, "foo: bar: 'baz'")
@@ -686,15 +736,14 @@ describe "Config", ->
expect(observeHandler).toHaveBeenCalledWith 'baz'
describe "when the config file contains invalid cson", ->
addErrorHandler = null
beforeEach ->
spyOn(console, 'error')
spyOn(atom.notifications, 'addError')
atom.notifications.onDidAddNotification addErrorHandler = jasmine.createSpy()
fs.writeFileSync(atom.config.configFilePath, "{{{{{")
it "logs an error to the console and does not overwrite the config file on a subsequent save", ->
atom.config.loadUserConfig()
expect(console.error).toHaveBeenCalled()
expect(atom.notifications.addError.callCount).toBe 1
expect(addErrorHandler.callCount).toBe 1
atom.config.set("hair", "blonde") # trigger a save
expect(atom.config.save).not.toHaveBeenCalled()
@@ -876,10 +925,11 @@ describe "Config", ->
expect(atom.config.get('foo.bar')).toBe 'newVal'
describe "when the config file changes to contain invalid cson", ->
addErrorHandler = null
beforeEach ->
spyOn(console, 'error')
atom.notifications.onDidAddNotification addErrorHandler = jasmine.createSpy()
writeConfigFile("}}}")
waitsFor "error to be logged", -> console.error.callCount > 0
waitsFor "error to be logged", -> addErrorHandler.callCount > 0
it "logs a warning and does not update config data", ->
expect(updatedHandler.callCount).toBe 0
@@ -1076,6 +1126,62 @@ describe "Config", ->
expect(atom.config.get('foo.bar.str', scope: ['.source.js'])).toBe 'omg'
expect(atom.config.get('foo.bar.str', scope: ['.source.coffee'])).toBe 'ok'
describe 'when a schema is added after config values have been set', ->
schema = null
beforeEach ->
schema =
type: 'object'
properties:
int:
type: 'integer'
default: 2
str:
type: 'string'
default: 'def'
it "respects the new schema when values are set", ->
expect(atom.config.set('foo.bar.str', 'global')).toBe true
expect(atom.config.set('foo.bar.str', 'scoped', scopeSelector: '.source.js')).toBe true
expect(atom.config.get('foo.bar.str')).toBe 'global'
expect(atom.config.get('foo.bar.str', scope: ['.source.js'])).toBe 'scoped'
expect(atom.config.set('foo.bar.noschema', 'nsGlobal')).toBe true
expect(atom.config.set('foo.bar.noschema', 'nsScoped', scopeSelector: '.source.js')).toBe true
expect(atom.config.get('foo.bar.noschema')).toBe 'nsGlobal'
expect(atom.config.get('foo.bar.noschema', scope: ['.source.js'])).toBe 'nsScoped'
expect(atom.config.set('foo.bar.int', 'nope')).toBe true
expect(atom.config.set('foo.bar.int', 'notanint', scopeSelector: '.source.js')).toBe true
expect(atom.config.set('foo.bar.int', 23, scopeSelector: '.source.coffee')).toBe true
expect(atom.config.get('foo.bar.int')).toBe 'nope'
expect(atom.config.get('foo.bar.int', scope: ['.source.js'])).toBe 'notanint'
expect(atom.config.get('foo.bar.int', scope: ['.source.coffee'])).toBe 23
atom.config.setSchema('foo.bar', schema)
expect(atom.config.get('foo.bar.str')).toBe 'global'
expect(atom.config.get('foo.bar.str', scope: ['.source.js'])).toBe 'scoped'
expect(atom.config.get('foo.bar.noschema')).toBe 'nsGlobal'
expect(atom.config.get('foo.bar.noschema', scope: ['.source.js'])).toBe 'nsScoped'
expect(atom.config.get('foo.bar.int')).toBe 2
expect(atom.config.get('foo.bar.int', scope: ['.source.js'])).toBe 2
expect(atom.config.get('foo.bar.int', scope: ['.source.coffee'])).toBe 23
it "sets all values that adhere to the schema", ->
expect(atom.config.set('foo.bar.int', 10)).toBe true
expect(atom.config.set('foo.bar.int', 15, scopeSelector: '.source.js')).toBe true
expect(atom.config.set('foo.bar.int', 23, scopeSelector: '.source.coffee')).toBe true
expect(atom.config.get('foo.bar.int')).toBe 10
expect(atom.config.get('foo.bar.int', scope: ['.source.js'])).toBe 15
expect(atom.config.get('foo.bar.int', scope: ['.source.coffee'])).toBe 23
atom.config.setSchema('foo.bar', schema)
expect(atom.config.get('foo.bar.int')).toBe 10
expect(atom.config.get('foo.bar.int', scope: ['.source.js'])).toBe 15
expect(atom.config.get('foo.bar.int', scope: ['.source.coffee'])).toBe 23
describe 'when the value has an "integer" type', ->
beforeEach ->
schema =
@@ -1363,6 +1469,14 @@ describe "Config", ->
atom.config.set('foo.bar.aColor', false)
expect(atom.config.get('foo.bar.aColor')).toEqual {red: 255, green: 255, blue: 255, alpha: 1}
it "returns a clone of the Color when returned in a parent object", ->
color1 = atom.config.get('foo.bar').aColor
color2 = atom.config.get('foo.bar').aColor
expect(color1.toRGBAString()).toBe 'rgba(255, 255, 255, 1)'
expect(color2.toRGBAString()).toBe 'rgba(255, 255, 255, 1)'
expect(color1).not.toBe color2
expect(color1).toEqual color2
describe 'when the `enum` key is used', ->
beforeEach ->
schema =
+9 -4
Ver Arquivo
@@ -351,11 +351,16 @@ describe "DisplayBuffer", ->
expect(displayBuffer.tokenizedLineForScreenRow(1).text).toMatch /^10/
describe "when there is another display buffer pointing to the same buffer", ->
it "does not create folds in the other display buffer", ->
it "does not consider folds to be nested inside of folds from the other display buffer", ->
otherDisplayBuffer = new DisplayBuffer({buffer, tabLength})
otherDisplayBuffer.createFold(1, 5)
displayBuffer.createFold(2, 4)
expect(otherDisplayBuffer.foldsStartingAtBufferRow(2).length).toBe 0
expect(displayBuffer.tokenizedLineForScreenRow(2).text).toBe '2'
expect(displayBuffer.tokenizedLineForScreenRow(3).text).toBe '5'
describe "when the buffer changes", ->
[fold1, fold2] = []
beforeEach ->
@@ -1058,7 +1063,7 @@ describe "DisplayBuffer", ->
[marker, decoration, decorationProperties] = []
beforeEach ->
marker = displayBuffer.markBufferRange([[2, 13], [3, 15]])
decorationProperties = {type: 'gutter', class: 'one'}
decorationProperties = {type: 'line-number', class: 'one'}
decoration = displayBuffer.decorateMarker(marker, decorationProperties)
it "can add decorations associated with markers and remove them", ->
@@ -1079,11 +1084,11 @@ describe "DisplayBuffer", ->
describe "when a decoration is updated via Decoration::update()", ->
it "emits an 'updated' event containing the new and old params", ->
decoration.onDidChangeProperties updatedSpy = jasmine.createSpy()
decoration.setProperties type: 'gutter', class: 'two'
decoration.setProperties type: 'line-number', class: 'two'
{oldProperties, newProperties} = updatedSpy.mostRecentCall.args[0]
expect(oldProperties).toEqual decorationProperties
expect(newProperties).toEqual type: 'gutter', class: 'two', id: decoration.id
expect(newProperties).toEqual type: 'line-number', class: 'two', id: decoration.id
describe "::getDecorations(properties)", ->
it "returns decorations matching the given optional properties", ->
+3
Ver Arquivo
@@ -0,0 +1,3 @@
"use 6to5";
module.exports = v => v + 1
+3
Ver Arquivo
@@ -0,0 +1,3 @@
'use 6to6';
module.exports = v => v + 1
+3
Ver Arquivo
@@ -0,0 +1,3 @@
'use 6to5';
module.exports = v => v + 1
@@ -0,0 +1,10 @@
module.exports =
activate: ->
deactivate: ->
consumeFirstServiceV3: ->
consumeFirstServiceV4: ->
consumeSecondService: ->
@@ -0,0 +1,17 @@
{
"name": "package-with-consumed-services",
"consumedServices": {
"service-1": {
"versions": {
">=0.2 <=0.3.6": "consumeFirstServiceV3",
"^0.4.1": "consumeFirstServiceV4"
}
},
"service-2": {
"versions": {
"0.2.1 || 0.2.2": "consumeSecondService"
}
}
}
}
@@ -0,0 +1,5 @@
class TestItem
getUri: -> "test"
exports.activate = ->
atom.workspace.addOpener -> new TestItem
@@ -0,0 +1,4 @@
{
"name": "package-with-invalid-styles",
"version": "1.0.0"
}
@@ -0,0 +1 @@
{
@@ -0,0 +1,13 @@
module.exports =
activate: ->
deactivate: ->
provideFirstServiceV3: ->
'first-service-v3'
provideFirstServiceV4: ->
'first-service-v4'
provideSecondService: ->
'second-service'
@@ -0,0 +1,19 @@
{
"name": "package-with-provided-services",
"providedServices": {
"service-1": {
"description": "The first service",
"versions": {
"0.3.1": "provideFirstServiceV3",
"0.4.1": "provideFirstServiceV4"
}
},
"service-2": {
"description": "The second service",
"versions": {
"0.2.1": "provideSecondService"
}
}
}
}
@@ -1,4 +1,4 @@
{
"theme": "ui",
"stylesheets": ["editor.less"]
"styleSheets": ["editor.less"]
}
@@ -1,4 +1,4 @@
{
"theme": "syntax",
"stylesheets": ["editor.less"]
"styleSheets": ["editor.less"]
}
+1 -1
Ver Arquivo
@@ -1,4 +1,4 @@
{
"theme": "ui",
"stylesheets": ["editor.less"]
"styleSheets": ["editor.less"]
}
+48
Ver Arquivo
@@ -1,5 +1,6 @@
{$, $$} = require '../src/space-pen-extensions'
Package = require '../src/package'
{Disposable} = require 'atom'
describe "PackageManager", ->
workspaceElement = null
@@ -22,6 +23,12 @@ describe "PackageManager", ->
expect(pack instanceof Package).toBe true
expect(pack.metadata.name).toBe "package-with-broken-keymap"
it "returns the package if it has an invalid stylesheet", ->
pack = atom.packages.loadPackage("package-with-invalid-styles")
expect(pack instanceof Package).toBe true
expect(pack.metadata.name).toBe "package-with-invalid-styles"
expect(pack.stylesheets.length).toBe 0
it "returns null if the package has an invalid package.json", ->
spyOn(console, 'warn')
expect(atom.packages.loadPackage("package-with-broken-package-json")).toBeNull()
@@ -445,6 +452,47 @@ describe "PackageManager", ->
runs ->
expect(atom.config.get 'editor.increaseIndentPattern', scope: ['.source.omg']).toBe '^a'
describe "service registration", ->
it "registers the package's provided and consumed services", ->
consumerModule = require "./fixtures/packages/package-with-consumed-services"
firstServiceV3Disposed = false
firstServiceV4Disposed = false
secondServiceDisposed = false
spyOn(consumerModule, 'consumeFirstServiceV3').andReturn(new Disposable -> firstServiceV3Disposed = true)
spyOn(consumerModule, 'consumeFirstServiceV4').andReturn(new Disposable -> firstServiceV4Disposed = true)
spyOn(consumerModule, 'consumeSecondService').andReturn(new Disposable -> secondServiceDisposed = true)
waitsForPromise ->
atom.packages.activatePackage("package-with-consumed-services")
waitsForPromise ->
atom.packages.activatePackage("package-with-provided-services")
runs ->
expect(consumerModule.consumeFirstServiceV3).toHaveBeenCalledWith('first-service-v3')
expect(consumerModule.consumeFirstServiceV4).toHaveBeenCalledWith('first-service-v4')
expect(consumerModule.consumeSecondService).toHaveBeenCalledWith('second-service')
consumerModule.consumeFirstServiceV3.reset()
consumerModule.consumeFirstServiceV4.reset()
consumerModule.consumeSecondService.reset()
atom.packages.deactivatePackage("package-with-provided-services")
expect(firstServiceV3Disposed).toBe true
expect(firstServiceV4Disposed).toBe true
expect(secondServiceDisposed).toBe true
atom.packages.deactivatePackage("package-with-consumed-services")
waitsForPromise ->
atom.packages.activatePackage("package-with-provided-services")
runs ->
expect(consumerModule.consumeFirstServiceV3).not.toHaveBeenCalled()
expect(consumerModule.consumeFirstServiceV4).not.toHaveBeenCalled()
expect(consumerModule.consumeSecondService).not.toHaveBeenCalled()
describe "::deactivatePackage(id)", ->
afterEach ->
atom.packages.unloadPackages()
+49
Ver Arquivo
@@ -37,6 +37,32 @@ describe "Project", ->
deserializedProject.getBuffers()[0].destroy()
expect(deserializedProject.getBuffers().length).toBe 0
it "does not deserialize buffers when their path is a directory that exists", ->
pathToOpen = path.join(temp.mkdirSync(), 'file.txt')
waitsForPromise ->
atom.project.open(pathToOpen)
runs ->
expect(atom.project.getBuffers().length).toBe 1
fs.mkdirSync(pathToOpen)
deserializedProject = atom.project.testSerialization()
expect(deserializedProject.getBuffers().length).toBe 0
it "does not deserialize buffers when their path is inaccessible", ->
pathToOpen = path.join(temp.mkdirSync(), 'file.txt')
fs.writeFileSync(pathToOpen, '')
waitsForPromise ->
atom.project.open(pathToOpen)
runs ->
expect(atom.project.getBuffers().length).toBe 1
fs.chmodSync(pathToOpen, '000')
deserializedProject = atom.project.testSerialization()
expect(deserializedProject.getBuffers().length).toBe 0
describe "when an editor is saved and the project has no path", ->
it "sets the project's path to the saved file's parent directory", ->
tempFile = temp.openSync().path
@@ -51,6 +77,29 @@ describe "Project", ->
editor.saveAs(tempFile)
expect(atom.project.getPaths()[0]).toBe path.dirname(tempFile)
describe "when a watch error is thrown from the TextBuffer", ->
editor = null
beforeEach ->
waitsForPromise ->
atom.project.open(require.resolve('./fixtures/dir/a')).then (o) -> editor = o
it "creates a warning notification", ->
atom.notifications.onDidAddNotification noteSpy = jasmine.createSpy()
error = new Error('SomeError')
error.eventType = 'resurrect'
editor.buffer.emitter.emit 'will-throw-watch-error',
handle: jasmine.createSpy()
error: error
expect(noteSpy).toHaveBeenCalled()
notification = noteSpy.mostRecentCall.args[0]
expect(notification.getType()).toBe 'warning'
expect(notification.getDetail()).toBe 'SomeError'
expect(notification.getMessage()).toContain '`resurrect`'
expect(notification.getMessage()).toContain 'fixtures/dir/a'
describe ".open(path)", ->
[absolutePath, newBufferHandler] = []
+11 -4
Ver Arquivo
@@ -17,6 +17,7 @@ Config = require '../src/config'
{Point} = require 'text-buffer'
Project = require '../src/project'
Workspace = require '../src/workspace'
ServiceHub = require 'service-hub'
TextEditor = require '../src/text-editor'
TextEditorView = require '../src/text-editor-view'
TextEditorElement = require '../src/text-editor-element'
@@ -77,6 +78,7 @@ beforeEach ->
projectPath = specProjectPath ? path.join(@specDirectory, 'fixtures')
atom.project = new Project(paths: [projectPath])
atom.workspace = new Workspace()
atom.packages.serviceHub = new ServiceHub
atom.keymaps.keyBindings = _.clone(keyBindingsToRestore)
atom.commands.restoreSnapshot(commandsToRestore)
atom.styles.restoreSnapshot(styleElementsToRestore)
@@ -213,6 +215,11 @@ jasmine.snapshotDeprecations = ->
jasmine.restoreDeprecationsSnapshot = ->
Grim.deprecations = deprecationsSnapshot
jasmine.useRealClock = ->
jasmine.unspy(window, 'setTimeout')
jasmine.unspy(window, 'clearTimeout')
jasmine.unspy(_._, 'now')
addCustomMatchers = (spec) ->
spec.addMatchers
toBeInstanceOf: (expected) ->
@@ -298,13 +305,13 @@ window.waitsForPromise = (args...) ->
window.waitsFor timeout, (moveOn) ->
promise = fn()
if shouldReject
promise.fail(moveOn)
promise.done ->
promise.catch(moveOn)
promise.then ->
jasmine.getEnv().currentSpec.fail("Expected promise to be rejected, but it was resolved")
moveOn()
else
promise.done(moveOn)
promise.fail (error) ->
promise.then(moveOn)
promise.catch (error) ->
jasmine.getEnv().currentSpec.fail("Expected promise to be resolved, but it was rejected with #{jasmine.pp(error)}")
moveOn()
+60 -25
Ver Arquivo
@@ -135,7 +135,7 @@ describe "TextEditorComponent", ->
expect(newLineHeightInPixels).not.toBe initialLineHeightInPixels
expect(component.lineNodeForScreenRow(1).offsetTop).toBe 1 * newLineHeightInPixels
it "updates the top position of lines when the font family changes", ->
xit "updates the top position of lines when the font family changes", ->
# Can't find a font that changes the line height, but we think one might exist
linesComponent = component.refs.lines
spyOn(linesComponent, 'measureLineHeightAndDefaultCharWidth').andCallFake -> editor.setLineHeightInPixels(10)
@@ -301,7 +301,9 @@ describe "TextEditorComponent", ->
expect(component.lineNodeForScreenRow(10).textContent).toBe nbsp
it "interleaves invisible line-ending characters with indent guides on empty lines", ->
component.setShowIndentGuide(true)
atom.config.set "editor.showIndentGuide", true
nextAnimationFrame()
editor.setTextInBufferRange([[10, 0], [11, 0]], "\r\n", normalizeLineEndings: false)
nextAnimationFrame()
expect(component.lineNodeForScreenRow(10).innerHTML).toBe '<span class="indent-guide"><span class="invisible-character">C</span><span class="invisible-character">E</span></span>'
@@ -334,7 +336,8 @@ describe "TextEditorComponent", ->
describe "when indent guides are enabled", ->
beforeEach ->
component.setShowIndentGuide(true)
atom.config.set "editor.showIndentGuide", true
nextAnimationFrame()
it "adds an 'indent-guide' class to spans comprising the leading whitespace", ->
line1LeafNodes = getLeafNodes(component.lineNodeForScreenRow(1))
@@ -374,6 +377,22 @@ describe "TextEditorComponent", ->
expect(line2LeafNodes[2].textContent).toBe ' '
expect(line2LeafNodes[2].classList.contains('indent-guide')).toBe true
it "renders indent guides correctly on lines containing only whitespace when invisibles are enabled", ->
atom.config.set 'editor.showInvisibles', true
atom.config.set 'editor.invisibles', space: '-', eol: 'x'
editor.getBuffer().insert([1, Infinity], '\n ')
nextAnimationFrame()
line2LeafNodes = getLeafNodes(component.lineNodeForScreenRow(2))
expect(line2LeafNodes.length).toBe 4
expect(line2LeafNodes[0].textContent).toBe '--'
expect(line2LeafNodes[0].classList.contains('indent-guide')).toBe true
expect(line2LeafNodes[1].textContent).toBe '--'
expect(line2LeafNodes[1].classList.contains('indent-guide')).toBe true
expect(line2LeafNodes[2].textContent).toBe '--'
expect(line2LeafNodes[2].classList.contains('indent-guide')).toBe true
expect(line2LeafNodes[3].textContent).toBe 'x'
it "does not render indent guides in trailing whitespace for lines containing non whitespace characters", ->
editor.getBuffer().setText " hi "
nextAnimationFrame()
@@ -410,7 +429,7 @@ describe "TextEditorComponent", ->
describe "when indent guides are disabled", ->
beforeEach ->
component.setShowIndentGuide(false)
expect(atom.config.get("editor.showIndentGuide")).toBe false
it "does not render indent guides on lines containing only whitespace", ->
editor.getBuffer().insert([1, Infinity], '\n ')
@@ -653,7 +672,7 @@ describe "TextEditorComponent", ->
expect(lineNumberHasClass(1, 'folded')).toBe false
describe "cursor rendering", ->
it "renders the currently visible cursors, translated relative to the scroll position", ->
it "renders the currently visible cursors", ->
cursor1 = editor.getLastCursor()
cursor1.setScreenPosition([0, 5])
@@ -690,9 +709,16 @@ describe "TextEditorComponent", ->
expect(cursorNodes[0].style['-webkit-transform']).toBe "translate(#{11 * charWidth}px, #{8 * lineHeightInPixels}px)"
expect(cursorNodes[1].style['-webkit-transform']).toBe "translate(#{10 * charWidth}px, #{4 * lineHeightInPixels}px)"
wrapperView.on 'cursor:moved', cursorMovedListener = jasmine.createSpy('cursorMovedListener')
cursor3.setScreenPosition([4, 11], autoscroll: false)
nextAnimationFrame()
expect(cursorNodes[1].style['-webkit-transform']).toBe "translate(#{11 * charWidth}px, #{4 * lineHeightInPixels}px)"
expect(cursorMovedListener).toHaveBeenCalled()
cursor3.destroy()
nextAnimationFrame()
cursorNodes = componentNode.querySelectorAll('.cursor')
expect(cursorNodes.length).toBe 1
expect(cursorNodes[0].style['-webkit-transform']).toBe "translate(#{11 * charWidth}px, #{8 * lineHeightInPixels}px)"
@@ -773,18 +799,23 @@ describe "TextEditorComponent", ->
cursorsNode = componentNode.querySelector('.cursors')
expect(cursorsNode.classList.contains('blink-off')).toBe false
advanceClock(component.props.cursorBlinkPeriod / 2)
nextAnimationFrame()
expect(cursorsNode.classList.contains('blink-off')).toBe true
advanceClock(component.props.cursorBlinkPeriod / 2)
nextAnimationFrame()
expect(cursorsNode.classList.contains('blink-off')).toBe false
# Stop blinking after moving the cursor
editor.moveRight()
nextAnimationFrame()
expect(cursorsNode.classList.contains('blink-off')).toBe false
advanceClock(component.props.cursorBlinkResumeDelay)
advanceClock(component.props.cursorBlinkPeriod / 2)
nextAnimationFrame()
expect(cursorsNode.classList.contains('blink-off')).toBe true
it "does not render cursors that are associated with non-empty selections", ->
@@ -915,7 +946,6 @@ describe "TextEditorComponent", ->
it "will flash the selection when flash:true is passed to editor::setSelectedBufferRange", ->
editor.setSelectedBufferRange([[1, 6], [1, 10]], flash: true)
nextAnimationFrame()
nextAnimationFrame() # flash starts on its own frame
selectionNode = componentNode.querySelector('.selection')
expect(selectionNode.classList.contains('flash')).toBe true
@@ -931,7 +961,7 @@ describe "TextEditorComponent", ->
beforeEach ->
marker = editor.displayBuffer.markBufferRange([[2, 13], [3, 15]], invalidate: 'inside')
decorationParams = {type: ['gutter', 'line'], class: 'a'}
decorationParams = {type: ['line-number', 'line'], class: 'a'}
decoration = editor.decorateMarker(marker, decorationParams)
nextAnimationFrame()
@@ -946,7 +976,7 @@ describe "TextEditorComponent", ->
# Add decorations that are out of range
marker2 = editor.displayBuffer.markBufferRange([[9, 0], [9, 0]])
editor.decorateMarker(marker2, type: ['gutter', 'line'], class: 'b')
editor.decorateMarker(marker2, type: ['line-number', 'line'], class: 'b')
nextAnimationFrame()
# Scroll decorations into view
@@ -970,7 +1000,7 @@ describe "TextEditorComponent", ->
marker.destroy()
marker = editor.markBufferRange([[0, 0], [0, 2]])
editor.decorateMarker(marker, type: ['gutter', 'line'], class: 'b')
editor.decorateMarker(marker, type: ['line-number', 'line'], class: 'b')
nextAnimationFrame()
expect(lineNumberHasClass(0, 'b')).toBe true
expect(lineNumberHasClass(1, 'b')).toBe false
@@ -1039,7 +1069,7 @@ describe "TextEditorComponent", ->
describe "when the decoration's 'onlyHead' property is true", ->
it "only applies the decoration's class to lines containing the marker's head", ->
editor.decorateMarker(marker, type: ['gutter', 'line'], class: 'only-head', onlyHead: true)
editor.decorateMarker(marker, type: ['line-number', 'line'], class: 'only-head', onlyHead: true)
nextAnimationFrame()
expect(lineAndLineNumberHaveClass(1, 'only-head')).toBe false
expect(lineAndLineNumberHaveClass(2, 'only-head')).toBe false
@@ -1048,7 +1078,7 @@ describe "TextEditorComponent", ->
describe "when the decoration's 'onlyEmpty' property is true", ->
it "only applies the decoration when its marker is empty", ->
editor.decorateMarker(marker, type: ['gutter', 'line'], class: 'only-empty', onlyEmpty: true)
editor.decorateMarker(marker, type: ['line-number', 'line'], class: 'only-empty', onlyEmpty: true)
nextAnimationFrame()
expect(lineAndLineNumberHaveClass(2, 'only-empty')).toBe false
expect(lineAndLineNumberHaveClass(3, 'only-empty')).toBe false
@@ -1060,7 +1090,7 @@ describe "TextEditorComponent", ->
describe "when the decoration's 'onlyNonEmpty' property is true", ->
it "only applies the decoration when its marker is non-empty", ->
editor.decorateMarker(marker, type: ['gutter', 'line'], class: 'only-non-empty', onlyNonEmpty: true)
editor.decorateMarker(marker, type: ['line-number', 'line'], class: 'only-non-empty', onlyNonEmpty: true)
nextAnimationFrame()
expect(lineAndLineNumberHaveClass(2, 'only-non-empty')).toBe true
expect(lineAndLineNumberHaveClass(3, 'only-non-empty')).toBe true
@@ -1089,14 +1119,14 @@ describe "TextEditorComponent", ->
nextAnimationFrame()
# Should not be rendering range containing the marker
expect(component.getRenderedRowRange()[1]).toBeLessThan 9
expect(component.presenter.computeEndRow()).toBeLessThan 9
regions = componentNode.querySelectorAll('.some-highlight .region')
# Nothing when outside the rendered row range
expect(regions.length).toBe 0
verticalScrollbarNode.scrollTop = 3.5 * lineHeightInPixels
verticalScrollbarNode.scrollTop = 4.5 * lineHeightInPixels
verticalScrollbarNode.dispatchEvent(new UIEvent('scroll'))
nextAnimationFrame()
@@ -1175,6 +1205,8 @@ describe "TextEditorComponent", ->
advanceClock(2)
decoration.flash('flash-class', 10)
nextAnimationFrame()
# Removed for 1 frame to force CSS transition to restart
expect(highlightNode.classList.contains('flash-class')).toBe false
@@ -1952,14 +1984,14 @@ describe "TextEditorComponent", ->
it "assigns the bottom/right of the scrollbars to the width of the opposite scrollbar if it is visible", ->
scrollbarCornerNode = componentNode.querySelector('.scrollbar-corner')
expect(verticalScrollbarNode.style.bottom).toBe ''
expect(horizontalScrollbarNode.style.right).toBe ''
expect(verticalScrollbarNode.style.bottom).toBe '0px'
expect(horizontalScrollbarNode.style.right).toBe '0px'
wrapperNode.style.height = 4.5 * lineHeightInPixels + 'px'
wrapperNode.style.width = '1000px'
component.measureHeightAndWidth()
nextAnimationFrame()
expect(verticalScrollbarNode.style.bottom).toBe ''
expect(verticalScrollbarNode.style.bottom).toBe '0px'
expect(horizontalScrollbarNode.style.right).toBe verticalScrollbarNode.offsetWidth + 'px'
expect(scrollbarCornerNode.style.display).toBe 'none'
@@ -1974,7 +2006,7 @@ describe "TextEditorComponent", ->
component.measureHeightAndWidth()
nextAnimationFrame()
expect(verticalScrollbarNode.style.bottom).toBe horizontalScrollbarNode.offsetHeight + 'px'
expect(horizontalScrollbarNode.style.right).toBe ''
expect(horizontalScrollbarNode.style.right).toBe '0px'
expect(scrollbarCornerNode.style.display).toBe 'none'
it "accounts for the width of the gutter in the scrollWidth of the horizontal scrollbar", ->
@@ -2059,7 +2091,7 @@ describe "TextEditorComponent", ->
componentNode.dispatchEvent(wheelEvent)
nextAnimationFrame()
expect(component.mouseWheelScreenRow).toBe null
expect(component.presenter.mouseWheelScreenRow).toBe null
it "clears the mouseWheelScreenRow after a delay even if the event does not cause scrolling", ->
expect(editor.getScrollTop()).toBe 0
@@ -2068,13 +2100,12 @@ describe "TextEditorComponent", ->
wheelEvent = new WheelEvent('mousewheel', wheelDeltaX: 0, wheelDeltaY: 10)
Object.defineProperty(wheelEvent, 'target', get: -> lineNode)
componentNode.dispatchEvent(wheelEvent)
expect(nextAnimationFrame).toBe noAnimationFrame
expect(editor.getScrollTop()).toBe 0
expect(component.mouseWheelScreenRow).toBe 0
advanceClock(component.mouseWheelScreenRowClearDelay)
expect(component.mouseWheelScreenRow).toBe null
expect(component.presenter.mouseWheelScreenRow).toBe 0
advanceClock(component.presenter.stoppedScrollingDelay)
expect(component.presenter.mouseWheelScreenRow).toBe null
it "does not preserve the line if it is on screen", ->
expect(componentNode.querySelectorAll('.line-number').length).toBe 14 # dummy line
@@ -2085,9 +2116,8 @@ describe "TextEditorComponent", ->
wheelEvent = new WheelEvent('mousewheel', wheelDeltaX: 0, wheelDeltaY: 100) # goes nowhere, we're already at scrollTop 0
Object.defineProperty(wheelEvent, 'target', get: -> lineNode)
componentNode.dispatchEvent(wheelEvent)
expect(nextAnimationFrame).toBe noAnimationFrame
expect(component.mouseWheelScreenRow).toBe 0
expect(component.presenter.mouseWheelScreenRow).toBe 0
editor.insertText("hello")
expect(componentNode.querySelectorAll('.line-number').length).toBe 14 # dummy line
expect(componentNode.querySelectorAll('.line').length).toBe 13
@@ -2508,6 +2538,7 @@ describe "TextEditorComponent", ->
it "does not assign a height on the component node", ->
wrapperNode.style.height = '200px'
component.measureHeightAndWidth()
nextAnimationFrame()
expect(componentNode.style.height).toBe ''
describe "when the wrapper view does not have an explicit height", ->
@@ -2565,6 +2596,7 @@ describe "TextEditorComponent", ->
it "works with the ::setEditorHeightInLines and ::setEditorWidthInChars helpers", ->
setEditorHeightInLines(wrapperView, 7)
nextAnimationFrame()
expect(componentNode.offsetHeight).toBe lineHeightInPixels * 7
setEditorWidthInChars(wrapperView, 10)
@@ -2698,6 +2730,7 @@ describe "TextEditorComponent", ->
beforeEach ->
atom.config.set 'editor.showIndentGuide', true, scopeSelector: '.source.js'
atom.config.set 'editor.showIndentGuide', false, scopeSelector: '.source.coffee'
nextAnimationFrame()
it "has an 'indent-guide' class when scoped editor.showIndentGuide is true, but not when scoped editor.showIndentGuide is false", ->
line1LeafNodes = getLeafNodes(component.lineNodeForScreenRow(1))
@@ -2706,6 +2739,7 @@ describe "TextEditorComponent", ->
expect(line1LeafNodes[1].classList.contains('indent-guide')).toBe false
editor.setGrammar(coffeeEditor.getGrammar())
nextAnimationFrame()
line1LeafNodes = getLeafNodes(component.lineNodeForScreenRow(1))
expect(line1LeafNodes[0].textContent).toBe ' '
@@ -2719,6 +2753,7 @@ describe "TextEditorComponent", ->
expect(line1LeafNodes[1].classList.contains('indent-guide')).toBe false
atom.config.set 'editor.showIndentGuide', false, scopeSelector: '.source.js'
nextAnimationFrame()
line1LeafNodes = getLeafNodes(component.lineNodeForScreenRow(1))
expect(line1LeafNodes[0].textContent).toBe ' '
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
+34 -7
Ver Arquivo
@@ -155,6 +155,25 @@ describe "TextEditor", ->
expect(editor2.getSoftTabs()).toBe true
expect(editor2.getEncoding()).toBe 'macroman'
it "uses scoped `core.fileEncoding` values", ->
editor1 = null
editor2 = null
atom.config.set('core.fileEncoding', 'utf16le')
atom.config.set('core.fileEncoding', 'macroman', scopeSelector: '.js')
waitsForPromise ->
atom.workspace.open('a').then (o) -> editor1 = o
runs ->
expect(editor1.getEncoding()).toBe 'utf16le'
waitsForPromise ->
atom.workspace.open('test.js').then (o) -> editor2 = o
runs ->
expect(editor2.getEncoding()).toBe 'macroman'
describe "title", ->
describe ".getTitle()", ->
it "uses the basename of the buffer's path as its title, or 'untitled' if the path is undefined", ->
@@ -2630,12 +2649,20 @@ describe "TextEditor", ->
atom.config.set("editor.autoIndentOnPaste", true)
describe "when only whitespace precedes the cursor", ->
it "auto-indents the lines spanned by the pasted text", ->
atom.clipboard.write("console.log(x);\nconsole.log(y);\n")
editor.setCursorBufferPosition([5, 2])
it "auto-indents the lines spanned by the pasted text, based on the first pasted line", ->
expect(editor.indentationForBufferRow(5)).toBe(3)
atom.clipboard.write("a(x);\n b(x);\n c(x);\n", indentBasis: 0)
editor.setCursorBufferPosition([5, 0])
editor.pasteText()
expect(editor.lineTextForBufferRow(5)).toBe(" console.log(x);")
expect(editor.lineTextForBufferRow(6)).toBe(" console.log(y);")
# Adjust the indentation of the pasted block
expect(editor.indentationForBufferRow(5)).toBe(3)
expect(editor.indentationForBufferRow(6)).toBe(4)
expect(editor.indentationForBufferRow(7)).toBe(5)
# Preserve the indentation of the next row
expect(editor.indentationForBufferRow(8)).toBe(3)
describe "when non-whitespace characters precede the cursor", ->
it "does not auto-indent the first line being pasted", ->
@@ -2732,9 +2759,9 @@ describe "TextEditor", ->
editor.setSelectedBufferRange([[1, 2], [1, Infinity]])
editor.pasteText()
expect(editor.lineTextForBufferRow(1)).toBe(" if (items.length <= 1) return items;")
expect(editor.lineTextForBufferRow(2)).toBe(" ")
expect(editor.lineTextForBufferRow(2)).toBe("")
expect(editor.lineTextForBufferRow(3)).toBe(" if (items.length <= 1) return items;")
expect(editor.getCursorBufferPosition()).toEqual([2, 2])
expect(editor.getCursorBufferPosition()).toEqual([2, 0])
describe "when there is no selection", ->
it "pastes the line above the cursor and retains the cursor's column", ->
+83 -47
Ver Arquivo
@@ -289,69 +289,105 @@ describe "ThemeManager", ->
# from within the theme itself
expect($("atom-text-editor").css("background-color")).toBe "rgb(0, 152, 255)"
describe "when the user stylesheet changes", ->
describe "user stylesheet", ->
userStylesheetPath = null
beforeEach ->
jasmine.snapshotDeprecations()
afterEach ->
jasmine.restoreDeprecationsSnapshot()
it "reloads it", ->
[styleElementAddedHandler, styleElementRemovedHandler] = []
[stylesheetRemovedHandler, stylesheetAddedHandler, stylesheetsChangedHandler] = []
userStylesheetPath = path.join(temp.mkdirSync("atom"), 'styles.less')
fs.writeFileSync(userStylesheetPath, 'body {border-style: dotted !important;}')
spyOn(atom.styles, 'getUserStyleSheetPath').andReturn userStylesheetPath
waitsForPromise ->
themeManager.activateThemes()
describe "when the user stylesheet changes", ->
beforeEach ->
jasmine.snapshotDeprecations()
runs ->
atom.styles.onDidRemoveStyleElement styleElementRemovedHandler = jasmine.createSpy("styleElementRemovedHandler")
atom.styles.onDidAddStyleElement styleElementAddedHandler = jasmine.createSpy("styleElementAddedHandler")
afterEach ->
jasmine.restoreDeprecationsSnapshot()
themeManager.onDidChangeStylesheets stylesheetsChangedHandler = jasmine.createSpy("stylesheetsChangedHandler")
themeManager.onDidRemoveStylesheet stylesheetRemovedHandler = jasmine.createSpy("stylesheetRemovedHandler")
themeManager.onDidAddStylesheet stylesheetAddedHandler = jasmine.createSpy("stylesheetAddedHandler")
spyOn(themeManager, 'loadUserStylesheet').andCallThrough()
it "reloads it", ->
[styleElementAddedHandler, styleElementRemovedHandler] = []
[stylesheetRemovedHandler, stylesheetAddedHandler, stylesheetsChangedHandler] = []
expect($(document.body).css('border-style')).toBe 'dotted'
fs.writeFileSync(userStylesheetPath, 'body {border-style: dashed}')
waitsForPromise ->
themeManager.activateThemes()
waitsFor ->
themeManager.loadUserStylesheet.callCount is 1
runs ->
atom.styles.onDidRemoveStyleElement styleElementRemovedHandler = jasmine.createSpy("styleElementRemovedHandler")
atom.styles.onDidAddStyleElement styleElementAddedHandler = jasmine.createSpy("styleElementAddedHandler")
runs ->
expect($(document.body).css('border-style')).toBe 'dashed'
themeManager.onDidChangeStylesheets stylesheetsChangedHandler = jasmine.createSpy("stylesheetsChangedHandler")
themeManager.onDidRemoveStylesheet stylesheetRemovedHandler = jasmine.createSpy("stylesheetRemovedHandler")
themeManager.onDidAddStylesheet stylesheetAddedHandler = jasmine.createSpy("stylesheetAddedHandler")
spyOn(themeManager, 'loadUserStylesheet').andCallThrough()
expect(styleElementRemovedHandler).toHaveBeenCalled()
expect(styleElementRemovedHandler.argsForCall[0][0].textContent).toContain 'dotted'
expect(stylesheetRemovedHandler).toHaveBeenCalled()
expect(stylesheetRemovedHandler.argsForCall[0][0].cssRules[0].style.border).toBe 'dotted'
expect($(document.body).css('border-style')).toBe 'dotted'
fs.writeFileSync(userStylesheetPath, 'body {border-style: dashed}')
expect(styleElementAddedHandler).toHaveBeenCalled()
expect(styleElementAddedHandler.argsForCall[0][0].textContent).toContain 'dashed'
expect(stylesheetAddedHandler).toHaveBeenCalled()
expect(stylesheetAddedHandler.argsForCall[0][0].cssRules[0].style.border).toBe 'dashed'
waitsFor ->
themeManager.loadUserStylesheet.callCount is 1
expect(stylesheetsChangedHandler).toHaveBeenCalled()
runs ->
expect($(document.body).css('border-style')).toBe 'dashed'
styleElementRemovedHandler.reset()
stylesheetRemovedHandler.reset()
stylesheetsChangedHandler.reset()
fs.removeSync(userStylesheetPath)
expect(styleElementRemovedHandler).toHaveBeenCalled()
expect(styleElementRemovedHandler.argsForCall[0][0].textContent).toContain 'dotted'
expect(stylesheetRemovedHandler).toHaveBeenCalled()
expect(stylesheetRemovedHandler.argsForCall[0][0].cssRules[0].style.border).toBe 'dotted'
waitsFor ->
themeManager.loadUserStylesheet.callCount is 2
expect(styleElementAddedHandler).toHaveBeenCalled()
expect(styleElementAddedHandler.argsForCall[0][0].textContent).toContain 'dashed'
expect(stylesheetAddedHandler).toHaveBeenCalled()
expect(stylesheetAddedHandler.argsForCall[0][0].cssRules[0].style.border).toBe 'dashed'
runs ->
expect(styleElementRemovedHandler).toHaveBeenCalled()
expect(styleElementRemovedHandler.argsForCall[0][0].textContent).toContain 'dashed'
expect(stylesheetRemovedHandler).toHaveBeenCalled()
expect(stylesheetRemovedHandler.argsForCall[0][0].cssRules[0].style.border).toBe 'dashed'
expect($(document.body).css('border-style')).toBe 'none'
expect(stylesheetsChangedHandler).toHaveBeenCalled()
expect(stylesheetsChangedHandler).toHaveBeenCalled()
styleElementRemovedHandler.reset()
stylesheetRemovedHandler.reset()
stylesheetsChangedHandler.reset()
fs.removeSync(userStylesheetPath)
waitsFor ->
themeManager.loadUserStylesheet.callCount is 2
runs ->
expect(styleElementRemovedHandler).toHaveBeenCalled()
expect(styleElementRemovedHandler.argsForCall[0][0].textContent).toContain 'dashed'
expect(stylesheetRemovedHandler).toHaveBeenCalled()
expect(stylesheetRemovedHandler.argsForCall[0][0].cssRules[0].style.border).toBe 'dashed'
expect($(document.body).css('border-style')).toBe 'none'
expect(stylesheetsChangedHandler).toHaveBeenCalled()
describe "when there is an error reading the stylesheet", ->
addErrorHandler = null
beforeEach ->
themeManager.loadUserStylesheet()
spyOn(themeManager.lessCache, 'cssForFile').andCallFake ->
throw new Error('EACCES permission denied "styles.less"')
atom.notifications.onDidAddNotification addErrorHandler = jasmine.createSpy()
it "creates an error notification and does not add the stylesheet", ->
themeManager.loadUserStylesheet()
expect(addErrorHandler).toHaveBeenCalled()
note = addErrorHandler.mostRecentCall.args[0]
expect(note.getType()).toBe 'error'
expect(note.getMessage()).toContain 'Error loading'
expect(atom.styles.styleElementsBySourcePath[atom.styles.getUserStyleSheetPath()]).toBeUndefined()
describe "when there is an error watching the user stylesheet", ->
addErrorHandler = null
beforeEach ->
{File} = require 'pathwatcher'
spyOn(File::, 'on').andCallFake (event) ->
if event.indexOf('contents-changed') > -1
throw new Error('Unable to watch path')
spyOn(themeManager, 'loadStylesheet').andReturn ''
atom.notifications.onDidAddNotification addErrorHandler = jasmine.createSpy()
it "creates an error notification", ->
themeManager.loadUserStylesheet()
expect(addErrorHandler).toHaveBeenCalled()
note = addErrorHandler.mostRecentCall.args[0]
expect(note.getType()).toBe 'error'
expect(note.getMessage()).toContain 'Unable to watch path'
describe "when a non-existent theme is present in the config", ->
beforeEach ->
+8
Ver Arquivo
@@ -169,6 +169,14 @@ describe "Window", ->
$("<a href='#scroll-me'>link</a>").appendTo(document.body).click().remove()
expect(shell.openExternal).not.toHaveBeenCalled()
describe "when a form is submitted", ->
it "prevents the default so that the window's URL isn't changed", ->
submitSpy = jasmine.createSpy('submit')
$(document).on('submit', 'form', submitSpy)
$("<form>foo</form>").appendTo(document.body).submit().remove()
expect(submitSpy.callCount).toBe 1
expect(submitSpy.argsForCall[0][0].isDefaultPrevented()).toBe true
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", ->
+188
Ver Arquivo
@@ -1,11 +1,13 @@
path = require 'path'
temp = require 'temp'
Workspace = require '../src/workspace'
Pane = require '../src/pane'
{View} = require '../src/space-pen-extensions'
platform = require './spec-helper-platform'
_ = require 'underscore-plus'
fstream = require 'fstream'
fs = require 'fs-plus'
Grim = require 'grim'
describe "Workspace", ->
workspace = null
@@ -250,6 +252,116 @@ describe "Workspace", ->
runs ->
expect(newEditorHandler.argsForCall[0][0].textEditor).toBe editor
it "records a deprecation warning on the appropriate package if the item has a ::getUri method instead of ::getURI", ->
jasmine.snapshotDeprecations()
waitsForPromise -> atom.packages.activatePackage('package-with-deprecated-pane-item-method')
waitsForPromise ->
atom.workspace.open("test")
runs ->
deprecations = Grim.getDeprecations()
expect(deprecations.length).toBe 1
expect(deprecations[0].message).toBe "Pane item with class `TestItem` should implement `::getURI` instead of `::getUri`."
expect(deprecations[0].getStacks()[0].metadata.packageName).toBe "package-with-deprecated-pane-item-method"
jasmine.restoreDeprecationsSnapshot()
describe "when there is an error opening the file", ->
notificationSpy = null
beforeEach ->
atom.notifications.onDidAddNotification notificationSpy = jasmine.createSpy()
describe "when a large file is opened", ->
beforeEach ->
spyOn(fs, 'getSizeSync').andReturn 2 * 1048577 # 2MB
it "creates a notification", ->
waitsForPromise ->
workspace.open('file1')
runs ->
expect(notificationSpy).toHaveBeenCalled()
notification = notificationSpy.mostRecentCall.args[0]
expect(notification.getType()).toBe 'warning'
expect(notification.getMessage()).toContain '< 2MB'
describe "when a file does not exist", ->
it "creates an empty buffer for the specified path", ->
waitsForPromise ->
workspace.open('not-a-file.md')
runs ->
editor = workspace.getActiveTextEditor()
expect(notificationSpy).not.toHaveBeenCalled()
expect(editor.getPath()).toContain 'not-a-file.md'
describe "when the user does not have access to the file", ->
beforeEach ->
spyOn(fs, 'openSync').andCallFake (path) ->
error = new Error("EACCES, permission denied '#{path}'")
error.path = path
error.code = 'EACCES'
throw error
it "creates a notification", ->
waitsForPromise ->
workspace.open('file1')
runs ->
expect(notificationSpy).toHaveBeenCalled()
notification = notificationSpy.mostRecentCall.args[0]
expect(notification.getType()).toBe 'warning'
expect(notification.getMessage()).toContain 'Permission denied'
expect(notification.getMessage()).toContain 'file1'
describe "when the the operation is not permitted", ->
beforeEach ->
spyOn(fs, 'openSync').andCallFake (path) ->
error = new Error("EPERM, operation not permitted '#{path}'")
error.path = path
error.code = 'EPERM'
throw error
it "creates a notification", ->
waitsForPromise ->
workspace.open('file1')
runs ->
expect(notificationSpy).toHaveBeenCalled()
notification = notificationSpy.mostRecentCall.args[0]
expect(notification.getType()).toBe 'warning'
expect(notification.getMessage()).toContain 'Unable to open'
expect(notification.getMessage()).toContain 'file1'
describe "when the the file is already open in windows", ->
beforeEach ->
spyOn(fs, 'openSync').andCallFake (path) ->
error = new Error("EBUSY, resource busy or locked '#{path}'")
error.path = path
error.code = 'EBUSY'
throw error
it "creates a notification", ->
waitsForPromise ->
workspace.open('file1')
runs ->
expect(notificationSpy).toHaveBeenCalled()
notification = notificationSpy.mostRecentCall.args[0]
expect(notification.getType()).toBe 'warning'
expect(notification.getMessage()).toContain 'Unable to open'
expect(notification.getMessage()).toContain 'file1'
describe "when there is an unhandled error", ->
beforeEach ->
spyOn(fs, 'openSync').andCallFake (path) ->
throw new Error("I dont even know what is happening right now!!")
it "creates a notification", ->
open = -> workspace.open('file1', workspace.getActivePane())
expect(open).toThrow()
describe "::reopenItem()", ->
it "opens the uri associated with the last closed pane that isn't currently open", ->
pane = workspace.getActivePane()
@@ -841,3 +953,79 @@ describe "Workspace", ->
expect(results[0].replacements).toBe 6
expect(editor.isModified()).toBeTruthy()
describe "::saveActivePaneItem()", ->
describe "when there is an error", ->
it "emits a warning notification when the file cannot be saved", ->
spyOn(Pane::, 'saveActiveItem').andCallFake ->
throw new Error("'/some/file' is a directory")
atom.notifications.onDidAddNotification addedSpy = jasmine.createSpy()
atom.workspace.saveActivePaneItem()
expect(addedSpy).toHaveBeenCalled()
expect(addedSpy.mostRecentCall.args[0].getType()).toBe 'warning'
it "emits a warning notification when the directory cannot be written to", ->
spyOn(Pane::, 'saveActiveItem').andCallFake ->
throw new Error("ENOTDIR, not a directory '/Some/dir/and-a-file.js'")
atom.notifications.onDidAddNotification addedSpy = jasmine.createSpy()
atom.workspace.saveActivePaneItem()
expect(addedSpy).toHaveBeenCalled()
expect(addedSpy.mostRecentCall.args[0].getType()).toBe 'warning'
it "emits a warning notification when the user does not have permission", ->
spyOn(Pane::, 'saveActiveItem').andCallFake ->
error = new Error("EACCES, permission denied '/Some/dir/and-a-file.js'")
error.code = 'EACCES'
error.path = '/Some/dir/and-a-file.js'
throw error
atom.notifications.onDidAddNotification addedSpy = jasmine.createSpy()
atom.workspace.saveActivePaneItem()
expect(addedSpy).toHaveBeenCalled()
expect(addedSpy.mostRecentCall.args[0].getType()).toBe 'warning'
it "emits a warning notification when the operation is not permitted", ->
spyOn(Pane::, 'saveActiveItem').andCallFake ->
error = new Error("EPERM, operation not permitted '/Some/dir/and-a-file.js'")
error.code = 'EPERM'
error.path = '/Some/dir/and-a-file.js'
throw error
it "emits a warning notification when the file is already open by another app", ->
spyOn(Pane::, 'saveActiveItem').andCallFake ->
error = new Error("EBUSY, resource busy or locked '/Some/dir/and-a-file.js'")
error.code = 'EBUSY'
error.path = '/Some/dir/and-a-file.js'
throw error
atom.notifications.onDidAddNotification addedSpy = jasmine.createSpy()
atom.workspace.saveActivePaneItem()
expect(addedSpy).toHaveBeenCalled()
notificaiton = addedSpy.mostRecentCall.args[0]
expect(notificaiton.getType()).toBe 'warning'
expect(notificaiton.getMessage()).toContain 'Unable to save'
it "emits a warning notification when the file system is read-only", ->
spyOn(Pane::, 'saveActiveItem').andCallFake ->
error = new Error("EROFS, read-only file system '/Some/dir/and-a-file.js'")
error.code = 'EROFS'
error.path = '/Some/dir/and-a-file.js'
throw error
atom.notifications.onDidAddNotification addedSpy = jasmine.createSpy()
atom.workspace.saveActivePaneItem()
expect(addedSpy).toHaveBeenCalled()
notification = addedSpy.mostRecentCall.args[0]
expect(notification.getType()).toBe 'warning'
expect(notification.getMessage()).toContain 'Unable to save'
it "emits a warning notification when the file cannot be saved", ->
spyOn(Pane::, 'saveActiveItem').andCallFake ->
throw new Error("no one knows")
save = -> atom.workspace.saveActivePaneItem()
expect(save).toThrow()
-37
Ver Arquivo
@@ -3,7 +3,6 @@ Q = require 'q'
path = require 'path'
temp = require 'temp'
TextEditorView = require '../src/text-editor-view'
Pane = require '../src/pane'
PaneView = require '../src/pane-view'
Workspace = require '../src/workspace'
@@ -295,39 +294,3 @@ describe "WorkspaceView", ->
modalContainer = workspaceElement.querySelector('atom-panel-container.modal')
expect(modalContainer.parentNode).toBe workspaceElement
describe "::saveActivePaneItem()", ->
describe "when there is an error", ->
it "emits a warning notification when the file cannot be saved", ->
spyOn(Pane::, 'saveActiveItem').andCallFake ->
throw new Error("'/some/file' is a directory")
atom.notifications.onDidAddNotification addedSpy = jasmine.createSpy()
atom.workspace.saveActivePaneItem()
expect(addedSpy).toHaveBeenCalled()
expect(addedSpy.mostRecentCall.args[0].getType()).toBe 'warning'
it "emits a warning notification when the directory cannot be written to", ->
spyOn(Pane::, 'saveActiveItem').andCallFake ->
throw new Error("ENOTDIR, not a directory '/Some/dir/and-a-file.js'")
atom.notifications.onDidAddNotification addedSpy = jasmine.createSpy()
atom.workspace.saveActivePaneItem()
expect(addedSpy).toHaveBeenCalled()
expect(addedSpy.mostRecentCall.args[0].getType()).toBe 'warning'
it "emits a warning notification when the user does not have permission", ->
spyOn(Pane::, 'saveActiveItem').andCallFake ->
throw new Error("EACCES, permission denied '/Some/dir/and-a-file.js'")
atom.notifications.onDidAddNotification addedSpy = jasmine.createSpy()
atom.workspace.saveActivePaneItem()
expect(addedSpy).toHaveBeenCalled()
expect(addedSpy.mostRecentCall.args[0].getType()).toBe 'warning'
it "emits a warning notification when the file cannot be saved", ->
spyOn(Pane::, 'saveActiveItem').andCallFake ->
throw new Error("no one knows")
save = -> atom.workspace.saveActivePaneItem()
expect(save).toThrow()
+161
Ver Arquivo
@@ -0,0 +1,161 @@
###
Cache for source code transpiled by 6to5.
Inspired by https://github.com/atom/atom/blob/6b963a562f8d495fbebe6abdbafbc7caf705f2c3/src/coffee-cache.coffee.
###
crypto = require 'crypto'
fs = require 'fs-plus'
path = require 'path'
to5 = null # Defer until used
stats =
hits: 0
misses: 0
defaultOptions =
# The Chrome dev tools will show the original version of the file
# when the source map is inlined.
sourceMap: 'inline'
# Because Atom is currently packaged with a fork of React v0.11,
# it makes sense to use the --react-compat option so the React
# JSX transformer produces pre-v0.12 code.
reactCompat: true
# Blacklisted features do not get transpiled. Features that are
# natively supported in the target environment should be listed
# here. Because Atom uses a bleeding edge version of Node/io.js,
# I think this can include es6.arrowFunctions, es6.classes, and
# possibly others, but I want to be conservative.
blacklist: [
'useStrict'
]
# Includes support for es7 features listed at:
# http://6to5.org/docs/usage/transformers/#es7-experimental-.
experimental: true
optional: [
# Target a version of the regenerator runtime that
# supports yield so the transpiled code is cleaner/smaller.
'asyncToGenerator'
]
###
shasum - Hash with an update() method.
value - Must be a value that could be returned by JSON.parse().
###
updateDigestForJsonValue = (shasum, value) ->
# Implmentation is similar to that of pretty-printing a JSON object, except:
# * Strings are not escaped.
# * No effort is made to avoid trailing commas.
# These shortcuts should not affect the correctness of this function.
type = typeof value
if type is 'string'
shasum.update('"', 'utf8')
shasum.update(value, 'utf8')
shasum.update('"', 'utf8')
else if type in ['boolean', 'number']
shasum.update(value.toString(), 'utf8')
else if value is null
shasum.update('null', 'utf8')
else if Array.isArray value
shasum.update('[', 'utf8')
for item in value
updateDigestForJsonValue(shasum, item)
shasum.update(',', 'utf8')
shasum.update(']', 'utf8')
else
# value must be an object: be sure to sort the keys.
keys = Object.keys value
keys.sort()
shasum.update('{', 'utf8')
for key in keys
updateDigestForJsonValue(shasum, key)
shasum.update(': ', 'utf8')
updateDigestForJsonValue(shasum, value[key])
shasum.update(',', 'utf8')
shasum.update('}', 'utf8')
create6to5VersionAndOptionsDigest = (version, options) ->
shasum = crypto.createHash('sha1')
# Include the version of 6to5 in the hash.
shasum.update('6to5-core', 'utf8')
shasum.update('\0', 'utf8')
shasum.update(version, 'utf8')
shasum.update('\0', 'utf8')
updateDigestForJsonValue(shasum, options)
shasum.digest('hex')
jsCacheDir = null
getCachePath = (sourceCode) ->
digest = crypto.createHash('sha1').update(sourceCode, 'utf8').digest('hex')
unless jsCacheDir?
to5Version = require('6to5-core/package.json').version
cacheDir = path.join(fs.absolute('~/.atom'), 'compile-cache')
jsCacheDir = path.join(cacheDir, 'js', '6to5', create6to5VersionAndOptionsDigest(to5Version, defaultOptions))
path.join(jsCacheDir, "#{digest}.js")
getCachedJavaScript = (cachePath) ->
if fs.isFileSync(cachePath)
try
cachedJavaScript = fs.readFileSync(cachePath, 'utf8')
stats.hits++
return cachedJavaScript
null
# Returns the 6to5 options that should be used to transpile filePath.
createOptions = (filePath) ->
options = filename: filePath
for key, value of defaultOptions
options[key] = value
options
transpile = (sourceCode, filePath, cachePath) ->
options = createOptions(filePath)
to5 ?= require '6to5-core'
js = to5.transform(sourceCode, options).code
stats.misses++
try
fs.writeFileSync(cachePath, js)
js
# Function that obeys the contract of an entry in the require.extensions map.
# Returns the transpiled version of the JavaScript code at filePath, which is
# either generated on the fly or pulled from cache.
loadFile = (module, filePath) ->
sourceCode = fs.readFileSync(filePath, 'utf8')
unless sourceCode.startsWith('"use 6to5"') or sourceCode.startsWith("'use 6to5'")
return module._compile(sourceCode, filePath)
cachePath = getCachePath(sourceCode)
js = getCachedJavaScript(cachePath) ? transpile(sourceCode, filePath, cachePath)
module._compile(js, filePath)
register = ->
Object.defineProperty(require.extensions, '.js', {
writable: false
value: loadFile
})
module.exports =
register: register
getCacheMisses: -> stats.misses
getCacheHits: -> stats.hits
# Visible for testing.
create6to5VersionAndOptionsDigest: create6to5VersionAndOptionsDigest
addPathToCache: (filePath) ->
return if path.extname(filePath) isnt '.js'
sourceCode = fs.readFileSync(filePath, 'utf8')
cachePath = getCachePath(sourceCode)
transpile(sourceCode, filePath, cachePath)
+18 -7
Ver Arquivo
@@ -3,7 +3,6 @@ ipc = require 'ipc'
os = require 'os'
path = require 'path'
remote = require 'remote'
screen = require 'screen'
shell = require 'shell'
_ = require 'underscore-plus'
@@ -43,6 +42,12 @@ class Atom extends Model
which returns an HTMLElement.
"""
serviceHubDeprecationMessage = """
atom.services is no longer available. To register service providers and
consumers, use the `providedServices` and `consumedServices` fields in
your package's package.json.
"""
Object.defineProperty atom, 'workspaceView',
get: ->
deprecate(workspaceViewDeprecationMessage)
@@ -51,6 +56,14 @@ class Atom extends Model
deprecate(workspaceViewDeprecationMessage)
atom.__workspaceView = newValue
Object.defineProperty atom, 'services',
get: ->
deprecate(serviceHubDeprecationMessage)
atom.packages.serviceHub
set: (newValue) ->
deprecate(serviceHubDeprecationMessage)
atom.packages.serviceHub = newValue
atom
# Deserializes the Atom environment from a state object
@@ -134,9 +147,6 @@ class Atom extends Model
# Public: A {Clipboard} instance
clipboard: null
# A {ServiceHub} instance
services: null
# Public: A {ContextMenuManager} instance
contextMenu: null
@@ -236,7 +246,6 @@ class Atom extends Model
NotificationManager = require './notification-manager'
PackageManager = require './package-manager'
Clipboard = require './clipboard'
ServiceHub = require './service-hub'
GrammarRegistry = require './grammar-registry'
ThemeManager = require './theme-manager'
StyleManager = require './style-manager'
@@ -272,7 +281,6 @@ class Atom extends Model
@contextMenu = new ContextMenuManager({resourcePath, devMode})
@menu = new MenuManager({resourcePath})
@clipboard = new Clipboard()
@services = new ServiceHub
@grammars = @deserializers.deserialize(@state.grammars ? @state.syntax) ? new GrammarRegistry()
@@ -547,6 +555,7 @@ class Atom extends Model
if @isValidDimensions(dimensions)
dimensions
else
screen = remote.require 'screen'
{width, height} = screen.getPrimaryDisplay().workAreaSize
{x: 0, y: 0, width: Math.min(1024, width), height}
@@ -776,7 +785,9 @@ class Atom extends Model
try
require(userInitScriptPath) if fs.isFileSync(userInitScriptPath)
catch error
console.error "Failed to load `#{userInitScriptPath}`", error.stack, error
atom.notifications.addError "Failed to load `#{userInitScriptPath}`",
detail: error.message
dismissable: true
# Require the module with the given globals.
#
+2
Ver Arquivo
@@ -216,6 +216,8 @@ class AtomApplication
ipc.on 'open', (event, options) =>
window = @windowForEvent(event)
if options?
if typeof options.pathsToOpen is 'string'
options.pathsToOpen = [options.pathsToOpen]
if options.pathsToOpen?.length > 0
options.window = window
@openPaths(options)
+8 -3
Ver Arquivo
@@ -23,13 +23,18 @@ class AtomWindow
# Normalize to make sure drive letter case is consistent on Windows
@resourcePath = path.normalize(@resourcePath) if @resourcePath
@browserWindow = new BrowserWindow
options =
show: false
title: 'Atom'
icon: @constructor.iconPath
'web-preferences':
'direct-write': false
'direct-write': true
'subpixel-font-scaling': false
# Don't set icon on Windows so the exe's ico will be used as window and
# taskbar's icon. See https://github.com/atom/atom/issues/4811 for more.
if process.platform is 'linux'
options.icon = @constructor.iconPath
@browserWindow = new BrowserWindow options
global.atomApplication.addWindow(this)
@handleEvents()
+3 -3
Ver Arquivo
@@ -47,7 +47,7 @@ class AutoUpdateManager
@setState(ErrorState)
console.error "Error Downloading Update: #{message}"
autoUpdater.on 'update-downloaded', (event, @releaseNotes, @releaseVersion) =>
autoUpdater.on 'update-downloaded', (event, releaseNotes, @releaseVersion) =>
@setState(UpdateAvailableState)
@emitUpdateAvailableEvent(@getWindows()...)
@@ -61,9 +61,9 @@ class AutoUpdateManager
@setState(UnsupportedState)
emitUpdateAvailableEvent: (windows...) ->
return unless @releaseVersion? and @releaseNotes
return unless @releaseVersion?
for atomWindow in windows
atomWindow.sendMessage('update-available', {@releaseVersion, @releaseNotes})
atomWindow.sendMessage('update-available', {@releaseVersion})
setState: (state) ->
return if @state is state
+5 -11
Ver Arquivo
@@ -13,16 +13,6 @@ process.on 'uncaughtException', (error={}) ->
nslog(error.message) if error.message?
nslog(error.stack) if error.stack?
# Patch fs.statSyncNoException/fs.lstatSyncNoException to fail for non-strings
# https://github.com/atom/atom-shell/issues/843
{lstatSyncNoException, statSyncNoException} = fs
fs.statSyncNoException = (pathToStat) ->
return false unless pathToStat and typeof pathToStat is 'string'
statSyncNoException(pathToStat)
fs.lstatSyncNoException = (pathToStat) ->
return false unless pathToStat and typeof pathToStat is 'string'
lstatSyncNoException(pathToStat)
start = ->
if process.platform is 'win32'
SquirrelUpdate = require './squirrel-update'
@@ -50,8 +40,12 @@ start = ->
app.removeListener 'open-file', addPathToOpen
app.removeListener 'open-url', addUrlToOpen
cwd = args.executedFrom?.toString() or process.cwd()
args.pathsToOpen = args.pathsToOpen.map (pathToOpen) ->
path.resolve(args.executedFrom ? process.cwd(), pathToOpen.toString())
if cwd
path.resolve(cwd, pathToOpen.toString())
else
path.resolve(pathToOpen.toString())
setupCoffeeScript()
if args.devMode
+1 -1
Ver Arquivo
@@ -125,7 +125,7 @@ addCommandsToPath = (callback) ->
atomShCommand = "#!/bin/sh\r\n\"$0/../#{relativeAtomShPath.replace(/\\/g, '/')}\" \"$@\""
apmCommandPath = path.join(binFolder, 'apm.cmd')
relativeApmPath = path.relative(binFolder, path.join(process.resourcesPath, 'app', 'apm', 'node_modules', 'atom-package-manager', 'bin', 'apm.cmd'))
relativeApmPath = path.relative(binFolder, path.join(process.resourcesPath, 'app', 'apm', 'bin', 'apm.cmd'))
apmCommand = "@echo off\r\n\"%~dp0\\#{relativeApmPath}\" %*"
apmShCommandPath = path.join(binFolder, 'apm')
+17 -16
Ver Arquivo
@@ -28,22 +28,23 @@ class BufferedProcess
# * `options` An {Object} with the following keys:
# * `command` The {String} command to execute.
# * `args` The {Array} of arguments to pass to the command (optional).
# * `options` The options {Object} to pass to Node's `ChildProcess.spawn`
# method (optional).
# * `stdout` The callback {Function} that receives a single argument which
# contains the standard output from the command. The callback is
# called as data is received but it's buffered to ensure only
# complete lines are passed until the source stream closes. After
# the source stream has closed all remaining data is sent in a
# final call (optional).
# * `stderr` The callback {Function} that receives a single argument which
# contains the standard error output from the command. The
# callback is called as data is received but it's buffered to
# ensure only complete lines are passed until the source stream
# closes. After the source stream has closed all remaining data
# is sent in a final call (optional).
# * `exit` The callback {Function} which receives a single argument
# containing the exit status (optional).
# * `options` {Object} (optional) The options {Object} to pass to Node's
# `ChildProcess.spawn` method.
# * `stdout` {Function} (optional) The callback that receives a single
# argument which contains the standard output from the command. The
# callback is called as data is received but it's buffered to ensure only
# complete lines are passed until the source stream closes. After the
# source stream has closed all remaining data is sent in a final call.
# * `data` {String}
# * `stderr` {Function} (optional) The callback that receives a single
# argument which contains the standard error output from the command. The
# callback is called as data is received but it's buffered to ensure only
# complete lines are passed until the source stream closes. After the
# source stream has closed all remaining data is sent in a final call.
# * `data` {String}
# * `exit` {Function} (optional) The callback which receives a single
# argument containing the exit status.
# * `code` {Number}
constructor: ({command, args, options, stdout, stderr, exit}={}) ->
@emitter = new Emitter
options ?= {}
+22 -8
Ver Arquivo
@@ -7,6 +7,10 @@ fs = require 'fs-plus'
cacheDir = path.join(fs.absolute('~/.atom'), 'compile-cache')
stats =
hits: 0
misses: 0
# Use separate compile cache when sudo'ing as root to avoid permission issues
if process.env.USER is 'root' and process.env.SUDO_USER and process.env.SUDO_USER isnt process.env.USER
cacheDir = path.join(cacheDir, 'root')
@@ -21,7 +25,10 @@ getCachePath = (coffee) ->
getCachedJavaScript = (cachePath) ->
if fs.isFileSync(cachePath)
try
fs.readFileSync(cachePath, 'utf8')
cachedJavaScript = fs.readFileSync(cachePath, 'utf8')
stats.hits++
return cachedJavaScript
return
convertFilePath = (filePath) ->
if process.platform is 'win32'
@@ -30,6 +37,7 @@ convertFilePath = (filePath) ->
compileCoffeeScript = (coffee, filePath, cachePath) ->
{js, v3SourceMap} = CoffeeScript.compile(coffee, filename: filePath, sourceMap: true)
stats.misses++
# Include source map in the web page environment.
if btoa? and JSON? and unescape? and encodeURIComponent?
js = "#{js}\n//# sourceMappingURL=data:application/json;base64,#{btoa unescape encodeURIComponent v3SourceMap}\n//# sourceURL=#{convertFilePath(filePath)}"
@@ -53,10 +61,16 @@ module.exports =
})
addPathToCache: (filePath) ->
extension = path.extname(filePath)
if extension is '.coffee'
content = fs.readFileSync(filePath, 'utf8')
cachePath = getCachePath(coffee)
compileCoffeeScript(coffee, filePath, cachePath)
else if extension is '.cson'
CSON.readFileSync(filePath)
switch path.extname(filePath)
when '.coffee'
content = fs.readFileSync(filePath, 'utf8')
cachePath = getCachePath(coffee)
compileCoffeeScript(coffee, filePath, cachePath)
when '.cson'
CSON.readFileSync(filePath)
when '.js'
require('./6to5').addPathToCache(filePath)
getCacheMisses: -> stats.misses
getCacheHits: -> stats.hits
+5 -5
Ver Arquivo
@@ -7,9 +7,9 @@ module.exports =
class Color
# Essential: Parse a {String} or {Object} into a {Color}.
#
# * `value` - A {String} such as `'white'`, `#ff00ff`, or
# `'rgba(255, 15, 60, .75)'` or an {Object} with `red`, `green`,
# `blue`, and `alpha` properties.
# * `value` A {String} such as `'white'`, `#ff00ff`, or
# `'rgba(255, 15, 60, .75)'` or an {Object} with `red`, `green`, `blue`,
# and `alpha` properties.
#
# Returns a {Color} or `null` if it cannot be parsed.
@parse: (value) ->
@@ -53,11 +53,11 @@ class Color
@blue = blue
@alpha = alpha
# Esssential: Returns a {String} in the form `'#abcdef'`.
# Essential: Returns a {String} in the form `'#abcdef'`.
toHexString: ->
"##{numberToHexString(@red)}#{numberToHexString(@green)}#{numberToHexString(@blue)}"
# Esssential: Returns a {String} in the form `'rgba(25, 50, 75, .9)'`.
# Essential: Returns a {String} in the form `'rgba(25, 50, 75, .9)'`.
toRGBAString: ->
"rgba(#{@red}, #{@green}, #{@blue}, #{@alpha})"
+82 -34
Ver Arquivo
@@ -83,7 +83,7 @@ ScopeDescriptor = require './scope-descriptor'
#
# ## Config Schemas
#
# We use [json schema](json-schema.org) which allows you to define your value's
# We use [json schema](http://json-schema.org) which allows you to define your value's
# default, the type it should be, etc. A simple example:
#
# ```coffee
@@ -519,7 +519,7 @@ class Config
# * `options` (optional) {Object} see the `options` argument to {::get}
#
# Returns an {Array} of {Object}s with the following keys:
# * `scopeSelector` The scope-selector {String} with which the value is associated
# * `scopeDescriptor` The {ScopeDescriptor} with which the value is associated
# * `value` The value for the key-path
getAll: (keyPath, options) ->
{scope, sources} = options if options?
@@ -583,6 +583,7 @@ class Config
Pass a `scopeSelector` in an options hash as the final argument instead.
"""
[scopeSelector, keyPath, value] = arguments
shouldSave = true
else
[keyPath, value, options] = arguments
scopeSelector = options?.scopeSelector
@@ -601,7 +602,7 @@ class Config
return false
if scopeSelector?
@setRawScopedValue(source, scopeSelector, keyPath, value)
@setRawScopedValue(keyPath, value, source, scopeSelector)
else
@setRawValue(keyPath, value)
@@ -640,8 +641,9 @@ class Config
@scopedSettingsStore.removePropertiesForSourceAndSelector(source, scopeSelector)
@emitChangeEvent()
else
@scopedSettingsStore.removePropertiesForSource(source)
if keyPath?
for scopeSelector of @scopedSettingsStore.propertiesForSource(source)
@unset(keyPath, {scopeSelector, source})
if keyPath? and source is @getUserConfigPath()
@set(keyPath, _.valueForKeyPath(@defaultSettings, keyPath))
# Extended: Get an {Array} of all of the `source` {String}s with which
@@ -795,6 +797,7 @@ class Config
_.extend rootSchema, schema
@setDefaults(keyPath, @extractDefaultsFromSchema(schema))
@setScopedDefaultsFromSchema(keyPath, schema)
@resetSettingsForSchemaChange()
load: ->
@initializeConfigDirectory()
@@ -832,25 +835,35 @@ class Config
@configFileHasErrors = false
catch error
@configFileHasErrors = true
@notifyFailure('Failed to load config.cson', error)
message = "Failed to load `#{path.basename(@configFilePath)}`"
detail = if error.location?
# stack is the output from CSON in this case
error.stack
else
# message will be EACCES permission denied, et al
error.message
@notifyFailure(message, detail)
observeUserConfig: ->
try
@watchSubscription ?= pathWatcher.watch @configFilePath, (eventType) =>
@debouncedLoad() if eventType is 'change' and @watchSubscription?
catch error
@notifyFailure('Failed to watch user config', error)
@notifyFailure """
Unable to watch path: `#{path.basename(@configFilePath)}`. Make sure you have permissions to
`#{@configFilePath}`. On linux there are currently problems with watch
sizes. See [this document][watches] for more info.
[watches]:https://github.com/atom/atom/blob/master/docs/build-instructions/linux.md#typeerror-unable-to-watch-path
"""
unobserveUserConfig: ->
@watchSubscription?.close()
@watchSubscription = null
notifyFailure: (errorMessage, error) ->
message = "#{errorMessage}"
detail = error.stack
atom.notifications.addError(message, {detail, dismissable: true})
console.error message
console.error detail
notifyFailure: (errorMessage, detail) ->
atom.notifications.addError(errorMessage, {detail, dismissable: true})
save: ->
allSettings = {'*': @settings}
@@ -888,16 +901,10 @@ class Config
defaultValue = _.valueForKeyPath(@defaultSettings, keyPath)
if value?
if value instanceof Color
value = value.clone()
else
value = _.deepClone(value)
_.defaults(value, defaultValue) if isPlainObject(value) and isPlainObject(defaultValue)
value = @deepClone(value)
_.defaults(value, defaultValue) if isPlainObject(value) and isPlainObject(defaultValue)
else
if defaultValue instanceof Color
value = defaultValue.clone()
else
value = _.deepClone(defaultValue)
value = @deepClone(defaultValue)
value
@@ -947,6 +954,16 @@ class Config
catch e
console.warn("'#{keyPath}' could not set the default. Attempted default: #{JSON.stringify(defaults)}; Schema: #{JSON.stringify(@getSchema(keyPath))}")
deepClone: (object) ->
if object instanceof Color
object.clone()
else if _.isArray(object)
object.map (value) => @deepClone(value)
else if isPlainObject(object)
_.mapObject object, (key, value) => [key, @deepClone(value)]
else
object
# `schema` will look something like this
#
# ```coffee
@@ -982,9 +999,28 @@ class Config
defaults[key] = @extractDefaultsFromSchema(value) for key, value of properties
defaults
makeValueConformToSchema: (keyPath, value) ->
value = @constructor.executeSchemaEnforcers(keyPath, value, schema) if schema = @getSchema(keyPath)
value
makeValueConformToSchema: (keyPath, value, options) ->
if options?.suppressException
try
@makeValueConformToSchema(keyPath, value)
catch e
undefined
else
value = @constructor.executeSchemaEnforcers(keyPath, value, schema) if schema = @getSchema(keyPath)
value
# When the schema is changed / added, there may be values set in the config
# that do not conform to the schema. This will reset make them conform.
resetSettingsForSchemaChange: (source=@getUserConfigPath()) ->
@transact =>
@settings = @makeValueConformToSchema(null, @settings, suppressException: true)
priority = @priorityForSource(source)
selectorsAndSettings = @scopedSettingsStore.propertiesForSource(source)
@scopedSettingsStore.removePropertiesForSource(source)
for scopeSelector, settings of selectorsAndSettings
settings = @makeValueConformToSchema(null, settings, suppressException: true)
@setRawScopedValue(null, settings, source, scopeSelector)
return
###
Section: Private Scoped Settings
@@ -1001,8 +1037,15 @@ class Config
resetUserScopedSettings: (newScopedSettings) ->
source = @getUserConfigPath()
priority = @priorityForSource(source)
@scopedSettingsStore.removePropertiesForSource(source)
@scopedSettingsStore.addProperties(source, newScopedSettings, priority: @priorityForSource(source))
for scopeSelector, settings of newScopedSettings
settings = @makeValueConformToSchema(null, settings, suppressException: true)
validatedSettings = {}
validatedSettings[scopeSelector] = withoutEmptyObjects(settings)
@scopedSettingsStore.addProperties(source, validatedSettings, {priority}) if validatedSettings[scopeSelector]?
@emitChangeEvent()
addScopedSettings: (source, selector, value, options) ->
@@ -1015,7 +1058,7 @@ class Config
disposable.dispose()
@emitChangeEvent()
setRawScopedValue: (source, selector, keyPath, value) ->
setRawScopedValue: (keyPath, value, source, selector, options) ->
if keyPath?
newValue = {}
_.setValueForKeyPath(newValue, keyPath, value)
@@ -1102,12 +1145,17 @@ Config.addSchemaEnforcers
return value unless schema.properties?
newValue = {}
for prop, childSchema of schema.properties
continue unless value.hasOwnProperty(prop)
try
newValue[prop] = @executeSchemaEnforcers("#{keyPath}.#{prop}", value[prop], childSchema)
catch error
console.warn "Error setting item in object: #{error.message}"
for prop, propValue of value
childSchema = schema.properties[prop]
if childSchema?
try
newValue[prop] = @executeSchemaEnforcers("#{keyPath}.#{prop}", propValue, childSchema)
catch error
console.warn "Error setting item in object: #{error.message}"
else
# Just pass through un-schema'd values
newValue[prop] = propValue
newValue
'array':
@@ -1152,7 +1200,7 @@ Config.addSchemaEnforcers
throw new Error("Validation failed at #{keyPath}, #{JSON.stringify(value)} is not one of #{JSON.stringify(possibleValues)}")
isPlainObject = (value) ->
_.isObject(value) and not _.isArray(value) and not _.isFunction(value) and not _.isString(value)
_.isObject(value) and not _.isArray(value) and not _.isFunction(value) and not _.isString(value) and not (value instanceof Color)
splitKeyPath = (keyPath) ->
return [] unless keyPath?
+1 -5
Ver Arquivo
@@ -7,12 +7,8 @@ CursorComponent = React.createClass
displayName: 'CursorComponent'
render: ->
{pixelRect, defaultCharWidth} = @props
{pixelRect} = @props
{top, left, height, width} = pixelRect
width = defaultCharWidth if width is 0
WebkitTransform = "translate(#{left}px, #{top}px)"
div className: 'cursor', style: {height, width, WebkitTransform}
shouldComponentUpdate: (newProps) ->
not isEqualForProperties(newProps, @props, 'pixelRect', 'defaultCharWidth')
+5 -46
Ver Arquivo
@@ -7,55 +7,14 @@ CursorComponent = require './cursor-component'
module.exports =
CursorsComponent = React.createClass
displayName: 'CursorsComponent'
mixins: [SubscriberMixin]
cursorBlinkIntervalHandle: null
render: ->
{performedInitialMeasurement, cursorPixelRects, defaultCharWidth} = @props
{blinkOff} = @state
{presenter} = @props
className = 'cursors'
className += ' blink-off' if blinkOff
className += ' blink-off' if presenter.state.content.blinkCursorsOff
div {className},
if performedInitialMeasurement
for key, pixelRect of cursorPixelRects
CursorComponent({key, pixelRect, defaultCharWidth})
getInitialState: ->
blinkOff: false
componentDidMount: ->
@startBlinkingCursors()
componentWillUnmount: ->
@stopBlinkingCursors()
shouldComponentUpdate: (newProps, newState) ->
not newState.blinkOff is @state.blinkOff or
not isEqualForProperties(newProps, @props, 'cursorPixelRects', 'scrollTop', 'scrollLeft', 'defaultCharWidth', 'useHardwareAcceleration')
componentWillUpdate: (newProps) ->
cursorsMoved = @props.cursorPixelRects? and
isEqualForProperties(newProps, @props, 'defaultCharWidth', 'scopedCharacterWidthsChangeCount') and
not isEqual(newProps.cursorPixelRects, @props.cursorPixelRects)
@pauseCursorBlinking() if cursorsMoved
startBlinkingCursors: ->
@toggleCursorBlinkHandle = setInterval(@toggleCursorBlink, @props.cursorBlinkPeriod / 2) if @isMounted()
startBlinkingCursorsAfterDelay: null # Created lazily
stopBlinkingCursors: ->
clearInterval(@toggleCursorBlinkHandle)
toggleCursorBlink: ->
@setState(blinkOff: not @state.blinkOff)
pauseCursorBlinking: ->
@state.blinkOff = false
@stopBlinkingCursors()
@startBlinkingCursorsAfterDelay ?= debounce(@startBlinkingCursors, @props.cursorBlinkResumeDelay)
@startBlinkingCursorsAfterDelay()
if presenter.hasRequiredMeasurements()
for key, pixelRect of presenter.state.content.cursors
CursorComponent({key, pixelRect})
+7 -5
Ver Arquivo
@@ -34,8 +34,8 @@ class Decoration
# Private: Check if the `decorationProperties.type` matches `type`
#
# * `decorationProperties` {Object} eg. `{type: 'gutter', class: 'my-new-class'}`
# * `type` {String} type like `'gutter'`, `'line'`, etc. `type` can also
# * `decorationProperties` {Object} eg. `{type: 'line-number', class: 'my-new-class'}`
# * `type` {String} type like `'line-number'`, `'line'`, etc. `type` can also
# be an {Array} of {String}s, where it will return true if the decoration's
# type matches any in the array.
#
@@ -72,6 +72,8 @@ class Decoration
@emitter.emit 'did-destroy'
@emitter.dispose()
isDestroyed: -> @destroyed
###
Section: Event Subscription
###
@@ -107,7 +109,7 @@ class Decoration
# Public: Check if this decoration is of type `type`
#
# * `type` {String} type like `'gutter'`, `'line'`, etc. `type` can also
# * `type` {String} type like `'line-number'`, `'line'`, etc. `type` can also
# be an {Array} of {String}s, where it will return true if the decoration's
# type matches any in the array.
#
@@ -131,10 +133,10 @@ class Decoration
# ## Examples
#
# ```coffee
# decoration.update({type: 'gutter', class: 'my-new-class'})
# decoration.update({type: 'line-number', class: 'my-new-class'})
# ```
#
# * `newProperties` {Object} eg. `{type: 'gutter', class: 'my-new-class'}`
# * `newProperties` {Object} eg. `{type: 'line-number', class: 'my-new-class'}`
setProperties: (newProperties) ->
return if @destroyed
oldProperties = @properties
+6 -6
Ver Arquivo
@@ -617,10 +617,10 @@ class DisplayBuffer extends Model
# bufferRange - The {Range} to convert
#
# Returns a {Range}.
screenRangeForBufferRange: (bufferRange) ->
screenRangeForBufferRange: (bufferRange, options) ->
bufferRange = Range.fromObject(bufferRange)
start = @screenPositionForBufferPosition(bufferRange.start)
end = @screenPositionForBufferPosition(bufferRange.end)
start = @screenPositionForBufferPosition(bufferRange.start, options)
end = @screenPositionForBufferPosition(bufferRange.end, options)
new Range(start, end)
# Given a screen range, this converts it into a buffer position.
@@ -897,8 +897,8 @@ class DisplayBuffer extends Model
getLineDecorations: (propertyFilter) ->
@getDecorations(propertyFilter).filter (decoration) -> decoration.isType('line')
getGutterDecorations: (propertyFilter) ->
@getDecorations(propertyFilter).filter (decoration) -> decoration.isType('gutter')
getLineNumberDecorations: (propertyFilter) ->
@getDecorations(propertyFilter).filter (decoration) -> decoration.isType('line-number')
getHighlightDecorations: (propertyFilter) ->
@getDecorations(propertyFilter).filter (decoration) -> decoration.isType('highlight')
@@ -1222,7 +1222,7 @@ class DisplayBuffer extends Model
@emitter.emit 'did-create-marker', marker
createFoldForMarker: (marker) ->
@decorateMarker(marker, type: 'gutter', class: 'folded')
@decorateMarker(marker, type: 'line-number', class: 'folded')
new Fold(this, marker)
foldForMarker: (marker) ->
+3 -4
Ver Arquivo
@@ -19,10 +19,9 @@ class Fold
# Returns whether this fold is contained within another fold
isInsideLargerFold: ->
if largestContainingFoldMarker = @displayBuffer.findMarker(class: 'fold', containsBufferRange: @getBufferRange())
not largestContainingFoldMarker.getBufferRange().isEqual(@getBufferRange())
else
false
largestContainingFoldMarker = @displayBuffer.findFoldMarker(containsRange: @getBufferRange())
largestContainingFoldMarker and
not largestContainingFoldMarker.getRange().isEqual(@getBufferRange())
# Destroys this fold
destroy: ->
+3 -3
Ver Arquivo
@@ -33,7 +33,7 @@ Task = require './task'
#
# ```coffee
# git = atom.project.getRepo()
# console.log git.getOriginUrl()
# console.log git.getOriginURL()
# ```
#
# ### Requiring in packages
@@ -234,8 +234,8 @@ class GitRepository
# for, only needed if the repository has submodules.
getOriginURL: (path) -> @getConfigValue('remote.origin.url', path)
getOriginUrl: (path) ->
deprecate("Use ::getOriginURL instead.")
@getOriginUrl()
deprecate 'Use ::getOriginURL instead.'
@getOriginURL(path)
# Public: Returns the upstream branch for the current HEAD, or null if there
# is no upstream branch for the current HEAD.
+61 -129
Ver Arquivo
@@ -1,7 +1,8 @@
_ = require 'underscore-plus'
React = require 'react-atom-fork'
{div} = require 'reactionary-atom-fork'
{isEqual, isEqualForProperties, multiplyString, toArray} = require 'underscore-plus'
_ = require 'underscore-plus'
{isEqual, isEqualForProperties, multiplyString, toArray} = _
Decoration = require './decoration'
SubscriberMixin = require './subscriber-mixin'
@@ -12,23 +13,26 @@ GutterComponent = React.createClass
displayName: 'GutterComponent'
mixins: [SubscriberMixin]
maxLineNumberDigits: null
dummyLineNumberNode: null
measuredWidth: null
render: ->
{scrollHeight, scrollViewHeight, backgroundColor, gutterBackgroundColor} = @props
{presenter} = @props
@newState = presenter.state.gutter
@oldState ?= {lineNumbers: {}}
if gutterBackgroundColor isnt 'rbga(0, 0, 0, 0)'
backgroundColor = gutterBackgroundColor
{scrollHeight, backgroundColor} = @newState
div className: 'gutter',
div className: 'line-numbers', ref: 'lineNumbers', style:
height: Math.max(scrollHeight, scrollViewHeight)
WebkitTransform: @getTransform()
height: scrollHeight
WebkitTransform: @getTransform() if presenter.hasRequiredMeasurements()
backgroundColor: backgroundColor
getTransform: ->
{scrollTop, useHardwareAcceleration} = @props
{useHardwareAcceleration} = @props
{scrollTop} = @newState
if useHardwareAcceleration
"translate3d(0px, #{-scrollTop}px, 0px)"
@@ -37,141 +41,81 @@ GutterComponent = React.createClass
componentWillMount: ->
@lineNumberNodesById = {}
@lineNumberIdsByScreenRow = {}
@screenRowsByLineNumberId = {}
@renderedDecorationsByLineNumberId = {}
componentDidMount: ->
{@maxLineNumberDigits} = @newState
@appendDummyLineNumber()
@updateLineNumbers() if @props.performedInitialMeasurement
@updateLineNumbers()
node = @getDOMNode()
node.addEventListener 'click', @onClick
node.addEventListener 'mousedown', @onMouseDown
# Only update the gutter if the visible row range has changed or if a
# non-zero-delta change to the screen lines has occurred within the current
# visible row range.
shouldComponentUpdate: (newProps) ->
return true unless isEqualForProperties(newProps, @props,
'renderedRowRange', 'scrollTop', 'lineHeightInPixels', 'mouseWheelScreenRow', 'lineDecorations',
'scrollViewHeight', 'useHardwareAcceleration', 'backgroundColor', 'gutterBackgroundColor'
)
{renderedRowRange, pendingChanges, lineDecorations} = newProps
return false unless renderedRowRange?
for change in pendingChanges when Math.abs(change.screenDelta) > 0 or Math.abs(change.bufferDelta) > 0
return true unless change.end <= renderedRowRange.start or renderedRowRange.end <= change.start
false
componentDidUpdate: (oldProps) ->
return unless @props.performedInitialMeasurement
unless isEqualForProperties(oldProps, @props, 'maxLineNumberDigits')
{maxLineNumberDigits} = @newState
unless maxLineNumberDigits is @maxLineNumberDigits
@maxLineNumberDigits = maxLineNumberDigits
@updateDummyLineNumber()
@removeLineNumberNodes()
node.remove() for id, node of @lineNumberNodesById
@oldState = {lineNumbers: {}}
@lineNumberNodesById = {}
@clearScreenRowCaches() unless oldProps.lineHeightInPixels is @props.lineHeightInPixels
@updateLineNumbers()
clearScreenRowCaches: ->
@lineNumberIdsByScreenRow = {}
@screenRowsByLineNumberId = {}
# This dummy line number element holds the gutter to the appropriate width,
# since the real line numbers are absolutely positioned for performance reasons.
appendDummyLineNumber: ->
{maxLineNumberDigits} = @props
WrapperDiv.innerHTML = @buildLineNumberHTML(-1, false, maxLineNumberDigits)
WrapperDiv.innerHTML = @buildLineNumberHTML({bufferRow: -1})
@dummyLineNumberNode = WrapperDiv.children[0]
@refs.lineNumbers.getDOMNode().appendChild(@dummyLineNumberNode)
updateDummyLineNumber: ->
@dummyLineNumberNode.innerHTML = @buildLineNumberInnerHTML(0, false, @props.maxLineNumberDigits)
@dummyLineNumberNode.innerHTML = @buildLineNumberInnerHTML(0, false)
updateLineNumbers: ->
lineNumberIdsToPreserve = @appendOrUpdateVisibleLineNumberNodes()
@removeLineNumberNodes(lineNumberIdsToPreserve)
appendOrUpdateVisibleLineNumberNodes: ->
{editor, renderedRowRange, scrollTop, maxLineNumberDigits, lineDecorations} = @props
[startRow, endRow] = renderedRowRange
newLineNumberIds = null
newLineNumbersHTML = null
visibleLineNumberIds = new Set
wrapCount = 0
for bufferRow, index in editor.bufferRowsForScreenRows(startRow, endRow - 1)
screenRow = startRow + index
if bufferRow is lastBufferRow
id = "#{bufferRow}-#{wrapCount++}"
else
id = bufferRow.toString()
lastBufferRow = bufferRow
wrapCount = 0
visibleLineNumberIds.add(id)
if @hasLineNumberNode(id)
@updateLineNumberNode(id, bufferRow, screenRow, wrapCount > 0)
for id, lineNumberState of @newState.lineNumbers
if @oldState.lineNumbers.hasOwnProperty(id)
@updateLineNumberNode(id, lineNumberState)
else
newLineNumberIds ?= []
newLineNumbersHTML ?= ""
newLineNumberIds.push(id)
newLineNumbersHTML += @buildLineNumberHTML(bufferRow, wrapCount > 0, maxLineNumberDigits, screenRow)
@screenRowsByLineNumberId[id] = screenRow
@lineNumberIdsByScreenRow[screenRow] = id
@renderedDecorationsByLineNumberId[id] = lineDecorations[screenRow]
newLineNumbersHTML += @buildLineNumberHTML(lineNumberState)
@oldState.lineNumbers[id] = _.clone(lineNumberState)
if newLineNumberIds?
WrapperDiv.innerHTML = newLineNumbersHTML
newLineNumberNodes = toArray(WrapperDiv.children)
node = @refs.lineNumbers.getDOMNode()
for lineNumberId, i in newLineNumberIds
for id, i in newLineNumberIds
lineNumberNode = newLineNumberNodes[i]
@lineNumberNodesById[lineNumberId] = lineNumberNode
@lineNumberNodesById[id] = lineNumberNode
node.appendChild(lineNumberNode)
visibleLineNumberIds
for id, lineNumberState of @oldState.lineNumbers
unless @newState.lineNumbers.hasOwnProperty(id)
@lineNumberNodesById[id].remove()
delete @lineNumberNodesById[id]
delete @oldState.lineNumbers[id]
removeLineNumberNodes: (lineNumberIdsToPreserve) ->
{mouseWheelScreenRow} = @props
node = @refs.lineNumbers.getDOMNode()
for lineNumberId, lineNumberNode of @lineNumberNodesById when not lineNumberIdsToPreserve?.has(lineNumberId)
screenRow = @screenRowsByLineNumberId[lineNumberId]
if not screenRow? or screenRow isnt mouseWheelScreenRow
delete @lineNumberNodesById[lineNumberId]
delete @lineNumberIdsByScreenRow[screenRow] if @lineNumberIdsByScreenRow[screenRow] is lineNumberId
delete @screenRowsByLineNumberId[lineNumberId]
delete @renderedDecorationsByLineNumberId[lineNumberId]
node.removeChild(lineNumberNode)
buildLineNumberHTML: (bufferRow, softWrapped, maxLineNumberDigits, screenRow) ->
{editor, lineHeightInPixels, lineDecorations} = @props
buildLineNumberHTML: (lineNumberState) ->
{screenRow, bufferRow, softWrapped, top, decorationClasses} = lineNumberState
if screenRow?
style = "position: absolute; top: #{screenRow * lineHeightInPixels}px;"
style = "position: absolute; top: #{top}px;"
else
style = "visibility: hidden;"
innerHTML = @buildLineNumberInnerHTML(bufferRow, softWrapped, maxLineNumberDigits)
className = @buildLineNumberClassName(lineNumberState)
innerHTML = @buildLineNumberInnerHTML(bufferRow, softWrapped)
classes = ''
if lineDecorations? and decorations = lineDecorations[screenRow]
for id, decoration of decorations
if Decoration.isType(decoration, 'gutter')
classes += decoration.class + ' '
"<div class=\"#{className}\" style=\"#{style}\" data-buffer-row=\"#{bufferRow}\" data-screen-row=\"#{screenRow}\">#{innerHTML}</div>"
classes += "foldable " if bufferRow >= 0 and editor.isFoldableAtBufferRow(bufferRow)
classes += "line-number line-number-#{bufferRow}"
buildLineNumberInnerHTML: (bufferRow, softWrapped) ->
{maxLineNumberDigits} = @newState
"<div class=\"#{classes}\" style=\"#{style}\" data-buffer-row=\"#{bufferRow}\" data-screen-row=\"#{screenRow}\">#{innerHTML}</div>"
buildLineNumberInnerHTML: (bufferRow, softWrapped, maxLineNumberDigits) ->
if softWrapped
lineNumber = ""
else
@@ -181,46 +125,34 @@ GutterComponent = React.createClass
iconHTML = '<div class="icon-right"></div>'
padding + lineNumber + iconHTML
updateLineNumberNode: (lineNumberId, bufferRow, screenRow, softWrapped) ->
{editor, lineDecorations} = @props
updateLineNumberNode: (lineNumberId, newLineNumberState) ->
oldLineNumberState = @oldState.lineNumbers[lineNumberId]
node = @lineNumberNodesById[lineNumberId]
if editor.isFoldableAtBufferRow(bufferRow)
node.classList.add('foldable')
else
node.classList.remove('foldable')
unless oldLineNumberState.foldable is newLineNumberState.foldable and _.isEqual(oldLineNumberState.decorationClasses, newLineNumberState.decorationClasses)
node.className = @buildLineNumberClassName(newLineNumberState)
oldLineNumberState.foldable = newLineNumberState.foldable
oldLineNumberState.decorationClasses = _.clone(newLineNumberState.decorationClasses)
decorations = lineDecorations[screenRow]
previousDecorations = @renderedDecorationsByLineNumberId[lineNumberId]
unless oldLineNumberState.top is newLineNumberState.top
node.style.top = newLineNumberState.top + 'px'
node.dataset.screenRow = newLineNumberState.screenRow
oldLineNumberState.top = newLineNumberState.top
oldLineNumberState.screenRow = newLineNumberState.screenRow
if previousDecorations?
for id, decoration of previousDecorations
if Decoration.isType(decoration, 'gutter') and not @hasDecoration(decorations, decoration)
node.classList.remove(decoration.class)
if decorations?
for id, decoration of decorations
if Decoration.isType(decoration, 'gutter') and not @hasDecoration(previousDecorations, decoration)
node.classList.add(decoration.class)
unless @screenRowsByLineNumberId[lineNumberId] is screenRow
{lineHeightInPixels} = @props
node.style.top = screenRow * lineHeightInPixels + 'px'
node.dataset.screenRow = screenRow
@screenRowsByLineNumberId[lineNumberId] = screenRow
@lineNumberIdsByScreenRow[screenRow] = lineNumberId
hasDecoration: (decorations, decoration) ->
decorations? and decorations[decoration.id] is decoration
hasLineNumberNode: (lineNumberId) ->
@lineNumberNodesById.hasOwnProperty(lineNumberId)
buildLineNumberClassName: ({bufferRow, foldable, decorationClasses}) ->
className = "line-number line-number-#{bufferRow}"
className += " " + decorationClasses.join(' ') if decorationClasses?
className += " foldable" if foldable
className
lineNumberNodeForScreenRow: (screenRow) ->
@lineNumberNodesById[@lineNumberIdsByScreenRow[screenRow]]
for id, lineNumberState of @oldState.lineNumbers
if lineNumberState.screenRow is screenRow
return @lineNumberNodesById[id]
null
onMouseDown: (event) ->
{editor} = @props
{target} = event
lineNumber = target.parentNode
+29 -77
Ver Arquivo
@@ -5,94 +5,46 @@ React = require 'react-atom-fork'
module.exports =
HighlightComponent = React.createClass
displayName: 'HighlightComponent'
currentFlashCount: 0
currentFlashClass: null
render: ->
{startPixelPosition, endPixelPosition, decoration} = @props
{state} = @props
className = 'highlight'
className += " #{decoration.class}" if decoration.class?
className += " #{state.class}" if state.class?
div {className},
if endPixelPosition.top is startPixelPosition.top
@renderSingleLineRegions(decoration.deprecatedRegionClass)
else
@renderMultiLineRegions(decoration.deprecatedRegionClass)
for region, i in state.regions
regionClassName = 'region'
regionClassName += " #{state.deprecatedRegionClass}" if state.deprecatedRegionClass?
div className: regionClassName, key: i, style: region
componentDidMount: ->
{editor, decoration} = @props
if decoration.id?
@decoration = editor.decorationForId(decoration.id)
@decorationDisposable = @decoration.onDidFlash @startFlashAnimation
@startFlashAnimation()
@flashIfRequested()
componentWillUnmount: ->
@decorationDisposable?.dispose()
@decorationDisposable = null
componentDidUpdate: ->
@flashIfRequested()
startFlashAnimation: ->
return unless flash = @decoration.consumeNextFlash()
flashIfRequested: ->
if @props.state.flashCount > @currentFlashCount
@currentFlashCount = @props.state.flashCount
node = @getDOMNode()
node.classList.remove(flash.class)
node = @getDOMNode()
{flashClass, flashDuration} = @props.state
requestAnimationFrame =>
node.classList.add(flash.class)
clearTimeout(@flashTimeoutId)
removeFlashClass = -> node.classList.remove(flash.class)
@flashTimeoutId = setTimeout(removeFlashClass, flash.duration)
addFlashClass = =>
node.classList.add(flashClass)
@currentFlashClass = flashClass
@flashTimeoutId = setTimeout(removeFlashClass, flashDuration)
renderSingleLineRegions: (regionClass) ->
{startPixelPosition, endPixelPosition, lineHeightInPixels} = @props
removeFlashClass = =>
node.classList.remove(@currentFlashClass)
@currentFlashClass = null
clearTimeout(@flashTimeoutId)
className = 'region'
className += " #{regionClass}" if regionClass?
[
div className: className, key: 0, style:
top: startPixelPosition.top
height: lineHeightInPixels
left: startPixelPosition.left
width: endPixelPosition.left - startPixelPosition.left
]
renderMultiLineRegions: (regionClass) ->
{startPixelPosition, endPixelPosition, lineHeightInPixels} = @props
className = 'region'
className += " #{regionClass}" if regionClass?
regions = []
index = 0
# First row, extending from selection start to the right side of screen
regions.push(
div className: className, key: index++, style:
top: startPixelPosition.top
left: startPixelPosition.left
height: lineHeightInPixels
right: 0
)
# Middle rows, extending from left side to right side of screen
if endPixelPosition.top - startPixelPosition.top > lineHeightInPixels
regions.push(
div className: className, key: index++, style:
top: startPixelPosition.top + lineHeightInPixels
height: endPixelPosition.top - startPixelPosition.top - lineHeightInPixels
left: 0
right: 0
)
# Last row, extending from left side of screen to selection end
regions.push(
div className: className, key: index, style:
top: endPixelPosition.top
height: lineHeightInPixels
left: 0
width: endPixelPosition.left
)
regions
shouldComponentUpdate: (newProps) ->
not isEqualForProperties(newProps, @props, 'startPixelPosition', 'endPixelPosition', 'lineHeightInPixels', 'decoration')
if @currentFlashClass?
removeFlashClass()
requestAnimationFrame(addFlashClass)
else
addFlashClass()
+4 -10
Ver Arquivo
@@ -9,16 +9,13 @@ HighlightsComponent = React.createClass
render: ->
div className: 'highlights',
@renderHighlights() if @props.performedInitialMeasurement
@renderHighlights()
renderHighlights: ->
{editor, highlightDecorations, lineHeightInPixels} = @props
{presenter} = @props
highlightComponents = []
for markerId, {startPixelPosition, endPixelPosition, decorations} of highlightDecorations
for decoration in decorations
highlightComponents.push(HighlightComponent({editor, key: "#{markerId}-#{decoration.id}", startPixelPosition, endPixelPosition, decoration, lineHeightInPixels}))
for key, state of presenter.state.content.highlights
highlightComponents.push(HighlightComponent({key, state}))
highlightComponents
componentDidMount: ->
@@ -26,6 +23,3 @@ HighlightsComponent = React.createClass
insertionPoint = document.createElement('content')
insertionPoint.setAttribute('select', '.underlayer')
@getDOMNode().appendChild(insertionPoint)
shouldComponentUpdate: (newProps) ->
not isEqualForProperties(newProps, @props, 'highlightDecorations', 'lineHeightInPixels', 'defaultCharWidth', 'scopedCharacterWidthsChangeCount')
+28 -3
Ver Arquivo
@@ -20,12 +20,37 @@ KeymapManager::getUserKeymapPath = ->
KeymapManager::loadUserKeymap = ->
userKeymapPath = @getUserKeymapPath()
if fs.isFileSync(userKeymapPath)
return unless fs.isFileSync(userKeymapPath)
try
@loadKeymap(userKeymapPath, watch: true, suppressErrors: true)
catch error
if error.message.indexOf('Unable to watch path') > -1
message = """
Unable to watch path: `#{path.basename(userKeymapPath)}`. Make sure you
have permission to read `#{userKeymapPath}`.
On linux there are currently problems with watch sizes. See
[this document][watches] for more info.
[watches]:https://github.com/atom/atom/blob/master/docs/build-instructions/linux.md#typeerror-unable-to-watch-path
"""
atom.notifications.addError(message, {dismissable: true})
else
detail = error.path
stack = error.stack
atom.notifications.addFatalError(error.message, {detail, stack, dismissable: true})
KeymapManager::subscribeToFileReadFailure = ->
this.onDidFailToReadFile (error) ->
atom.notifications.addError('Failed to load keymap.cson', {detail: error.stack, dismissable: true})
@onDidFailToReadFile (error) =>
userKeymapPath = @getUserKeymapPath()
message = "Failed to load `#{userKeymapPath}`"
detail = if error.location?
error.stack
else
error.message
atom.notifications.addError(message, {detail: detail, dismissable: true})
# This enables command handlers registered via jQuery to call
# `.abortKeyBinding()` on the `jQuery.Event` object passed to the handler.
+1 -1
Ver Arquivo
@@ -32,7 +32,7 @@ class LanguageMode
return unless commentStartEntry?
commentEndEntry = atom.config.getAll('editor.commentEnd', {scope}).find (entry) ->
commentEndEntry = _.find atom.config.getAll('editor.commentEnd', {scope}), (entry) ->
entry.scopeSelector is commentStartEntry.scopeSelector
commentStartString = commentStartEntry?.value
commentEndString = commentEndEntry?.value
+104 -146
Ver Arquivo
@@ -4,7 +4,6 @@ React = require 'react-atom-fork'
{debounce, isEqual, isEqualForProperties, multiplyString, toArray} = require 'underscore-plus'
{$$} = require 'space-pen'
Decoration = require './decoration'
CursorsComponent = require './cursors-component'
HighlightsComponent = require './highlights-component'
OverlayManager = require './overlay-manager'
@@ -18,33 +17,26 @@ LinesComponent = React.createClass
displayName: 'LinesComponent'
render: ->
{performedInitialMeasurement, cursorBlinkPeriod, cursorBlinkResumeDelay} = @props
{editor, presenter} = @props
@oldState ?= {lines: {}}
@newState = presenter.state.content
if performedInitialMeasurement
{editor, overlayDecorations, highlightDecorations, scrollHeight, scrollWidth, placeholderText, backgroundColor} = @props
{lineHeightInPixels, defaultCharWidth, scrollViewHeight, scopedCharacterWidthsChangeCount} = @props
{scrollTop, scrollLeft, cursorPixelRects} = @props
style =
height: Math.max(scrollHeight, scrollViewHeight)
width: scrollWidth
WebkitTransform: @getTransform()
backgroundColor: if editor.isMini() then null else backgroundColor
{scrollHeight, scrollWidth, backgroundColor, placeholderText} = @newState
style =
height: scrollHeight
width: scrollWidth
WebkitTransform: @getTransform()
backgroundColor: backgroundColor
div {className: 'lines', style},
div className: 'placeholder-text', placeholderText if placeholderText?
CursorsComponent {
cursorPixelRects, cursorBlinkPeriod, cursorBlinkResumeDelay, lineHeightInPixels,
defaultCharWidth, scopedCharacterWidthsChangeCount, performedInitialMeasurement
}
HighlightsComponent {
editor, highlightDecorations, lineHeightInPixels, defaultCharWidth,
scopedCharacterWidthsChangeCount, performedInitialMeasurement
}
CursorsComponent {presenter}
HighlightsComponent {presenter}
getTransform: ->
{scrollTop, scrollLeft, useHardwareAcceleration} = @props
{scrollTop, scrollLeft} = @newState
{useHardwareAcceleration} = @props
if useHardwareAcceleration
"translate3d(#{-scrollLeft}px, #{-scrollTop}px, 0px)"
@@ -52,7 +44,7 @@ LinesComponent = React.createClass
"translate(#{-scrollLeft}px, #{-scrollTop}px)"
componentWillMount: ->
@measuredLines = new WeakSet
@measuredLines = new Set
@lineNodesByLineId = {}
@screenRowsByLineId = {}
@lineIdsByScreenRow = {}
@@ -71,124 +63,91 @@ LinesComponent = React.createClass
else
@overlayManager = new OverlayManager(@getDOMNode())
shouldComponentUpdate: (newProps) ->
return true unless isEqualForProperties(newProps, @props,
'renderedRowRange', 'lineDecorations', 'highlightDecorations', 'lineHeightInPixels', 'defaultCharWidth',
'overlayDecorations', 'scrollTop', 'scrollLeft', 'showIndentGuide', 'scrollingVertically', 'visible',
'scrollViewHeight', 'mouseWheelScreenRow', 'scopedCharacterWidthsChangeCount', 'lineWidth', 'useHardwareAcceleration',
'placeholderText', 'performedInitialMeasurement', 'backgroundColor', 'cursorPixelRects'
)
componentDidUpdate: ->
{visible, presenter} = @props
{renderedRowRange, pendingChanges} = newProps
return false unless renderedRowRange?
[renderedStartRow, renderedEndRow] = renderedRowRange
for change in pendingChanges
if change.screenDelta is 0
return true unless change.end < renderedStartRow or renderedEndRow <= change.start
else
return true unless renderedEndRow <= change.start
false
componentDidUpdate: (prevProps) ->
{visible, scrollingVertically, performedInitialMeasurement} = @props
return unless performedInitialMeasurement
@clearScreenRowCaches() unless prevProps.lineHeightInPixels is @props.lineHeightInPixels
@removeLineNodes() unless isEqualForProperties(prevProps, @props, 'showIndentGuide')
@updateLines(@props.lineWidth isnt prevProps.lineWidth)
@measureCharactersInNewLines() if visible and not scrollingVertically
@removeLineNodes() unless @oldState.indentGuidesVisible is @newState.indentGuidesVisible
@updateLineNodes()
@measureCharactersInNewLines() if visible and not @newState.scrollingVertically
@overlayManager?.render(@props)
@oldState.indentGuidesVisible = @newState.indentGuidesVisible
@oldState.scrollWidth = @newState.scrollWidth
clearScreenRowCaches: ->
@screenRowsByLineId = {}
@lineIdsByScreenRow = {}
updateLines: (updateWidth) ->
{tokenizedLines, renderedRowRange, showIndentGuide, selectionChanged, lineDecorations} = @props
[startRow] = renderedRowRange
removeLineNodes: ->
@removeLineNode(id) for id of @oldState.lines
@removeLineNodes(tokenizedLines)
@appendOrUpdateVisibleLineNodes(tokenizedLines, startRow, updateWidth)
removeLineNode: (id) ->
@lineNodesByLineId[id].remove()
delete @lineNodesByLineId[id]
delete @lineIdsByScreenRow[@screenRowsByLineId[id]]
delete @screenRowsByLineId[id]
delete @oldState.lines[id]
removeLineNodes: (visibleLines=[]) ->
{mouseWheelScreenRow} = @props
visibleLineIds = new Set
visibleLineIds.add(line.id.toString()) for line in visibleLines
node = @getDOMNode()
for lineId, lineNode of @lineNodesByLineId when not visibleLineIds.has(lineId)
screenRow = @screenRowsByLineId[lineId]
if not screenRow? or screenRow isnt mouseWheelScreenRow
delete @lineNodesByLineId[lineId]
delete @lineIdsByScreenRow[screenRow] if @lineIdsByScreenRow[screenRow] is lineId
delete @screenRowsByLineId[lineId]
delete @renderedDecorationsByLineId[lineId]
node.removeChild(lineNode)
updateLineNodes: ->
{presenter} = @props
appendOrUpdateVisibleLineNodes: (visibleLines, startRow, updateWidth) ->
{lineDecorations} = @props
for id of @oldState.lines
unless @newState.lines.hasOwnProperty(id)
@removeLineNode(id)
newLines = null
newLineIds = null
newLinesHTML = null
for line, index in visibleLines
screenRow = startRow + index
if @hasLineNode(line.id)
@updateLineNode(line, screenRow, updateWidth)
for id, lineState of @newState.lines
if @oldState.lines.hasOwnProperty(id)
@updateLineNode(id)
else
newLines ?= []
newLineIds ?= []
newLinesHTML ?= ""
newLines.push(line)
newLinesHTML += @buildLineHTML(line, screenRow)
@screenRowsByLineId[line.id] = screenRow
@lineIdsByScreenRow[screenRow] = line.id
newLineIds.push(id)
newLinesHTML += @buildLineHTML(id)
@screenRowsByLineId[id] = lineState.screenRow
@lineIdsByScreenRow[lineState.screenRow] = id
@oldState.lines[id] = _.clone(lineState)
@renderedDecorationsByLineId[line.id] = lineDecorations[screenRow]
return unless newLines?
return unless newLineIds?
WrapperDiv.innerHTML = newLinesHTML
newLineNodes = toArray(WrapperDiv.children)
node = @getDOMNode()
for line, i in newLines
for id, i in newLineIds
lineNode = newLineNodes[i]
@lineNodesByLineId[line.id] = lineNode
@lineNodesByLineId[id] = lineNode
node.appendChild(lineNode)
hasLineNode: (lineId) ->
@lineNodesByLineId.hasOwnProperty(lineId)
buildLineHTML: (line, screenRow) ->
{showIndentGuide, lineHeightInPixels, lineDecorations, lineWidth} = @props
{tokens, text, lineEnding, fold, isSoftWrapped, indentLevel} = line
buildLineHTML: (id) ->
{presenter} = @props
{scrollWidth} = @newState
{screenRow, tokens, text, top, lineEnding, fold, isSoftWrapped, indentLevel, decorationClasses} = @newState.lines[id]
classes = ''
if decorations = lineDecorations[screenRow]
for id, decoration of decorations
if Decoration.isType(decoration, 'line')
classes += decoration.class + ' '
if decorationClasses?
for decorationClass in decorationClasses
classes += decorationClass + ' '
classes += 'line'
top = screenRow * lineHeightInPixels
lineHTML = "<div class=\"#{classes}\" style=\"position: absolute; top: #{top}px; width: #{lineWidth}px;\" data-screen-row=\"#{screenRow}\">"
lineHTML = "<div class=\"#{classes}\" style=\"position: absolute; top: #{top}px; width: #{scrollWidth}px;\" data-screen-row=\"#{screenRow}\">"
if text is ""
lineHTML += @buildEmptyLineInnerHTML(line)
lineHTML += @buildEmptyLineInnerHTML(id)
else
lineHTML += @buildLineInnerHTML(line)
lineHTML += @buildLineInnerHTML(id)
lineHTML += '<span class="fold-marker"></span>' if fold
lineHTML += "</div>"
lineHTML
buildEmptyLineInnerHTML: (line) ->
{showIndentGuide} = @props
{indentLevel, tabLength, endOfLineInvisibles} = line
buildEmptyLineInnerHTML: (id) ->
{indentGuidesVisible} = @newState
{indentLevel, tabLength, endOfLineInvisibles} = @newState.lines[id]
if showIndentGuide and indentLevel > 0
if indentGuidesVisible and indentLevel > 0
invisibleIndex = 0
lineHTML = ''
for i in [0...indentLevel]
@@ -201,31 +160,30 @@ LinesComponent = React.createClass
lineHTML += "</span>"
while invisibleIndex < endOfLineInvisibles?.length
lineHTML += "<span class='invisible-character'>#{line.endOfLineInvisibles[invisibleIndex++]}</span>"
lineHTML += "<span class='invisible-character'>#{endOfLineInvisibles[invisibleIndex++]}</span>"
lineHTML
else
@buildEndOfLineHTML(line) or '&nbsp;'
@buildEndOfLineHTML(id) or '&nbsp;'
buildLineInnerHTML: (line) ->
{editor, showIndentGuide} = @props
{tokens, text} = line
buildLineInnerHTML: (id) ->
{editor} = @props
{indentGuidesVisible} = @newState
{tokens, text, isOnlyWhitespace} = @newState.lines[id]
innerHTML = ""
scopeStack = []
firstTrailingWhitespacePosition = text.search(/\s*$/)
lineIsWhitespaceOnly = firstTrailingWhitespacePosition is 0
for token in tokens
innerHTML += @updateScopeStack(scopeStack, token.scopes)
hasIndentGuide = not editor.isMini() and showIndentGuide and (token.hasLeadingWhitespace() or (token.hasTrailingWhitespace() and lineIsWhitespaceOnly))
hasIndentGuide = indentGuidesVisible and (token.hasLeadingWhitespace() or (token.hasTrailingWhitespace() and isOnlyWhitespace))
innerHTML += token.getValueAsHtml({hasIndentGuide})
innerHTML += @popScope(scopeStack) while scopeStack.length > 0
innerHTML += @buildEndOfLineHTML(line)
innerHTML += @buildEndOfLineHTML(id)
innerHTML
buildEndOfLineHTML: (line) ->
{endOfLineInvisibles} = line
buildEndOfLineHTML: (id) ->
{endOfLineInvisibles} = @newState.lines[id]
html = ''
if endOfLineInvisibles?
@@ -258,33 +216,30 @@ LinesComponent = React.createClass
scopeStack.push(scope)
"<span class=\"#{scope.replace(/\.+/g, ' ')}\">"
updateLineNode: (line, screenRow, updateWidth) ->
{lineHeightInPixels, lineDecorations, lineWidth} = @props
lineNode = @lineNodesByLineId[line.id]
updateLineNode: (id) ->
{scrollWidth} = @newState
{screenRow, top} = @newState.lines[id]
decorations = lineDecorations[screenRow]
previousDecorations = @renderedDecorationsByLineId[line.id]
lineNode = @lineNodesByLineId[id]
if previousDecorations?
for id, decoration of previousDecorations
if Decoration.isType(decoration, 'line') and not @hasDecoration(decorations, decoration)
lineNode.classList.remove(decoration.class)
newDecorationClasses = @newState.lines[id].decorationClasses
oldDecorationClasses = @oldState.lines[id].decorationClasses
if decorations?
for id, decoration of decorations
if Decoration.isType(decoration, 'line') and not @hasDecoration(previousDecorations, decoration)
lineNode.classList.add(decoration.class)
if oldDecorationClasses?
for decorationClass in oldDecorationClasses
unless newDecorationClasses? and decorationClass in newDecorationClasses
lineNode.classList.remove(decorationClass)
lineNode.style.width = lineWidth + 'px' if updateWidth
if newDecorationClasses?
for decorationClass in newDecorationClasses
unless oldDecorationClasses? and decorationClass in oldDecorationClasses
lineNode.classList.add(decorationClass)
unless @screenRowsByLineId[line.id] is screenRow
lineNode.style.top = screenRow * lineHeightInPixels + 'px'
lineNode.dataset.screenRow = screenRow
@screenRowsByLineId[line.id] = screenRow
@lineIdsByScreenRow[screenRow] = line.id
hasDecoration: (decorations, decoration) ->
decorations? and decorations[decoration.id] is decoration
lineNode.style.width = scrollWidth + 'px'
lineNode.style.top = top + 'px'
lineNode.dataset.screenRow = screenRow
@screenRowsByLineId[id] = screenRow
@lineIdsByScreenRow[screenRow] = id
lineNodeForScreenRow: (screenRow) ->
@lineNodesByLineId[@lineIdsByScreenRow[screenRow]]
@@ -296,26 +251,27 @@ LinesComponent = React.createClass
charWidth = DummyLineNode.firstChild.getBoundingClientRect().width
node.removeChild(DummyLineNode)
{editor} = @props
{editor, presenter} = @props
presenter.setLineHeight(lineHeightInPixels)
editor.setLineHeightInPixels(lineHeightInPixels)
presenter.setBaseCharacterWidth(charWidth)
editor.setDefaultCharWidth(charWidth)
remeasureCharacterWidths: ->
return unless @props.performedInitialMeasurement
return unless @props.presenter.hasRequiredMeasurements()
@clearScopedCharWidths()
@measureCharactersInNewLines()
measureCharactersInNewLines: ->
{editor, tokenizedLines, renderedRowRange} = @props
[visibleStartRow] = renderedRowRange
{editor} = @props
node = @getDOMNode()
editor.batchCharacterMeasurement =>
for tokenizedLine in tokenizedLines
unless @measuredLines.has(tokenizedLine)
lineNode = @lineNodesByLineId[tokenizedLine.id]
@measureCharactersInLine(tokenizedLine, lineNode)
for id, lineState of @oldState.lines
unless @measuredLines.has(id)
lineNode = @lineNodesByLineId[id]
@measureCharactersInLine(lineState, lineNode)
return
measureCharactersInLine: (tokenizedLine, lineNode) ->
@@ -358,11 +314,13 @@ LinesComponent = React.createClass
rangeForMeasurement.setEnd(textNode, i + charLength)
charWidth = rangeForMeasurement.getBoundingClientRect().width
editor.setScopedCharWidth(scopes, char, charWidth)
@props.presenter.setScopedCharWidth(scopes, char, charWidth)
charIndex += charLength
@measuredLines.add(tokenizedLine)
@measuredLines.add(tokenizedLine.id)
clearScopedCharWidths: ->
@measuredLines.clear()
@props.editor.clearScopedCharWidths()
@props.presenter.clearScopedCharWidths()
+4 -4
Ver Arquivo
@@ -268,7 +268,7 @@ class Marker
# Extended: Sets the buffer position of the marker's head.
#
# * `screenRange` The new {Point} to use
# * `bufferPosition` The new {Point} to use
# * `properties` (optional) {Object} properties to associate with the marker.
setHeadBufferPosition: (bufferPosition, properties) ->
@bufferMarker.setHeadPosition(bufferPosition, properties)
@@ -281,7 +281,7 @@ class Marker
# Extended: Sets the screen position of the marker's head.
#
# * `screenRange` The new {Point} to use
# * `screenPosition` The new {Point} to use
# * `properties` (optional) {Object} properties to associate with the marker.
setHeadScreenPosition: (screenPosition, properties) ->
screenPosition = @displayBuffer.clipScreenPosition(screenPosition, properties)
@@ -295,7 +295,7 @@ class Marker
# Extended: Sets the buffer position of the marker's tail.
#
# * `screenRange` The new {Point} to use
# * `bufferPosition` The new {Point} to use
# * `properties` (optional) {Object} properties to associate with the marker.
setTailBufferPosition: (bufferPosition) ->
@bufferMarker.setTailPosition(bufferPosition)
@@ -308,7 +308,7 @@ class Marker
# Extended: Sets the screen position of the marker's tail.
#
# * `screenRange` The new {Point} to use
# * `screenPosition` The new {Point} to use
# * `properties` (optional) {Object} properties to associate with the marker.
setTailScreenPosition: (screenPosition, options) ->
screenPosition = @displayBuffer.clipScreenPosition(screenPosition, options)
+1 -1
Ver Arquivo
@@ -176,7 +176,7 @@ class MenuManager
element?.classList.toString().split(' ') ? []
sortPackagesMenu: ->
packagesMenu = @template.find ({label}) -> MenuHelpers.normalizeLabel(label) is 'Packages'
packagesMenu = _.find @template, ({label}) -> MenuHelpers.normalizeLabel(label) is 'Packages'
return unless packagesMenu?.submenu?
packagesMenu.submenu.sort (item1, item2) ->
+23 -28
Ver Arquivo
@@ -1,46 +1,41 @@
module.exports =
class OverlayManager
constructor: (@container) ->
@overlays = {}
@overlayNodesById = {}
render: (props) ->
{editor, overlayDecorations, lineHeightInPixels} = props
{presenter} = props
existingDecorations = null
for markerId, {headPixelPosition, tailPixelPosition, decorations} of overlayDecorations
for decoration in decorations
pixelPosition =
if decoration.position is 'tail' then tailPixelPosition else headPixelPosition
for decorationId, {pixelPosition, item} of presenter.state.content.overlays
@renderOverlay(presenter, decorationId, item, pixelPosition)
@renderOverlay(editor, decoration, pixelPosition, lineHeightInPixels)
existingDecorations ?= {}
existingDecorations[decoration.id] = true
for id, overlay of @overlays
unless existingDecorations? and id of existingDecorations
@container.removeChild(overlay)
delete @overlays[id]
for id, overlayNode of @overlayNodesById
unless presenter.state.content.overlays.hasOwnProperty(id)
overlayNode.remove()
delete @overlayNodesById[id]
return
renderOverlay: (editor, decoration, pixelPosition, lineHeightInPixels) ->
item = atom.views.getView(decoration.item)
unless overlay = @overlays[decoration.id]
overlay = @overlays[decoration.id] = document.createElement('atom-overlay')
overlay.appendChild(item)
@container.appendChild(overlay)
renderOverlay: (presenter, decorationId, item, pixelPosition) ->
item = atom.views.getView(item)
unless overlayNode = @overlayNodesById[decorationId]
overlayNode = @overlayNodesById[decorationId] = document.createElement('atom-overlay')
overlayNode.appendChild(item)
@container.appendChild(overlayNode)
itemWidth = item.offsetWidth
itemHeight = item.offsetHeight
{scrollTop, scrollLeft} = presenter.state.content
left = pixelPosition.left
if left + itemWidth - editor.getScrollLeft() > editor.getWidth() and left - itemWidth >= editor.getScrollLeft()
if left + itemWidth - scrollLeft > presenter.contentFrameWidth and left - itemWidth >= scrollLeft
left -= itemWidth
top = pixelPosition.top + lineHeightInPixels
if top + itemHeight - editor.getScrollTop() > editor.getHeight() and top - itemHeight - lineHeightInPixels >= editor.getScrollTop()
top -= itemHeight + lineHeightInPixels
top = pixelPosition.top + presenter.lineHeight
if top + itemHeight - scrollTop > presenter.computeHeight() and top - itemHeight - presenter.lineHeight >= scrollTop
top -= itemHeight + presenter.lineHeight
overlay.style.top = top + 'px'
overlay.style.left = left + 'px'
overlayNode.style.top = top + 'px'
overlayNode.style.left = left + 'px'
+9 -1
Ver Arquivo
@@ -7,6 +7,7 @@ fs = require 'fs-plus'
Q = require 'q'
Grim = require 'grim'
ServiceHub = require 'service-hub'
Package = require './package'
ThemePackage = require './theme-package'
@@ -40,6 +41,7 @@ class PackageManager
@loadedPackages = {}
@activePackages = {}
@packageStates = {}
@serviceHub = new ServiceHub
@packageActivators = []
@registerPackageActivator(this, ['atom', 'textmate'])
@@ -128,9 +130,15 @@ class PackageManager
#
# Return a {String} file path to apm.
getApmPath: ->
return @apmPath if @apmPath?
commandName = 'apm'
commandName += '.cmd' if process.platform is 'win32'
@apmPath ?= path.resolve(__dirname, '..', 'apm', 'node_modules', 'atom-package-manager', 'bin', commandName)
apmRoot = path.resolve(__dirname, '..', 'apm')
@apmPath = path.join(apmRoot, 'bin', commandName)
unless fs.isFileSync(@apmPath)
@apmPath = path.join(apmRoot, 'node_modules', 'atom-package-manager', 'bin', commandName)
@apmPath
# Public: Get the paths being used to look for packages.
#
+13 -3
Ver Arquivo
@@ -44,11 +44,11 @@ class Package
metadata.name = packageName
if metadata.stylesheetMain?
deprecate("Use the `mainStyleSheet` key instead of `stylesheetMain` in your `package.json`", {packageName})
deprecate("Use the `mainStyleSheet` key instead of `stylesheetMain` in the `package.json` of `#{packageName}`", {packageName})
metadata.mainStyleSheet = metadata.stylesheetMain
if metadata.stylesheets?
deprecate("Use the `styleSheets` key instead of `stylesheets` in your `package.json`", {packageName})
deprecate("Use the `styleSheets` key instead of `stylesheets` in the `package.json` of `#{packageName}`", {packageName})
metadata.styleSheets = metadata.stylesheets
metadata
@@ -159,6 +159,7 @@ class Package
if @requireMainModule()
@mainModule.activate(atom.packages.getPackageState(@name) ? {})
@mainActivated = true
@activateServices()
catch e
console.warn "Failed to activate package named '#{@name}'", e.stack
@@ -209,6 +210,15 @@ class Package
settings.activate() for settings in @settings
@settingsActivated = true
activateServices: ->
for name, {versions} of @metadata.providedServices
for version, methodName of versions
@activationDisposables.add atom.packages.serviceHub.provide(name, version, @mainModule[methodName]())
for name, {versions} of @metadata.consumedServices
for version, methodName of versions
@activationDisposables.add atom.packages.serviceHub.consume(name, version, @mainModule[methodName].bind(@mainModule))
loadKeymaps: ->
if @bundledPackage and packagesCache[@name]?
@keymaps = (["#{atom.packages.resourcePath}#{path.sep}#{keymapPath}", keymapObject] for keymapPath, keymapObject of packagesCache[@name].keymaps)
@@ -241,7 +251,7 @@ class Package
getStylesheetsPath: ->
if fs.isDirectorySync(path.join(@path, 'stylesheets'))
deprecate("Store package style sheets in the `styles/` directory instead of `stylesheets/`", packageName: @name)
deprecate("Store package style sheets in the `styles/` directory instead of `stylesheets/` in the `#{@name}` package", packageName: @name)
path.join(@path, 'stylesheets')
else
path.join(@path, 'styles')
+2 -2
Ver Arquivo
@@ -48,7 +48,7 @@ class PaneContainer extends Model
deserializeParams: (params) ->
params.root = atom.deserializers.deserialize(params.root, container: this)
params.destroyEmptyPanes = atom.config.get('core.destroyEmptyPanes')
params.activePane = params.root.getPanes().find (pane) -> pane.id is params.activePaneId
params.activePane = find params.root.getPanes(), (pane) -> pane.id is params.activePaneId
params
serializeParams: (params) ->
@@ -147,7 +147,7 @@ class PaneContainer extends Model
find @getPanes(), (pane) -> pane.itemForURI(uri)?
paneForItem: (item) ->
@getPanes().find (pane) -> item in pane.getItems()
find @getPanes(), (pane) -> item in pane.getItems()
saveAll: ->
pane.saveItems() for pane in @getPanes()
+34 -8
Ver Arquivo
@@ -41,9 +41,14 @@ class Pane extends Model
# Called by the Serializable mixin during serialization.
serializeParams: ->
if typeof @activeItem?.getURI is 'function'
activeItemURI = @activeItem.getURI()
else if typeof @activeItem?.getUri is 'function'
activeItemURI = @activeItem.getUri()
id: @id
items: compact(@items.map((item) -> item.serialize?()))
activeItemURI: @activeItem?.getURI?() ? @activeItem?.getUri?()
activeItemURI: activeItemURI
focused: @focused
# Called by the Serializable mixin during deserialization.
@@ -51,7 +56,13 @@ class Pane extends Model
{items, activeItemURI, activeItemUri} = params
activeItemURI ?= activeItemUri
params.items = compact(items.map (itemState) -> atom.deserializers.deserialize(itemState))
params.activeItem = find params.items, (item) -> (item.getURI?() ? item.getUri?()) is activeItemURI
params.activeItem = find params.items, (item) ->
if typeof item.getURI is 'function'
itemURI = item.getURI()
else if typeof item.getUri is 'function'
itemURI = item.getUri()
itemURI is activeItemURI
params
getParent: -> @parent
@@ -427,13 +438,17 @@ class Pane extends Model
@destroyItem(item) for item in @getItems() when item isnt @activeItem
promptToSaveItem: (item) ->
if typeof item.getUri is 'function' and typeof item.getURI isnt 'function'
Grim.deprecate("Implement `::getURI` on pane items instead of `::getUri`")
return true unless item.shouldPromptToSave?()
return true unless (typeof item.getURI is 'function' or typeof item.getUri is 'function') and item.shouldPromptToSave?()
if typeof item.getURI is 'function'
uri = item.getURI()
else if typeof item.getUri is 'function'
uri = item.getUri()
else
return true
chosen = atom.confirm
message: "'#{item.getTitle?() ? item.getURI?() ? item.getUri?()}' has changes, do you want to save them?"
message: "'#{item.getTitle?() ? uri}' has changes, do you want to save them?"
detailedMessage: "Your changes will be lost if you close this item without saving."
buttons: ["Save", "Cancel", "Don't Save"]
@@ -460,7 +475,12 @@ class Pane extends Model
# * `nextAction` (optional) {Function} which will be called after the item is
# successfully saved.
saveItem: (item, nextAction) ->
if (item?.getURI?() ? item?.getUri?())
if typeof item?.getURI is 'function'
itemURI = item.getURI()
else if typeof item?.getUri is 'function'
itemURI = item.getUri()
if itemURI?
item.save?()
nextAction?()
else
@@ -490,7 +510,13 @@ class Pane extends Model
#
# * `uri` {String} containing a URI.
itemForURI: (uri) ->
find @items, (item) -> item.getURI?() is uri or item.getUri?() is uri
find @items, (item) ->
if typeof item.getURI is 'function'
itemUri = item.getURI()
else if typeof item.getUri is 'function'
itemUri = item.getUri()
itemUri is uri
itemForUri: (uri) ->
Grim.deprecate("Use `::itemForURI` instead.")
+35 -10
Ver Arquivo
@@ -39,9 +39,7 @@ class Project extends Model
@emitter = new Emitter
@buffers ?= []
for buffer in @buffers
do (buffer) =>
buffer.onDidDestroy => @removeBuffer(buffer)
@subscribeToBuffer(buffer) for buffer in @buffers
Grim.deprecate("Pass 'paths' array instead of 'path' to project constructor") if path?
paths ?= _.compact([path])
@@ -68,9 +66,17 @@ class Project extends Model
buffers: _.compact(@buffers.map (buffer) -> buffer.serialize() if buffer.isRetained())
deserializeParams: (params) ->
params.buffers = params.buffers.map (bufferState) -> atom.deserializers.deserialize(bufferState)
params
params.buffers = _.compact params.buffers.map (bufferState) ->
# Check that buffer's file path is accessible
return if fs.isDirectorySync(bufferState.filePath)
if bufferState.filePath
try
fs.closeSync(fs.openSync(bufferState.filePath, 'r'))
catch error
return unless error.code is 'ENOENT'
atom.deserializers.deserialize(bufferState)
params
###
Section: Event Subscription
@@ -218,6 +224,14 @@ class Project extends Model
# Returns a promise that resolves to an {TextEditor}.
open: (filePath, options={}) ->
filePath = @resolvePath(filePath)
if filePath?
try
fs.closeSync(fs.openSync(filePath, 'r'))
catch error
# allow ENOENT errors to create an editor for paths that dont exist
throw error unless error.code is 'ENOENT'
@bufferForPath(filePath).then (buffer) =>
@buildEditorForBuffer(buffer, options)
@@ -266,7 +280,6 @@ class Project extends Model
# Still needed when deserializing a tokenized buffer
buildBufferSync: (absoluteFilePath) ->
buffer = new TextBuffer({filePath: absoluteFilePath})
buffer.setEncoding(atom.config.get('core.fileEncoding'))
@addBuffer(buffer)
buffer.loadSync()
buffer
@@ -279,10 +292,11 @@ class Project extends Model
# Returns a promise that resolves to the {TextBuffer}.
buildBuffer: (absoluteFilePath) ->
if fs.getSizeSync(absoluteFilePath) >= 2 * 1048576 # 2MB
throw new Error("Atom can only handle files < 2MB for now.")
error = new Error("Atom can only handle files < 2MB for now.")
error.code = 'EFILETOOLARGE'
throw error
buffer = new TextBuffer({filePath: absoluteFilePath})
buffer.setEncoding(atom.config.get('core.fileEncoding'))
@addBuffer(buffer)
buffer.load()
.then((buffer) -> buffer)
@@ -290,11 +304,11 @@ class Project extends Model
addBuffer: (buffer, options={}) ->
@addBufferAtIndex(buffer, @buffers.length, options)
buffer.onDidDestroy => @removeBuffer(buffer)
@subscribeToBuffer(buffer)
addBufferAtIndex: (buffer, index, options={}) ->
@buffers.splice(index, 0, buffer)
buffer.onDidDestroy => @removeBuffer(buffer)
@subscribeToBuffer(buffer)
@emit 'buffer-created', buffer
buffer
@@ -323,6 +337,17 @@ class Project extends Model
else
@on 'buffer-created', (buffer) -> callback(buffer)
subscribeToBuffer: (buffer) ->
buffer.onDidDestroy => @removeBuffer(buffer)
buffer.onWillThrowWatchError ({error, handle}) ->
handle()
atom.notifications.addWarning """
Unable to read file after file `#{error.eventType}` event.
Make sure you have permission to access `#{buffer.getPath()}`.
""",
detail: error.message
dismissable: true
# Deprecated: delegate
registerOpener: (opener) ->
deprecate("Use Workspace::addOpener instead")
+18 -26
Ver Arquivo
@@ -7,28 +7,33 @@ ScrollbarComponent = React.createClass
displayName: 'ScrollbarComponent'
render: ->
{orientation, className, scrollHeight, scrollWidth, visible} = @props
{scrollableInOppositeDirection, horizontalScrollbarHeight, verticalScrollbarWidth} = @props
{useHardwareAcceleration} = @props
{presenter, orientation, className, useHardwareAcceleration} = @props
switch orientation
when 'vertical'
@newState = presenter.state.verticalScrollbar
when 'horizontal'
@newState = presenter.state.horizontalScrollbar
style = {}
style.display = 'none' unless visible
style.display = 'none' unless @newState.visible
style.transform = 'translateZ(0)' if useHardwareAcceleration # See atom/atom#3559
switch orientation
when 'vertical'
style.width = verticalScrollbarWidth
style.bottom = horizontalScrollbarHeight if scrollableInOppositeDirection
style.width = @newState.width
style.bottom = @newState.bottom
when 'horizontal'
style.left = 0
style.right = verticalScrollbarWidth if scrollableInOppositeDirection
style.height = horizontalScrollbarHeight
style.right = @newState.right
style.height = @newState.height
div {className, style},
switch orientation
when 'vertical'
div className: 'scrollbar-content', style: {height: scrollHeight}
div className: 'scrollbar-content', style: {height: @newState.scrollHeight}
when 'horizontal'
div className: 'scrollbar-content', style: {width: scrollWidth}
div className: 'scrollbar-content', style: {width: @newState.scrollWidth}
componentDidMount: ->
{orientation} = @props
@@ -41,26 +46,15 @@ ScrollbarComponent = React.createClass
componentWillUnmount: ->
@getDOMNode().removeEventListener 'scroll', @onScroll
shouldComponentUpdate: (newProps) ->
return true if newProps.visible isnt @props.visible
switch @props.orientation
when 'vertical'
not isEqualForProperties(newProps, @props, 'scrollHeight', 'scrollTop', 'scrollableInOppositeDirection', 'verticalScrollbarWidth')
when 'horizontal'
not isEqualForProperties(newProps, @props, 'scrollWidth', 'scrollLeft', 'scrollableInOppositeDirection', 'horizontalScrollbarHeight')
componentDidUpdate: ->
{orientation, scrollTop, scrollLeft} = @props
{orientation} = @props
node = @getDOMNode()
switch orientation
when 'vertical'
node.scrollTop = scrollTop
@props.scrollTop = node.scrollTop # Ensure scrollTop reflects actual DOM without triggering another update
node.scrollTop = @newState.scrollTop
when 'horizontal'
node.scrollLeft = scrollLeft
@props.scrollLeft = node.scrollLeft # Ensure scrollLeft reflects actual DOM without triggering another update
node.scrollLeft = @newState.scrollLeft
onScroll: ->
{orientation, onScroll} = @props
@@ -69,9 +63,7 @@ ScrollbarComponent = React.createClass
switch orientation
when 'vertical'
scrollTop = node.scrollTop
@props.scrollTop = scrollTop # Ensure scrollTop reflects actual DOM without triggering another update
onScroll(scrollTop)
when 'horizontal'
scrollLeft = node.scrollLeft
@props.scrollLeft = scrollLeft # Ensure scrollLeft reflects actual DOM without triggering another update
onScroll(scrollLeft)
+5 -4
Ver Arquivo
@@ -7,7 +7,11 @@ ScrollbarCornerComponent = React.createClass
displayName: 'ScrollbarCornerComponent'
render: ->
{visible, measuringScrollbars, width, height} = @props
{presenter, measuringScrollbars} = @props
visible = presenter.state.horizontalScrollbar.visible and presenter.state.verticalScrollbar.visible
width = presenter.state.verticalScrollbar.width
height = presenter.state.horizontalScrollbar.height
if measuringScrollbars
height = 25
@@ -19,6 +23,3 @@ ScrollbarCornerComponent = React.createClass
div style:
height: height + 1
width: width + 1
shouldComponentUpdate: (newProps) ->
not isEqualForProperties(newProps, @props, 'measuringScrollbars', 'visible', 'width', 'height')
+14 -8
Ver Arquivo
@@ -362,7 +362,7 @@ class Selection extends Model
precedingText = @editor.getTextInRange([[oldBufferRange.start.row, 0], oldBufferRange.start])
startLevel = @editor.indentLevelForLine(precedingText)
if options.indentBasis? and not options.autoIndent
if options.indentBasis?
text = @adjustIndent(text, startLevel - options.indentBasis)
newBufferRange = @editor.buffer.setTextInRange(oldBufferRange, text, pick(options, 'undo', 'normalizeLineEndings'))
@@ -375,8 +375,16 @@ class Selection extends Model
if options.autoIndent
precedingText = @editor.getTextInBufferRange([[newBufferRange.start.row, 0], newBufferRange.start])
unless NonWhitespaceRegExp.test(precedingText)
@editor.autoIndentBufferRow(newBufferRange.getRows()[0])
@editor.autoIndentBufferRow(row) for row, i in newBufferRange.getRows() when i > 0
rowsToIndent = newBufferRange.getRows()
firstRow = rowsToIndent.shift()
rowsToIndent.pop() if text.endsWith("\n")
suggestedIndent = @editor.suggestedIndentForBufferRow(firstRow)
actualIndent = @editor.indentationForBufferRow(firstRow)
@editor.setIndentationForBufferRow(firstRow, suggestedIndent)
indentChange = suggestedIndent - actualIndent
for row in rowsToIndent
newIndent = @editor.indentationForBufferRow(row) + indentChange
@editor.setIndentationForBufferRow(row, newIndent)
else if options.autoIndentNewline and text == '\n'
currentIndentation = @editor.indentationForBufferRow(newBufferRange.start.row)
@editor.autoIndentBufferRow(newBufferRange.end.row, preserveLeadingWhitespace: true, skipBlankLines: false)
@@ -595,14 +603,12 @@ class Selection extends Model
@editor.createFold(range.start.row, range.end.row)
@cursor.setBufferPosition([range.end.row + 1, 0])
# Public: Increases the indentation level of
#
# * `indentIncrease` The beginning indent level.
# Private: Increase the indentation level of the given text by given number
# of levels. Leaves the first line unchanged.
adjustIndent: (text, indentIncrease) ->
lines = text.split('\n')
for line, i in lines when i > 0
if indentIncrease == 0
if indentIncrease == 0 or line is ''
continue
else if indentIncrease > 0
lines[i] = @editor.buildIndentString(indentIncrease) + line
-93
Ver Arquivo
@@ -1,93 +0,0 @@
_ServiceHub = require('service-hub')
# Experimental: This class facilitates communication between Atom packages
# through semantically-versioned services. If you want your package to provide
# an API for other packages to interact with, provide or consume a service via
# the global instance of this class available as `atom.services`.
#
# If you're providing an API for other packages, the most straightforward is to
# `provide` a module namespaced under your package's name as follows.
#
# ```coffee
# atom.services.provide "status-bar", "1.0.0",
# addRightItem: (item) -> # ...
# addLeftItem: (item) -> # ...
# ```
#
# Then other packages can interact with your package by consuming the provided
# service. Note that a service consumer can provide an npm-style version range
# string to express the required API version of the consumed service. The
# callback will be invoked with the service immediately or when the service
# becomes available. If multiple services match the provided key-path and
# version range, the callback will be invoked multiple times.
#
# ```coffee
# atom.services.consume "status-bar", "^1.0.0", (statusBar) ->
# statusBar.addLeftItem(new GrammarChanger)
# ```
#
# You can also provide multiple services end-points under the same namespace by
# passing a dot-separated key path. In this example, we also provide a global
# reference to the status bar's DOM element so other packages can modify it
# directly. Doing this via `atom.services` is superior to querying from the DOM
# manually because you can use semantic versioning to indicate when the DOM
# structure changes in a breaking way.
#
# ```coffee
# atom.services.provide "status-bar.view", "1.0.0", statusBarElement
# ```
#
# By convention, every package owns its package name in the services namespace.
# Your package can provide a service under another package's namespace, but you
# should always conform to that package's API. If you want to make additions to
# the API, add them under your own namespace.
#
# When upgrading your package's API, consider retaining previous versions with
# shims if at all possible to minimize breakage and to give the ecosystem time
# to catch up with your changes.
#
# You can also apply an inverted pattern, where your package consumes services
# under its own namespace. In this pattern, you would define a contract for
# services that other packages provide and your package consumes. For example,
# say we were adding the ability to add custom completion providers to
# autocomplete:
#
# ```coffee
# atom.services.consume "autocomplete", "1.0.0", (provider) ->
# addCompletionProvider(provider)
# ```
#
# In this use case, you would want to consume a specific version number rather
# than a range. You could consume multiple version numbers to provide backward
# compatibility.
module.exports =
class ServiceHub extends _ServiceHub
# Experimental: Provide a service by invoking the callback of all current and
# future consumers matching the given key path and version range.
#
# * `keyPath` A {String} of `.` separated keys indicating the services's
# location in the namespace of all services.
# * `version` A {String} containing a [semantic version](http://semver.org/)
# for the service's API.
# * `service` An object exposing the service API.
#
# Returns a {Disposable} on which `.dispose()` can be called to remove the
# provided service.
provide: (keyPath, version, service) ->
super
# Experimental: Consume a service by invoking the given callback for all
# current and future provided services matching the given key path and version
# range.
#
# * `keyPath` A {String} of `.` separated keys indicating the services's
# location in the namespace of all services.
# * `versionRange` A {String} containing a [semantic version range](https://www.npmjs.org/doc/misc/semver.html)
# that any provided services for the given key path must satisfy.
# * `callback` A {Function} to be called with current and future matching
# service objects.
#
# Returns a {Disposable} on which `.dispose()` can be called to remove the
# consumer.
consume: (keyPath, versionRange, callback) ->
super
+44 -254
Ver Arquivo
@@ -8,6 +8,7 @@ grim = require 'grim'
{CompositeDisposable} = require 'event-kit'
ipc = require 'ipc'
TextEditorPresenter = require './text-editor-presenter'
GutterComponent = require './gutter-component'
InputComponent = require './input-component'
LinesComponent = require './lines-component'
@@ -21,9 +22,6 @@ TextEditorComponent = React.createClass
mixins: [SubscriberMixin]
visible: false
autoHeight: false
backgroundColor: null
gutterBackgroundColor: null
pendingScrollTop: null
pendingScrollLeft: null
selectOnMouseMove: false
@@ -32,13 +30,9 @@ TextEditorComponent = React.createClass
updateRequestedWhilePaused: false
cursorMoved: false
selectionChanged: false
scrollingVertically: false
mouseWheelScreenRow: null
mouseWheelScreenRowClearDelay: 150
scrollSensitivity: 0.4
heightAndWidthMeasurementRequested: false
inputEnabled: true
scopedCharacterWidthsChangeCount: null
domPollingInterval: 100
domPollingIntervalId: null
domPollingPaused: false
@@ -47,46 +41,19 @@ TextEditorComponent = React.createClass
remeasureCharacterWidthsWhenShown: false
render: ->
{focused, showIndentGuide, showLineNumbers, visible} = @state
{focused, showLineNumbers} = @state
{editor, cursorBlinkPeriod, cursorBlinkResumeDelay, hostElement, useShadowDOM} = @props
maxLineNumberDigits = editor.getLineCount().toString().length
hasSelection = editor.getLastSelection()? and !editor.getLastSelection().isEmpty()
style = {}
@performedInitialMeasurement = false if editor.isDestroyed()
if @performedInitialMeasurement
renderedRowRange = @getRenderedRowRange()
[renderedStartRow, renderedEndRow] = renderedRowRange
cursorPixelRects = @getCursorPixelRects(renderedRowRange)
tokenizedLines = editor.tokenizedLinesForScreenRows(renderedStartRow, renderedEndRow - 1)
decorations = editor.decorationsForScreenRowRange(renderedStartRow, renderedEndRow)
highlightDecorations = @getHighlightDecorations(decorations)
overlayDecorations = @getOverlayDecorations(decorations)
lineDecorations = @getLineDecorations(decorations)
placeholderText = editor.getPlaceholderText() if editor.isEmpty()
visible = @isVisible()
scrollHeight = editor.getScrollHeight()
scrollWidth = editor.getScrollWidth()
scrollTop = editor.getScrollTop()
scrollLeft = editor.getScrollLeft()
lineHeightInPixels = editor.getLineHeightInPixels()
defaultCharWidth = editor.getDefaultCharWidth()
scrollViewHeight = editor.getHeight()
lineWidth = Math.max(scrollWidth, editor.getWidth())
horizontalScrollbarHeight = editor.getHorizontalScrollbarHeight()
verticalScrollbarWidth = editor.getVerticalScrollbarWidth()
verticallyScrollable = editor.verticallyScrollable()
horizontallyScrollable = editor.horizontallyScrollable()
hiddenInputStyle = @getHiddenInputPosition()
hiddenInputStyle.WebkitTransform = 'translateZ(0)' if @useHardwareAcceleration
if @mouseWheelScreenRow? and not (renderedStartRow <= @mouseWheelScreenRow < renderedEndRow)
mouseWheelScreenRow = @mouseWheelScreenRow
style.height = scrollViewHeight if @autoHeight
style.height = @presenter.state.height if @presenter.state.height?
if useShadowDOM
className = 'editor-contents--private'
@@ -98,10 +65,8 @@ TextEditorComponent = React.createClass
div {className, style},
if @gutterVisible
GutterComponent {
ref: 'gutter', onMouseDown: @onGutterMouseDown, lineDecorations,
defaultCharWidth, editor, renderedRowRange, maxLineNumberDigits, scrollViewHeight,
scrollTop, scrollHeight, lineHeightInPixels, @pendingChanges, mouseWheelScreenRow,
@useHardwareAcceleration, @performedInitialMeasurement, @backgroundColor, @gutterBackgroundColor
ref: 'gutter', onMouseDown: @onGutterMouseDown,
@presenter, editor, @useHardwareAcceleration
}
div ref: 'scrollView', className: 'scroll-view',
@@ -111,53 +76,30 @@ TextEditorComponent = React.createClass
style: hiddenInputStyle
LinesComponent {
ref: 'lines',
editor, lineHeightInPixels, defaultCharWidth, tokenizedLines,
lineDecorations, highlightDecorations, overlayDecorations, hostElement,
showIndentGuide, renderedRowRange, @pendingChanges, scrollTop, scrollLeft,
@scrollingVertically, scrollHeight, scrollWidth, mouseWheelScreenRow,
visible, scrollViewHeight, @scopedCharacterWidthsChangeCount, lineWidth, @useHardwareAcceleration,
placeholderText, @performedInitialMeasurement, @backgroundColor, cursorPixelRects,
cursorBlinkPeriod, cursorBlinkResumeDelay, useShadowDOM
ref: 'lines', @presenter, editor, hostElement, @useHardwareAcceleration, useShadowDOM, visible
}
ScrollbarComponent
ref: 'horizontalScrollbar'
className: 'horizontal-scrollbar'
orientation: 'horizontal'
presenter: @presenter
onScroll: @onHorizontalScroll
scrollLeft: scrollLeft
scrollWidth: scrollWidth
visible: horizontallyScrollable
scrollableInOppositeDirection: verticallyScrollable
verticalScrollbarWidth: verticalScrollbarWidth
horizontalScrollbarHeight: horizontalScrollbarHeight
useHardwareAcceleration: @useHardwareAcceleration
ScrollbarComponent
ref: 'verticalScrollbar'
className: 'vertical-scrollbar'
orientation: 'vertical'
presenter: @presenter
onScroll: @onVerticalScroll
scrollTop: scrollTop
scrollHeight: scrollHeight
visible: verticallyScrollable
scrollableInOppositeDirection: horizontallyScrollable
verticalScrollbarWidth: verticalScrollbarWidth
horizontalScrollbarHeight: horizontalScrollbarHeight
useHardwareAcceleration: @useHardwareAcceleration
# Also used to measure the height/width of scrollbars after the initial render
ScrollbarCornerComponent
ref: 'scrollbarCorner'
visible: horizontallyScrollable and verticallyScrollable
presenter: @presenter
measuringScrollbars: @measuringScrollbars
height: horizontalScrollbarHeight
width: verticalScrollbarWidth
getPageRows: ->
{editor} = @props
Math.max(1, Math.ceil(editor.getHeight() / editor.getLineHeightInPixels()))
getInitialState: -> {}
@@ -167,11 +109,23 @@ TextEditorComponent = React.createClass
lineOverdrawMargin: 15
componentWillMount: ->
@pendingChanges = []
@props.editor.manageScrollPosition = true
@observeConfig()
@setScrollSensitivity(atom.config.get('editor.scrollSensitivity'))
{editor, lineOverdrawMargin, cursorBlinkPeriod, cursorBlinkResumeDelay} = @props
@presenter = new TextEditorPresenter
model: editor
scrollTop: editor.getScrollTop()
scrollLeft: editor.getScrollLeft()
lineOverdrawMargin: lineOverdrawMargin
cursorBlinkPeriod: cursorBlinkPeriod
cursorBlinkResumeDelay: cursorBlinkResumeDelay
stoppedScrollingDelay: 200
@presenter.onDidUpdateState(@requestUpdate)
componentDidMount: ->
{editor, stylesElement} = @props
@@ -202,7 +156,6 @@ TextEditorComponent = React.createClass
componentDidUpdate: (prevProps, prevState) ->
cursorMoved = @cursorMoved
selectionChanged = @selectionChanged
@pendingChanges.length = 0
@cursorMoved = false
@selectionChanged = false
@@ -215,16 +168,16 @@ TextEditorComponent = React.createClass
becameVisible: ->
@updatesPaused = true
@measureScrollbars() if @measureScrollbarsWhenShown
@sampleFontStyling()
@sampleBackgroundColors()
@measureHeightAndWidth()
@measureScrollbars() if @measureScrollbarsWhenShown
@measureLineHeightAndDefaultCharWidth() if @measureLineHeightAndDefaultCharWidthWhenShown
@remeasureCharacterWidths() if @remeasureCharacterWidthsWhenShown
@props.editor.setVisible(true)
@performedInitialMeasurement = true
@updatesPaused = false
@forceUpdate() if @updateRequestedWhilePaused and @canUpdate()
@forceUpdate() if @canUpdate()
requestUpdate: ->
return unless @canUpdate()
@@ -257,13 +210,6 @@ TextEditorComponent = React.createClass
getTopmostDOMNode: ->
@props.hostElement
getRenderedRowRange: ->
{editor, lineOverdrawMargin} = @props
[visibleStartRow, visibleEndRow] = editor.getVisibleRowRange()
renderedStartRow = Math.max(0, visibleStartRow - lineOverdrawMargin)
renderedEndRow = Math.min(editor.getScreenLineCount(), visibleEndRow + lineOverdrawMargin)
[renderedStartRow, renderedEndRow]
getHiddenInputPosition: ->
{editor} = @props
{focused} = @state
@@ -277,117 +223,13 @@ TextEditorComponent = React.createClass
left = Math.max(0, Math.min(editor.getWidth() - width, left))
{top, left}
getCursorScreenRanges: (renderedRowRange) ->
{editor} = @props
[renderedStartRow, renderedEndRow] = renderedRowRange
cursorScreenRanges = {}
for selection in editor.getSelections() when selection.isEmpty()
{cursor} = selection
screenRange = cursor.getScreenRange()
if renderedStartRow <= screenRange.start.row < renderedEndRow
cursorScreenRanges[cursor.id] = screenRange
cursorScreenRanges
getCursorPixelRects: (renderedRowRange) ->
{editor} = @props
[renderedStartRow, renderedEndRow] = renderedRowRange
cursorPixelRects = {}
for selection in editor.getSelections() when selection.isEmpty()
{cursor} = selection
screenRange = cursor.getScreenRange()
if renderedStartRow <= screenRange.start.row < renderedEndRow
cursorPixelRects[cursor.id] = editor.pixelRectForScreenRange(screenRange)
cursorPixelRects
getLineDecorations: (decorationsByMarkerId) ->
{editor} = @props
return {} if editor.isMini()
decorationsByScreenRow = {}
for markerId, decorations of decorationsByMarkerId
marker = editor.getMarker(markerId)
screenRange = null
headScreenRow = null
if marker.isValid()
for decoration in decorations
if decoration.isType('gutter') or decoration.isType('line')
decorationParams = decoration.getProperties()
screenRange ?= marker.getScreenRange()
headScreenRow ?= marker.getHeadScreenPosition().row
startRow = screenRange.start.row
endRow = screenRange.end.row
endRow-- if not screenRange.isEmpty() and screenRange.end.column == 0
for screenRow in [startRow..endRow]
continue if decorationParams.onlyHead and screenRow isnt headScreenRow
if screenRange.isEmpty()
continue if decorationParams.onlyNonEmpty
else
continue if decorationParams.onlyEmpty
decorationsByScreenRow[screenRow] ?= {}
decorationsByScreenRow[screenRow][decoration.id] = decorationParams
decorationsByScreenRow
getHighlightDecorations: (decorationsByMarkerId) ->
{editor} = @props
filteredDecorations = {}
for markerId, decorations of decorationsByMarkerId
marker = editor.getMarker(markerId)
screenRange = marker.getScreenRange()
if marker.isValid() and not screenRange.isEmpty()
for decoration in decorations
if decoration.isType('highlight')
decorationParams = decoration.getProperties()
filteredDecorations[markerId] ?=
id: markerId
startPixelPosition: editor.pixelPositionForScreenPosition(screenRange.start, true)
endPixelPosition: editor.pixelPositionForScreenPosition(screenRange.end, true)
decorations: []
filteredDecorations[markerId].decorations.push decorationParams
filteredDecorations
getOverlayDecorations: (decorationsByMarkerId) ->
{editor} = @props
filteredDecorations = {}
for markerId, decorations of decorationsByMarkerId
marker = editor.getMarker(markerId)
headScreenPosition = marker.getHeadScreenPosition()
tailScreenPosition = marker.getTailScreenPosition()
if marker.isValid()
for decoration in decorations
if decoration.isType('overlay')
decorationParams = decoration.getProperties()
filteredDecorations[markerId] ?=
id: markerId
headPixelPosition: editor.pixelPositionForScreenPosition(headScreenPosition, true)
tailPixelPosition: editor.pixelPositionForScreenPosition(tailScreenPosition, true)
decorations: []
filteredDecorations[markerId].decorations.push decorationParams
filteredDecorations
observeEditor: ->
{editor} = @props
@subscribe editor.onDidChange(@onScreenLinesChanged)
@subscribe editor.onDidChangeGutterVisible(@updateGutterVisible)
@subscribe editor.onDidChangeMini(@setMini)
@subscribe editor.observeGrammar(@onGrammarChanged)
@subscribe editor.observeCursors(@onCursorAdded)
@subscribe editor.observeSelections(@onSelectionAdded)
@subscribe editor.observeDecorations(@onDecorationAdded)
@subscribe editor.onDidRemoveDecoration(@onDecorationRemoved)
@subscribe editor.onDidChangeCharacterWidths(@onCharacterWidthsChanged)
@subscribe editor.onDidChangePlaceholderText(@onPlaceholderTextChanged)
@subscribe editor.$scrollTop.changes, @onScrollTopChanged
@subscribe editor.$scrollLeft.changes, @requestUpdate
@subscribe editor.$verticalScrollbarWidth.changes, @requestUpdate
@subscribe editor.$horizontalScrollbarHeight.changes, @requestUpdate
@subscribe editor.$height.changes, @requestUpdate
@subscribe editor.$width.changes, @requestUpdate
@subscribe editor.$defaultCharWidth.changes, @requestUpdate
@subscribe editor.$lineHeightInPixels.changes, @requestUpdate
listenForDOMEvents: ->
node = @getDOMNode()
@@ -458,7 +300,7 @@ TextEditorComponent = React.createClass
scopeDescriptor = editor.getRootScopeDescriptor()
subscriptions.add atom.config.observe 'editor.showIndentGuide', scope: scopeDescriptor, @setShowIndentGuide
subscriptions.add atom.config.observe 'editor.showIndentGuide', scope: scopeDescriptor, @requestUpdate
subscriptions.add atom.config.observe 'editor.showLineNumbers', scope: scopeDescriptor, @updateGutterVisible
subscriptions.add atom.config.observe 'editor.scrollSensitivity', scope: scopeDescriptor, @setScrollSensitivity
@@ -505,7 +347,7 @@ TextEditorComponent = React.createClass
@requestAnimationFrame =>
pendingScrollTop = @pendingScrollTop
@pendingScrollTop = null
@props.editor.setScrollTop(pendingScrollTop)
@presenter.setScrollTop(pendingScrollTop)
onHorizontalScroll: (scrollLeft) ->
{editor} = @props
@@ -516,7 +358,7 @@ TextEditorComponent = React.createClass
@pendingScrollLeft = scrollLeft
unless animationFramePending
@requestAnimationFrame =>
@props.editor.setScrollLeft(@pendingScrollLeft)
@presenter.setScrollLeft(@pendingScrollLeft)
@pendingScrollLeft = null
onMouseWheel: (event) ->
@@ -537,15 +379,13 @@ TextEditorComponent = React.createClass
if Math.abs(wheelDeltaX) > Math.abs(wheelDeltaY)
# Scrolling horizontally
previousScrollLeft = editor.getScrollLeft()
editor.setScrollLeft(previousScrollLeft - Math.round(wheelDeltaX * @scrollSensitivity))
@presenter.setScrollLeft(previousScrollLeft - Math.round(wheelDeltaX * @scrollSensitivity))
event.preventDefault() unless previousScrollLeft is editor.getScrollLeft()
else
# Scrolling vertically
@mouseWheelScreenRow = @screenRowForNode(event.target)
@clearMouseWheelScreenRowAfterDelay ?= debounce(@clearMouseWheelScreenRow, @mouseWheelScreenRowClearDelay)
@clearMouseWheelScreenRowAfterDelay()
previousScrollTop = editor.getScrollTop()
editor.setScrollTop(previousScrollTop - Math.round(wheelDeltaY * @scrollSensitivity))
@presenter.setMouseWheelScreenRow(@screenRowForNode(event.target))
previousScrollTop = @presenter.scrollTop
@presenter.setScrollTop(previousScrollTop - Math.round(wheelDeltaY * @scrollSensitivity))
event.preventDefault() unless previousScrollTop is editor.getScrollTop()
onScrollViewScroll: ->
@@ -682,11 +522,6 @@ TextEditorComponent = React.createClass
@sampleBackgroundColors()
@remeasureCharacterWidths()
onScreenLinesChanged: (change) ->
{editor} = @props
@pendingChanges.push(change)
@requestUpdate() if editor.intersectsVisibleRowRange(change.start, change.end + 1) # TODO: Use closed-open intervals for change events
onSelectionAdded: (selection) ->
{editor} = @props
@@ -705,21 +540,6 @@ TextEditorComponent = React.createClass
@selectionChanged = true
@requestUpdate()
onScrollTopChanged: ->
@scrollingVertically = true
@requestUpdate()
@onStoppedScrollingAfterDelay ?= debounce(@onStoppedScrolling, 200)
@onStoppedScrollingAfterDelay()
onStoppedScrolling: ->
return unless @isMounted()
@scrollingVertically = false
@mouseWheelScreenRow = null
@requestUpdate()
onStoppedScrollingAfterDelay: null # created lazily
onCursorAdded: (cursor) ->
@subscribe cursor.onDidChangePosition @onCursorMoved
@@ -727,23 +547,6 @@ TextEditorComponent = React.createClass
@cursorMoved = true
@requestUpdate()
onDecorationAdded: (decoration) ->
@subscribe decoration.onDidChangeProperties(@onDecorationChanged)
@subscribe decoration.getMarker().onDidChange(@onDecorationChanged)
@requestUpdate()
onDecorationChanged: ->
@requestUpdate()
onDecorationRemoved: ->
@requestUpdate()
onCharacterWidthsChanged: (@scopedCharacterWidthsChangeCount) ->
@requestUpdate()
onPlaceholderTextChanged: ->
@requestUpdate()
handleDragUntilMouseUp: (event, dragHandler) ->
{editor} = @props
dragging = false
@@ -840,20 +643,19 @@ TextEditorComponent = React.createClass
{height} = hostElement.style
if position is 'absolute' or height
if @autoHeight
@autoHeight = false
@forceUpdate() if not @updatesPaused and @canUpdate()
clientHeight = scrollViewNode.clientHeight
editor.setHeight(clientHeight) if clientHeight > 0
@presenter.setAutoHeight(false)
height = hostElement.offsetHeight
if height > 0
@presenter.setExplicitHeight(height)
else
editor.setHeight(null)
@autoHeight = true
@presenter.setAutoHeight(true)
@presenter.setExplicitHeight(null)
clientWidth = scrollViewNode.clientWidth
paddingLeft = parseInt(getComputedStyle(scrollViewNode).paddingLeft)
clientWidth -= paddingLeft
editor.setWidth(clientWidth) if clientWidth > 0
if clientWidth > 0
@presenter.setContentFrameWidth(clientWidth)
sampleFontStyling: ->
oldFontSize = @fontSize
@@ -870,18 +672,13 @@ TextEditorComponent = React.createClass
sampleBackgroundColors: (suppressUpdate) ->
{hostElement} = @props
{showLineNumbers} = @state
{backgroundColor} = getComputedStyle(hostElement)
if backgroundColor isnt @backgroundColor
@backgroundColor = backgroundColor
@requestUpdate() unless suppressUpdate
@presenter.setBackgroundColor(backgroundColor)
if @refs.gutter?
gutterBackgroundColor = getComputedStyle(@refs.gutter.getDOMNode()).backgroundColor
if gutterBackgroundColor isnt @gutterBackgroundColor
@gutterBackgroundColor = gutterBackgroundColor
@requestUpdate() unless suppressUpdate
@presenter.setGutterBackgroundColor(gutterBackgroundColor)
measureLineHeightAndDefaultCharWidth: ->
if @isVisible()
@@ -909,8 +706,8 @@ TextEditorComponent = React.createClass
width = (cornerNode.offsetWidth - cornerNode.clientWidth) or 15
height = (cornerNode.offsetHeight - cornerNode.clientHeight) or 15
editor.setVerticalScrollbarWidth(width)
editor.setHorizontalScrollbarHeight(height)
@presenter.setVerticalScrollbarWidth(width)
@presenter.setHorizontalScrollbarHeight(height)
cornerNode.style.display = originalDisplayValue
@@ -955,13 +752,6 @@ TextEditorComponent = React.createClass
horizontalNode.style.display = originalHorizontalDisplayValue
cornerNode.style.display = originalCornerDisplayValue
clearMouseWheelScreenRow: ->
if @mouseWheelScreenRow?
@mouseWheelScreenRow = null
@requestUpdate()
clearMouseWheelScreenRowAfterDelay: null # created lazily
consolidateSelections: (e) ->
e.abortKeyBinding() unless @props.editor.consolidateSelections()
@@ -995,7 +785,7 @@ TextEditorComponent = React.createClass
@sampleFontStyling()
setShowIndentGuide: (showIndentGuide) ->
@setState({showIndentGuide})
atom.config.set("editor.showIndentGuide", showIndentGuide)
setMini: ->
@updateGutterVisible()
+14
Ver Arquivo
@@ -190,6 +190,20 @@ class TextEditorElement extends HTMLElement
pixelPositionForScreenPosition: (screenPosition) ->
@getModel().pixelPositionForScreenPosition(screenPosition, true)
# Extended: Retrieves the number of the row that is visible and currently at the
# top of the editor.
#
# Returns a {Number}.
getFirstVisibleScreenRow: ->
@getModel().getFirstVisibleScreenRow(true)
# Extended: Retrieves the number of the row that is visible and currently at the
# bottom of the editor.
#
# Returns a {Number}.
getLastVisibleScreenRow: ->
@getModel().getLastVisibleScreenRow(true)
# Extended: call the given `callback` when the editor is attached to the DOM.
#
# * `callback` {Function}
+831
Ver Arquivo
@@ -0,0 +1,831 @@
{CompositeDisposable, Emitter} = require 'event-kit'
{Point, Range} = require 'text-buffer'
_ = require 'underscore-plus'
module.exports =
class TextEditorPresenter
toggleCursorBlinkHandle: null
startBlinkingCursorsAfterDelay: null
stoppedScrollingTimeoutId: null
mouseWheelScreenRow: null
constructor: (params) ->
{@model, @autoHeight, @explicitHeight, @contentFrameWidth, @scrollTop, @scrollLeft} = params
{@horizontalScrollbarHeight, @verticalScrollbarWidth} = params
{@lineHeight, @baseCharacterWidth, @lineOverdrawMargin, @backgroundColor, @gutterBackgroundColor} = params
{@cursorBlinkPeriod, @cursorBlinkResumeDelay, @stoppedScrollingDelay} = params
@disposables = new CompositeDisposable
@emitter = new Emitter
@charWidthsByScope = {}
@transferMeasurementsToModel()
@observeModel()
@observeConfig()
@buildState()
@startBlinkingCursors()
destroy: ->
@disposables.dispose()
onDidUpdateState: (callback) ->
@emitter.on 'did-update-state', callback
transferMeasurementsToModel: ->
@model.setHeight(@explicitHeight) if @explicitHeight?
@model.setWidth(@contentFrameWidth) if @contentFrameWidth?
@model.setLineHeightInPixels(@lineHeight) if @lineHeight?
@model.setDefaultCharWidth(@baseCharacterWidth) if @baseCharacterWidth?
@model.setScrollTop(@scrollTop) if @scrollTop?
@model.setScrollLeft(@scrollLeft) if @scrollLeft?
@model.setVerticalScrollbarWidth(@verticalScrollbarWidth) if @verticalScrollbarWidth?
@model.setHorizontalScrollbarHeight(@horizontalScrollbarHeight) if @horizontalScrollbarHeight?
observeModel: ->
@disposables.add @model.onDidChange =>
@updateHeightState()
@updateVerticalScrollState()
@updateHorizontalScrollState()
@updateScrollbarsState()
@updateContentState()
@updateDecorations()
@updateLinesState()
@updateGutterState()
@updateLineNumbersState()
@disposables.add @model.onDidChangeGrammar(@updateContentState.bind(this))
@disposables.add @model.onDidChangePlaceholderText(@updateContentState.bind(this))
@disposables.add @model.onDidChangeMini =>
@updateContentState()
@updateDecorations()
@updateLinesState()
@updateLineNumbersState()
@disposables.add @model.onDidAddDecoration(@didAddDecoration.bind(this))
@disposables.add @model.onDidAddCursor(@didAddCursor.bind(this))
@disposables.add @model.onDidChangeScrollTop(@setScrollTop.bind(this))
@disposables.add @model.onDidChangeScrollLeft(@setScrollLeft.bind(this))
@observeDecoration(decoration) for decoration in @model.getDecorations()
@observeCursor(cursor) for cursor in @model.getCursors()
observeConfig: ->
@disposables.add atom.config.onDidChange 'editor.showIndentGuide', scope: @model.getRootScopeDescriptor(), @updateContentState.bind(this)
buildState: ->
@state =
horizontalScrollbar: {}
verticalScrollbar: {}
content:
scrollingVertically: false
blinkCursorsOff: false
lines: {}
highlights: {}
overlays: {}
gutter:
lineNumbers: {}
@updateState()
updateState: ->
@updateHeightState()
@updateVerticalScrollState()
@updateHorizontalScrollState()
@updateScrollbarsState()
@updateContentState()
@updateDecorations()
@updateLinesState()
@updateCursorsState()
@updateOverlaysState()
@updateGutterState()
@updateLineNumbersState()
updateHeightState: ->
if @autoHeight
@state.height = @computeContentHeight()
else
@state.height = null
@emitter.emit 'did-update-state'
updateVerticalScrollState: ->
scrollHeight = @computeScrollHeight()
@state.content.scrollHeight = scrollHeight
@state.gutter.scrollHeight = scrollHeight
@state.verticalScrollbar.scrollHeight = scrollHeight
scrollTop = @computeScrollTop()
@state.content.scrollTop = scrollTop
@state.gutter.scrollTop = scrollTop
@state.verticalScrollbar.scrollTop = scrollTop
@emitter.emit 'did-update-state'
updateHorizontalScrollState: ->
scrollWidth = @computeScrollWidth()
@state.content.scrollWidth = scrollWidth
@state.horizontalScrollbar.scrollWidth = scrollWidth
scrollLeft = @computeScrollLeft()
@state.content.scrollLeft = scrollLeft
@state.horizontalScrollbar.scrollLeft = scrollLeft
@emitter.emit 'did-update-state'
updateScrollbarsState: ->
horizontalScrollbarHeight = @computeHorizontalScrollbarHeight()
verticalScrollbarWidth = @computeVerticalScrollbarWidth()
@state.horizontalScrollbar.visible = horizontalScrollbarHeight > 0
@state.horizontalScrollbar.height = @horizontalScrollbarHeight
@state.horizontalScrollbar.right = verticalScrollbarWidth
@state.verticalScrollbar.visible = verticalScrollbarWidth > 0
@state.verticalScrollbar.width = @verticalScrollbarWidth
@state.verticalScrollbar.bottom = horizontalScrollbarHeight
@emitter.emit 'did-update-state'
updateContentState: ->
@state.content.scrollWidth = @computeScrollWidth()
@state.content.scrollLeft = @scrollLeft
@state.content.indentGuidesVisible = not @model.isMini() and atom.config.get('editor.showIndentGuide', scope: @model.getRootScopeDescriptor())
@state.content.backgroundColor = if @model.isMini() then null else @backgroundColor
@state.content.placeholderText = if @model.isEmpty() then @model.getPlaceholderText() else null
@emitter.emit 'did-update-state'
updateLinesState: ->
return unless @hasRequiredMeasurements()
visibleLineIds = {}
startRow = @computeStartRow()
endRow = @computeEndRow()
row = startRow
while row < endRow
line = @model.tokenizedLineForScreenRow(row)
visibleLineIds[line.id] = true
if @state.content.lines.hasOwnProperty(line.id)
@updateLineState(row, line)
else
@buildLineState(row, line)
row++
if @mouseWheelScreenRow?
preservedLine = @model.tokenizedLineForScreenRow(@mouseWheelScreenRow)
visibleLineIds[preservedLine.id] = true
for id, line of @state.content.lines
unless visibleLineIds.hasOwnProperty(id)
delete @state.content.lines[id]
@emitter.emit 'did-update-state'
updateLineState: (row, line) ->
lineState = @state.content.lines[line.id]
lineState.screenRow = row
lineState.top = row * @lineHeight
lineState.decorationClasses = @lineDecorationClassesForRow(row)
buildLineState: (row, line) ->
@state.content.lines[line.id] =
screenRow: row
text: line.text
tokens: line.tokens
isOnlyWhitespace: line.isOnlyWhitespace()
endOfLineInvisibles: line.endOfLineInvisibles
indentLevel: line.indentLevel
tabLength: line.tabLength
fold: line.fold
top: row * @lineHeight
decorationClasses: @lineDecorationClassesForRow(row)
updateCursorsState: ->
@state.content.cursors = {}
return unless @hasRequiredMeasurements()
startRow = @computeStartRow()
endRow = @computeEndRow()
for cursor in @model.getCursors()
if cursor.isVisible() and startRow <= cursor.getScreenRow() < endRow
pixelRect = @pixelRectForScreenRange(cursor.getScreenRange())
pixelRect.width = @baseCharacterWidth if pixelRect.width is 0
@state.content.cursors[cursor.id] = pixelRect
@emitter.emit 'did-update-state'
updateOverlaysState: ->
return unless @hasRequiredMeasurements()
visibleDecorationIds = {}
for decoration in @model.getOverlayDecorations()
continue unless decoration.getMarker().isValid()
{item, position} = decoration.getProperties()
if position is 'tail'
screenPosition = decoration.getMarker().getTailScreenPosition()
else
screenPosition = decoration.getMarker().getHeadScreenPosition()
@state.content.overlays[decoration.id] ?= {item}
@state.content.overlays[decoration.id].pixelPosition = @pixelPositionForScreenPosition(screenPosition)
visibleDecorationIds[decoration.id] = true
for id of @state.content.overlays
delete @state.content.overlays[id] unless visibleDecorationIds[id]
@emitter.emit "did-update-state"
updateGutterState: ->
@state.gutter.maxLineNumberDigits = @model.getLineCount().toString().length
@state.gutter.backgroundColor = if @gutterBackgroundColor isnt "rgba(0, 0, 0, 0)"
@gutterBackgroundColor
else
@backgroundColor
@emitter.emit "did-update-state"
updateLineNumbersState: ->
startRow = @computeStartRow()
endRow = @computeEndRow()
visibleLineNumberIds = {}
if startRow > 0
rowBeforeStartRow = startRow - 1
lastBufferRow = @model.bufferRowForScreenRow(rowBeforeStartRow)
wrapCount = rowBeforeStartRow - @model.screenRowForBufferRow(lastBufferRow)
else
lastBufferRow = null
wrapCount = 0
for bufferRow, i in @model.bufferRowsForScreenRows(startRow, endRow - 1)
if bufferRow is lastBufferRow
wrapCount++
id = bufferRow + '-' + wrapCount
softWrapped = true
else
id = bufferRow
wrapCount = 0
lastBufferRow = bufferRow
softWrapped = false
screenRow = startRow + i
top = screenRow * @lineHeight
decorationClasses = @lineNumberDecorationClassesForRow(screenRow)
foldable = @model.isFoldableAtScreenRow(screenRow)
@state.gutter.lineNumbers[id] = {screenRow, bufferRow, softWrapped, top, decorationClasses, foldable}
visibleLineNumberIds[id] = true
if @mouseWheelScreenRow?
bufferRow = @model.bufferRowForScreenRow(@mouseWheelScreenRow)
wrapCount = @mouseWheelScreenRow - @model.screenRowForBufferRow(bufferRow)
id = bufferRow
id += '-' + wrapCount if wrapCount > 0
visibleLineNumberIds[id] = true
for id of @state.gutter.lineNumbers
delete @state.gutter.lineNumbers[id] unless visibleLineNumberIds[id]
@emitter.emit 'did-update-state'
buildHighlightRegions: (screenRange) ->
lineHeightInPixels = @lineHeight
startPixelPosition = @pixelPositionForScreenPosition(screenRange.start, true)
endPixelPosition = @pixelPositionForScreenPosition(screenRange.end, true)
spannedRows = screenRange.end.row - screenRange.start.row + 1
if spannedRows is 1
[
top: startPixelPosition.top
height: lineHeightInPixels
left: startPixelPosition.left
width: endPixelPosition.left - startPixelPosition.left
]
else
regions = []
# First row, extending from selection start to the right side of screen
regions.push(
top: startPixelPosition.top
left: startPixelPosition.left
height: lineHeightInPixels
right: 0
)
# Middle rows, extending from left side to right side of screen
if spannedRows > 2
regions.push(
top: startPixelPosition.top + lineHeightInPixels
height: endPixelPosition.top - startPixelPosition.top - lineHeightInPixels
left: 0
right: 0
)
# Last row, extending from left side of screen to selection end
if screenRange.end.column > 0
regions.push(
top: endPixelPosition.top
height: lineHeightInPixels
left: 0
width: endPixelPosition.left
)
regions
computeStartRow: ->
startRow = Math.floor(@computeScrollTop() / @lineHeight) - @lineOverdrawMargin
Math.max(0, startRow)
computeEndRow: ->
startRow = Math.floor(@computeScrollTop() / @lineHeight)
visibleLinesCount = Math.ceil(@computeHeight() / @lineHeight) + 1
endRow = startRow + visibleLinesCount + @lineOverdrawMargin
Math.min(@model.getScreenLineCount(), endRow)
computeScrollWidth: ->
Math.max(@computeContentWidth(), @contentFrameWidth)
computeScrollHeight: ->
Math.max(@computeContentHeight(), @computeHeight())
computeContentWidth: ->
contentWidth = @pixelPositionForScreenPosition([@model.getLongestScreenRow(), Infinity]).left
contentWidth += 1 unless @model.isSoftWrapped() # account for cursor width
contentWidth
computeContentHeight: ->
@lineHeight * @model.getScreenLineCount()
computeClientHeight: ->
@computeHeight() - @computeHorizontalScrollbarHeight()
computeClientWidth: ->
@contentFrameWidth - @computeVerticalScrollbarWidth()
computeScrollTop: ->
@scrollTop = @constrainScrollTop(@scrollTop)
constrainScrollTop: (scrollTop) ->
if @hasRequiredMeasurements()
Math.max(0, Math.min(scrollTop, @computeScrollHeight() - @computeClientHeight()))
else
Math.max(0, scrollTop) if scrollTop?
computeScrollLeft: ->
@scrollLeft = @constrainScrollLeft(@scrollLeft)
constrainScrollLeft: (scrollLeft) ->
if @hasRequiredMeasurements()
Math.max(0, Math.min(scrollLeft, @computeScrollWidth() - @computeClientWidth()))
else
Math.max(0, scrollLeft) if scrollLeft?
computeHorizontalScrollbarHeight: ->
contentWidth = @computeContentWidth()
contentHeight = @computeContentHeight()
clientWidthWithoutVerticalScrollbar = @contentFrameWidth
clientWidthWithVerticalScrollbar = clientWidthWithoutVerticalScrollbar - @verticalScrollbarWidth
clientHeightWithoutHorizontalScrollbar = @computeHeight()
clientHeightWithHorizontalScrollbar = clientHeightWithoutHorizontalScrollbar - @horizontalScrollbarHeight
horizontalScrollbarVisible =
contentWidth > clientWidthWithoutVerticalScrollbar or
contentWidth > clientWidthWithVerticalScrollbar and contentHeight > clientHeightWithoutHorizontalScrollbar
if horizontalScrollbarVisible
@horizontalScrollbarHeight
else
0
computeVerticalScrollbarWidth: ->
contentWidth = @computeContentWidth()
contentHeight = @computeContentHeight()
clientWidthWithoutVerticalScrollbar = @contentFrameWidth
clientWidthWithVerticalScrollbar = clientWidthWithoutVerticalScrollbar - @verticalScrollbarWidth
clientHeightWithoutHorizontalScrollbar = @computeHeight()
clientHeightWithHorizontalScrollbar = clientHeightWithoutHorizontalScrollbar - @horizontalScrollbarHeight
verticalScrollbarVisible =
contentHeight > clientHeightWithoutHorizontalScrollbar or
contentHeight > clientHeightWithHorizontalScrollbar and contentWidth > clientWidthWithoutVerticalScrollbar
if verticalScrollbarVisible
@verticalScrollbarWidth
else
0
lineDecorationClassesForRow: (row) ->
return null if @model.isMini()
decorationClasses = null
for id, decoration of @lineDecorationsByScreenRow[row]
decorationClasses ?= []
decorationClasses.push(decoration.getProperties().class)
decorationClasses
lineNumberDecorationClassesForRow: (row) ->
return null if @model.isMini()
decorationClasses = null
for id, decoration of @lineNumberDecorationsByScreenRow[row]
decorationClasses ?= []
decorationClasses.push(decoration.getProperties().class)
decorationClasses
getCursorBlinkPeriod: -> @cursorBlinkPeriod
getCursorBlinkResumeDelay: -> @cursorBlinkResumeDelay
hasRequiredMeasurements: ->
@lineHeight? and
@baseCharacterWidth? and
@scrollTop? and
@contentFrameWidth? and
@scrollLeft? and
@verticalScrollbarWidth? and
@horizontalScrollbarHeight?
setScrollTop: (scrollTop) ->
scrollTop = @constrainScrollTop(scrollTop)
unless @scrollTop is scrollTop
@scrollTop = scrollTop
@model.setScrollTop(scrollTop)
@didStartScrolling()
@updateVerticalScrollState()
@updateDecorations()
@updateLinesState()
@updateCursorsState()
@updateLineNumbersState()
didStartScrolling: ->
if @stoppedScrollingTimeoutId?
clearTimeout(@stoppedScrollingTimeoutId)
@stoppedScrollingTimeoutId = null
@stoppedScrollingTimeoutId = setTimeout(@didStopScrolling.bind(this), @stoppedScrollingDelay)
@state.content.scrollingVertically = true
@emitter.emit 'did-update-state'
didStopScrolling: ->
@state.content.scrollingVertically = false
if @mouseWheelScreenRow?
@mouseWheelScreenRow = null
@updateLinesState()
@updateLineNumbersState()
else
@emitter.emit 'did-update-state'
setScrollLeft: (scrollLeft) ->
scrollLeft = @constrainScrollLeft(scrollLeft)
unless @scrollLeft is scrollLeft
@scrollLeft = scrollLeft
@model.setScrollLeft(scrollLeft)
@updateHorizontalScrollState()
setHorizontalScrollbarHeight: (horizontalScrollbarHeight) ->
unless @horizontalScrollbarHeight is horizontalScrollbarHeight
@horizontalScrollbarHeight = horizontalScrollbarHeight
@model.setHorizontalScrollbarHeight(horizontalScrollbarHeight)
@updateScrollbarsState()
@updateVerticalScrollState()
setVerticalScrollbarWidth: (verticalScrollbarWidth) ->
unless @verticalScrollbarWidth is verticalScrollbarWidth
@verticalScrollbarWidth = verticalScrollbarWidth
@model.setVerticalScrollbarWidth(verticalScrollbarWidth)
@updateScrollbarsState()
@updateHorizontalScrollState()
setAutoHeight: (autoHeight) ->
unless @autoHeight is autoHeight
@autoHeight = autoHeight
@updateHeightState()
setExplicitHeight: (explicitHeight) ->
unless @explicitHeight is explicitHeight
@explicitHeight = explicitHeight
@model.setHeight(explicitHeight)
@updateVerticalScrollState()
@updateScrollbarsState()
@updateDecorations()
@updateLinesState()
@updateCursorsState()
@updateLineNumbersState()
computeHeight: ->
@explicitHeight ? @computeContentHeight()
setContentFrameWidth: (contentFrameWidth) ->
unless @contentFrameWidth is contentFrameWidth
@contentFrameWidth = contentFrameWidth
@model.setWidth(contentFrameWidth)
@updateVerticalScrollState()
@updateHorizontalScrollState()
@updateScrollbarsState()
@updateContentState()
@updateDecorations()
@updateLinesState()
setBackgroundColor: (backgroundColor) ->
unless @backgroundColor is backgroundColor
@backgroundColor = backgroundColor
@updateContentState()
setGutterBackgroundColor: (gutterBackgroundColor) ->
unless @gutterBackgroundColor is gutterBackgroundColor
@gutterBackgroundColor = gutterBackgroundColor
@updateGutterState()
setLineHeight: (lineHeight) ->
unless @lineHeight is lineHeight
@lineHeight = lineHeight
@updateHeightState()
@updateVerticalScrollState()
@updateDecorations()
@updateLinesState()
@updateCursorsState()
@updateLineNumbersState()
@updateOverlaysState()
setMouseWheelScreenRow: (mouseWheelScreenRow) ->
unless @mouseWheelScreenRow is mouseWheelScreenRow
@mouseWheelScreenRow = mouseWheelScreenRow
@didStartScrolling()
setBaseCharacterWidth: (baseCharacterWidth) ->
unless @baseCharacterWidth is baseCharacterWidth
@baseCharacterWidth = baseCharacterWidth
@model.setDefaultCharWidth(baseCharacterWidth)
@characterWidthsChanged()
getScopedCharWidth: (scopeNames, char) ->
@getScopedCharWidths(scopeNames)[char]
getScopedCharWidths: (scopeNames) ->
scope = @charWidthsByScope
for scopeName in scopeNames
scope[scopeName] ?= {}
scope = scope[scopeName]
scope.charWidths ?= {}
scope.charWidths
batchCharacterMeasurement: (fn) ->
oldChangeCount = @scopedCharacterWidthsChangeCount
@batchingCharacterMeasurement = true
fn()
@batchingCharacterMeasurement = false
@characterWidthsChanged() if oldChangeCount isnt @scopedCharacterWidthsChangeCount
setScopedCharWidth: (scopeNames, char, width) ->
@getScopedCharWidths(scopeNames)[char] = width
@scopedCharacterWidthsChangeCount++
@characterWidthsChanged() unless @batchingCharacterMeasurement
characterWidthsChanged: ->
@updateHorizontalScrollState()
@updateContentState()
@updateDecorations()
@updateLinesState()
@updateCursorsState()
@updateOverlaysState()
clearScopedCharWidths: ->
@charWidthsByScope = {}
pixelPositionForScreenPosition: (screenPosition, clip=true) ->
screenPosition = Point.fromObject(screenPosition)
screenPosition = @model.clipScreenPosition(screenPosition) if clip
targetRow = screenPosition.row
targetColumn = screenPosition.column
baseCharacterWidth = @baseCharacterWidth
top = targetRow * @lineHeight
left = 0
column = 0
for token in @model.tokenizedLineForScreenRow(targetRow).tokens
charWidths = @getScopedCharWidths(token.scopes)
valueIndex = 0
while valueIndex < token.value.length
if token.hasPairedCharacter
char = token.value.substr(valueIndex, 2)
charLength = 2
valueIndex += 2
else
char = token.value[valueIndex]
charLength = 1
valueIndex++
return {top, left} if column is targetColumn
left += charWidths[char] ? baseCharacterWidth unless char is '\0'
column += charLength
{top, left}
pixelRectForScreenRange: (screenRange) ->
if screenRange.end.row > screenRange.start.row
top = @pixelPositionForScreenPosition(screenRange.start).top
left = 0
height = (screenRange.end.row - screenRange.start.row + 1) * @lineHeight
width = @computeScrollWidth()
else
{top, left} = @pixelPositionForScreenPosition(screenRange.start, false)
height = @lineHeight
width = @pixelPositionForScreenPosition(screenRange.end, false).left - left
{top, left, width, height}
observeDecoration: (decoration) ->
decorationDisposables = new CompositeDisposable
decorationDisposables.add decoration.getMarker().onDidChange(@decorationMarkerDidChange.bind(this, decoration))
if decoration.isType('highlight')
decorationDisposables.add decoration.onDidChangeProperties(@updateHighlightState.bind(this, decoration))
decorationDisposables.add decoration.onDidFlash(@highlightDidFlash.bind(this, decoration))
decorationDisposables.add decoration.onDidDestroy =>
@disposables.remove(decorationDisposables)
decorationDisposables.dispose()
@didDestroyDecoration(decoration)
@disposables.add(decorationDisposables)
decorationMarkerDidChange: (decoration, change) ->
if decoration.isType('line') or decoration.isType('line-number')
intersectsVisibleRowRange = false
startRow = @computeStartRow()
endRow = @computeEndRow()
oldRange = new Range(change.oldTailScreenPosition, change.oldHeadScreenPosition)
newRange = new Range(change.newTailScreenPosition, change.newHeadScreenPosition)
if oldRange.intersectsRowRange(startRow, endRow - 1)
@removeFromLineDecorationCaches(decoration, oldRange)
intersectsVisibleRowRange = true
if newRange.intersectsRowRange(startRow, endRow - 1)
@addToLineDecorationCaches(decoration, newRange)
intersectsVisibleRowRange = true
if intersectsVisibleRowRange
@updateLinesState() if decoration.isType('line')
@updateLineNumbersState() if decoration.isType('line-number')
if decoration.isType('highlight')
@updateHighlightState(decoration)
if decoration.isType('overlay')
@updateOverlaysState()
didDestroyDecoration: (decoration) ->
if decoration.isType('line') or decoration.isType('line-number')
@removeFromLineDecorationCaches(decoration, decoration.getMarker().getScreenRange())
@updateLinesState() if decoration.isType('line')
@updateLineNumbersState() if decoration.isType('line-number')
if decoration.isType('highlight')
@updateHighlightState(decoration)
if decoration.isType('overlay')
@updateOverlaysState()
highlightDidFlash: (decoration) ->
flash = decoration.consumeNextFlash()
if decorationState = @state.content.highlights[decoration.id]
decorationState.flashCount++
decorationState.flashClass = flash.class
decorationState.flashDuration = flash.duration
@emitter.emit "did-update-state"
didAddDecoration: (decoration) ->
@observeDecoration(decoration)
if decoration.isType('line') or decoration.isType('line-number')
@addToLineDecorationCaches(decoration, decoration.getMarker().getScreenRange())
@updateLinesState() if decoration.isType('line')
@updateLineNumbersState() if decoration.isType('line-number')
else if decoration.isType('highlight')
@updateHighlightState(decoration)
else if decoration.isType('overlay')
@updateOverlaysState()
updateDecorations: ->
@lineDecorationsByScreenRow = {}
@lineNumberDecorationsByScreenRow = {}
@highlightDecorationsById = {}
visibleHighlights = {}
startRow = @computeStartRow()
endRow = @computeEndRow()
return unless 0 <= startRow <= endRow <= Infinity
for markerId, decorations of @model.decorationsForScreenRowRange(startRow, endRow - 1)
range = @model.getMarker(markerId).getScreenRange()
for decoration in decorations
if decoration.isType('line') or decoration.isType('line-number')
@addToLineDecorationCaches(decoration, range)
else if decoration.isType('highlight')
visibleHighlights[decoration.id] = @updateHighlightState(decoration)
for id of @state.content.highlights
unless visibleHighlights[id]
delete @state.content.highlights[id]
@emitter.emit 'did-update-state'
removeFromLineDecorationCaches: (decoration, range) ->
for row in [range.start.row..range.end.row] by 1
delete @lineDecorationsByScreenRow[row]?[decoration.id]
delete @lineNumberDecorationsByScreenRow[row]?[decoration.id]
addToLineDecorationCaches: (decoration, range) ->
marker = decoration.getMarker()
properties = decoration.getProperties()
return unless marker.isValid()
if range.isEmpty()
return if properties.onlyNonEmpty
else
return if properties.onlyEmpty
omitLastRow = range.end.column is 0
for row in [range.start.row..range.end.row] by 1
continue if properties.onlyHead and row isnt marker.getHeadScreenPosition().row
continue if omitLastRow and row is range.end.row
if decoration.isType('line')
@lineDecorationsByScreenRow[row] ?= {}
@lineDecorationsByScreenRow[row][decoration.id] = decoration
if decoration.isType('line-number')
@lineNumberDecorationsByScreenRow[row] ?= {}
@lineNumberDecorationsByScreenRow[row][decoration.id] = decoration
updateHighlightState: (decoration) ->
return unless @hasRequiredMeasurements()
startRow = @computeStartRow()
endRow = @computeEndRow()
properties = decoration.getProperties()
marker = decoration.getMarker()
range = marker.getScreenRange()
if decoration.isDestroyed() or not marker.isValid() or range.isEmpty() or not range.intersectsRowRange(startRow, endRow - 1)
delete @state.content.highlights[decoration.id]
@emitter.emit 'did-update-state'
return
if range.start.row < startRow
range.start.row = startRow
range.start.column = 0
if range.end.row >= endRow
range.end.row = endRow
range.end.column = 0
if range.isEmpty()
delete @state.content.highlights[decoration.id]
@emitter.emit 'did-update-state'
return
highlightState = @state.content.highlights[decoration.id] ?= {
flashCount: 0
flashDuration: null
flashClass: null
}
highlightState.class = properties.class
highlightState.deprecatedRegionClass = properties.deprecatedRegionClass
highlightState.regions = @buildHighlightRegions(range)
@emitter.emit 'did-update-state'
true
observeCursor: (cursor) ->
didChangePositionDisposable = cursor.onDidChangePosition =>
@pauseCursorBlinking()
@updateCursorsState()
didChangeVisibilityDisposable = cursor.onDidChangeVisibility(@updateCursorsState.bind(this))
didDestroyDisposable = cursor.onDidDestroy =>
@disposables.remove(didChangePositionDisposable)
@disposables.remove(didChangeVisibilityDisposable)
@disposables.remove(didDestroyDisposable)
@updateCursorsState()
@disposables.add(didChangePositionDisposable)
@disposables.add(didChangeVisibilityDisposable)
@disposables.add(didDestroyDisposable)
didAddCursor: (cursor) ->
@observeCursor(cursor)
@pauseCursorBlinking()
@updateCursorsState()
startBlinkingCursors: ->
@toggleCursorBlinkHandle = setInterval(@toggleCursorBlink.bind(this), @getCursorBlinkPeriod() / 2)
stopBlinkingCursors: ->
clearInterval(@toggleCursorBlinkHandle)
toggleCursorBlink: ->
@state.content.blinkCursorsOff = not @state.content.blinkCursorsOff
@emitter.emit 'did-update-state'
pauseCursorBlinking: ->
@state.content.blinkCursorsOff = false
@stopBlinkingCursors()
@startBlinkingCursorsAfterDelay ?= _.debounce(@startBlinkingCursors, @getCursorBlinkResumeDelay())
@startBlinkingCursorsAfterDelay()
@emitter.emit 'did-update-state'

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