diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index cf6d36a6b..86d60dc0f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -38,7 +38,7 @@ For more information on how to work with Atom's official packages, see [JavaScript](https://github.com/styleguide/javascript), and [CSS](https://github.com/styleguide/css) styleguides. * Include thoughtfully-worded, well-structured - [Jasmine](http://pivotal.github.com/jasmine) specs. + [Jasmine](http://jasmine.github.io/) specs. * Document new code based on the [Documentation Styleguide](#documentation-styleguide) * End files with a newline. diff --git a/apm/package.json b/apm/package.json index 77a4ed210..372ef16b8 100644 --- a/apm/package.json +++ b/apm/package.json @@ -6,6 +6,6 @@ "url": "https://github.com/atom/atom.git" }, "dependencies": { - "atom-package-manager": "0.56.0" + "atom-package-manager": "0.57.0" } } diff --git a/build/tasks/docs-task.coffee b/build/tasks/docs-task.coffee index fdc49ad68..e60ea72cb 100644 --- a/build/tasks/docs-task.coffee +++ b/build/tasks/docs-task.coffee @@ -143,7 +143,7 @@ downloadFileFromRepo = ({repo, file}, callback) -> downloadIncludes = (callback) -> includes = [ - {repo: 'atom-keymap', file: 'src/keymap.coffee'} + {repo: 'atom-keymap', file: 'src/keymap-manager.coffee'} {repo: 'atom-keymap', file: 'src/key-binding.coffee'} {repo: 'first-mate', file: 'src/grammar.coffee'} {repo: 'first-mate', file: 'src/grammar-registry.coffee'} diff --git a/docs/build-instructions/linux.md b/docs/build-instructions/linux.md index 259ef71d5..8c36adeb1 100644 --- a/docs/build-instructions/linux.md +++ b/docs/build-instructions/linux.md @@ -10,6 +10,7 @@ Ubuntu LTS 12.04 64-bit is the recommended platform. * libgnome-keyring-dev `sudo apt-get install libgnome-keyring-dev` (refer to your distribution's manual on how to install packages if you are not on Debian or Ubuntu-based systems) * `npm config set python /usr/bin/python2 -g` to ensure that gyp uses Python 2 + ## Instructions ```sh @@ -20,4 +21,29 @@ Ubuntu LTS 12.04 64-bit is the recommended platform. script/grunt mkdeb # Generates a .deb package at $TMPDIR/atom-build ``` + ## Troubleshooting + + +### Exception: "TypeError: Unable to watch path" + +If you get following error with a big traceback right after Atom starts: + + ``` + TypeError: Unable to watch path + ``` + +you have to increase number of watched files by inotify. For testing if +this is the reason for this error you can issue + + ```sh + sudo sysctl fs.inotify.max_user_watches=32768 + ``` + +and restart Atom. If Atom now works fine, you can make this setting permanent: + + ```sh + echo 32768 > /proc/sys/fs/inotify/max_user_watches + ``` + +See also https://github.com/atom/atom/issues/2082. diff --git a/docs/build-instructions/windows.md b/docs/build-instructions/windows.md index 8652a291e..8d9a65c88 100644 --- a/docs/build-instructions/windows.md +++ b/docs/build-instructions/windows.md @@ -3,7 +3,7 @@ ## Requirements * Windows 7 or later - * [Visual C++ 2010 SP1 Express](http://www.visualstudio.com/en-us/downloads/download-visual-studio-vs#DownloadFamilies_4) + * [Visual C++ 2010 Express](http://www.visualstudio.com/en-us/downloads/download-visual-studio-vs#DownloadFamilies_4) with [SP1](http://www.microsoft.com/en-us/download/details.aspx?id=23691) * [node.js](http://nodejs.org/download/) v0.10.x * [Python](http://www.python.org/download/) v2.7.x * [GitHub for Windows](http://windows.github.com/) @@ -16,7 +16,7 @@ cd C:\ git clone https://github.com/atom/atom/ cd atom - script\build + script/build ``` ## Why do I have to use GitHub for Windows? diff --git a/docs/index.md b/docs/index.md index 246675cd7..c513f1c20 100644 --- a/docs/index.md +++ b/docs/index.md @@ -5,6 +5,7 @@ * [Creating a Package](creating-a-package.md) * [Creating a Theme](creating-a-theme.md) * [Publishing a Package](publishing-a-package.md) +* [Writing Specs](writing-specs.md) * [Converting a TextMate Bundle](converting-a-text-mate-bundle.md) * [Converting a TextMate Theme](converting-a-text-mate-theme.md) * [Contributing](contributing.md) diff --git a/docs/writing-specs.md b/docs/writing-specs.md new file mode 100644 index 000000000..61899d599 --- /dev/null +++ b/docs/writing-specs.md @@ -0,0 +1,61 @@ +# Writting 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. + +## Create a new spec + +[Atom specs](https://github.com/atom/atom/tree/master/spec) and [package specs](https://github.com/atom/markdown-preview/tree/master/spec) are added to their respective `spec` directory. The example below creates a spec for Atom core. + +0. Create a spec file + + Spec files **must** end with `-spec` so add `sample-spec.coffee` to `atom/spec`. + +0. Add one or more `describe` method + + 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", -> + # contents + ``` + + or + + ```coffee + describe "Editor::moveUp", -> + # contents + ``` + +0. Add one or more `it` method + + The `it` method also takes two arugments, a description and a function. Try and make the description flow with the `it` method. For example, a description of `this should work` doesn't read well as `it this should work`. But a description of `should work` sounds great as `it should work`. + + ```coffee + describe "when a test is written", -> + it "has some expectations that should pass", -> + # Expectations + ``` + +0. Add one or more expectations + + The best way to learn about expectations is to read the [jamsine documentation](http://jasmine.github.io/2.0/introduction.html#section-Expectations) about them. Below is a simple example. + + ```coffee + describe "when a test is written", -> + it "has some expectations that should pass", -> + expect("apples").toEqual("apples") + expect("oranges").not.toEqual("apples") + ``` + +## Runnings specs + +Most of the time you'll want to run specs by triggering the `window:run-package-specs` command. This command is not only to run package specs, it is also for Atom core specs. This will run all the specs in the current project's spec directory. If you want to run the Atom core specs and **all** the default package specs trigger the `window:run-all-specs` command. + +To run a limited subset of specs use the `fdescribe` or `fit` methods. You can use those to focus a single spec or several specs. In the example above, focusing an individual spec looks like this: + +```coffee +describe "when a test is written", -> + fit "has some expectations that should pass", -> + expect("apples").toEqual("apples") + expect("oranges").not.toEqual("apples") +``` diff --git a/docs/your-first-package.md b/docs/your-first-package.md index 931380f51..4fc41d044 100644 --- a/docs/your-first-package.md +++ b/docs/your-first-package.md @@ -149,6 +149,8 @@ ASCII art professional! * [Getting your project on GitHub guide](http://guides.github.com/overviews/desktop) +* [Writing specs](writing-specs.md) for your package + * [Creating a package guide](creating-a-package.html) for more information on the mechanics of packages diff --git a/package.json b/package.json index 7c702f288..0ce7e07f9 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "atom", "productName": "Atom", - "version": "0.97.0", + "version": "0.98.0", "description": "A hackable text editor for the 21st Century.", "main": "./src/browser/main.js", "repository": { @@ -20,7 +20,7 @@ "atomShellVersion": "0.12.5", "dependencies": { "async": "0.2.6", - "atom-keymap": "^0.21.0", + "atom-keymap": "^0.22.0", "bootstrap": "git+https://github.com/atom/bootstrap.git#6af81906189f1747fd6c93479e3d998ebe041372", "clear-cut": "0.4.0", "coffee-script": "1.7.0", @@ -45,8 +45,8 @@ "property-accessors": "^1", "q": "^1.0.1", "random-words": "0.0.1", - "react": "^0.10.0", - "reactionary": "^0.9.0", + "react-atom-fork": "^0.10.0", + "reactionary-atom-fork": "^0.9.0", "runas": "^0.5", "scandal": "0.15.2", "scoped-property-store": "^0.9.0", @@ -54,7 +54,7 @@ "season": "^1.0.2", "semver": "1.1.4", "serializable": "^1", - "space-pen": "3.1.1", + "space-pen": "3.2.0", "temp": "0.5.0", "text-buffer": "^2.2.2", "theorist": "^1", @@ -85,7 +85,7 @@ "fuzzy-finder": "0.51.0", "git-diff": "0.28.0", "go-to-line": "0.21.0", - "grammar-selector": "0.26.0", + "grammar-selector": "0.27.0", "image-view": "0.33.0", "keybinding-resolver": "0.17.0", "link": "0.22.0", @@ -94,7 +94,7 @@ "open-on-github": "0.28.0", "package-generator": "0.30.0", "release-notes": "0.31.0", - "settings-view": "0.115.0", + "settings-view": "0.116.0", "snippets": "0.43.0", "spell-check": "0.35.0", "status-bar": "0.40.0", diff --git a/spec/display-buffer-spec.coffee b/spec/display-buffer-spec.coffee index 4abce359a..40c5f1384 100644 --- a/spec/display-buffer-spec.coffee +++ b/spec/display-buffer-spec.coffee @@ -948,7 +948,7 @@ describe "DisplayBuffer", -> it "returns the start and end positions of the marker based on the line height and character widths assigned to the DisplayBuffer", -> marker = displayBuffer.markScreenRange([[5, 10], [6, 4]]) - displayBuffer.setLineHeight(20) + displayBuffer.setLineHeightInPixels(20) displayBuffer.setDefaultCharWidth(10) displayBuffer.setScopedCharWidths(["source.js", "keyword.control.js"], r: 11, e: 11, t: 11, u: 11, n: 11) @@ -959,7 +959,7 @@ describe "DisplayBuffer", -> describe "::setScrollTop", -> beforeEach -> displayBuffer.manageScrollPosition = true - displayBuffer.setLineHeight(10) + displayBuffer.setLineHeightInPixels(10) it "disallows negative values", -> displayBuffer.setHeight(displayBuffer.getScrollHeight() + 100) @@ -979,7 +979,7 @@ describe "DisplayBuffer", -> describe "::setScrollLeft", -> beforeEach -> displayBuffer.manageScrollPosition = true - displayBuffer.setLineHeight(10) + displayBuffer.setLineHeightInPixels(10) displayBuffer.setDefaultCharWidth(10) it "disallows negative values", -> @@ -1000,7 +1000,7 @@ describe "DisplayBuffer", -> describe "::scrollToScreenPosition(position)", -> it "sets the scroll top and scroll left so the given screen position is in view", -> displayBuffer.manageScrollPosition = true - displayBuffer.setLineHeight(10) + displayBuffer.setLineHeightInPixels(10) displayBuffer.setDefaultCharWidth(10) displayBuffer.setHorizontalScrollbarHeight(0) diff --git a/spec/editor-component-spec.coffee b/spec/editor-component-spec.coffee index d6bddd95d..f3fbde79a 100644 --- a/spec/editor-component-spec.coffee +++ b/spec/editor-component-spec.coffee @@ -39,7 +39,7 @@ describe "EditorComponent", -> component.setLineHeight(1.3) component.setFontSize(20) - lineHeightInPixels = editor.getLineHeight() + lineHeightInPixels = editor.getLineHeightInPixels() charWidth = editor.getDefaultCharWidth() node = component.getDOMNode() verticalScrollbarNode = node.querySelector('.vertical-scrollbar') @@ -90,6 +90,43 @@ describe "EditorComponent", -> expect(component.lineNodeForScreenRow(3).offsetTop).toBe 3 * lineHeightInPixels expect(component.lineNodeForScreenRow(4).offsetTop).toBe 4 * lineHeightInPixels + it "updates the top position of lines when the line height changes", -> + initialLineHeightInPixels = editor.getLineHeightInPixels() + component.setLineHeight(2) + + newLineHeightInPixels = editor.getLineHeightInPixels() + expect(newLineHeightInPixels).not.toBe initialLineHeightInPixels + expect(component.lineNodeForScreenRow(1).offsetTop).toBe 1 * newLineHeightInPixels + + it "updates the top position of lines when the font size changes", -> + initialLineHeightInPixels = editor.getLineHeightInPixels() + component.setFontSize(10) + + newLineHeightInPixels = editor.getLineHeightInPixels() + 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", -> + # Can't find a font that changes the line height, but we think one might exist + linesComponent = component.refs.scrollView.refs.lines + spyOn(linesComponent, 'measureLineHeightInPixelsAndCharWidth').andCallFake -> editor.setLineHeightInPixels(10) + + initialLineHeightInPixels = editor.getLineHeightInPixels() + component.setFontFamily('sans-serif') + + expect(linesComponent.measureLineHeightInPixelsAndCharWidth).toHaveBeenCalled() + newLineHeightInPixels = editor.getLineHeightInPixels() + expect(newLineHeightInPixels).not.toBe initialLineHeightInPixels + expect(component.lineNodeForScreenRow(1).offsetTop).toBe 1 * newLineHeightInPixels + + it "renders the .lines div at the full height of the editor if there aren't enough lines to scroll vertically", -> + editor.setText('') + node.style.height = '300px' + component.measureHeightAndWidth() + + linesNode = node.querySelector('.lines') + expect(linesNode.offsetHeight).toBe 300 + describe "when showInvisibles is enabled", -> invisibles = null @@ -764,3 +801,24 @@ describe "EditorComponent", -> expect(editor.consolidateSelections).toHaveBeenCalled() expect(event.abortKeyBinding).toHaveBeenCalled() + + describe "hiding and showing the editor", -> + describe "when fontSize, fontFamily, or lineHeight changes while the editor is hidden", -> + it "does not attempt to measure the lineHeight and defaultCharWidth until the editor becomes visible again", -> + wrapperView.hide() + initialLineHeightInPixels = editor.getLineHeightInPixels() + initialCharWidth = editor.getDefaultCharWidth() + + component.setLineHeight(2) + expect(editor.getLineHeightInPixels()).toBe initialLineHeightInPixels + expect(editor.getDefaultCharWidth()).toBe initialCharWidth + component.setFontSize(22) + expect(editor.getLineHeightInPixels()).toBe initialLineHeightInPixels + expect(editor.getDefaultCharWidth()).toBe initialCharWidth + component.setFontFamily('monospace') + expect(editor.getLineHeightInPixels()).toBe initialLineHeightInPixels + expect(editor.getDefaultCharWidth()).toBe initialCharWidth + + wrapperView.show() + expect(editor.getLineHeightInPixels()).not.toBe initialLineHeightInPixels + expect(editor.getDefaultCharWidth()).not.toBe initialCharWidth diff --git a/spec/editor-spec.coffee b/spec/editor-spec.coffee index 277effef6..f037ac6af 100644 --- a/spec/editor-spec.coffee +++ b/spec/editor-spec.coffee @@ -728,7 +728,7 @@ describe "Editor", -> editor.manageScrollPosition = true editor.setVerticalScrollMargin(2) editor.setHorizontalScrollMargin(2) - editor.setLineHeight(10) + editor.setLineHeightInPixels(10) editor.setDefaultCharWidth(10) editor.setHorizontalScrollbarHeight(0) editor.setHeight(5.5 * 10) @@ -1167,7 +1167,7 @@ describe "Editor", -> describe "when the 'autoscroll' option is true", -> it "autoscrolls to the selection", -> editor.manageScrollPosition = true - editor.setLineHeight(10) + editor.setLineHeightInPixels(10) editor.setDefaultCharWidth(10) editor.setHeight(50) editor.setWidth(50) @@ -3155,7 +3155,7 @@ describe "Editor", -> describe ".scrollToCursorPosition()", -> it "scrolls the last cursor into view", -> editor.setCursorScreenPosition([8, 8]) - editor.setLineHeight(10) + editor.setLineHeightInPixels(10) editor.setDefaultCharWidth(10) editor.setHeight(50) editor.setWidth(50) @@ -3171,7 +3171,7 @@ describe "Editor", -> it "scrolls one screen height up or down", -> editor.manageScrollPosition = true - editor.setLineHeight(10) + editor.setLineHeightInPixels(10) editor.setHeight(50) expect(editor.getScrollHeight()).toBe 130 diff --git a/src/atom.coffee b/src/atom.coffee index 0a1783df4..bed5657e4 100644 --- a/src/atom.coffee +++ b/src/atom.coffee @@ -24,7 +24,7 @@ WindowEventHandler = require './window-event-handler' # * `atom.config` - A {Config} instance # * `atom.contextMenu` - A {ContextMenuManager} instance # * `atom.deserializers` - A {DeserializerManager} instance -# * `atom.keymaps` - A {Keymap} instance +# * `atom.keymaps` - A {KeymapManager} instance # * `atom.menu` - A {MenuManager} instance # * `atom.packages` - A {PackageManager} instance # * `atom.project` - A {Project} instance @@ -38,8 +38,8 @@ class Atom extends Model # Public: Load or create the Atom environment in the given mode. # - # - mode: Pass 'editor' or 'spec' depending on the kind of environment you - # want to build. + # mode - Pass 'editor' or 'spec' depending on the kind of environment you + # want to build. # # Returns an Atom instance, fully initialized @loadOrCreate: (mode) -> @@ -212,11 +212,11 @@ class Atom extends Model # in the dimensions parameter. If x or y are omitted the window will be # centered. If height or width are omitted only the position will be changed. # - # * dimensions: - # + x: The new x coordinate. - # + y: The new y coordinate. - # + width: The new width. - # + height: The new height. + # dimensions - An {Object} with the following keys: + # :x - The new x coordinate. + # :y - The new y coordinate. + # :width - The new width. + # :height - The new height. setWindowDimensions: ({x, y, width, height}) -> if width? and height? @setSize(width, height) diff --git a/src/cursor-component.coffee b/src/cursor-component.coffee index 36ba33a08..902be9d28 100644 --- a/src/cursor-component.coffee +++ b/src/cursor-component.coffee @@ -1,5 +1,5 @@ -React = require 'react' -{div} = require 'reactionary' +React = require 'react-atom-fork' +{div} = require 'reactionary-atom-fork' module.exports = CursorComponent = React.createClass diff --git a/src/cursors-component.coffee b/src/cursors-component.coffee index bb43287ba..4d3e14deb 100644 --- a/src/cursors-component.coffee +++ b/src/cursors-component.coffee @@ -1,5 +1,5 @@ -React = require 'react' -{div} = require 'reactionary' +React = require 'react-atom-fork' +{div} = require 'reactionary-atom-fork' {debounce, toArray} = require 'underscore-plus' SubscriberMixin = require './subscriber-mixin' CursorComponent = require './cursor-component' diff --git a/src/display-buffer.coffee b/src/display-buffer.coffee index 5c6a05d1e..1c753e24d 100644 --- a/src/display-buffer.coffee +++ b/src/display-buffer.coffee @@ -23,7 +23,7 @@ class DisplayBuffer extends Model manageScrollPosition: false softWrap: null editorWidthInChars: null - lineHeight: null + lineHeightInPixels: null defaultCharWidth: null height: null width: null @@ -198,8 +198,8 @@ class DisplayBuffer extends Model @setScrollLeft(scrollRight - @width) @getScrollRight() - getLineHeight: -> @lineHeight - setLineHeight: (@lineHeight) -> @lineHeight + getLineHeightInPixels: -> @lineHeightInPixels + setLineHeightInPixels: (@lineHeightInPixels) -> @lineHeightInPixels getDefaultCharWidth: -> @defaultCharWidth setDefaultCharWidth: (@defaultCharWidth) -> @defaultCharWidth @@ -227,20 +227,20 @@ class DisplayBuffer extends Model @charWidthsByScope = {} getScrollHeight: -> - unless @getLineHeight() > 0 - throw new Error("You must assign lineHeight before calling ::getScrollHeight()") + unless @getLineHeightInPixels() > 0 + throw new Error("You must assign lineHeightInPixels before calling ::getScrollHeight()") - @getLineCount() * @getLineHeight() + @getLineCount() * @getLineHeightInPixels() getScrollWidth: -> (@getMaxLineLength() * @getDefaultCharWidth()) + @getCursorWidth() getVisibleRowRange: -> - unless @getLineHeight() > 0 - throw new Error("You must assign a non-zero lineHeight before calling ::getVisibleRowRange()") + unless @getLineHeightInPixels() > 0 + throw new Error("You must assign a non-zero lineHeightInPixels before calling ::getVisibleRowRange()") - heightInLines = Math.ceil(@getHeight() / @getLineHeight()) + 1 - startRow = Math.floor(@getScrollTop() / @getLineHeight()) + heightInLines = Math.ceil(@getHeight() / @getLineHeightInPixels()) + 1 + startRow = Math.floor(@getScrollTop() / @getLineHeightInPixels()) endRow = Math.min(@getLineCount(), startRow + heightInLines) [startRow, endRow] @@ -254,7 +254,7 @@ class DisplayBuffer extends Model @intersectsVisibleRowRange(start.row, end.row + 1) scrollToScreenRange: (screenRange) -> - verticalScrollMarginInPixels = @getVerticalScrollMargin() * @getLineHeight() + verticalScrollMarginInPixels = @getVerticalScrollMargin() * @getLineHeightInPixels() horizontalScrollMarginInPixels = @getHorizontalScrollMargin() * @getDefaultCharWidth() {top, left, height, width} = @pixelRectForScreenRange(screenRange) @@ -285,11 +285,11 @@ class DisplayBuffer extends Model if screenRange.end.row > screenRange.start.row top = @pixelPositionForScreenPosition(screenRange.start).top left = 0 - height = (screenRange.end.row - screenRange.start.row + 1) * @getLineHeight() + height = (screenRange.end.row - screenRange.start.row + 1) * @getLineHeightInPixels() width = @getScrollWidth() else {top, left} = @pixelPositionForScreenPosition(screenRange.start) - height = @getLineHeight() + height = @getLineHeightInPixels() width = @pixelPositionForScreenPosition(screenRange.end).left - left {top, left, width, height} @@ -512,7 +512,7 @@ class DisplayBuffer extends Model targetColumn = screenPosition.column defaultCharWidth = @defaultCharWidth - top = targetRow * @lineHeight + top = targetRow * @lineHeightInPixels left = 0 column = 0 for token in @lineForRow(targetRow).tokens @@ -527,7 +527,7 @@ class DisplayBuffer extends Model targetTop = pixelPosition.top targetLeft = pixelPosition.left defaultCharWidth = @defaultCharWidth - row = Math.floor(targetTop / @getLineHeight()) + row = Math.floor(targetTop / @getLineHeightInPixels()) row = Math.min(row, @getLastRow()) row = Math.max(0, row) diff --git a/src/editor-component.coffee b/src/editor-component.coffee index cdc615d2b..14cca3dea 100644 --- a/src/editor-component.coffee +++ b/src/editor-component.coffee @@ -1,5 +1,5 @@ -React = require 'react' -{div, span} = require 'reactionary' +React = require 'react-atom-fork' +{div, span} = require 'reactionary-atom-fork' {debounce, defaults} = require 'underscore-plus' scrollbarStyle = require 'scrollbar-style' @@ -31,7 +31,7 @@ EditorComponent = React.createClass mouseWheelScreenRow: null render: -> - {focused, fontSize, lineHeight, fontFamily, showIndentGuide, showInvisibles} = @state + {focused, fontSize, lineHeight, fontFamily, showIndentGuide, showInvisibles, visible} = @state {editor, cursorBlinkPeriod, cursorBlinkResumeDelay} = @props maxLineNumberDigits = editor.getScreenLineCount().toString().length invisibles = if showInvisibles then @state.invisibles else {} @@ -42,7 +42,8 @@ EditorComponent = React.createClass scrollWidth = editor.getScrollWidth() scrollTop = editor.getScrollTop() scrollLeft = editor.getScrollLeft() - lineHeightInPixels = editor.getLineHeight() + lineHeightInPixels = editor.getLineHeightInPixels() + scrollViewHeight = editor.getHeight() horizontalScrollbarHeight = editor.getHorizontalScrollbarHeight() verticalScrollbarWidth = editor.getVerticalScrollbarWidth() verticallyScrollable = editor.verticallyScrollable() @@ -54,17 +55,17 @@ EditorComponent = React.createClass div className: className, style: {fontSize, lineHeight, fontFamily}, tabIndex: -1, GutterComponent { ref: 'gutter', editor, renderedRowRange, maxLineNumberDigits, - scrollTop, scrollHeight, lineHeight: lineHeightInPixels, fontSize, fontFamily, + scrollTop, scrollHeight, lineHeight, lineHeightInPixels, fontSize, fontFamily, @pendingChanges, onWidthChanged: @onGutterWidthChanged, @mouseWheelScreenRow } EditorScrollViewComponent { ref: 'scrollView', editor, fontSize, fontFamily, showIndentGuide, - lineHeight: lineHeightInPixels, renderedRowRange, @pendingChanges, + lineHeight, lineHeightInPixels, renderedRowRange, @pendingChanges, scrollTop, scrollLeft, scrollHeight, scrollWidth, @scrollingVertically, @cursorsMoved, @selectionChanged, @selectionAdded, cursorBlinkPeriod, cursorBlinkResumeDelay, @onInputFocused, @onInputBlurred, @mouseWheelScreenRow, - invisibles + invisibles, visible, scrollViewHeight } ScrollbarComponent @@ -106,7 +107,8 @@ EditorComponent = React.createClass renderedEndRow = Math.min(editor.getScreenLineCount(), visibleEndRow + lineOverdrawMargin) [renderedStartRow, renderedEndRow] - getInitialState: -> {} + getInitialState: -> + visible: true getDefaultProps: -> cursorBlinkPeriod: 800 @@ -157,7 +159,7 @@ EditorComponent = React.createClass @subscribe editor.$height.changes, @requestUpdate @subscribe editor.$width.changes, @requestUpdate @subscribe editor.$defaultCharWidth.changes, @requestUpdate - @subscribe editor.$lineHeight.changes, @requestUpdate + @subscribe editor.$lineHeightInPixels.changes, @requestUpdate listenForDOMEvents: -> node = @getDOMNode() @@ -262,6 +264,7 @@ EditorComponent = React.createClass 'editor:scroll-to-cursor': => editor.scrollToCursorPosition() 'core:page-up': => editor.pageUp() 'core:page-down': => editor.pageDown() + 'benchmark:scroll': @runScrollBenchmark addCommandListeners: (listenersByCommandName) -> {parentView} = @props @@ -272,6 +275,7 @@ EditorComponent = React.createClass observeConfig: -> @subscribe atom.config.observe 'editor.fontFamily', @setFontFamily @subscribe atom.config.observe 'editor.fontSize', @setFontSize + @subscribe atom.config.observe 'editor.lineHeight', @setLineHeight @subscribe atom.config.observe 'editor.showIndentGuide', @setShowIndentGuide @subscribe atom.config.observe 'editor.invisibles', @setInvisibles @subscribe atom.config.observe 'editor.showInvisibles', @setShowInvisibles @@ -469,3 +473,44 @@ EditorComponent = React.createClass lineNodeForScreenRow: (screenRow) -> @refs.scrollView.lineNodeForScreenRow(screenRow) lineNumberNodeForScreenRow: (screenRow) -> @refs.gutter.lineNumberNodeForScreenRow(screenRow) + + hide: -> + @setState(visible: false) + + show: -> + @setState(visible: true) + + runScrollBenchmark: -> + unless process.env.NODE_ENV is 'production' + ReactPerf = require 'react-atom-fork/lib/ReactDefaultPerf' + ReactPerf.start() + + node = @getDOMNode() + + scroll = (delta, done) -> + dispatchMouseWheelEvent = -> + node.dispatchEvent(new WheelEvent('mousewheel', wheelDeltaX: -0, wheelDeltaY: -delta)) + + stopScrolling = -> + clearInterval(interval) + done?() + + interval = setInterval(dispatchMouseWheelEvent, 10) + setTimeout(stopScrolling, 500) + + console.timeline('scroll') + scroll 50, -> + scroll 100, -> + scroll 200, -> + scroll 400, -> + scroll 800, -> + scroll 1600, -> + console.timelineEnd('scroll') + unless process.env.NODE_ENV is 'production' + ReactPerf.stop() + console.log "Inclusive" + ReactPerf.printInclusive() + console.log "Exclusive" + ReactPerf.printExclusive() + console.log "Wasted" + ReactPerf.printWasted() diff --git a/src/editor-scroll-view-component.coffee b/src/editor-scroll-view-component.coffee index 81b37ce8b..f87bc1ad7 100644 --- a/src/editor-scroll-view-component.coffee +++ b/src/editor-scroll-view-component.coffee @@ -1,5 +1,5 @@ -React = require 'react' -{div} = require 'reactionary' +React = require 'react-atom-fork' +{div} = require 'reactionary-atom-fork' {debounce} = require 'underscore-plus' InputComponent = require './input-component' @@ -16,8 +16,8 @@ EditorScrollViewComponent = React.createClass overflowChangedWhilePaused: false render: -> - {editor, fontSize, fontFamily, lineHeight, showIndentGuide, invisibles} = @props - {renderedRowRange, pendingChanges, scrollTop, scrollLeft, scrollHeight, scrollWidth, scrollingVertically, mouseWheelScreenRow} = @props + {editor, fontSize, fontFamily, lineHeight, lineHeightInPixels, showIndentGuide, invisibles, visible} = @props + {renderedRowRange, pendingChanges, scrollTop, scrollLeft, scrollHeight, scrollWidth, scrollViewHeight, scrollingVertically, mouseWheelScreenRow} = @props {selectionChanged, selectionAdded, cursorBlinkPeriod, cursorBlinkResumeDelay, cursorsMoved, onInputFocused, onInputBlurred} = @props if @isMounted() @@ -35,9 +35,10 @@ EditorScrollViewComponent = React.createClass CursorsComponent({editor, scrollTop, scrollLeft, cursorsMoved, selectionAdded, cursorBlinkPeriod, cursorBlinkResumeDelay}) LinesComponent { - ref: 'lines', editor, fontSize, fontFamily, lineHeight, showIndentGuide, - renderedRowRange, pendingChanges, scrollTop, scrollLeft, scrollingVertically, - selectionChanged, scrollHeight, scrollWidth, mouseWheelScreenRow, invisibles + ref: 'lines', editor, fontSize, fontFamily, lineHeight, lineHeightInPixels, + showIndentGuide, renderedRowRange, pendingChanges, scrollTop, scrollLeft, scrollingVertically, + selectionChanged, scrollHeight, scrollWidth, mouseWheelScreenRow, invisibles, + visible, scrollViewHeight } componentDidMount: -> diff --git a/src/editor.coffee b/src/editor.coffee index 25411c765..161a4455c 100644 --- a/src/editor.coffee +++ b/src/editor.coffee @@ -149,7 +149,7 @@ class Editor extends Model 'autoDecreaseIndentForBufferRow', 'toggleLineCommentForBufferRow', 'toggleLineCommentsForBufferRows', toProperty: 'languageMode' - @delegatesProperties '$lineHeight', '$defaultCharWidth', '$height', '$width', + @delegatesProperties '$lineHeightInPixels', '$defaultCharWidth', '$height', '$width', '$scrollTop', '$scrollLeft', 'manageScrollPosition', toProperty: 'displayBuffer' constructor: ({@softTabs, initialLine, initialColumn, tabLength, softWrap, @displayBuffer, buffer, registerEditor, suppressCursorCreation}) -> @@ -1867,8 +1867,8 @@ class Editor extends Model getHorizontalScrollMargin: -> @displayBuffer.getHorizontalScrollMargin() setHorizontalScrollMargin: (horizontalScrollMargin) -> @displayBuffer.setHorizontalScrollMargin(horizontalScrollMargin) - getLineHeight: -> @displayBuffer.getLineHeight() - setLineHeight: (lineHeight) -> @displayBuffer.setLineHeight(lineHeight) + getLineHeightInPixels: -> @displayBuffer.getLineHeightInPixels() + setLineHeightInPixels: (lineHeightInPixels) -> @displayBuffer.setLineHeightInPixels(lineHeightInPixels) getScopedCharWidth: (scopeNames, char) -> @displayBuffer.getScopedCharWidth(scopeNames, char) setScopedCharWidth: (scopeNames, char, width) -> @displayBuffer.setScopedCharWidth(scopeNames, char, width) diff --git a/src/gutter-component.coffee b/src/gutter-component.coffee index a72aba5f6..ec1c8e041 100644 --- a/src/gutter-component.coffee +++ b/src/gutter-component.coffee @@ -1,5 +1,5 @@ -React = require 'react' -{div} = require 'reactionary' +React = require 'react-atom-fork' +{div} = require 'reactionary-atom-fork' {isEqual, isEqualForProperties, multiplyString, toArray} = require 'underscore-plus' SubscriberMixin = require './subscriber-mixin' @@ -33,7 +33,7 @@ GutterComponent = React.createClass # 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', 'lineHeight', 'fontSize') + return true unless isEqualForProperties(newProps, @props, 'renderedRowRange', 'scrollTop', 'lineHeightInPixels', 'fontSize') {renderedRowRange, pendingChanges} = newProps for change in pendingChanges when Math.abs(change.screenDelta) > 0 or Math.abs(change.bufferDelta) > 0 @@ -47,7 +47,7 @@ GutterComponent = React.createClass @removeLineNumberNodes() @measureWidth() unless @lastMeasuredWidth? and isEqualForProperties(oldProps, @props, 'maxLineNumberDigits', 'fontSize', 'fontFamily') - @clearScreenRowCaches() unless oldProps.lineHeight is @props.lineHeight + @clearScreenRowCaches() unless oldProps.lineHeightInPixels is @props.lineHeightInPixels @updateLineNumbers() clearScreenRowCaches: -> @@ -125,8 +125,8 @@ GutterComponent = React.createClass buildLineNumberHTML: (bufferRow, softWrapped, maxLineNumberDigits, screenRow) -> if screenRow? - {lineHeight} = @props - style = "position: absolute; top: #{screenRow * lineHeight}px;" + {lineHeightInPixels} = @props + style = "position: absolute; top: #{screenRow * lineHeightInPixels}px;" else style = "visibility: hidden;" innerHTML = @buildLineNumberInnerHTML(bufferRow, softWrapped, maxLineNumberDigits) @@ -145,8 +145,8 @@ GutterComponent = React.createClass updateLineNumberNode: (lineNumberId, screenRow) -> unless @screenRowsByLineNumberId[lineNumberId] is screenRow - {lineHeight} = @props - @lineNumberNodesById[lineNumberId].style.top = screenRow * lineHeight + 'px' + {lineHeightInPixels} = @props + @lineNumberNodesById[lineNumberId].style.top = screenRow * lineHeightInPixels + 'px' @lineNumberNodesById[lineNumberId].dataset.screenRow = screenRow @screenRowsByLineNumberId[lineNumberId] = screenRow @lineNumberIdsByScreenRow[screenRow] = lineNumberId diff --git a/src/input-component.coffee b/src/input-component.coffee index ada93cf2a..372bccf9c 100644 --- a/src/input-component.coffee +++ b/src/input-component.coffee @@ -1,7 +1,7 @@ punycode = require 'punycode' {last, isEqual} = require 'underscore-plus' -React = require 'react' -{input} = require 'reactionary' +React = require 'react-atom-fork' +{input} = require 'reactionary-atom-fork' module.exports = InputComponent = React.createClass diff --git a/src/lines-component.coffee b/src/lines-component.coffee index 8b4dc29fa..136910422 100644 --- a/src/lines-component.coffee +++ b/src/lines-component.coffee @@ -1,5 +1,5 @@ -React = require 'react' -{div, span} = require 'reactionary' +React = require 'react-atom-fork' +{div, span} = require 'reactionary-atom-fork' {debounce, isEqual, isEqualForProperties, multiplyString, toArray} = require 'underscore-plus' {$$} = require 'space-pen' @@ -13,16 +13,18 @@ module.exports = LinesComponent = React.createClass displayName: 'LinesComponent' + measureWhenShown: false + render: -> if @isMounted() - {editor, scrollTop, scrollLeft, scrollHeight, scrollWidth, lineHeight} = @props + {editor, scrollTop, scrollLeft, scrollHeight, scrollWidth, lineHeightInPixels, scrollViewHeight} = @props style = - height: scrollHeight + height: Math.max(scrollHeight, scrollViewHeight) width: scrollWidth WebkitTransform: "translate3d(#{-scrollLeft}px, #{-scrollTop}px, 0px)" div {className: 'lines', style}, - SelectionsComponent({editor, lineHeight}) if @isMounted() + SelectionsComponent({editor, lineHeightInPixels}) if @isMounted() componentWillMount: -> @measuredLines = new WeakSet @@ -31,11 +33,15 @@ LinesComponent = React.createClass @lineIdsByScreenRow = {} componentDidMount: -> - @measureLineHeightAndCharWidth() + @measureLineHeightInPixelsAndCharWidth() shouldComponentUpdate: (newProps) -> return true if newProps.selectionChanged - return true unless isEqualForProperties(newProps, @props, 'renderedRowRange', 'fontSize', 'fontFamily', 'lineHeight', 'scrollTop', 'scrollLeft', 'showIndentGuide', 'scrollingVertically', 'invisibles') + return true unless isEqualForProperties(newProps, @props, + 'renderedRowRange', 'fontSize', 'fontFamily', 'lineHeight', 'lineHeightInPixels', + 'scrollTop', 'scrollLeft', 'showIndentGuide', 'scrollingVertically', 'invisibles', + 'visible', 'scrollViewHeight' + ) {renderedRowRange, pendingChanges} = newProps for change in pendingChanges @@ -44,8 +50,8 @@ LinesComponent = React.createClass false componentDidUpdate: (prevProps) -> - @measureLineHeightAndCharWidth() unless isEqualForProperties(prevProps, @props, 'fontSize', 'fontFamily', 'lineHeight') - @clearScreenRowCaches() unless prevProps.lineHeight is @props.lineHeight + @measureLineHeightInPixelsAndCharWidthIfNeeded(prevProps) + @clearScreenRowCaches() unless prevProps.lineHeightInPixels is @props.lineHeightInPixels @removeLineNodes() unless isEqualForProperties(prevProps, @props, 'showIndentGuide', 'invisibles') @updateLines() @clearScopedCharWidths() unless isEqualForProperties(prevProps, @props, 'fontSize', 'fontFamily') @@ -77,7 +83,6 @@ LinesComponent = React.createClass node.removeChild(lineNode) appendOrUpdateVisibleLineNodes: (visibleLines, startRow) -> - {lineHeight} = @props newLines = null newLinesHTML = null @@ -108,9 +113,10 @@ LinesComponent = React.createClass @lineNodesByLineId.hasOwnProperty(lineId) buildLineHTML: (line, screenRow) -> - {editor, mini, showIndentGuide, lineHeight} = @props + {editor, mini, showIndentGuide, lineHeightInPixels} = @props {tokens, text, lineEnding, fold, isSoftWrapped, indentLevel} = line - top = screenRow * lineHeight + + top = screenRow * lineHeightInPixels lineHTML = "