Skip to content
This repository has been archived by the owner on Mar 3, 2023. It is now read-only.

Implement text editor DOM updates manually instead of via React #5624

Merged
merged 40 commits into from Feb 20, 2015

Conversation

nathansobo
Copy link
Contributor

React is an amazing abstraction, but very few abstractions come without at least some overhead. In the case of Atom's text editor, it's worth the effort to avoid this overhead by hand coding all DOM updates. The update code is repetitive, but it's also pretty straightforward. In exchange, we get much simpler profile traces, drop a big dependency, and reduce the overall number of moving parts in Atom. Below are some flame graphs for entering a character at the end of a syntax-highlighted line. The comparison is somewhat unfair because the React case could have been a bit more optimized, but I think the pictures also show how much simpler the manual approach is in our case.

screenshot_2015-02-18_15_03_39

screenshot_2015-02-18_15_01_42

React is a great tool for many cases, for our particular needs in this particular case I decided it would be easier to just do things for ourselves.

Remaining Tasks:

  • Don't use React for EditorComponent
  • Fix autocomplete spec.
  • Unify DOM updates / polling across editors.

@benogle
Copy link
Contributor

benogle commented Feb 18, 2015

Legit

@nathansobo
Copy link
Contributor Author

I'm going to keep the basic structure of the code with the TextEditorComponent separate from the TextEditorElement the same for now. It might be nice to clean it up but it's not really a huge deal and I think time could be spent better in other ways. Someday I would love to get to a more minimal DOM structure as well, but it doesn't seem worth the risk of breaking anything to lose a couple extra nodes.

Nathan Sobo added 27 commits February 19, 2015 17:14
Also, remove ability to disable hardware acceleration since there’s
no longer a need for it and it complicated this conversion.
Signed-off-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
Signed-off-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
Signed-off-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
Signed-off-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
@lee-dohm
Copy link
Contributor

@nathansobo I've been working with it for the past hour. I haven't noticed any issues. But it does seem a bit more responsive 😀

@thomasjo
Copy link
Contributor

Been using this all day and haven't noticed any regressions. The constant console logging is very annoying though, hehe 👼

@thomasjo
Copy link
Contributor

I've not really noticed any major performance issues in the past few months, so hard to tell whether the speedup is noticeable or not on my machine, but things do seem a bit more responsive. Could be confirmation bias though.

@postcasio
Copy link
Contributor

Same here. No issues so far, playing well with my other packages. Scrolling is silky smooth!

@as-cii
Copy link
Contributor

as-cii commented Feb 20, 2015

Same here, it has been working fine so far. Great job, @nathansobo! 👍

The blinking cursor was ensuring that we never polled in certain cases.
We need to allow the interval to continue polling at a normal pace, but
just avoid doing any work that could delay the next animation frame.
@nathansobo
Copy link
Contributor Author

Thanks to all of you for your time! @thomasjo thanks for pointing out the console logging. I accidentally left it in on the last commit and just force-pushed an amendment.

nathansobo pushed a commit that referenced this pull request Feb 20, 2015
WIP: Implement text editor DOM updates manually instead of via React
@nathansobo nathansobo merged commit f4116c7 into master Feb 20, 2015
@nathansobo nathansobo deleted the ns-manual-dom-updates branch February 20, 2015 16:52
@kevinsawicki
Copy link
Contributor

📈 💚 ♻️

@lee-dohm
Copy link
Contributor

👏 Awesome work!

@nathansobo
Copy link
Contributor Author

I force pushed back the merge of this PR so we can get a release out this morning without me having to worry about this code over the weekend. Will merge it again once the release is out.

@performDocumentPollAfterUpdate = true
else
@performDocumentPollAfterUpdate = false
poller() for poller in @documentPollers
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might want to return after this line to prevent array return.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 Very good catch.

@nathansobo nathansobo restored the ns-manual-dom-updates branch February 20, 2015 19:22
@nathansobo
Copy link
Contributor Author

Okay, merged in 9648093. Sorry for the false 🚨.

@nathansobo nathansobo changed the title WIP: Implement text editor DOM updates manually instead of via React Implement text editor DOM updates manually instead of via React Feb 25, 2015
@hemanth
Copy link

hemanth commented Mar 1, 2015

Interesting stats, but was curious about why was react opted before...

@nathansobo
Copy link
Contributor Author

@hemanth There was also a huge improvement over the first version of the editor when we added React. It was much like scaffolding.

@hemanth
Copy link

hemanth commented Mar 1, 2015

👍

@mcharytoniuk
Copy link

Why didn't you use React's PureRenderMixin (http://facebook.github.io/react/docs/pure-render-mixin.html), 'shouldComponentUpdate' custom implementation, batched updates and other optimisations that would produce better results than manual updates and abandoned the entire solution rapidly instead?

@kl3ryk
Copy link

kl3ryk commented Mar 1, 2015

@mcharytoniuk +1

@benogle
Copy link
Contributor

benogle commented Mar 2, 2015

@mcharytoniuk The old implementation was doing the same thing as described in the doc page:

Under the hood, the mixin implements shouldComponentUpdate, in which it compares the current props and state with the next ones and returns false if the equalities pass.

See

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'
)
{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

@mcharytoniuk
Copy link

I know that maybe it's too late for such ideas but I'd try moving away at least cursor component out of lines component and position it absolutely on top of it to prevent lines component updates caused by cursor movement. Also, Flipboard published react-canvas recently. Did you ever consider using canvas for animations / some editor elements as they did? Many people criticised them for negative SEO impact but inside text editor every crazy trick is acceptable imo. :)

@nathansobo
Copy link
Contributor Author

A big selling-point of Atom is the ability to style content with CSS. Used properly, the DOM doesn't introduce prohibitive overhead for our use case.

React being removed is purely an implementation detail in this case and shouldn't affect package authors in any way. Appreciate your concern and suggestions here, but on balance it's easier to achieve our goals without it in this case.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

10 participants