From 27423fcc157884c95ddd643515b3915c3a46027f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20Sch=C3=BC=C3=9Fler?= Date: Wed, 16 Jul 2014 18:14:30 +0200 Subject: [PATCH 01/61] Add Arch build instructions --- docs/build-instructions/linux.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/build-instructions/linux.md b/docs/build-instructions/linux.md index 64db5848d..2f6908f9c 100644 --- a/docs/build-instructions/linux.md +++ b/docs/build-instructions/linux.md @@ -8,15 +8,18 @@ Ubuntu LTS 12.04 64-bit is the recommended platform. * C++ toolchain * on Ubuntu/Debian: `sudo apt-get install build-essential` * on Fedora: `sudo yum --assumeyes install make gcc gcc-c++ glibc-devel` + * on Arch: `sudo pacman -S base-devel` * [node.js](http://nodejs.org/download/) v0.10.x * [Ubuntu/Debian/Mint instructions](https://github.com/joyent/node/wiki/Installing-Node.js-via-package-manager#ubuntu-mint-elementary-os) * [Fedora instructions](https://github.com/joyent/node/wiki/Installing-Node.js-via-package-manager#fedora) + * on Arch: `sudo pacman -S nodejs` * [npm](http://www.npmjs.org/) v1.4.x * `npm` comes with node.js so no explicit installation is needed here. * You can check `npm` 1.4 or above is installed by running `npm -v`. * libgnome-keyring-dev * on Ubuntu/Debian: `sudo apt-get install libgnome-keyring-dev` * on Fedora: `sudo yum --assumeyes install libgnome-keyring-devel` + * on Arch: `sudo pacman -S libgnome-keyring` * on other distributions refer to the manual on how to install packages * `npm config set python /usr/bin/python2 -g` to ensure that gyp uses Python 2 * This command may require `sudo` depending on how you have @@ -24,6 +27,7 @@ Ubuntu LTS 12.04 64-bit is the recommended platform. * Git * on Ubuntu/Debian: `sudo apt-get install git` * on Fedora: `sudo yum install git-core` + * on Arch: `sudo pacman -S git` ## Instructions From af5384cd5108db74ab46f43f500e8861f0d1bcd4 Mon Sep 17 00:00:00 2001 From: jordanbtucker Date: Wed, 16 Jul 2014 11:30:47 -0700 Subject: [PATCH 02/61] support ctrl-shift-home/end on Windows fixes issue #2989 --- keymaps/win32.cson | 2 ++ 1 file changed, 2 insertions(+) diff --git a/keymaps/win32.cson b/keymaps/win32.cson index ec3290d91..774300cd6 100644 --- a/keymaps/win32.cson +++ b/keymaps/win32.cson @@ -84,6 +84,8 @@ 'ctrl-delete': 'editor:delete-to-end-of-word' 'ctrl-home': 'core:move-to-top' 'ctrl-end': 'core:move-to-bottom' + 'ctrl-shift-home': 'core:select-to-top' + 'ctrl-shift-end': 'core:select-to-bottom' # Sublime Parity 'ctrl-a': 'core:select-all' From 1278f88dd97f97ca591cc029b6ac475d261f228f Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Thu, 17 Jul 2014 19:00:21 -0700 Subject: [PATCH 03/61] Upgrade to language-gfm@0.43 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index eda32c25a..18c189478 100644 --- a/package.json +++ b/package.json @@ -111,7 +111,7 @@ "language-c": "0.22.0", "language-coffee-script": "0.27.0", "language-css": "0.17.0", - "language-gfm": "0.42.0", + "language-gfm": "0.43.0", "language-git": "0.9.0", "language-go": "0.13.0", "language-html": "0.22.0", From 4960a63bd830cb396769053a0d77a250d58a1dca Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Fri, 18 Jul 2014 17:14:10 -0700 Subject: [PATCH 04/61] Upgrade solarized dark for better C and py styles --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 18c189478..3385e342f 100644 --- a/package.json +++ b/package.json @@ -67,7 +67,7 @@ "atom-light-syntax": "0.20.0", "atom-light-ui": "0.28.0", "base16-tomorrow-dark-theme": "0.19.0", - "solarized-dark-syntax": "0.20.0", + "solarized-dark-syntax": "0.21.0", "solarized-light-syntax": "0.11.0", "archive-view": "0.34.0", "autocomplete": "0.28.0", From a5f2a44a45f9786ab47b0065f0a86832938f5998 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20Sch=C3=BC=C3=9Fler?= Date: Sat, 19 Jul 2014 14:50:02 +0200 Subject: [PATCH 05/61] Arch requires export PYTHON=/usr/bin/python2 to build --- docs/build-instructions/linux.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/build-instructions/linux.md b/docs/build-instructions/linux.md index 2f6908f9c..7080fc17b 100644 --- a/docs/build-instructions/linux.md +++ b/docs/build-instructions/linux.md @@ -24,6 +24,7 @@ Ubuntu LTS 12.04 64-bit is the recommended platform. * `npm config set python /usr/bin/python2 -g` to ensure that gyp uses Python 2 * This command may require `sudo` depending on how you have [configured npm](https://github.com/joyent/node/wiki/Installing-Node.js-via-package-manager#ubuntu-mint-elementary-os). + * on Arch: `export PYTHON=/usr/bin/python2` * Git * on Ubuntu/Debian: `sudo apt-get install git` * on Fedora: `sudo yum install git-core` From c264855f87fae4d23b828be2ca74c73485830475 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20Sch=C3=BC=C3=9Fler?= Date: Sat, 19 Jul 2014 15:33:11 +0200 Subject: [PATCH 06/61] Cleanup linux build instructions --- docs/build-instructions/linux.md | 38 ++++++++++++++------------------ 1 file changed, 17 insertions(+), 21 deletions(-) diff --git a/docs/build-instructions/linux.md b/docs/build-instructions/linux.md index 7080fc17b..c6b812bc2 100644 --- a/docs/build-instructions/linux.md +++ b/docs/build-instructions/linux.md @@ -6,29 +6,25 @@ Ubuntu LTS 12.04 64-bit is the recommended platform. * OS with 64-bit or 32-bit architecture * C++ toolchain - * on Ubuntu/Debian: `sudo apt-get install build-essential` - * on Fedora: `sudo yum --assumeyes install make gcc gcc-c++ glibc-devel` - * on Arch: `sudo pacman -S base-devel` + * git * [node.js](http://nodejs.org/download/) v0.10.x - * [Ubuntu/Debian/Mint instructions](https://github.com/joyent/node/wiki/Installing-Node.js-via-package-manager#ubuntu-mint-elementary-os) - * [Fedora instructions](https://github.com/joyent/node/wiki/Installing-Node.js-via-package-manager#fedora) - * on Arch: `sudo pacman -S nodejs` - * [npm](http://www.npmjs.org/) v1.4.x - * `npm` comes with node.js so no explicit installation is needed here. - * You can check `npm` 1.4 or above is installed by running `npm -v`. + * [npm](http://www.npmjs.org/) 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). * libgnome-keyring-dev - * on Ubuntu/Debian: `sudo apt-get install libgnome-keyring-dev` - * on Fedora: `sudo yum --assumeyes install libgnome-keyring-devel` - * on Arch: `sudo pacman -S libgnome-keyring` - * on other distributions refer to the manual on how to install packages - * `npm config set python /usr/bin/python2 -g` to ensure that gyp uses Python 2 - * This command may require `sudo` depending on how you have - [configured npm](https://github.com/joyent/node/wiki/Installing-Node.js-via-package-manager#ubuntu-mint-elementary-os). - * on Arch: `export PYTHON=/usr/bin/python2` - * Git - * on Ubuntu/Debian: `sudo apt-get install git` - * on Fedora: `sudo yum install git-core` - * on Arch: `sudo pacman -S git` + +### Ubuntu / Debian +* `sudo apt-get install build-essential git libgnome-keyring-dev` +* Instructions for [node.js](https://github.com/joyent/node/wiki/Installing-Node.js-via-package-manager#ubuntu-mint-elementary-os). + +### Fedora +* `sudo yum --assumeyes install make gcc gcc-c++ glibc-devel git-core libgnome-keyring-devel` +* Instructions for [node.js](https://github.com/joyent/node/wiki/Installing-Node.js-via-package-manager#fedora). + +### Arch +* `sudo pacman -S base-devel git nodejs libgnome-keyring` +* `export PYTHON=/usr/bin/python2` before building Atom. ## Instructions From 7bf2b7237ed454cc9016bc81f1a5b087e2a30c3b Mon Sep 17 00:00:00 2001 From: Joel Kuntz Date: Sat, 19 Jul 2014 13:06:36 -0300 Subject: [PATCH 07/61] Update linux.md Improve installation instructions --- docs/build-instructions/linux.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/build-instructions/linux.md b/docs/build-instructions/linux.md index 64db5848d..7eb64d6b4 100644 --- a/docs/build-instructions/linux.md +++ b/docs/build-instructions/linux.md @@ -27,13 +27,17 @@ Ubuntu LTS 12.04 64-bit is the recommended platform. ## Instructions +If you have problems with permissions don't forget to prefix with `sudo` + ```sh git clone https://github.com/atom/atom cd atom script/build # Creates application at $TMPDIR/atom-build/Atom sudo script/grunt install # Installs command to /usr/local/bin/atom - script/grunt mkdeb # Generates a .deb package at $TMPDIR/atom-build + script/grunt mkdeb # Generates a .deb package at $TMPDIR/atom-build, i.e. /tmp/atom-build ``` + +To run `atom` and `apm` from a terminal open atom's command pallete `ctrl+shift+p` and run `Window: Install Shell Commands` ## Troubleshooting From ca39e7f7dea0e63dfa1e6fd2ee3cabc357b2bf10 Mon Sep 17 00:00:00 2001 From: Joel Kuntz Date: Sat, 19 Jul 2014 13:08:24 -0300 Subject: [PATCH 08/61] Fix typo --- docs/build-instructions/linux.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/build-instructions/linux.md b/docs/build-instructions/linux.md index 7eb64d6b4..156e6c41d 100644 --- a/docs/build-instructions/linux.md +++ b/docs/build-instructions/linux.md @@ -37,7 +37,7 @@ If you have problems with permissions don't forget to prefix with `sudo` script/grunt mkdeb # Generates a .deb package at $TMPDIR/atom-build, i.e. /tmp/atom-build ``` -To run `atom` and `apm` from a terminal open atom's command pallete `ctrl+shift+p` and run `Window: Install Shell Commands` +To run `atom` and `apm` from a terminal open atom's command palette `ctrl+shift+p` and run `Window: Install Shell Commands` ## Troubleshooting From d2da914151416ee089b3189b58b59adbe12f0a6a Mon Sep 17 00:00:00 2001 From: Joel Kuntz Date: Sat, 19 Jul 2014 14:20:32 -0300 Subject: [PATCH 09/61] Fix abbreviation --- docs/build-instructions/linux.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/build-instructions/linux.md b/docs/build-instructions/linux.md index 156e6c41d..a65fbfdf4 100644 --- a/docs/build-instructions/linux.md +++ b/docs/build-instructions/linux.md @@ -34,7 +34,7 @@ If you have problems with permissions don't forget to prefix with `sudo` cd atom script/build # Creates application at $TMPDIR/atom-build/Atom sudo script/grunt install # Installs command to /usr/local/bin/atom - script/grunt mkdeb # Generates a .deb package at $TMPDIR/atom-build, i.e. /tmp/atom-build + script/grunt mkdeb # Generates a .deb package at $TMPDIR/atom-build, e.g. /tmp/atom-build ``` To run `atom` and `apm` from a terminal open atom's command palette `ctrl+shift+p` and run `Window: Install Shell Commands` From 79ee887c9a48ca87e108a0d158a19bfc9979d330 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Sun, 20 Jul 2014 15:09:14 -0700 Subject: [PATCH 10/61] Fix a bug where scopes were being duplicated for every single token This improves scroll performance by generating many fewer span elements to render lines. --- src/lines-component.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lines-component.coffee b/src/lines-component.coffee index 4908221f4..52c81ba06 100644 --- a/src/lines-component.coffee +++ b/src/lines-component.coffee @@ -212,7 +212,7 @@ LinesComponent = React.createClass # Find a common prefix for scope, i in desiredScopes - break unless scopeStack[i]?.scope is desiredScopes[i] + break unless scopeStack[i] is desiredScopes[i] # Pop scopes until we're at the common prefx until scopeStack.length is i From 3d53749771432527f8c2af959346409e81bb0b5c Mon Sep 17 00:00:00 2001 From: Desmond Brand Date: Mon, 21 Jul 2014 00:20:39 -0700 Subject: [PATCH 11/61] Fix typo in event trigger destoryed -> destroyed --- src/decoration.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/decoration.coffee b/src/decoration.coffee index 7ac64d4e6..96acfcee1 100644 --- a/src/decoration.coffee +++ b/src/decoration.coffee @@ -61,7 +61,7 @@ class Decoration return if @isDestroyed @isDestroyed = true @displayBuffer.removeDecoration(this) - @emit 'destoryed' + @emit 'destroyed' # Public: Update the marker with new params. Allows you to change the decoration's class. # From 2d07d6662c9b55ffd3bb8dc8b5163d07f147dd1e Mon Sep 17 00:00:00 2001 From: batjko Date: Mon, 21 Jul 2014 09:33:17 +0100 Subject: [PATCH 12/61] Update Windows build instructions Just a few suggested changes to reflect experiences over a few months of building on several Windows machines. - The Posix tools comment doesn't seem to be applicable (anymore)? - Added further instructions when using pre-installed Git. - Added the frequent issue of first-time build failures (random `node-gyp` errors), which don't reoccur on second try. - Added a few extra words on opening a new issue, to clarify that appropriate details should be provided, if the issue hasn't been reported before. Feel free to amend or suggest further changes. --- docs/build-instructions/windows.md | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/docs/build-instructions/windows.md b/docs/build-instructions/windows.md index 9b9dcb799..dc818d784 100644 --- a/docs/build-instructions/windows.md +++ b/docs/build-instructions/windows.md @@ -35,10 +35,14 @@ ## Why do I have to use GitHub for Windows? -You don't, You can use your existing Git! GitHub for Windows's Git Shell is just -easier to set up. You need to have Posix tools in your `%PATH%` (i.e. `grep`, -`sed`, et al.), which isn't the default configuration when you install Git. To -fix this, you probably need to fiddle with your system PATH. +You don't. You can use your existing Git! GitHub for Windows's Git Shell is just +easier to set up. + +If you _prefer_ using your existing Git installation, make sure git's cmd directory is in your PATH env variable (e.g. `C:\Program Files (x86)\Git\cmd`) before you open your powershell or command window. +Note that you may have to open your command window as administrator. For powershell that doesn't seem to always be the case, though. + +If none of this works, do install Github for Windows and use its Git shell. Makes life easier. + ## Troubleshooting @@ -62,5 +66,11 @@ fix this, you probably need to fiddle with your system PATH. * https://github.com/TooTallNate/node-gyp/issues/297 * https://code.google.com/p/gyp/issues/detail?id=393 +* Other `node-gyp` errors on first build attempt, even though the right node and python versions are installed. + * Do try the build command one more time, as experience shows it often works on second try in many of these cases. + + ### Windows build error reports in atom/atom -* Use [this search](https://github.com/atom/atom/search?q=label%3Abuild-error+label%3Awindows&type=Issues) to get a list of reports about build errors on Windows. +* If all fails, use [this search](https://github.com/atom/atom/search?q=label%3Abuild-error+label%3Awindows&type=Issues) to get a list of reports about build errors on Windows, and see if yours has already been reported. + +* If it hasn't, please open a new issue with a print/screenshot of your Windows version, 32/64bit and build output, incl. the node and python versions. From 364e06483cd820f71ab2f9e8ff75b9b1bede3d24 Mon Sep 17 00:00:00 2001 From: batjko Date: Mon, 21 Jul 2014 09:37:16 +0100 Subject: [PATCH 13/61] corrected grammar --- docs/build-instructions/windows.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/build-instructions/windows.md b/docs/build-instructions/windows.md index dc818d784..9e86e5a2a 100644 --- a/docs/build-instructions/windows.md +++ b/docs/build-instructions/windows.md @@ -73,4 +73,4 @@ If none of this works, do install Github for Windows and use its Git shell. Make ### Windows build error reports in atom/atom * If all fails, use [this search](https://github.com/atom/atom/search?q=label%3Abuild-error+label%3Awindows&type=Issues) to get a list of reports about build errors on Windows, and see if yours has already been reported. -* If it hasn't, please open a new issue with a print/screenshot of your Windows version, 32/64bit and build output, incl. the node and python versions. +* If it hasn't, please open a new issue with your Windows version 32/64bit and a print/screenshot of build output, incl. the node and python versions. From 09e8aa05715750702f51b113ccda6b29e34c7b66 Mon Sep 17 00:00:00 2001 From: batjko Date: Mon, 21 Jul 2014 09:38:00 +0100 Subject: [PATCH 14/61] typo --- docs/build-instructions/windows.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/build-instructions/windows.md b/docs/build-instructions/windows.md index 9e86e5a2a..c74be5049 100644 --- a/docs/build-instructions/windows.md +++ b/docs/build-instructions/windows.md @@ -73,4 +73,4 @@ If none of this works, do install Github for Windows and use its Git shell. Make ### Windows build error reports in atom/atom * If all fails, use [this search](https://github.com/atom/atom/search?q=label%3Abuild-error+label%3Awindows&type=Issues) to get a list of reports about build errors on Windows, and see if yours has already been reported. -* If it hasn't, please open a new issue with your Windows version 32/64bit and a print/screenshot of build output, incl. the node and python versions. +* If it hasn't, please open a new issue with your Windows version 32/64bit and a print/screenshot of your build output, incl. the node and python versions. From ac8a67822edc69cd4ce482c4a6b0d9d3a2a733b1 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Mon, 21 Jul 2014 10:11:28 -0700 Subject: [PATCH 15/61] Upgrade to apm 0.81 --- apm/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apm/package.json b/apm/package.json index 88b8a25d0..959972e54 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.80.0" + "atom-package-manager": "0.81.0" } } From 754f995c91f59660754982d7be1345c9720687b0 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Mon, 21 Jul 2014 10:34:10 -0700 Subject: [PATCH 16/61] Upgrade to language-yaml@0.12 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3385e342f..336878512 100644 --- a/package.json +++ b/package.json @@ -136,7 +136,7 @@ "language-todo": "0.10.0", "language-toml": "0.12.0", "language-xml": "0.15.0", - "language-yaml": "0.11.0" + "language-yaml": "0.12.0" }, "private": true, "scripts": { From f16ea63a957dcc4f5788c67f8911c649e5f062e1 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 9 Jul 2014 09:26:34 -0600 Subject: [PATCH 17/61] Export ReactEditorView as EditorView from 'atom' module Also, remove a few early requires of 'exports/atom.coffee' in the spec suite that were causing failures. --- exports/atom.coffee | 2 +- spec/jasmine-helper.coffee | 3 ++- spec/spec-suite.coffee | 1 - 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/exports/atom.coffee b/exports/atom.coffee index 773b24f3a..b852d68a7 100644 --- a/exports/atom.coffee +++ b/exports/atom.coffee @@ -15,7 +15,7 @@ unless process.env.ATOM_SHELL_INTERNAL_RUN_AS_NODE module.exports.$ = $ module.exports.$$ = $$ module.exports.$$$ = $$$ - module.exports.EditorView = require '../src/editor-view' + module.exports.EditorView = require '../src/react-editor-view' module.exports.ScrollView = require '../src/scroll-view' module.exports.SelectListView = require '../src/select-list-view' module.exports.Task = require '../src/task' diff --git a/spec/jasmine-helper.coffee b/spec/jasmine-helper.coffee index 87765634c..3de6d065f 100644 --- a/spec/jasmine-helper.coffee +++ b/spec/jasmine-helper.coffee @@ -1,7 +1,8 @@ fs = require 'fs' module.exports.runSpecSuite = (specSuite, logFile, logErrors=true) -> - {$, $$} = require 'atom' + {$, $$} = require '../src/space-pen-extensions' + window[key] = value for key, value of require '../vendor/jasmine' {TerminalReporter} = require 'jasmine-tagged' diff --git a/spec/spec-suite.coffee b/spec/spec-suite.coffee index e0adaf24b..817de7986 100644 --- a/spec/spec-suite.coffee +++ b/spec/spec-suite.coffee @@ -1,6 +1,5 @@ _ = require 'underscore-plus' fs = require 'fs-plus' -{Git} = require 'atom' path = require 'path' require './spec-helper' From e999ef00e7e5eb150c6e0bfa292231d5ded11fcc Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Sun, 13 Jul 2014 11:56:14 -0600 Subject: [PATCH 18/61] Base editor dimensions on the wrapper view The goal is to make the editor behave like a standard block-level element. The horizontal behavior is simple: we stretch horizontally to fill our container. The vertical behavior is more nuanced. If an explicit height is assigned on the wrapper view, we honor that height. But if no explicit height is assigned, the editor stretches vertically so that its contents are visible. This prepares us to support mini editors, which need to be 1-line tall without an explicit height assignment. --- spec/editor-component-spec.coffee | 395 +++++++++++++++--------------- src/editor-component.coffee | 19 +- static/editor.less | 4 + 3 files changed, 212 insertions(+), 206 deletions(-) diff --git a/spec/editor-component-spec.coffee b/spec/editor-component-spec.coffee index da551e2b4..42f7bdb3b 100644 --- a/spec/editor-component-spec.coffee +++ b/spec/editor-component-spec.coffee @@ -6,7 +6,7 @@ EditorComponent = require '../src/editor-component' nbsp = String.fromCharCode(160) describe "EditorComponent", -> - [contentNode, editor, wrapperView, component, node, verticalScrollbarNode, horizontalScrollbarNode] = [] + [contentNode, editor, wrapperView, wrapperNode, component, componentNode, verticalScrollbarNode, horizontalScrollbarNode] = [] [lineHeightInPixels, charWidth, delayAnimationFrames, nextAnimationFrame, runSetImmediateCallbacks, lineOverdrawMargin] = [] beforeEach -> @@ -48,6 +48,7 @@ describe "EditorComponent", -> wrapperView = new ReactEditorView(editor, {lineOverdrawMargin}) wrapperView.attachToDom() + wrapperNode = wrapperView.element {component} = wrapperView component.performSyncUpdates = false @@ -56,12 +57,12 @@ describe "EditorComponent", -> lineHeightInPixels = editor.getLineHeightInPixels() charWidth = editor.getDefaultCharWidth() - node = component.getDOMNode() - verticalScrollbarNode = node.querySelector('.vertical-scrollbar') - horizontalScrollbarNode = node.querySelector('.horizontal-scrollbar') + componentNode = component.getDOMNode() + verticalScrollbarNode = componentNode.querySelector('.vertical-scrollbar') + horizontalScrollbarNode = componentNode.querySelector('.horizontal-scrollbar') - node.style.height = editor.getLineCount() * lineHeightInPixels + 'px' - node.style.width = '1000px' + wrapperNode.style.height = editor.getLineCount() * lineHeightInPixels + 'px' + wrapperNode.style.width = '1000px' component.measureScrollView() runSetImmediateCallbacks() @@ -70,13 +71,13 @@ describe "EditorComponent", -> describe "line rendering", -> it "renders the currently-visible lines plus the overdraw margin", -> - node.style.height = 4.5 * lineHeightInPixels + 'px' + wrapperNode.style.height = 4.5 * lineHeightInPixels + 'px' component.measureScrollView() runSetImmediateCallbacks() - linesNode = node.querySelector('.lines') + linesNode = componentNode.querySelector('.lines') expect(linesNode.style['-webkit-transform']).toBe "translate3d(0px, 0px, 0px)" - expect(node.querySelectorAll('.line').length).toBe 6 + 2 # no margin above + expect(componentNode.querySelectorAll('.line').length).toBe 6 + 2 # no margin above expect(component.lineNodeForScreenRow(0).textContent).toBe editor.lineForScreenRow(0).text expect(component.lineNodeForScreenRow(0).offsetTop).toBe 0 expect(component.lineNodeForScreenRow(5).textContent).toBe editor.lineForScreenRow(5).text @@ -86,7 +87,7 @@ describe "EditorComponent", -> verticalScrollbarNode.dispatchEvent(new UIEvent('scroll')) expect(linesNode.style['-webkit-transform']).toBe "translate3d(0px, #{-4.5 * lineHeightInPixels}px, 0px)" - expect(node.querySelectorAll('.line').length).toBe 6 + 4 # margin above and below + expect(componentNode.querySelectorAll('.line').length).toBe 6 + 4 # margin above and below expect(component.lineNodeForScreenRow(2).offsetTop).toBe 2 * lineHeightInPixels expect(component.lineNodeForScreenRow(2).textContent).toBe editor.lineForScreenRow(2).text expect(component.lineNodeForScreenRow(9).offsetTop).toBe 9 * lineHeightInPixels @@ -96,7 +97,7 @@ describe "EditorComponent", -> editor.getBuffer().deleteRows(0, 1) runSetImmediateCallbacks() - lineNodes = node.querySelectorAll('.line') + lineNodes = componentNode.querySelectorAll('.line') expect(component.lineNodeForScreenRow(0).offsetTop).toBe 0 expect(component.lineNodeForScreenRow(1).offsetTop).toBe 1 * lineHeightInPixels expect(component.lineNodeForScreenRow(2).offsetTop).toBe 2 * lineHeightInPixels @@ -104,7 +105,7 @@ describe "EditorComponent", -> editor.getBuffer().insert([0, 0], '\n\n') runSetImmediateCallbacks() - lineNodes = node.querySelectorAll('.line') + lineNodes = componentNode.querySelectorAll('.line') expect(component.lineNodeForScreenRow(0).offsetTop).toBe 0 * lineHeightInPixels expect(component.lineNodeForScreenRow(1).offsetTop).toBe 1 * lineHeightInPixels expect(component.lineNodeForScreenRow(2).offsetTop).toBe 2 * lineHeightInPixels @@ -112,7 +113,7 @@ describe "EditorComponent", -> expect(component.lineNodeForScreenRow(4).offsetTop).toBe 4 * lineHeightInPixels it "updates the lines when lines are inserted or removed above the rendered row range", -> - node.style.height = 4.5 * lineHeightInPixels + 'px' + wrapperNode.style.height = 4.5 * lineHeightInPixels + 'px' component.measureScrollView() runSetImmediateCallbacks() verticalScrollbarNode.scrollTop = 5 * lineHeightInPixels @@ -161,19 +162,19 @@ describe "EditorComponent", -> 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' + wrapperNode.style.height = '300px' component.measureScrollView() runSetImmediateCallbacks() - linesNode = node.querySelector('.lines') + linesNode = componentNode.querySelector('.lines') expect(linesNode.offsetHeight).toBe 300 it "assigns the width of each line so it extends across the full width of the editor", -> - gutterWidth = node.querySelector('.gutter').offsetWidth - scrollViewNode = node.querySelector('.scroll-view') - lineNodes = node.querySelectorAll('.line') + gutterWidth = componentNode.querySelector('.gutter').offsetWidth + scrollViewNode = componentNode.querySelector('.scroll-view') + lineNodes = componentNode.querySelectorAll('.line') - node.style.width = gutterWidth + (30 * charWidth) + 'px' + componentNode.style.width = gutterWidth + (30 * charWidth) + 'px' component.measureScrollView() runSetImmediateCallbacks() expect(editor.getScrollWidth()).toBeGreaterThan scrollViewNode.offsetWidth @@ -185,7 +186,7 @@ describe "EditorComponent", -> for lineNode in lineNodes expect(lineNode.style.width).toBe editor.getScrollWidth() + 'px' - node.style.width = gutterWidth + editor.getScrollWidth() + 100 + 'px' + componentNode.style.width = gutterWidth + editor.getScrollWidth() + 100 + 'px' component.measureScrollView() runSetImmediateCallbacks() scrollViewWidth = scrollViewNode.offsetWidth @@ -259,7 +260,7 @@ describe "EditorComponent", -> editor.setText "a line that wraps " editor.setSoftWrap(true) runSetImmediateCallbacks() - node.style.width = 16 * charWidth + 'px' + componentNode.style.width = 16 * charWidth + 'px' component.measureScrollView() runSetImmediateCallbacks() @@ -396,18 +397,18 @@ describe "EditorComponent", -> {gutter} = component.refs it "renders the currently-visible line numbers", -> - node.style.height = 4.5 * lineHeightInPixels + 'px' + wrapperNode.style.height = 4.5 * lineHeightInPixels + 'px' component.measureScrollView() runSetImmediateCallbacks() - expect(node.querySelectorAll('.line-number').length).toBe 6 + 2 + 1 # line overdraw margin below + dummy line number + expect(componentNode.querySelectorAll('.line-number').length).toBe 6 + 2 + 1 # line overdraw margin below + dummy line number expect(component.lineNumberNodeForScreenRow(0).textContent).toBe "#{nbsp}1" expect(component.lineNumberNodeForScreenRow(5).textContent).toBe "#{nbsp}6" verticalScrollbarNode.scrollTop = 2.5 * lineHeightInPixels verticalScrollbarNode.dispatchEvent(new UIEvent('scroll')) - expect(node.querySelectorAll('.line-number').length).toBe 6 + 4 + 1 # line overdraw margin above/below + dummy line number + expect(componentNode.querySelectorAll('.line-number').length).toBe 6 + 4 + 1 # line overdraw margin above/below + dummy line number expect(component.lineNumberNodeForScreenRow(2).textContent).toBe "#{nbsp}3" expect(component.lineNumberNodeForScreenRow(2).offsetTop).toBe 2 * lineHeightInPixels @@ -418,7 +419,7 @@ describe "EditorComponent", -> editor.getBuffer().insert([0, 0], '\n\n') runSetImmediateCallbacks() - lineNumberNodes = node.querySelectorAll('.line-number') + lineNumberNodes = componentNode.querySelectorAll('.line-number') expect(component.lineNumberNodeForScreenRow(0).offsetTop).toBe 0 expect(component.lineNumberNodeForScreenRow(1).offsetTop).toBe 1 * lineHeightInPixels expect(component.lineNumberNodeForScreenRow(2).offsetTop).toBe 2 * lineHeightInPixels @@ -438,12 +439,12 @@ describe "EditorComponent", -> it "renders • characters for soft-wrapped lines", -> editor.setSoftWrap(true) - node.style.height = 4.5 * lineHeightInPixels + 'px' - node.style.width = 30 * charWidth + 'px' + wrapperNode.style.height = 4.5 * lineHeightInPixels + 'px' + wrapperNode.style.width = 30 * charWidth + 'px' component.measureScrollView() runSetImmediateCallbacks() - expect(node.querySelectorAll('.line-number').length).toBe 6 + lineOverdrawMargin + 1 # 1 dummy line node + expect(componentNode.querySelectorAll('.line-number').length).toBe 6 + lineOverdrawMargin + 1 # 1 dummy line componentNode expect(component.lineNumberNodeForScreenRow(0).textContent).toBe "#{nbsp}1" expect(component.lineNumberNodeForScreenRow(1).textContent).toBe "#{nbsp}•" expect(component.lineNumberNodeForScreenRow(2).textContent).toBe "#{nbsp}2" @@ -458,7 +459,7 @@ describe "EditorComponent", -> expect(component.lineNumberNodeForScreenRow(screenRow).textContent).toBe "#{nbsp}#{screenRow + 1}" expect(component.lineNumberNodeForScreenRow(9).textContent).toBe "10" - gutterNode = node.querySelector('.gutter') + gutterNode = componentNode.querySelector('.gutter') initialGutterWidth = gutterNode.offsetWidth # Removes padding when the max number of digits goes down @@ -477,10 +478,10 @@ describe "EditorComponent", -> expect(gutterNode.offsetWidth).toBe initialGutterWidth it "renders the .line-numbers div at the full height of the editor even if it's taller than its content", -> - node.style.height = node.offsetHeight + 100 + 'px' + wrapperNode.style.height = componentNode.offsetHeight + 100 + 'px' component.measureScrollView() runSetImmediateCallbacks() - expect(node.querySelector('.line-numbers').offsetHeight).toBe node.offsetHeight + expect(componentNode.querySelector('.line-numbers').offsetHeight).toBe componentNode.offsetHeight describe "when the editor.showLineNumbers config is false", -> it "doesn't render any line numbers", -> @@ -523,7 +524,7 @@ describe "EditorComponent", -> runSetImmediateCallbacks() expect(lineNumberHasClass(11, 'foldable')).toBe false - it "adds, updates and removes the folded class on the correct line number nodes", -> + it "adds, updates and removes the folded class on the correct line number componentNodes", -> editor.foldBufferRow(4) runSetImmediateCallbacks() expect(lineNumberHasClass(4, 'folded')).toBe true @@ -544,7 +545,7 @@ describe "EditorComponent", -> buildMouseEvent('click', {target}) beforeEach -> - gutterNode = node.querySelector('.gutter') + gutterNode = componentNode.querySelector('.gutter') it "folds and unfolds the block represented by the fold indicator when clicked", -> expect(lineNumberHasClass(1, 'folded')).toBe false @@ -561,7 +562,7 @@ describe "EditorComponent", -> runSetImmediateCallbacks() expect(lineNumberHasClass(1, 'folded')).toBe false - it "does not fold when the line number node is clicked", -> + it "does not fold when the line number componentNode is clicked", -> lineNumber = component.lineNumberNodeForScreenRow(1) lineNumber.dispatchEvent(buildClickEvent(lineNumber)) runSetImmediateCallbacks() @@ -572,12 +573,12 @@ describe "EditorComponent", -> cursor1 = editor.getCursor() cursor1.setScreenPosition([0, 5]) - node.style.height = 4.5 * lineHeightInPixels + 'px' - node.style.width = 20 * lineHeightInPixels + 'px' + wrapperNode.style.height = 4.5 * lineHeightInPixels + 'px' + wrapperNode.style.width = 20 * lineHeightInPixels + 'px' component.measureScrollView() runSetImmediateCallbacks() - cursorNodes = node.querySelectorAll('.cursor') + cursorNodes = componentNode.querySelectorAll('.cursor') expect(cursorNodes.length).toBe 1 expect(cursorNodes[0].offsetHeight).toBe lineHeightInPixels expect(cursorNodes[0].offsetWidth).toBe charWidth @@ -587,7 +588,7 @@ describe "EditorComponent", -> cursor3 = editor.addCursorAtScreenPosition([4, 10]) runSetImmediateCallbacks() - cursorNodes = node.querySelectorAll('.cursor') + cursorNodes = componentNode.querySelectorAll('.cursor') expect(cursorNodes.length).toBe 2 expect(cursorNodes[0].offsetTop).toBe 0 expect(cursorNodes[0].style['-webkit-transform']).toBe "translate3d(#{5 * charWidth}px, #{0 * lineHeightInPixels}px, 0px)" @@ -598,14 +599,14 @@ describe "EditorComponent", -> horizontalScrollbarNode.scrollLeft = 3.5 * charWidth horizontalScrollbarNode.dispatchEvent(new UIEvent('scroll')) - cursorNodes = node.querySelectorAll('.cursor') + cursorNodes = componentNode.querySelectorAll('.cursor') expect(cursorNodes.length).toBe 2 expect(cursorNodes[0].style['-webkit-transform']).toBe "translate3d(#{(11 - 3.5) * charWidth}px, #{(8 - 4.5) * lineHeightInPixels}px, 0px)" expect(cursorNodes[1].style['-webkit-transform']).toBe "translate3d(#{(10 - 3.5) * charWidth}px, #{(4 - 4.5) * lineHeightInPixels}px, 0px)" cursor3.destroy() runSetImmediateCallbacks() - cursorNodes = node.querySelectorAll('.cursor') + cursorNodes = componentNode.querySelectorAll('.cursor') expect(cursorNodes.length).toBe 1 expect(cursorNodes[0].style['-webkit-transform']).toBe "translate3d(#{(11 - 3.5) * charWidth}px, #{(6 - 2.5) * lineHeightInPixels}px, 0px)" @@ -614,7 +615,7 @@ describe "EditorComponent", -> editor.setCursorScreenPosition([0, 16]) runSetImmediateCallbacks() - cursor = node.querySelector('.cursor') + cursor = componentNode.querySelector('.cursor') cursorRect = cursor.getBoundingClientRect() cursorLocationTextNode = component.lineNodeForScreenRow(0).querySelector('.storage.type.function.js').firstChild @@ -639,7 +640,7 @@ describe "EditorComponent", -> runSetImmediateCallbacks() # re-measure characters once for a synchronous set of stylesheet changes runSetImmediateCallbacks() # update based on new measurements - cursor = node.querySelector('.cursor') + cursor = componentNode.querySelector('.cursor') cursorRect = cursor.getBoundingClientRect() cursorLocationTextNode = component.lineNodeForScreenRow(0).querySelector('.storage.type.function.js').firstChild @@ -656,18 +657,18 @@ describe "EditorComponent", -> it "sets the cursor to the default character width at the end of a line", -> editor.setCursorScreenPosition([0, Infinity]) runSetImmediateCallbacks() - cursorNode = node.querySelector('.cursor') + cursorNode = componentNode.querySelector('.cursor') expect(cursorNode.offsetWidth).toBe charWidth it "gives the cursor a non-zero width even if it's inside atomic tokens", -> editor.setCursorScreenPosition([1, 0]) runSetImmediateCallbacks() - cursorNode = node.querySelector('.cursor') + cursorNode = componentNode.querySelector('.cursor') expect(cursorNode.offsetWidth).toBe charWidth it "blinks cursors when they aren't moving", -> spyOn(_._, 'now').andCallFake -> window.now # Ensure _.debounce is based on our fake spec timeline - cursorsNode = node.querySelector('.cursors') + cursorsNode = componentNode.querySelector('.cursors') expect(cursorsNode.classList.contains('blink-off')).toBe false advanceClock(component.props.cursorBlinkPeriod / 2) @@ -689,7 +690,7 @@ describe "EditorComponent", -> editor.addCursorAtScreenPosition([6, 8]) runSetImmediateCallbacks() - cursorNodes = node.querySelectorAll('.cursor') + cursorNodes = componentNode.querySelectorAll('.cursor') expect(cursorNodes.length).toBe 1 expect(cursorNodes[0].style['-webkit-transform']).toBe "translate3d(#{8 * charWidth}px, #{6 * lineHeightInPixels}px, 0px)" @@ -697,21 +698,21 @@ describe "EditorComponent", -> editor.setCursorBufferPosition([1, 10]) component.setLineHeight(2) runSetImmediateCallbacks() - cursorNode = node.querySelector('.cursor') + cursorNode = componentNode.querySelector('.cursor') expect(cursorNode.style['-webkit-transform']).toBe "translate3d(#{10 * editor.getDefaultCharWidth()}px, #{editor.getLineHeightInPixels()}px, 0px)" it "updates cursor positions when the font size changes", -> editor.setCursorBufferPosition([1, 10]) component.setFontSize(10) runSetImmediateCallbacks() - cursorNode = node.querySelector('.cursor') + cursorNode = componentNode.querySelector('.cursor') expect(cursorNode.style['-webkit-transform']).toBe "translate3d(#{10 * editor.getDefaultCharWidth()}px, #{editor.getLineHeightInPixels()}px, 0px)" it "updates cursor positions when the font family changes", -> editor.setCursorBufferPosition([1, 10]) component.setFontFamily('sans-serif') runSetImmediateCallbacks() - cursorNode = node.querySelector('.cursor') + cursorNode = componentNode.querySelector('.cursor') {left} = editor.pixelPositionForScreenPosition([1, 10]) expect(cursorNode.style['-webkit-transform']).toBe "translate3d(#{left}px, #{editor.getLineHeightInPixels()}px, 0px)" @@ -720,14 +721,14 @@ describe "EditorComponent", -> [scrollViewNode, scrollViewClientLeft] = [] beforeEach -> - scrollViewNode = node.querySelector('.scroll-view') - scrollViewClientLeft = node.querySelector('.scroll-view').getBoundingClientRect().left + scrollViewNode = componentNode.querySelector('.scroll-view') + scrollViewClientLeft = componentNode.querySelector('.scroll-view').getBoundingClientRect().left it "renders 1 region for 1-line selections", -> # 1-line selection editor.setSelectedScreenRange([[1, 6], [1, 10]]) runSetImmediateCallbacks() - regions = node.querySelectorAll('.selection .region') + regions = componentNode.querySelectorAll('.selection .region') expect(regions.length).toBe 1 regionRect = regions[0].getBoundingClientRect() @@ -739,7 +740,7 @@ describe "EditorComponent", -> it "renders 2 regions for 2-line selections", -> editor.setSelectedScreenRange([[1, 6], [2, 10]]) runSetImmediateCallbacks() - regions = node.querySelectorAll('.selection .region') + regions = componentNode.querySelectorAll('.selection .region') expect(regions.length).toBe 2 region1Rect = regions[0].getBoundingClientRect() @@ -757,7 +758,7 @@ describe "EditorComponent", -> it "renders 3 regions for selections with more than 2 lines", -> editor.setSelectedScreenRange([[1, 6], [5, 10]]) runSetImmediateCallbacks() - regions = node.querySelectorAll('.selection .region') + regions = componentNode.querySelectorAll('.selection .region') expect(regions.length).toBe 3 region1Rect = regions[0].getBoundingClientRect() @@ -784,20 +785,20 @@ describe "EditorComponent", -> expect(editor.getSelection(0).isEmpty()).toBe true expect(editor.getSelection(1).isEmpty()).toBe true - expect(node.querySelectorAll('.selection').length).toBe 0 + expect(componentNode.querySelectorAll('.selection').length).toBe 0 it "updates selections when the line height changes", -> editor.setSelectedBufferRange([[1, 6], [1, 10]]) component.setLineHeight(2) runSetImmediateCallbacks() - selectionNode = node.querySelector('.region') + selectionNode = componentNode.querySelector('.region') expect(selectionNode.offsetTop).toBe editor.getLineHeightInPixels() it "updates selections when the font size changes", -> editor.setSelectedBufferRange([[1, 6], [1, 10]]) component.setFontSize(10) runSetImmediateCallbacks() - selectionNode = node.querySelector('.region') + selectionNode = componentNode.querySelector('.region') expect(selectionNode.offsetTop).toBe editor.getLineHeightInPixels() expect(selectionNode.offsetLeft).toBe 6 * editor.getDefaultCharWidth() @@ -805,14 +806,14 @@ describe "EditorComponent", -> editor.setSelectedBufferRange([[1, 6], [1, 10]]) component.setFontFamily('sans-serif') runSetImmediateCallbacks() - selectionNode = node.querySelector('.region') + selectionNode = componentNode.querySelector('.region') expect(selectionNode.offsetTop).toBe editor.getLineHeightInPixels() expect(selectionNode.offsetLeft).toBe editor.pixelPositionForScreenPosition([1, 6]).left it "will flash the selection when flash:true is passed to editor::setSelectedBufferRange", -> editor.setSelectedBufferRange([[1, 6], [1, 10]], flash: true) runSetImmediateCallbacks() - selectionNode = node.querySelector('.selection') + selectionNode = componentNode.querySelector('.selection') expect(selectionNode.classList.contains('flash')).toBe true advanceClock editor.selectionFlashDuration @@ -836,7 +837,7 @@ describe "EditorComponent", -> expect(lineAndLineNumberHaveClass(3, 'a')).toBe true # Shrink editor vertically - node.style.height = 4.5 * lineHeightInPixels + 'px' + wrapperNode.style.height = 4.5 * lineHeightInPixels + 'px' component.measureScrollView() runSetImmediateCallbacks() @@ -859,7 +860,7 @@ describe "EditorComponent", -> it "only applies decorations to screen rows that are spanned by their marker when lines are soft-wrapped", -> editor.setText("a line that wraps, ok") editor.setSoftWrap(true) - node.style.width = 16 * charWidth + 'px' + componentNode.style.width = 16 * charWidth + 'px' component.measureScrollView() runSetImmediateCallbacks() @@ -968,14 +969,14 @@ describe "EditorComponent", -> describe "highlight decoration rendering", -> [marker, decoration, decorationParams, scrollViewClientLeft] = [] beforeEach -> - scrollViewClientLeft = node.querySelector('.scroll-view').getBoundingClientRect().left + scrollViewClientLeft = componentNode.querySelector('.scroll-view').getBoundingClientRect().left marker = editor.displayBuffer.markBufferRange([[2, 13], [3, 15]], invalidate: 'inside') decorationParams = {type: 'highlight', class: 'test-highlight'} decoration = editor.decorateMarker(marker, decorationParams) runSetImmediateCallbacks() it "does not render highlights for off-screen lines until they come on-screen", -> - node.style.height = 2.5 * lineHeightInPixels + 'px' + wrapperNode.style.height = 2.5 * lineHeightInPixels + 'px' component.measureScrollView() runSetImmediateCallbacks() @@ -986,7 +987,7 @@ describe "EditorComponent", -> # Should not be rendering range containing the marker expect(component.getRenderedRowRange()[1]).toBeLessThan 9 - regions = node.querySelectorAll('.some-highlight .region') + regions = componentNode.querySelectorAll('.some-highlight .region') # Nothing when outside the rendered row range expect(regions.length).toBe 0 @@ -994,7 +995,7 @@ describe "EditorComponent", -> verticalScrollbarNode.scrollTop = 3.5 * lineHeightInPixels verticalScrollbarNode.dispatchEvent(new UIEvent('scroll')) - regions = node.querySelectorAll('.some-highlight .region') + regions = componentNode.querySelectorAll('.some-highlight .region') expect(regions.length).toBe 1 regionRect = regions[0].style @@ -1004,24 +1005,24 @@ describe "EditorComponent", -> expect(regionRect.width).toBe 2 * charWidth + 'px' it "renders highlights decoration's marker is added", -> - regions = node.querySelectorAll('.test-highlight .region') + regions = componentNode.querySelectorAll('.test-highlight .region') expect(regions.length).toBe 2 it "removes highlights when a decoration is removed", -> decoration.destroy() runSetImmediateCallbacks() - regions = node.querySelectorAll('.test-highlight .region') + regions = componentNode.querySelectorAll('.test-highlight .region') expect(regions.length).toBe 0 it "does not render a highlight that is within a fold", -> editor.foldBufferRow(1) runSetImmediateCallbacks() - expect(node.querySelectorAll('.test-highlight').length).toBe 0 + expect(componentNode.querySelectorAll('.test-highlight').length).toBe 0 it "removes highlights when a decoration's marker is destroyed", -> marker.destroy() runSetImmediateCallbacks() - regions = node.querySelectorAll('.test-highlight .region') + regions = componentNode.querySelectorAll('.test-highlight .region') expect(regions.length).toBe 0 it "only renders highlights when a decoration's marker is valid", -> @@ -1029,20 +1030,20 @@ describe "EditorComponent", -> runSetImmediateCallbacks() expect(marker.isValid()).toBe false - regions = node.querySelectorAll('.test-highlight .region') + regions = componentNode.querySelectorAll('.test-highlight .region') expect(regions.length).toBe 0 editor.getBuffer().undo() runSetImmediateCallbacks() expect(marker.isValid()).toBe true - regions = node.querySelectorAll('.test-highlight .region') + regions = componentNode.querySelectorAll('.test-highlight .region') expect(regions.length).toBe 2 describe "when flashing a decoration via Decoration::flash()", -> highlightNode = null beforeEach -> - highlightNode = node.querySelector('.test-highlight') + highlightNode = componentNode.querySelector('.test-highlight') it "adds and removes the flash class specified in ::flash", -> expect(highlightNode.classList.contains('flash-class')).toBe false @@ -1074,45 +1075,45 @@ describe "EditorComponent", -> describe "when a decoration's marker moves", -> it "moves rendered highlights when the buffer is changed", -> - regionStyle = node.querySelector('.test-highlight .region').style + regionStyle = componentNode.querySelector('.test-highlight .region').style originalTop = parseInt(regionStyle.top) editor.getBuffer().insert([0, 0], '\n') runSetImmediateCallbacks() - regionStyle = node.querySelector('.test-highlight .region').style + regionStyle = componentNode.querySelector('.test-highlight .region').style newTop = parseInt(regionStyle.top) expect(newTop).toBe originalTop + lineHeightInPixels it "moves rendered highlights when the marker is manually moved", -> - regionStyle = node.querySelector('.test-highlight .region').style + regionStyle = componentNode.querySelector('.test-highlight .region').style expect(parseInt(regionStyle.top)).toBe 2 * lineHeightInPixels marker.setBufferRange([[5, 8], [5, 13]]) runSetImmediateCallbacks() - regionStyle = node.querySelector('.test-highlight .region').style + regionStyle = componentNode.querySelector('.test-highlight .region').style expect(parseInt(regionStyle.top)).toBe 5 * lineHeightInPixels describe "when a decoration is updated via Decoration::update", -> it "renders the decoration's new params", -> - expect(node.querySelector('.test-highlight')).toBeTruthy() + expect(componentNode.querySelector('.test-highlight')).toBeTruthy() decoration.update(type: 'highlight', class: 'new-test-highlight') runSetImmediateCallbacks() - expect(node.querySelector('.test-highlight')).toBeFalsy() - expect(node.querySelector('.new-test-highlight')).toBeTruthy() + expect(componentNode.querySelector('.test-highlight')).toBeFalsy() + expect(componentNode.querySelector('.new-test-highlight')).toBeTruthy() describe "hidden input field", -> it "renders the hidden input field at the position of the last cursor if the cursor is on screen and the editor is focused", -> editor.setVerticalScrollMargin(0) editor.setHorizontalScrollMargin(0) - inputNode = node.querySelector('.hidden-input') - node.style.height = 5 * lineHeightInPixels + 'px' - node.style.width = 10 * charWidth + 'px' + inputNode = componentNode.querySelector('.hidden-input') + wrapperNode.style.height = 5 * lineHeightInPixels + 'px' + wrapperNode.style.width = 10 * charWidth + 'px' component.measureScrollView() runSetImmediateCallbacks() @@ -1156,13 +1157,13 @@ describe "EditorComponent", -> beforeEach -> delayAnimationFrames = true - linesNode = node.querySelector('.lines') + linesNode = componentNode.querySelector('.lines') describe "when a non-folded line is single-clicked", -> describe "when no modifier keys are held down", -> it "moves the cursor to the nearest screen position", -> - node.style.height = 4.5 * lineHeightInPixels + 'px' - node.style.width = 10 * charWidth + 'px' + wrapperNode.style.height = 4.5 * lineHeightInPixels + 'px' + wrapperNode.style.width = 10 * charWidth + 'px' component.measureScrollView() editor.setScrollTop(3.5 * lineHeightInPixels) editor.setScrollLeft(2 * charWidth) @@ -1288,7 +1289,7 @@ describe "EditorComponent", -> gutterNode = null beforeEach -> - gutterNode = node.querySelector('.gutter') + gutterNode = componentNode.querySelector('.gutter') describe "when the gutter is clicked", -> it "moves the cursor to the beginning of the clicked row", -> @@ -1383,20 +1384,20 @@ describe "EditorComponent", -> inputNode = null beforeEach -> - inputNode = node.querySelector('.hidden-input') + inputNode = componentNode.querySelector('.hidden-input') it "transfers focus to the hidden input", -> expect(document.activeElement).toBe document.body - node.focus() + componentNode.focus() expect(document.activeElement).toBe inputNode it "adds the 'is-focused' class to the editor when the hidden input is focused", -> expect(document.activeElement).toBe document.body inputNode.focus() - expect(node.classList.contains('is-focused')).toBe true + expect(componentNode.classList.contains('is-focused')).toBe true expect(wrapperView.hasClass('is-focused')).toBe true inputNode.blur() - expect(node.classList.contains('is-focused')).toBe false + expect(componentNode.classList.contains('is-focused')).toBe false expect(wrapperView.hasClass('is-focused')).toBe false describe "selection handling", -> @@ -1408,19 +1409,19 @@ describe "EditorComponent", -> runSetImmediateCallbacks() it "adds the 'has-selection' class to the editor when there is a selection", -> - expect(node.classList.contains('has-selection')).toBe false + expect(componentNode.classList.contains('has-selection')).toBe false editor.selectDown() runSetImmediateCallbacks() - expect(node.classList.contains('has-selection')).toBe true + expect(componentNode.classList.contains('has-selection')).toBe true cursor.moveDown() runSetImmediateCallbacks() - expect(node.classList.contains('has-selection')).toBe false + expect(componentNode.classList.contains('has-selection')).toBe false describe "scrolling", -> it "updates the vertical scrollbar when the scrollTop is changed in the model", -> - node.style.height = 4.5 * lineHeightInPixels + 'px' + wrapperNode.style.height = 4.5 * lineHeightInPixels + 'px' component.measureScrollView() runSetImmediateCallbacks() @@ -1431,11 +1432,11 @@ describe "EditorComponent", -> expect(verticalScrollbarNode.scrollTop).toBe 10 it "updates the horizontal scrollbar and the x transform of the lines based on the scrollLeft of the model", -> - node.style.width = 30 * charWidth + 'px' + componentNode.style.width = 30 * charWidth + 'px' component.measureScrollView() runSetImmediateCallbacks() - linesNode = node.querySelector('.lines') + linesNode = componentNode.querySelector('.lines') expect(linesNode.style['-webkit-transform']).toBe "translate3d(0px, 0px, 0px)" expect(horizontalScrollbarNode.scrollLeft).toBe 0 @@ -1445,7 +1446,7 @@ describe "EditorComponent", -> expect(horizontalScrollbarNode.scrollLeft).toBe 100 it "updates the scrollLeft of the model when the scrollLeft of the horizontal scrollbar changes", -> - node.style.width = 30 * charWidth + 'px' + componentNode.style.width = 30 * charWidth + 'px' component.measureScrollView() runSetImmediateCallbacks() @@ -1456,8 +1457,8 @@ describe "EditorComponent", -> expect(editor.getScrollLeft()).toBe 100 it "does not obscure the last line with the horizontal scrollbar", -> - node.style.height = 4.5 * lineHeightInPixels + 'px' - node.style.width = 10 * charWidth + 'px' + wrapperNode.style.height = 4.5 * lineHeightInPixels + 'px' + wrapperNode.style.width = 10 * charWidth + 'px' component.measureScrollView() editor.setScrollBottom(editor.getScrollHeight()) runSetImmediateCallbacks() @@ -1467,16 +1468,16 @@ describe "EditorComponent", -> expect(bottomOfLastLine).toBe topOfHorizontalScrollbar # Scroll so there's no space below the last line when the horizontal scrollbar disappears - node.style.width = 100 * charWidth + 'px' + wrapperNode.style.width = 100 * charWidth + 'px' component.measureScrollView() runSetImmediateCallbacks() bottomOfLastLine = lastLineNode.getBoundingClientRect().bottom - bottomOfEditor = node.getBoundingClientRect().bottom + bottomOfEditor = componentNode.getBoundingClientRect().bottom expect(bottomOfLastLine).toBe bottomOfEditor it "does not obscure the last character of the longest line with the vertical scrollbar", -> - node.style.height = 7 * lineHeightInPixels + 'px' - node.style.width = 10 * charWidth + 'px' + wrapperNode.style.height = 7 * lineHeightInPixels + 'px' + wrapperNode.style.width = 10 * charWidth + 'px' component.measureScrollView() editor.setScrollLeft(Infinity) runSetImmediateCallbacks() @@ -1489,22 +1490,22 @@ describe "EditorComponent", -> expect(verticalScrollbarNode.style.display).toBe 'none' expect(horizontalScrollbarNode.style.display).toBe 'none' - node.style.height = 4.5 * lineHeightInPixels + 'px' - node.style.width = '1000px' + wrapperNode.style.height = 4.5 * lineHeightInPixels + 'px' + wrapperNode.style.width = '1000px' component.measureScrollView() runSetImmediateCallbacks() expect(verticalScrollbarNode.style.display).toBe '' expect(horizontalScrollbarNode.style.display).toBe 'none' - node.style.width = 10 * charWidth + 'px' + componentNode.style.width = 10 * charWidth + 'px' component.measureScrollView() runSetImmediateCallbacks() expect(verticalScrollbarNode.style.display).toBe '' expect(horizontalScrollbarNode.style.display).toBe '' - node.style.height = 20 * lineHeightInPixels + 'px' + wrapperNode.style.height = 20 * lineHeightInPixels + 'px' component.measureScrollView() runSetImmediateCallbacks() @@ -1512,8 +1513,8 @@ describe "EditorComponent", -> expect(horizontalScrollbarNode.style.display).toBe '' it "makes the dummy scrollbar divs only as tall/wide as the actual scrollbars", -> - node.style.height = 4 * lineHeightInPixels + 'px' - node.style.width = 10 * charWidth + 'px' + wrapperNode.style.height = 4 * lineHeightInPixels + 'px' + wrapperNode.style.width = 10 * charWidth + 'px' component.measureScrollView() runSetImmediateCallbacks() @@ -1524,34 +1525,34 @@ describe "EditorComponent", -> } """ - scrollbarCornerNode = node.querySelector('.scrollbar-corner') + scrollbarCornerNode = componentNode.querySelector('.scrollbar-corner') expect(verticalScrollbarNode.offsetWidth).toBe 8 expect(horizontalScrollbarNode.offsetHeight).toBe 8 expect(scrollbarCornerNode.offsetWidth).toBe 8 expect(scrollbarCornerNode.offsetHeight).toBe 8 it "assigns the bottom/right of the scrollbars to the width of the opposite scrollbar if it is visible", -> - scrollbarCornerNode = node.querySelector('.scrollbar-corner') + scrollbarCornerNode = componentNode.querySelector('.scrollbar-corner') expect(verticalScrollbarNode.style.bottom).toBe '' expect(horizontalScrollbarNode.style.right).toBe '' - node.style.height = 4.5 * lineHeightInPixels + 'px' - node.style.width = '1000px' + wrapperNode.style.height = 4.5 * lineHeightInPixels + 'px' + wrapperNode.style.width = '1000px' component.measureScrollView() runSetImmediateCallbacks() expect(verticalScrollbarNode.style.bottom).toBe '' expect(horizontalScrollbarNode.style.right).toBe verticalScrollbarNode.offsetWidth + 'px' expect(scrollbarCornerNode.style.display).toBe 'none' - node.style.width = 10 * charWidth + 'px' + componentNode.style.width = 10 * charWidth + 'px' component.measureScrollView() runSetImmediateCallbacks() expect(verticalScrollbarNode.style.bottom).toBe horizontalScrollbarNode.offsetHeight + 'px' expect(horizontalScrollbarNode.style.right).toBe verticalScrollbarNode.offsetWidth + 'px' expect(scrollbarCornerNode.style.display).toBe '' - node.style.height = 20 * lineHeightInPixels + 'px' + wrapperNode.style.height = 20 * lineHeightInPixels + 'px' component.measureScrollView() runSetImmediateCallbacks() expect(verticalScrollbarNode.style.bottom).toBe horizontalScrollbarNode.offsetHeight + 'px' @@ -1559,8 +1560,8 @@ describe "EditorComponent", -> expect(scrollbarCornerNode.style.display).toBe 'none' it "accounts for the width of the gutter in the scrollWidth of the horizontal scrollbar", -> - gutterNode = node.querySelector('.gutter') - node.style.width = 10 * charWidth + 'px' + gutterNode = componentNode.querySelector('.gutter') + componentNode.style.width = 10 * charWidth + 'px' component.measureScrollView() runSetImmediateCallbacks() @@ -1572,8 +1573,8 @@ describe "EditorComponent", -> describe "updating scrollTop and scrollLeft", -> beforeEach -> - node.style.height = 4.5 * lineHeightInPixels + 'px' - node.style.width = 20 * charWidth + 'px' + wrapperNode.style.height = 4.5 * lineHeightInPixels + 'px' + wrapperNode.style.width = 20 * charWidth + 'px' component.measureScrollView() runSetImmediateCallbacks() @@ -1581,55 +1582,55 @@ describe "EditorComponent", -> expect(verticalScrollbarNode.scrollTop).toBe 0 expect(horizontalScrollbarNode.scrollLeft).toBe 0 - node.dispatchEvent(new WheelEvent('mousewheel', wheelDeltaX: -5, wheelDeltaY: -10)) + componentNode.dispatchEvent(new WheelEvent('mousewheel', wheelDeltaX: -5, wheelDeltaY: -10)) expect(verticalScrollbarNode.scrollTop).toBe 10 expect(horizontalScrollbarNode.scrollLeft).toBe 0 - node.dispatchEvent(new WheelEvent('mousewheel', wheelDeltaX: -15, wheelDeltaY: -5)) + componentNode.dispatchEvent(new WheelEvent('mousewheel', wheelDeltaX: -15, wheelDeltaY: -5)) expect(verticalScrollbarNode.scrollTop).toBe 10 expect(horizontalScrollbarNode.scrollLeft).toBe 15 it "updates the scrollLeft or scrollTop according to the scroll sensitivity", -> atom.config.set('editor.scrollSensitivity', 50) - node.dispatchEvent(new WheelEvent('mousewheel', wheelDeltaX: -5, wheelDeltaY: -10)) + componentNode.dispatchEvent(new WheelEvent('mousewheel', wheelDeltaX: -5, wheelDeltaY: -10)) expect(horizontalScrollbarNode.scrollLeft).toBe 0 - node.dispatchEvent(new WheelEvent('mousewheel', wheelDeltaX: -15, wheelDeltaY: -5)) + componentNode.dispatchEvent(new WheelEvent('mousewheel', wheelDeltaX: -15, wheelDeltaY: -5)) expect(verticalScrollbarNode.scrollTop).toBe 5 expect(horizontalScrollbarNode.scrollLeft).toBe 7 it "uses the previous scrollSensitivity when the value is not an int", -> atom.config.set('editor.scrollSensitivity', 'nope') - node.dispatchEvent(new WheelEvent('mousewheel', wheelDeltaX: 0, wheelDeltaY: -10)) + componentNode.dispatchEvent(new WheelEvent('mousewheel', wheelDeltaX: 0, wheelDeltaY: -10)) expect(verticalScrollbarNode.scrollTop).toBe 10 it "parses negative scrollSensitivity values as positive", -> atom.config.set('editor.scrollSensitivity', -50) - node.dispatchEvent(new WheelEvent('mousewheel', wheelDeltaX: 0, wheelDeltaY: -10)) + componentNode.dispatchEvent(new WheelEvent('mousewheel', wheelDeltaX: 0, wheelDeltaY: -10)) expect(verticalScrollbarNode.scrollTop).toBe 5 describe "when the mousewheel event's target is a line", -> it "keeps the line on the DOM if it is scrolled off-screen", -> - node.style.height = 4.5 * lineHeightInPixels + 'px' - node.style.width = 20 * charWidth + 'px' + wrapperNode.style.height = 4.5 * lineHeightInPixels + 'px' + wrapperNode.style.width = 20 * charWidth + 'px' component.measureScrollView() - lineNode = node.querySelector('.line') + lineNode = componentNode.querySelector('.line') wheelEvent = new WheelEvent('mousewheel', wheelDeltaX: 0, wheelDeltaY: -500) Object.defineProperty(wheelEvent, 'target', get: -> lineNode) - node.dispatchEvent(wheelEvent) + componentNode.dispatchEvent(wheelEvent) - expect(node.contains(lineNode)).toBe true + expect(componentNode.contains(lineNode)).toBe true it "does not set the mouseWheelScreenRow if scrolling horizontally", -> - node.style.height = 4.5 * lineHeightInPixels + 'px' - node.style.width = 20 * charWidth + 'px' + wrapperNode.style.height = 4.5 * lineHeightInPixels + 'px' + wrapperNode.style.width = 20 * charWidth + 'px' component.measureScrollView() - lineNode = node.querySelector('.line') + lineNode = componentNode.querySelector('.line') wheelEvent = new WheelEvent('mousewheel', wheelDeltaX: 10, wheelDeltaY: 0) Object.defineProperty(wheelEvent, 'target', get: -> lineNode) - node.dispatchEvent(wheelEvent) + componentNode.dispatchEvent(wheelEvent) expect(component.mouseWheelScreenRow).toBe null @@ -1638,10 +1639,10 @@ describe "EditorComponent", -> expect(editor.getScrollTop()).toBe 0 - lineNode = node.querySelector('.line') + lineNode = componentNode.querySelector('.line') wheelEvent = new WheelEvent('mousewheel', wheelDeltaX: 0, wheelDeltaY: 10) Object.defineProperty(wheelEvent, 'target', get: -> lineNode) - node.dispatchEvent(wheelEvent) + componentNode.dispatchEvent(wheelEvent) expect(editor.getScrollTop()).toBe 0 @@ -1650,38 +1651,38 @@ describe "EditorComponent", -> expect(component.mouseWheelScreenRow).toBe null it "does not preserve the line if it is on screen", -> - expect(node.querySelectorAll('.line-number').length).toBe 14 # dummy line - lineNodes = node.querySelectorAll('.line') + expect(componentNode.querySelectorAll('.line-number').length).toBe 14 # dummy line + lineNodes = componentNode.querySelectorAll('.line') expect(lineNodes.length).toBe 13 lineNode = lineNodes[0] wheelEvent = new WheelEvent('mousewheel', wheelDeltaX: 0, wheelDeltaY: 100) # goes nowhere, we're already at scrollTop 0 Object.defineProperty(wheelEvent, 'target', get: -> lineNode) - node.dispatchEvent(wheelEvent) + componentNode.dispatchEvent(wheelEvent) expect(component.mouseWheelScreenRow).toBe 0 editor.insertText("hello") - expect(node.querySelectorAll('.line-number').length).toBe 14 # dummy line - expect(node.querySelectorAll('.line').length).toBe 13 + expect(componentNode.querySelectorAll('.line-number').length).toBe 14 # dummy line + expect(componentNode.querySelectorAll('.line').length).toBe 13 describe "when the mousewheel event's target is a line number", -> it "keeps the line number on the DOM if it is scrolled off-screen", -> - node.style.height = 4.5 * lineHeightInPixels + 'px' - node.style.width = 20 * charWidth + 'px' + wrapperNode.style.height = 4.5 * lineHeightInPixels + 'px' + wrapperNode.style.width = 20 * charWidth + 'px' component.measureScrollView() - lineNumberNode = node.querySelectorAll('.line-number')[1] + lineNumberNode = componentNode.querySelectorAll('.line-number')[1] wheelEvent = new WheelEvent('mousewheel', wheelDeltaX: 0, wheelDeltaY: -500) Object.defineProperty(wheelEvent, 'target', get: -> lineNumberNode) - node.dispatchEvent(wheelEvent) + componentNode.dispatchEvent(wheelEvent) - expect(node.contains(lineNumberNode)).toBe true + expect(componentNode.contains(lineNumberNode)).toBe true describe "input events", -> inputNode = null beforeEach -> - inputNode = node.querySelector('.hidden-input') + inputNode = componentNode.querySelector('.hidden-input') buildTextInputEvent = ({data, target}) -> event = new Event('textInput') @@ -1690,28 +1691,28 @@ describe "EditorComponent", -> event it "inserts the newest character in the input's value into the buffer", -> - node.dispatchEvent(buildTextInputEvent(data: 'x', target: inputNode)) + componentNode.dispatchEvent(buildTextInputEvent(data: 'x', target: inputNode)) runSetImmediateCallbacks() expect(editor.lineForBufferRow(0)).toBe 'xvar quicksort = function () {' - node.dispatchEvent(buildTextInputEvent(data: 'y', target: inputNode)) + componentNode.dispatchEvent(buildTextInputEvent(data: 'y', target: inputNode)) runSetImmediateCallbacks() expect(editor.lineForBufferRow(0)).toBe 'xyvar quicksort = function () {' it "replaces the last character if the length of the input's value doesn't increase, as occurs with the accented character menu", -> - node.dispatchEvent(buildTextInputEvent(data: 'u', target: inputNode)) + componentNode.dispatchEvent(buildTextInputEvent(data: 'u', target: inputNode)) runSetImmediateCallbacks() expect(editor.lineForBufferRow(0)).toBe 'uvar quicksort = function () {' # simulate the accented character suggestion's selection of the previous character inputNode.setSelectionRange(0, 1) - node.dispatchEvent(buildTextInputEvent(data: 'ü', target: inputNode)) + componentNode.dispatchEvent(buildTextInputEvent(data: 'ü', target: inputNode)) runSetImmediateCallbacks() expect(editor.lineForBufferRow(0)).toBe 'üvar quicksort = function () {' it "does not handle input events when input is disabled", -> component.setInputEnabled(false) - node.dispatchEvent(buildTextInputEvent(data: 'x', target: inputNode)) + componentNode.dispatchEvent(buildTextInputEvent(data: 'x', target: inputNode)) runSetImmediateCallbacks() expect(editor.lineForBufferRow(0)).toBe 'var quicksort = function () {' @@ -1725,51 +1726,51 @@ describe "EditorComponent", -> event beforeEach -> - inputNode = inputNode = node.querySelector('.hidden-input') + inputNode = inputNode = componentNode.querySelector('.hidden-input') describe "when nothing is selected", -> it "inserts the chosen completion", -> - node.dispatchEvent(buildIMECompositionEvent('compositionstart', target: inputNode)) - node.dispatchEvent(buildIMECompositionEvent('compositionupdate', data: 's', target: inputNode)) + componentNode.dispatchEvent(buildIMECompositionEvent('compositionstart', target: inputNode)) + componentNode.dispatchEvent(buildIMECompositionEvent('compositionupdate', data: 's', target: inputNode)) expect(editor.lineForBufferRow(0)).toBe 'svar quicksort = function () {' - node.dispatchEvent(buildIMECompositionEvent('compositionupdate', data: 'sd', target: inputNode)) + componentNode.dispatchEvent(buildIMECompositionEvent('compositionupdate', data: 'sd', target: inputNode)) expect(editor.lineForBufferRow(0)).toBe 'sdvar quicksort = function () {' - node.dispatchEvent(buildIMECompositionEvent('compositionend', target: inputNode)) - node.dispatchEvent(buildTextInputEvent(data: '速度', target: inputNode)) + componentNode.dispatchEvent(buildIMECompositionEvent('compositionend', target: inputNode)) + componentNode.dispatchEvent(buildTextInputEvent(data: '速度', target: inputNode)) expect(editor.lineForBufferRow(0)).toBe '速度var quicksort = function () {' it "reverts back to the original text when the completion helper is dismissed", -> - node.dispatchEvent(buildIMECompositionEvent('compositionstart', target: inputNode)) - node.dispatchEvent(buildIMECompositionEvent('compositionupdate', data: 's', target: inputNode)) + componentNode.dispatchEvent(buildIMECompositionEvent('compositionstart', target: inputNode)) + componentNode.dispatchEvent(buildIMECompositionEvent('compositionupdate', data: 's', target: inputNode)) expect(editor.lineForBufferRow(0)).toBe 'svar quicksort = function () {' - node.dispatchEvent(buildIMECompositionEvent('compositionupdate', data: 'sd', target: inputNode)) + componentNode.dispatchEvent(buildIMECompositionEvent('compositionupdate', data: 'sd', target: inputNode)) expect(editor.lineForBufferRow(0)).toBe 'sdvar quicksort = function () {' - node.dispatchEvent(buildIMECompositionEvent('compositionend', target: inputNode)) + componentNode.dispatchEvent(buildIMECompositionEvent('compositionend', target: inputNode)) expect(editor.lineForBufferRow(0)).toBe 'var quicksort = function () {' it "allows multiple accented character to be inserted with the ' on a US international layout", -> inputNode.value = "'" inputNode.setSelectionRange(0, 1) - node.dispatchEvent(buildIMECompositionEvent('compositionstart', target: inputNode)) - node.dispatchEvent(buildIMECompositionEvent('compositionupdate', data: "'", target: inputNode)) + componentNode.dispatchEvent(buildIMECompositionEvent('compositionstart', target: inputNode)) + componentNode.dispatchEvent(buildIMECompositionEvent('compositionupdate', data: "'", target: inputNode)) expect(editor.lineForBufferRow(0)).toBe "'var quicksort = function () {" - node.dispatchEvent(buildIMECompositionEvent('compositionend', target: inputNode)) - node.dispatchEvent(buildTextInputEvent(data: 'á', target: inputNode)) + componentNode.dispatchEvent(buildIMECompositionEvent('compositionend', target: inputNode)) + componentNode.dispatchEvent(buildTextInputEvent(data: 'á', target: inputNode)) expect(editor.lineForBufferRow(0)).toBe "ávar quicksort = function () {" inputNode.value = "'" inputNode.setSelectionRange(0, 1) - node.dispatchEvent(buildIMECompositionEvent('compositionstart', target: inputNode)) - node.dispatchEvent(buildIMECompositionEvent('compositionupdate', data: "'", target: inputNode)) + componentNode.dispatchEvent(buildIMECompositionEvent('compositionstart', target: inputNode)) + componentNode.dispatchEvent(buildIMECompositionEvent('compositionupdate', data: "'", target: inputNode)) expect(editor.lineForBufferRow(0)).toBe "á'var quicksort = function () {" - node.dispatchEvent(buildIMECompositionEvent('compositionend', target: inputNode)) - node.dispatchEvent(buildTextInputEvent(data: 'á', target: inputNode)) + componentNode.dispatchEvent(buildIMECompositionEvent('compositionend', target: inputNode)) + componentNode.dispatchEvent(buildTextInputEvent(data: 'á', target: inputNode)) expect(editor.lineForBufferRow(0)).toBe "áávar quicksort = function () {" describe "when a string is selected", -> @@ -1777,26 +1778,26 @@ describe "EditorComponent", -> editor.setSelectedBufferRange [[0, 4], [0, 9]] # select 'quick' it "inserts the chosen completion", -> - node.dispatchEvent(buildIMECompositionEvent('compositionstart', target: inputNode)) - node.dispatchEvent(buildIMECompositionEvent('compositionupdate', data: 's', target: inputNode)) + componentNode.dispatchEvent(buildIMECompositionEvent('compositionstart', target: inputNode)) + componentNode.dispatchEvent(buildIMECompositionEvent('compositionupdate', data: 's', target: inputNode)) expect(editor.lineForBufferRow(0)).toBe 'var ssort = function () {' - node.dispatchEvent(buildIMECompositionEvent('compositionupdate', data: 'sd', target: inputNode)) + componentNode.dispatchEvent(buildIMECompositionEvent('compositionupdate', data: 'sd', target: inputNode)) expect(editor.lineForBufferRow(0)).toBe 'var sdsort = function () {' - node.dispatchEvent(buildIMECompositionEvent('compositionend', target: inputNode)) - node.dispatchEvent(buildTextInputEvent(data: '速度', target: inputNode)) + componentNode.dispatchEvent(buildIMECompositionEvent('compositionend', target: inputNode)) + componentNode.dispatchEvent(buildTextInputEvent(data: '速度', target: inputNode)) expect(editor.lineForBufferRow(0)).toBe 'var 速度sort = function () {' it "reverts back to the original text when the completion helper is dismissed", -> - node.dispatchEvent(buildIMECompositionEvent('compositionstart', target: inputNode)) - node.dispatchEvent(buildIMECompositionEvent('compositionupdate', data: 's', target: inputNode)) + componentNode.dispatchEvent(buildIMECompositionEvent('compositionstart', target: inputNode)) + componentNode.dispatchEvent(buildIMECompositionEvent('compositionupdate', data: 's', target: inputNode)) expect(editor.lineForBufferRow(0)).toBe 'var ssort = function () {' - node.dispatchEvent(buildIMECompositionEvent('compositionupdate', data: 'sd', target: inputNode)) + componentNode.dispatchEvent(buildIMECompositionEvent('compositionupdate', data: 'sd', target: inputNode)) expect(editor.lineForBufferRow(0)).toBe 'var sdsort = function () {' - node.dispatchEvent(buildIMECompositionEvent('compositionend', target: inputNode)) + componentNode.dispatchEvent(buildIMECompositionEvent('compositionend', target: inputNode)) expect(editor.lineForBufferRow(0)).toBe 'var quicksort = function () {' describe "commands", -> @@ -1806,7 +1807,7 @@ describe "EditorComponent", -> event = new CustomEvent('editor:consolidate-selections', bubbles: true, cancelable: true) event.abortKeyBinding = jasmine.createSpy("event.abortKeyBinding") - node.dispatchEvent(event) + componentNode.dispatchEvent(event) expect(editor.consolidateSelections).toHaveBeenCalled() expect(event.abortKeyBinding).toHaveBeenCalled() @@ -1849,8 +1850,8 @@ describe "EditorComponent", -> editor.setCursorBufferPosition([0, Infinity]) runSetImmediateCallbacks() - cursorLeft = node.querySelector('.cursor').getBoundingClientRect().left - line0Right = node.querySelector('.line > span:last-child').getBoundingClientRect().right + cursorLeft = componentNode.querySelector('.cursor').getBoundingClientRect().left + line0Right = componentNode.querySelector('.line > span:last-child').getBoundingClientRect().right expect(cursorLeft).toBe line0Right describe "when the fontFamily changes while the editor is hidden", -> @@ -1876,8 +1877,8 @@ describe "EditorComponent", -> editor.setCursorBufferPosition([0, Infinity]) runSetImmediateCallbacks() - cursorLeft = node.querySelector('.cursor').getBoundingClientRect().left - line0Right = node.querySelector('.line > span:last-child').getBoundingClientRect().right + cursorLeft = componentNode.querySelector('.cursor').getBoundingClientRect().left + line0Right = componentNode.querySelector('.line > span:last-child').getBoundingClientRect().right expect(cursorLeft).toBe line0Right describe "when stylesheets change while the editor is hidden", -> @@ -1899,8 +1900,8 @@ describe "EditorComponent", -> editor.setCursorBufferPosition([0, Infinity]) runSetImmediateCallbacks() - cursorLeft = node.querySelector('.cursor').getBoundingClientRect().left - line0Right = node.querySelector('.line > span:last-child').getBoundingClientRect().right + cursorLeft = componentNode.querySelector('.cursor').getBoundingClientRect().left + line0Right = componentNode.querySelector('.line > span:last-child').getBoundingClientRect().right expect(cursorLeft).toBe line0Right describe "when lines are changed while the editor is hidden", -> @@ -1911,7 +1912,7 @@ describe "EditorComponent", -> editor.setCursorBufferPosition([0, Infinity]) runSetImmediateCallbacks() wrapperView.show() - expect(node.querySelector('.cursor').style['-webkit-transform']).toBe "translate3d(#{9 * charWidth}px, 0px, 0px)" + expect(componentNode.querySelector('.cursor').style['-webkit-transform']).toBe "translate3d(#{9 * charWidth}px, 0px, 0px)" describe "soft wrapping", -> beforeEach -> @@ -1919,23 +1920,23 @@ describe "EditorComponent", -> it "updates the wrap location when the editor is resized", -> newHeight = 4 * editor.getLineHeightInPixels() + "px" - expect(newHeight).toBeLessThan node.style.height - node.style.height = newHeight + expect(newHeight).toBeLessThan wrapperNode.style.height + wrapperNode.style.height = newHeight advanceClock(component.scrollViewMeasurementInterval) runSetImmediateCallbacks() - expect(node.querySelectorAll('.line')).toHaveLength(4 + lineOverdrawMargin + 1) + expect(componentNode.querySelectorAll('.line')).toHaveLength(4 + lineOverdrawMargin + 1) - gutterWidth = node.querySelector('.gutter').offsetWidth - node.style.width = gutterWidth + 14 * charWidth + 'px' + gutterWidth = componentNode.querySelector('.gutter').offsetWidth + componentNode.style.width = gutterWidth + 14 * charWidth + 'px' advanceClock(component.scrollViewMeasurementInterval) runSetImmediateCallbacks() - expect(node.querySelector('.line').textContent).toBe "var quicksort " + expect(componentNode.querySelector('.line').textContent).toBe "var quicksort " it "accounts for the scroll view's padding when determining the wrap location", -> - scrollViewNode = node.querySelector('.scroll-view') + scrollViewNode = componentNode.querySelector('.scroll-view') scrollViewNode.style.paddingLeft = 20 + 'px' - node.style.width = 30 * charWidth + 'px' + componentNode.style.width = 30 * charWidth + 'px' advanceClock(component.scrollViewMeasurementInterval) runSetImmediateCallbacks() @@ -2014,14 +2015,14 @@ describe "EditorComponent", -> clientCoordinatesForScreenPosition = (screenPosition) -> positionOffset = editor.pixelPositionForScreenPosition(screenPosition) - scrollViewClientRect = node.querySelector('.scroll-view').getBoundingClientRect() + scrollViewClientRect = componentNode.querySelector('.scroll-view').getBoundingClientRect() clientX = scrollViewClientRect.left + positionOffset.left - editor.getScrollLeft() clientY = scrollViewClientRect.top + positionOffset.top - editor.getScrollTop() {clientX, clientY} clientCoordinatesForScreenRowInGutter = (screenRow) -> positionOffset = editor.pixelPositionForScreenPosition([screenRow, 1]) - gutterClientRect = node.querySelector('.gutter').getBoundingClientRect() + gutterClientRect = componentNode.querySelector('.gutter').getBoundingClientRect() clientX = gutterClientRect.left + positionOffset.left - editor.getScrollLeft() clientY = gutterClientRect.top + positionOffset.top - editor.getScrollTop() {clientX, clientY} diff --git a/src/editor-component.coffee b/src/editor-component.coffee index 1d1ce4318..217b1c20f 100644 --- a/src/editor-component.coffee +++ b/src/editor-component.coffee @@ -759,21 +759,22 @@ EditorComponent = React.createClass return if @scrollViewMeasurementPaused return unless @isMounted() - {editor} = @props - editorNode = @getDOMNode() + {editor, parentView} = @props + parentNode = parentView.element scrollViewNode = @refs.scrollView.getDOMNode() - {position} = getComputedStyle(editorNode) - {width, height} = editorNode.style + {position} = getComputedStyle(parentNode) + {height} = parentNode.style if position is 'absolute' or height clientHeight = scrollViewNode.clientHeight editor.setHeight(clientHeight) if clientHeight > 0 + else + editor.setHeight(null) - if position is 'absolute' or width - clientWidth = scrollViewNode.clientWidth - paddingLeft = parseInt(getComputedStyle(scrollViewNode).paddingLeft) - clientWidth -= paddingLeft - editor.setWidth(clientWidth) if clientWidth > 0 + clientWidth = scrollViewNode.clientWidth + paddingLeft = parseInt(getComputedStyle(scrollViewNode).paddingLeft) + clientWidth -= paddingLeft + editor.setWidth(clientWidth) if clientWidth > 0 measureLineHeightAndCharWidthsIfNeeded: (prevState) -> if not isEqualForProperties(prevState, @state, 'lineHeight', 'fontSize', 'fontFamily') diff --git a/static/editor.less b/static/editor.less index 03ce26693..345096c7c 100644 --- a/static/editor.less +++ b/static/editor.less @@ -3,6 +3,10 @@ @import "octicon-mixins"; .editor.react { + .editor-contents { + width: 100%; + } + .underlayer { position: absolute; top: 0; From 4020ed153585bbb7c3551f6010b7de804ccfa08d Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Sun, 13 Jul 2014 12:07:36 -0600 Subject: [PATCH 19/61] Support ReactEditorView construction with params hash --- src/react-editor-view.coffee | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/react-editor-view.coffee b/src/react-editor-view.coffee index 7a97d5355..ecb5b24b7 100644 --- a/src/react-editor-view.coffee +++ b/src/react-editor-view.coffee @@ -1,7 +1,9 @@ {View, $} = require 'space-pen' React = require 'react-atom-fork' -EditorComponent = require './editor-component' {defaults} = require 'underscore-plus' +TextBuffer = require 'text-buffer' +Editor = require './editor' +EditorComponent = require './editor-component' module.exports = class ReactEditorView extends View @@ -9,7 +11,20 @@ class ReactEditorView extends View focusOnAttach: false - constructor: (@editor, @props) -> + constructor: (editorOrParams, @props) -> + if editorOrParams instanceof Editor + @editor = editorOrParams + else + {@editor, mini, placeholderText} = editorOrParams + @props ?= {} + @props.mini = mini + @props.placeholderText = placeholderText + @editor ?= new Editor + buffer: new TextBuffer + softWrap: false + tabLength: 2 + softTabs: true + super getEditor: -> @editor From cc8b7b13b3a75c7d718cf0e6be6906fdf6a37d88 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Sun, 13 Jul 2014 13:06:37 -0600 Subject: [PATCH 20/61] Don't show the gutter when 'mini' is true on React editors --- spec/editor-component-spec.coffee | 7 +++++++ src/editor-component.coffee | 4 ++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/spec/editor-component-spec.coffee b/spec/editor-component-spec.coffee index 42f7bdb3b..2c2f8ed16 100644 --- a/spec/editor-component-spec.coffee +++ b/spec/editor-component-spec.coffee @@ -1991,6 +1991,13 @@ describe "EditorComponent", -> runSetImmediateCallbacks() expect(lineNumberHasClass(4, 'cursor-line-no-selection')).toBe false + describe "when the 'mini' property is true", -> + beforeEach -> + component.setProps(mini: true) + + it "does not render the gutter", -> + expect(componentNode.querySelector('.gutter')).toBeNull() + describe "legacy editor compatibility", -> it "triggers the screen-lines-changed event before the editor:display-update event", -> editor.setSoftWrap(true) diff --git a/src/editor-component.coffee b/src/editor-component.coffee index 217b1c20f..59d969c3a 100644 --- a/src/editor-component.coffee +++ b/src/editor-component.coffee @@ -50,7 +50,7 @@ EditorComponent = React.createClass render: -> {focused, fontSize, lineHeight, fontFamily, showIndentGuide, showInvisibles, showLineNumbers, visible} = @state - {editor, cursorBlinkPeriod, cursorBlinkResumeDelay} = @props + {editor, mini, cursorBlinkPeriod, cursorBlinkResumeDelay} = @props maxLineNumberDigits = editor.getLineCount().toString().length invisibles = if showInvisibles then @state.invisibles else {} hasSelection = editor.getSelection()? and !editor.getSelection().isEmpty() @@ -86,7 +86,7 @@ EditorComponent = React.createClass className += ' has-selection' if hasSelection div className: className, style: {fontSize, lineHeight, fontFamily}, tabIndex: -1, - if showLineNumbers + if not mini and showLineNumbers GutterComponent { ref: 'gutter', onMouseDown: @onGutterMouseDown, onWidthChanged: @onGutterWidthChanged, lineDecorations, defaultCharWidth, editor, renderedRowRange, maxLineNumberDigits, scrollViewHeight, From 635f288050feb6474e6ead20f960e6b100aa46bd Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Sun, 13 Jul 2014 13:50:33 -0600 Subject: [PATCH 21/61] Explicitly assign height of editor-contents when height is auto --- spec/editor-component-spec.coffee | 16 +++++++++++++--- src/editor-component.coffee | 10 +++++++++- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/spec/editor-component-spec.coffee b/spec/editor-component-spec.coffee index 2c2f8ed16..d81d114d7 100644 --- a/spec/editor-component-spec.coffee +++ b/spec/editor-component-spec.coffee @@ -61,8 +61,6 @@ describe "EditorComponent", -> verticalScrollbarNode = componentNode.querySelector('.vertical-scrollbar') horizontalScrollbarNode = componentNode.querySelector('.horizontal-scrollbar') - wrapperNode.style.height = editor.getLineCount() * lineHeightInPixels + 'px' - wrapperNode.style.width = '1000px' component.measureScrollView() runSetImmediateCallbacks() @@ -1920,7 +1918,7 @@ describe "EditorComponent", -> it "updates the wrap location when the editor is resized", -> newHeight = 4 * editor.getLineHeightInPixels() + "px" - expect(newHeight).toBeLessThan wrapperNode.style.height + expect(parseInt(newHeight)).toBeLessThan wrapperNode.offsetHeight wrapperNode.style.height = newHeight advanceClock(component.scrollViewMeasurementInterval) @@ -1991,6 +1989,18 @@ describe "EditorComponent", -> runSetImmediateCallbacks() expect(lineNumberHasClass(4, 'cursor-line-no-selection')).toBe false + describe "height", -> + describe "when the wrapper view has an explicit height", -> + it "does not assign a height on the component node", -> + wrapperNode.style.height = '200px' + component.measureScrollView() + expect(componentNode.style.height).toBe '' + + describe "when the wrapper view does not have an explicit height", -> + it "assigns a height on the component node based on the editor's content", -> + expect(wrapperNode.style.height).toBe '' + expect(componentNode.style.height).toBe editor.getScreenLineCount() * lineHeightInPixels + 'px' + describe "when the 'mini' property is true", -> beforeEach -> component.setProps(mini: true) diff --git a/src/editor-component.coffee b/src/editor-component.coffee index 59d969c3a..1a15e553a 100644 --- a/src/editor-component.coffee +++ b/src/editor-component.coffee @@ -47,6 +47,7 @@ EditorComponent = React.createClass scrollViewMeasurementInterval: 100 scopedCharacterWidthsChangeCount: null scrollViewMeasurementPaused: false + autoHeight: false render: -> {focused, fontSize, lineHeight, fontFamily, showIndentGuide, showInvisibles, showLineNumbers, visible} = @state @@ -54,6 +55,7 @@ EditorComponent = React.createClass maxLineNumberDigits = editor.getLineCount().toString().length invisibles = if showInvisibles then @state.invisibles else {} hasSelection = editor.getSelection()? and !editor.getSelection().isEmpty() + style = {fontSize, lineHeight, fontFamily} if @isMounted() renderedRowRange = @getRenderedRowRange() @@ -80,12 +82,13 @@ EditorComponent = React.createClass hiddenInputStyle.WebkitTransform = 'translateZ(0)' if @useHardwareAcceleration if @mouseWheelScreenRow? and not (renderedStartRow <= @mouseWheelScreenRow < renderedEndRow) mouseWheelScreenRow = @mouseWheelScreenRow + style.height = scrollViewHeight if @autoHeight className = 'editor-contents editor-colors' className += ' is-focused' if focused className += ' has-selection' if hasSelection - div className: className, style: {fontSize, lineHeight, fontFamily}, tabIndex: -1, + div {className, style, tabIndex: -1}, if not mini and showLineNumbers GutterComponent { ref: 'gutter', onMouseDown: @onGutterMouseDown, onWidthChanged: @onGutterWidthChanged, @@ -766,10 +769,15 @@ EditorComponent = React.createClass {height} = parentNode.style if position is 'absolute' or height + if @autoHeight + @autoHeight = false + @forceUpdate() + clientHeight = scrollViewNode.clientHeight editor.setHeight(clientHeight) if clientHeight > 0 else editor.setHeight(null) + @autoHeight = true clientWidth = scrollViewNode.clientWidth paddingLeft = parseInt(getComputedStyle(scrollViewNode).paddingLeft) From 56c9f75e8c6a568d37caffdfbdba761a282ae0d6 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Sun, 13 Jul 2014 13:59:32 -0600 Subject: [PATCH 22/61] Add the 'mini' class to the React wrapper view for mini editors --- spec/editor-component-spec.coffee | 3 +++ src/editor-component.coffee | 5 +++++ 2 files changed, 8 insertions(+) diff --git a/spec/editor-component-spec.coffee b/spec/editor-component-spec.coffee index d81d114d7..d3e859ac4 100644 --- a/spec/editor-component-spec.coffee +++ b/spec/editor-component-spec.coffee @@ -2008,6 +2008,9 @@ describe "EditorComponent", -> it "does not render the gutter", -> expect(componentNode.querySelector('.gutter')).toBeNull() + it "adds the 'mini' class to the wrapper view", -> + expect(wrapperNode.classList.contains('mini')).toBe true + describe "legacy editor compatibility", -> it "triggers the screen-lines-changed event before the editor:display-update event", -> editor.setSoftWrap(true) diff --git a/src/editor-component.coffee b/src/editor-component.coffee index 1a15e553a..0f1a0a5d9 100644 --- a/src/editor-component.coffee +++ b/src/editor-component.coffee @@ -200,6 +200,7 @@ EditorComponent = React.createClass if @props.editor.isAlive() @updateParentViewFocusedClassIfNeeded(prevState) + @updateParentViewMiniClassIfNeeded(prevState) @props.parentView.trigger 'cursor:moved' if cursorsMoved @props.parentView.trigger 'selection:changed' if selectionChanged @props.parentView.trigger 'editor:display-updated' @@ -949,6 +950,10 @@ EditorComponent = React.createClass if prevState.focused isnt @state.focused @props.parentView.toggleClass('is-focused', @props.focused) + updateParentViewMiniClassIfNeeded: (prevProps) -> + if prevProps.mini isnt @props.mini + @props.parentView.toggleClass('mini', @props.mini) + runScrollBenchmark: -> unless process.env.NODE_ENV is 'production' ReactPerf = require 'react-atom-fork/lib/ReactDefaultPerf' From 759dbc061de327ca2d38c5ab56a9e6dcb88d3abf Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Sun, 13 Jul 2014 14:03:51 -0600 Subject: [PATCH 23/61] Don't render invisible characters in React mini editors --- spec/editor-component-spec.coffee | 5 +++++ src/editor-component.coffee | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/spec/editor-component-spec.coffee b/spec/editor-component-spec.coffee index d3e859ac4..c4e01ddd2 100644 --- a/spec/editor-component-spec.coffee +++ b/spec/editor-component-spec.coffee @@ -2011,6 +2011,11 @@ describe "EditorComponent", -> it "adds the 'mini' class to the wrapper view", -> expect(wrapperNode.classList.contains('mini')).toBe true + it "does not render invisible characters", -> + component.setInvisibles(eol: 'E') + component.setShowInvisibles(true) + expect(component.lineNodeForScreenRow(0).textContent).toBe 'var quicksort = function () {' + describe "legacy editor compatibility", -> it "triggers the screen-lines-changed event before the editor:display-update event", -> editor.setSoftWrap(true) diff --git a/src/editor-component.coffee b/src/editor-component.coffee index 0f1a0a5d9..748db128e 100644 --- a/src/editor-component.coffee +++ b/src/editor-component.coffee @@ -53,7 +53,7 @@ EditorComponent = React.createClass {focused, fontSize, lineHeight, fontFamily, showIndentGuide, showInvisibles, showLineNumbers, visible} = @state {editor, mini, cursorBlinkPeriod, cursorBlinkResumeDelay} = @props maxLineNumberDigits = editor.getLineCount().toString().length - invisibles = if showInvisibles then @state.invisibles else {} + invisibles = if showInvisibles and not mini then @state.invisibles else {} hasSelection = editor.getSelection()? and !editor.getSelection().isEmpty() style = {fontSize, lineHeight, fontFamily} From 544c759fd1e0e8273718f456271acf8de5126e95 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Sun, 13 Jul 2014 14:45:38 -0600 Subject: [PATCH 24/61] Don't set an explicit line height on mini editors This allows the line height to be styled via CSS. I would actually like to allow all these properties to be assigned via CSS rather than explicitly via the settings view, but that can be deferred until the old editor is removed. --- spec/editor-component-spec.coffee | 3 +++ src/editor-component.coffee | 3 ++- static/editor.less | 9 ++++++--- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/spec/editor-component-spec.coffee b/spec/editor-component-spec.coffee index c4e01ddd2..d6a807c8a 100644 --- a/spec/editor-component-spec.coffee +++ b/spec/editor-component-spec.coffee @@ -2016,6 +2016,9 @@ describe "EditorComponent", -> component.setShowInvisibles(true) expect(component.lineNodeForScreenRow(0).textContent).toBe 'var quicksort = function () {' + it "does not assign an explicit line-height on the editor contents", -> + expect(componentNode.style.lineHeight).toBe '' + describe "legacy editor compatibility", -> it "triggers the screen-lines-changed event before the editor:display-update event", -> editor.setSoftWrap(true) diff --git a/src/editor-component.coffee b/src/editor-component.coffee index 748db128e..5527c9fc0 100644 --- a/src/editor-component.coffee +++ b/src/editor-component.coffee @@ -55,7 +55,8 @@ EditorComponent = React.createClass maxLineNumberDigits = editor.getLineCount().toString().length invisibles = if showInvisibles and not mini then @state.invisibles else {} hasSelection = editor.getSelection()? and !editor.getSelection().isEmpty() - style = {fontSize, lineHeight, fontFamily} + style = {fontSize, fontFamily} + style.lineHeight = lineHeight unless mini if @isMounted() renderedRowRange = @getRenderedRowRange() diff --git a/static/editor.less b/static/editor.less index 345096c7c..ed8d6ba84 100644 --- a/static/editor.less +++ b/static/editor.less @@ -85,15 +85,18 @@ } } +.editor { + z-index: 0; + font-family: Inconsolata, Monaco, Consolas, 'Courier New', Courier; + line-height: 1.3; +} + .editor, .editor-contents { overflow: hidden; cursor: text; display: -webkit-flex; -webkit-user-select: none; position: relative; - z-index: 0; - font-family: Inconsolata, Monaco, Consolas, 'Courier New', Courier; - line-height: 1.3; } .editor .gutter .line-number.cursor-line { From a9c7842a5049abd0c43241f23c66211abf22ccfb Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Sun, 13 Jul 2014 14:56:34 -0600 Subject: [PATCH 25/61] Don't render line decorations on mini editors --- spec/editor-component-spec.coffee | 3 +++ src/editor-component.coffee | 4 +++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/spec/editor-component-spec.coffee b/spec/editor-component-spec.coffee index d6a807c8a..3ec8d5123 100644 --- a/spec/editor-component-spec.coffee +++ b/spec/editor-component-spec.coffee @@ -2019,6 +2019,9 @@ describe "EditorComponent", -> it "does not assign an explicit line-height on the editor contents", -> expect(componentNode.style.lineHeight).toBe '' + it "does not apply cursor-line decorations", -> + expect(component.lineNodeForScreenRow(0).classList.contains('cursor-line')).toBe false + describe "legacy editor compatibility", -> it "triggers the screen-lines-changed event before the editor:display-update event", -> editor.setSoftWrap(true) diff --git a/src/editor-component.coffee b/src/editor-component.coffee index 5527c9fc0..a5f7fa008 100644 --- a/src/editor-component.coffee +++ b/src/editor-component.coffee @@ -278,7 +278,9 @@ EditorComponent = React.createClass cursorPixelRects getLineDecorations: (decorationsByMarkerId) -> - {editor} = @props + {editor, mini} = @props + return {} if mini + decorationsByScreenRow = {} for markerId, decorations of decorationsByMarkerId marker = editor.getMarker(markerId) From d0893ccdaf8106aa945420b9fc066f489680c046 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Sun, 13 Jul 2014 15:08:48 -0600 Subject: [PATCH 26/61] Add placeholderText to React editors --- spec/editor-component-spec.coffee | 11 +++++++++++ src/editor-component.coffee | 4 +++- src/editor.coffee | 2 ++ src/lines-component.coffee | 6 ++++-- 4 files changed, 20 insertions(+), 3 deletions(-) diff --git a/spec/editor-component-spec.coffee b/spec/editor-component-spec.coffee index 3ec8d5123..a1b214043 100644 --- a/spec/editor-component-spec.coffee +++ b/spec/editor-component-spec.coffee @@ -2022,6 +2022,17 @@ describe "EditorComponent", -> it "does not apply cursor-line decorations", -> expect(component.lineNodeForScreenRow(0).classList.contains('cursor-line')).toBe false + describe "when placholderText is specified", -> + it "renders the placeholder text when the buffer is empty", -> + component.setProps(placeholderText: 'Hello World') + expect(componentNode.querySelector('.placeholder-text')).toBeNull() + editor.setText('') + runSetImmediateCallbacks() + expect(componentNode.querySelector('.placeholder-text').textContent).toBe "Hello World" + editor.setText('hey') + runSetImmediateCallbacks() + expect(componentNode.querySelector('.placeholder-text')).toBeNull() + describe "legacy editor compatibility", -> it "triggers the screen-lines-changed event before the editor:display-update event", -> editor.setSoftWrap(true) diff --git a/src/editor-component.coffee b/src/editor-component.coffee index a5f7fa008..d12f1b870 100644 --- a/src/editor-component.coffee +++ b/src/editor-component.coffee @@ -66,6 +66,7 @@ EditorComponent = React.createClass decorations = editor.decorationsForScreenRowRange(renderedStartRow, renderedEndRow) highlightDecorations = @getHighlightDecorations(decorations) lineDecorations = @getLineDecorations(decorations) + placeholderText = @props.placeholderText if @props.placeholderText? and editor.isEmpty() scrollHeight = editor.getScrollHeight() scrollWidth = editor.getScrollWidth() @@ -114,7 +115,8 @@ EditorComponent = React.createClass editor, lineHeightInPixels, defaultCharWidth, lineDecorations, highlightDecorations, showIndentGuide, renderedRowRange, @pendingChanges, scrollTop, scrollLeft, @scrollingVertically, scrollHeight, scrollWidth, mouseWheelScreenRow, invisibles, - visible, scrollViewHeight, @scopedCharacterWidthsChangeCount, lineWidth, @useHardwareAcceleration + visible, scrollViewHeight, @scopedCharacterWidthsChangeCount, lineWidth, @useHardwareAcceleration, + placeholderText } ScrollbarComponent diff --git a/src/editor.coffee b/src/editor.coffee index 0e622b3a0..870c14b6c 100644 --- a/src/editor.coffee +++ b/src/editor.coffee @@ -518,6 +518,8 @@ class Editor extends Model # {Delegates to: TextBuffer.isModified} isModified: -> @buffer.isModified() + isEmpty: -> @buffer.isEmpty() + # Public: Determine whether the user should be prompted to save before closing # this editor. shouldPromptToSave: -> @isModified() and not @buffer.hasMultipleEditors() diff --git a/src/lines-component.coffee b/src/lines-component.coffee index 52c81ba06..eeee69f01 100644 --- a/src/lines-component.coffee +++ b/src/lines-component.coffee @@ -17,7 +17,7 @@ LinesComponent = React.createClass render: -> if @isMounted() - {editor, highlightDecorations, scrollHeight, scrollWidth} = @props + {editor, highlightDecorations, scrollHeight, scrollWidth, placeholderText} = @props {lineHeightInPixels, defaultCharWidth, scrollViewHeight, scopedCharacterWidthsChangeCount} = @props style = height: Math.max(scrollHeight, scrollViewHeight) @@ -27,6 +27,7 @@ LinesComponent = React.createClass # The lines div must have the 'editor-colors' class so it has an opaque # background to avoid sub-pixel anti-aliasing problems on the GPU div {className: 'lines editor-colors', style}, + div className: 'placeholder-text', placeholderText if placeholderText? HighlightsComponent({editor, highlightDecorations, lineHeightInPixels, defaultCharWidth, scopedCharacterWidthsChangeCount}) getTransform: -> @@ -48,7 +49,8 @@ LinesComponent = React.createClass return true unless isEqualForProperties(newProps, @props, 'renderedRowRange', 'lineDecorations', 'highlightDecorations', 'lineHeightInPixels', 'defaultCharWidth', 'scrollTop', 'scrollLeft', 'showIndentGuide', 'scrollingVertically', 'invisibles', 'visible', - 'scrollViewHeight', 'mouseWheelScreenRow', 'scopedCharacterWidthsChangeCount', 'lineWidth', 'useHardwareAcceleration' + 'scrollViewHeight', 'mouseWheelScreenRow', 'scopedCharacterWidthsChangeCount', 'lineWidth', 'useHardwareAcceleration', + 'placeholderText' ) {renderedRowRange, pendingChanges} = newProps From a0f75f163936d5cf3c9d61fcef56920bdcc13e8f Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Sun, 13 Jul 2014 19:49:36 -0600 Subject: [PATCH 27/61] Determine visibility by checking offsetWidth/Height of the editor's node This could still use some cleanup --- src/editor-component.coffee | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/src/editor-component.coffee b/src/editor-component.coffee index d12f1b870..080a47c6d 100644 --- a/src/editor-component.coffee +++ b/src/editor-component.coffee @@ -84,6 +84,7 @@ EditorComponent = React.createClass hiddenInputStyle.WebkitTransform = 'translateZ(0)' if @useHardwareAcceleration if @mouseWheelScreenRow? and not (renderedStartRow <= @mouseWheelScreenRow < renderedEndRow) mouseWheelScreenRow = @mouseWheelScreenRow + style.height = scrollViewHeight if @autoHeight className = 'editor-contents editor-colors' @@ -155,8 +156,7 @@ EditorComponent = React.createClass {editor} = @props Math.max(1, Math.ceil(editor.getHeight() / editor.getLineHeightInPixels())) - getInitialState: -> - visible: true + getInitialState: -> {} getDefaultProps: -> cursorBlinkPeriod: 800 @@ -183,6 +183,7 @@ EditorComponent = React.createClass editor.setVisible(true) + @visible = @isVisible() @measureLineHeightAndDefaultCharWidth() @measureScrollView() @measureScrollbars() @@ -208,8 +209,9 @@ EditorComponent = React.createClass @props.parentView.trigger 'selection:changed' if selectionChanged @props.parentView.trigger 'editor:display-updated' + @visible = @isVisible() @measureScrollbars() if @measuringScrollbars - @measureLineHeightAndCharWidthsIfNeeded(prevState) + @measureLineHeightAndDefaultCharWidthIfNeeded(prevState) @remeasureCharacterWidthsIfNeeded(prevState) requestUpdate: -> @@ -665,7 +667,7 @@ EditorComponent = React.createClass onStylesheetsChanged: (stylesheet) -> @refreshScrollbars() if @containsScrollbarSelector(stylesheet) @remeasureCharacterWidthsIfVisibleAfterNextUpdate = true - @requestUpdate() if @state.visible + @requestUpdate() if @visible onScreenLinesChanged: (change) -> {editor} = @props @@ -742,6 +744,10 @@ EditorComponent = React.createClass window.addEventListener('mousemove', onMouseMove) window.addEventListener('mouseup', onMouseUp) + isVisible: -> + node = @getDOMNode() + node.offsetHeight > 0 and node.offsetWidth > 0 + pauseScrollViewMeasurement: -> @scrollViewMeasurementPaused = true @resumeScrollViewMeasurementAfterDelay ?= debounce(@resumeScrollViewMeasurement, 100) @@ -790,26 +796,26 @@ EditorComponent = React.createClass clientWidth -= paddingLeft editor.setWidth(clientWidth) if clientWidth > 0 - measureLineHeightAndCharWidthsIfNeeded: (prevState) -> + measureLineHeightAndDefaultCharWidthIfNeeded: (prevState) -> if not isEqualForProperties(prevState, @state, 'lineHeight', 'fontSize', 'fontFamily') - if @state.visible + if @visible @measureLineHeightAndDefaultCharWidth() else @measureLineHeightAndDefaultCharWidthWhenShown = true - else if @measureLineHeightAndDefaultCharWidthWhenShown and @state.visible and not prevState.visible + else if @measureLineHeightAndDefaultCharWidthWhenShown and @visible + @measureLineHeightAndDefaultCharWidthWhenShown = false @measureLineHeightAndDefaultCharWidth() measureLineHeightAndDefaultCharWidth: -> - @measureLineHeightAndDefaultCharWidthWhenShown = false @refs.lines.measureLineHeightAndDefaultCharWidth() remeasureCharacterWidthsIfNeeded: (prevState) -> if not isEqualForProperties(prevState, @state, 'fontSize', 'fontFamily') - if @state.visible + if @visible @remeasureCharacterWidths() else @remeasureCharacterWidthsIfVisibleAfterNextUpdate = true - else if @remeasureCharacterWidthsIfVisibleAfterNextUpdate and @state.visible + else if @remeasureCharacterWidthsIfVisibleAfterNextUpdate and @visible @remeasureCharacterWidthsIfVisibleAfterNextUpdate = false @remeasureCharacterWidths() @@ -877,10 +883,12 @@ EditorComponent = React.createClass null hide: -> - @setState(visible: false) + @visible = false show: -> - @setState(visible: true) + unless @visible + @visible = true + @forceUpdate() getFontSize: -> @state.fontSize From e81db5d706a682912c6e4c3732455f0dda47a18e Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Mon, 14 Jul 2014 12:10:39 -0600 Subject: [PATCH 28/61] Pull out EditorComponent::pollDOM method This makes the actions that we perform in the poll loop explicit, and will prevent the accumulation of polling-related behavior in the ::measureScrollView method. --- spec/editor-component-spec.coffee | 6 ++--- src/editor-component.coffee | 43 +++++++++++++++++++------------ 2 files changed, 30 insertions(+), 19 deletions(-) diff --git a/spec/editor-component-spec.coffee b/spec/editor-component-spec.coffee index a1b214043..2330bc556 100644 --- a/spec/editor-component-spec.coffee +++ b/spec/editor-component-spec.coffee @@ -1921,13 +1921,13 @@ describe "EditorComponent", -> expect(parseInt(newHeight)).toBeLessThan wrapperNode.offsetHeight wrapperNode.style.height = newHeight - advanceClock(component.scrollViewMeasurementInterval) + advanceClock(component.domPollingInterval) runSetImmediateCallbacks() expect(componentNode.querySelectorAll('.line')).toHaveLength(4 + lineOverdrawMargin + 1) gutterWidth = componentNode.querySelector('.gutter').offsetWidth componentNode.style.width = gutterWidth + 14 * charWidth + 'px' - advanceClock(component.scrollViewMeasurementInterval) + advanceClock(component.domPollingInterval) runSetImmediateCallbacks() expect(componentNode.querySelector('.line').textContent).toBe "var quicksort " @@ -1936,7 +1936,7 @@ describe "EditorComponent", -> scrollViewNode.style.paddingLeft = 20 + 'px' componentNode.style.width = 30 * charWidth + 'px' - advanceClock(component.scrollViewMeasurementInterval) + advanceClock(component.domPollingInterval) runSetImmediateCallbacks() expect(component.lineNodeForScreenRow(0).textContent).toBe "var quicksort = " diff --git a/src/editor-component.coffee b/src/editor-component.coffee index 080a47c6d..cd51d39d6 100644 --- a/src/editor-component.coffee +++ b/src/editor-component.coffee @@ -22,6 +22,8 @@ EditorComponent = React.createClass statics: performSyncUpdates: false + visible: true + autoHeight: false pendingScrollTop: null pendingScrollLeft: null selectOnMouseMove: false @@ -44,10 +46,10 @@ EditorComponent = React.createClass measureLineHeightAndDefaultCharWidthWhenShown: false remeasureCharacterWidthsIfVisibleAfterNextUpdate: false inputEnabled: true - scrollViewMeasurementInterval: 100 scopedCharacterWidthsChangeCount: null - scrollViewMeasurementPaused: false - autoHeight: false + domPollingInterval: 100 + domPollingIntervalId: null + domPollingPaused: false render: -> {focused, fontSize, lineHeight, fontFamily, showIndentGuide, showInvisibles, showLineNumbers, visible} = @state @@ -172,7 +174,7 @@ EditorComponent = React.createClass componentDidMount: -> {editor} = @props - @scrollViewMeasurementIntervalId = setInterval(@measureScrollView, @scrollViewMeasurementInterval) + @domPollingIntervalId = setInterval(@pollDOM, @domPollingInterval) @observeEditor() @listenForDOMEvents() @@ -183,7 +185,6 @@ EditorComponent = React.createClass editor.setVisible(true) - @visible = @isVisible() @measureLineHeightAndDefaultCharWidth() @measureScrollView() @measureScrollbars() @@ -191,8 +192,8 @@ EditorComponent = React.createClass componentWillUnmount: -> @props.parentView.trigger 'editor:will-be-removed', [@props.parentView] @unsubscribe() - clearInterval(@scrollViewMeasurementIntervalId) - @scrollViewMeasurementIntervalId = null + clearInterval(@domPollingIntervalId) + @domPollingIntervalId = null componentDidUpdate: (prevProps, prevState) -> cursorsMoved = @cursorsMoved @@ -229,7 +230,7 @@ EditorComponent = React.createClass requestAnimationFrame: (fn) -> @updatesPaused = true - @pauseScrollViewMeasurement() + @pauseDOMPolling() requestAnimationFrame => fn() @updatesPaused = false @@ -748,15 +749,26 @@ EditorComponent = React.createClass node = @getDOMNode() node.offsetHeight > 0 and node.offsetWidth > 0 - pauseScrollViewMeasurement: -> - @scrollViewMeasurementPaused = true - @resumeScrollViewMeasurementAfterDelay ?= debounce(@resumeScrollViewMeasurement, 100) - @resumeScrollViewMeasurementAfterDelay() + pauseDOMPolling: -> + @domPollingPaused = true + @resumeDOMPollingAfterDelay ?= debounce(@resumeDOMPolling, 100) + @resumeDOMPollingAfterDelay() - resumeScrollViewMeasurement: -> - @scrollViewMeasurementPaused = false + resumeDOMPolling: -> + @domPollingPaused = false - resumeScrollViewMeasurementAfterDelay: null # created lazily + resumeDOMPollingAfterDelay: null # created lazily + + pollDOM: -> + return if @domPollingPaused or not @isMounted() + + wasVisible = @visible + @visible = @isVisible() + if @visible + if wasVisible + @measureScrollView() + else + @requestUpdate() requestScrollViewMeasurement: -> return if @scrollViewMeasurementRequested @@ -771,7 +783,6 @@ EditorComponent = React.createClass # and use the scrollHeight / scrollWidth as its height and width in # calculations. measureScrollView: -> - return if @scrollViewMeasurementPaused return unless @isMounted() {editor, parentView} = @props From 783ef730e202f02f59f729f72e557197dbf82893 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Mon, 14 Jul 2014 12:16:32 -0600 Subject: [PATCH 29/61] Rename EditorComponent::measureScrollView to ::measureHeightAndWidth Since we also check if we're auto-height in this method, this name seems like a better description of the objectives of this method. --- spec/editor-component-spec.coffee | 70 +++++++++++++++---------------- src/editor-component.coffee | 20 ++++----- 2 files changed, 45 insertions(+), 45 deletions(-) diff --git a/spec/editor-component-spec.coffee b/spec/editor-component-spec.coffee index 2330bc556..d7acac7b3 100644 --- a/spec/editor-component-spec.coffee +++ b/spec/editor-component-spec.coffee @@ -61,7 +61,7 @@ describe "EditorComponent", -> verticalScrollbarNode = componentNode.querySelector('.vertical-scrollbar') horizontalScrollbarNode = componentNode.querySelector('.horizontal-scrollbar') - component.measureScrollView() + component.measureHeightAndWidth() runSetImmediateCallbacks() afterEach -> @@ -70,7 +70,7 @@ describe "EditorComponent", -> describe "line rendering", -> it "renders the currently-visible lines plus the overdraw margin", -> wrapperNode.style.height = 4.5 * lineHeightInPixels + 'px' - component.measureScrollView() + component.measureHeightAndWidth() runSetImmediateCallbacks() linesNode = componentNode.querySelector('.lines') @@ -112,7 +112,7 @@ describe "EditorComponent", -> it "updates the lines when lines are inserted or removed above the rendered row range", -> wrapperNode.style.height = 4.5 * lineHeightInPixels + 'px' - component.measureScrollView() + component.measureHeightAndWidth() runSetImmediateCallbacks() verticalScrollbarNode.scrollTop = 5 * lineHeightInPixels verticalScrollbarNode.dispatchEvent(new UIEvent('scroll')) @@ -161,7 +161,7 @@ describe "EditorComponent", -> it "renders the .lines div at the full height of the editor if there aren't enough lines to scroll vertically", -> editor.setText('') wrapperNode.style.height = '300px' - component.measureScrollView() + component.measureHeightAndWidth() runSetImmediateCallbacks() linesNode = componentNode.querySelector('.lines') @@ -173,7 +173,7 @@ describe "EditorComponent", -> lineNodes = componentNode.querySelectorAll('.line') componentNode.style.width = gutterWidth + (30 * charWidth) + 'px' - component.measureScrollView() + component.measureHeightAndWidth() runSetImmediateCallbacks() expect(editor.getScrollWidth()).toBeGreaterThan scrollViewNode.offsetWidth @@ -185,7 +185,7 @@ describe "EditorComponent", -> expect(lineNode.style.width).toBe editor.getScrollWidth() + 'px' componentNode.style.width = gutterWidth + editor.getScrollWidth() + 100 + 'px' - component.measureScrollView() + component.measureHeightAndWidth() runSetImmediateCallbacks() scrollViewWidth = scrollViewNode.offsetWidth @@ -259,7 +259,7 @@ describe "EditorComponent", -> editor.setSoftWrap(true) runSetImmediateCallbacks() componentNode.style.width = 16 * charWidth + 'px' - component.measureScrollView() + component.measureHeightAndWidth() runSetImmediateCallbacks() it "doesn't show end of line invisibles at the end of wrapped lines", -> @@ -396,7 +396,7 @@ describe "EditorComponent", -> it "renders the currently-visible line numbers", -> wrapperNode.style.height = 4.5 * lineHeightInPixels + 'px' - component.measureScrollView() + component.measureHeightAndWidth() runSetImmediateCallbacks() expect(componentNode.querySelectorAll('.line-number').length).toBe 6 + 2 + 1 # line overdraw margin below + dummy line number @@ -439,7 +439,7 @@ describe "EditorComponent", -> editor.setSoftWrap(true) wrapperNode.style.height = 4.5 * lineHeightInPixels + 'px' wrapperNode.style.width = 30 * charWidth + 'px' - component.measureScrollView() + component.measureHeightAndWidth() runSetImmediateCallbacks() expect(componentNode.querySelectorAll('.line-number').length).toBe 6 + lineOverdrawMargin + 1 # 1 dummy line componentNode @@ -477,7 +477,7 @@ describe "EditorComponent", -> it "renders the .line-numbers div at the full height of the editor even if it's taller than its content", -> wrapperNode.style.height = componentNode.offsetHeight + 100 + 'px' - component.measureScrollView() + component.measureHeightAndWidth() runSetImmediateCallbacks() expect(componentNode.querySelector('.line-numbers').offsetHeight).toBe componentNode.offsetHeight @@ -573,7 +573,7 @@ describe "EditorComponent", -> wrapperNode.style.height = 4.5 * lineHeightInPixels + 'px' wrapperNode.style.width = 20 * lineHeightInPixels + 'px' - component.measureScrollView() + component.measureHeightAndWidth() runSetImmediateCallbacks() cursorNodes = componentNode.querySelectorAll('.cursor') @@ -836,7 +836,7 @@ describe "EditorComponent", -> # Shrink editor vertically wrapperNode.style.height = 4.5 * lineHeightInPixels + 'px' - component.measureScrollView() + component.measureHeightAndWidth() runSetImmediateCallbacks() # Add decorations that are out of range @@ -859,7 +859,7 @@ describe "EditorComponent", -> editor.setText("a line that wraps, ok") editor.setSoftWrap(true) componentNode.style.width = 16 * charWidth + 'px' - component.measureScrollView() + component.measureHeightAndWidth() runSetImmediateCallbacks() marker.destroy() @@ -975,7 +975,7 @@ describe "EditorComponent", -> it "does not render highlights for off-screen lines until they come on-screen", -> wrapperNode.style.height = 2.5 * lineHeightInPixels + 'px' - component.measureScrollView() + component.measureHeightAndWidth() runSetImmediateCallbacks() marker = editor.displayBuffer.markBufferRange([[9, 2], [9, 4]], invalidate: 'inside') @@ -1112,7 +1112,7 @@ describe "EditorComponent", -> inputNode = componentNode.querySelector('.hidden-input') wrapperNode.style.height = 5 * lineHeightInPixels + 'px' wrapperNode.style.width = 10 * charWidth + 'px' - component.measureScrollView() + component.measureHeightAndWidth() runSetImmediateCallbacks() expect(editor.getCursorScreenPosition()).toEqual [0, 0] @@ -1162,7 +1162,7 @@ describe "EditorComponent", -> it "moves the cursor to the nearest screen position", -> wrapperNode.style.height = 4.5 * lineHeightInPixels + 'px' wrapperNode.style.width = 10 * charWidth + 'px' - component.measureScrollView() + component.measureHeightAndWidth() editor.setScrollTop(3.5 * lineHeightInPixels) editor.setScrollLeft(2 * charWidth) runSetImmediateCallbacks() @@ -1420,7 +1420,7 @@ describe "EditorComponent", -> describe "scrolling", -> it "updates the vertical scrollbar when the scrollTop is changed in the model", -> wrapperNode.style.height = 4.5 * lineHeightInPixels + 'px' - component.measureScrollView() + component.measureHeightAndWidth() runSetImmediateCallbacks() expect(verticalScrollbarNode.scrollTop).toBe 0 @@ -1431,7 +1431,7 @@ describe "EditorComponent", -> it "updates the horizontal scrollbar and the x transform of the lines based on the scrollLeft of the model", -> componentNode.style.width = 30 * charWidth + 'px' - component.measureScrollView() + component.measureHeightAndWidth() runSetImmediateCallbacks() linesNode = componentNode.querySelector('.lines') @@ -1445,7 +1445,7 @@ describe "EditorComponent", -> it "updates the scrollLeft of the model when the scrollLeft of the horizontal scrollbar changes", -> componentNode.style.width = 30 * charWidth + 'px' - component.measureScrollView() + component.measureHeightAndWidth() runSetImmediateCallbacks() expect(editor.getScrollLeft()).toBe 0 @@ -1457,7 +1457,7 @@ describe "EditorComponent", -> it "does not obscure the last line with the horizontal scrollbar", -> wrapperNode.style.height = 4.5 * lineHeightInPixels + 'px' wrapperNode.style.width = 10 * charWidth + 'px' - component.measureScrollView() + component.measureHeightAndWidth() editor.setScrollBottom(editor.getScrollHeight()) runSetImmediateCallbacks() lastLineNode = component.lineNodeForScreenRow(editor.getLastScreenRow()) @@ -1467,7 +1467,7 @@ describe "EditorComponent", -> # Scroll so there's no space below the last line when the horizontal scrollbar disappears wrapperNode.style.width = 100 * charWidth + 'px' - component.measureScrollView() + component.measureHeightAndWidth() runSetImmediateCallbacks() bottomOfLastLine = lastLineNode.getBoundingClientRect().bottom bottomOfEditor = componentNode.getBoundingClientRect().bottom @@ -1476,7 +1476,7 @@ describe "EditorComponent", -> it "does not obscure the last character of the longest line with the vertical scrollbar", -> wrapperNode.style.height = 7 * lineHeightInPixels + 'px' wrapperNode.style.width = 10 * charWidth + 'px' - component.measureScrollView() + component.measureHeightAndWidth() editor.setScrollLeft(Infinity) runSetImmediateCallbacks() @@ -1490,21 +1490,21 @@ describe "EditorComponent", -> wrapperNode.style.height = 4.5 * lineHeightInPixels + 'px' wrapperNode.style.width = '1000px' - component.measureScrollView() + component.measureHeightAndWidth() runSetImmediateCallbacks() expect(verticalScrollbarNode.style.display).toBe '' expect(horizontalScrollbarNode.style.display).toBe 'none' componentNode.style.width = 10 * charWidth + 'px' - component.measureScrollView() + component.measureHeightAndWidth() runSetImmediateCallbacks() expect(verticalScrollbarNode.style.display).toBe '' expect(horizontalScrollbarNode.style.display).toBe '' wrapperNode.style.height = 20 * lineHeightInPixels + 'px' - component.measureScrollView() + component.measureHeightAndWidth() runSetImmediateCallbacks() expect(verticalScrollbarNode.style.display).toBe 'none' @@ -1513,7 +1513,7 @@ describe "EditorComponent", -> it "makes the dummy scrollbar divs only as tall/wide as the actual scrollbars", -> wrapperNode.style.height = 4 * lineHeightInPixels + 'px' wrapperNode.style.width = 10 * charWidth + 'px' - component.measureScrollView() + component.measureHeightAndWidth() runSetImmediateCallbacks() atom.themes.applyStylesheet "test", """ @@ -1537,21 +1537,21 @@ describe "EditorComponent", -> wrapperNode.style.height = 4.5 * lineHeightInPixels + 'px' wrapperNode.style.width = '1000px' - component.measureScrollView() + component.measureHeightAndWidth() runSetImmediateCallbacks() expect(verticalScrollbarNode.style.bottom).toBe '' expect(horizontalScrollbarNode.style.right).toBe verticalScrollbarNode.offsetWidth + 'px' expect(scrollbarCornerNode.style.display).toBe 'none' componentNode.style.width = 10 * charWidth + 'px' - component.measureScrollView() + component.measureHeightAndWidth() runSetImmediateCallbacks() expect(verticalScrollbarNode.style.bottom).toBe horizontalScrollbarNode.offsetHeight + 'px' expect(horizontalScrollbarNode.style.right).toBe verticalScrollbarNode.offsetWidth + 'px' expect(scrollbarCornerNode.style.display).toBe '' wrapperNode.style.height = 20 * lineHeightInPixels + 'px' - component.measureScrollView() + component.measureHeightAndWidth() runSetImmediateCallbacks() expect(verticalScrollbarNode.style.bottom).toBe horizontalScrollbarNode.offsetHeight + 'px' expect(horizontalScrollbarNode.style.right).toBe '' @@ -1560,7 +1560,7 @@ describe "EditorComponent", -> it "accounts for the width of the gutter in the scrollWidth of the horizontal scrollbar", -> gutterNode = componentNode.querySelector('.gutter') componentNode.style.width = 10 * charWidth + 'px' - component.measureScrollView() + component.measureHeightAndWidth() runSetImmediateCallbacks() expect(horizontalScrollbarNode.scrollWidth).toBe gutterNode.offsetWidth + editor.getScrollWidth() @@ -1573,7 +1573,7 @@ describe "EditorComponent", -> beforeEach -> wrapperNode.style.height = 4.5 * lineHeightInPixels + 'px' wrapperNode.style.width = 20 * charWidth + 'px' - component.measureScrollView() + component.measureHeightAndWidth() runSetImmediateCallbacks() it "updates the scrollLeft or scrollTop on mousewheel events depending on which delta is greater (x or y)", -> @@ -1611,7 +1611,7 @@ describe "EditorComponent", -> it "keeps the line on the DOM if it is scrolled off-screen", -> wrapperNode.style.height = 4.5 * lineHeightInPixels + 'px' wrapperNode.style.width = 20 * charWidth + 'px' - component.measureScrollView() + component.measureHeightAndWidth() lineNode = componentNode.querySelector('.line') wheelEvent = new WheelEvent('mousewheel', wheelDeltaX: 0, wheelDeltaY: -500) @@ -1623,7 +1623,7 @@ describe "EditorComponent", -> it "does not set the mouseWheelScreenRow if scrolling horizontally", -> wrapperNode.style.height = 4.5 * lineHeightInPixels + 'px' wrapperNode.style.width = 20 * charWidth + 'px' - component.measureScrollView() + component.measureHeightAndWidth() lineNode = componentNode.querySelector('.line') wheelEvent = new WheelEvent('mousewheel', wheelDeltaX: 10, wheelDeltaY: 0) @@ -1667,7 +1667,7 @@ describe "EditorComponent", -> it "keeps the line number on the DOM if it is scrolled off-screen", -> wrapperNode.style.height = 4.5 * lineHeightInPixels + 'px' wrapperNode.style.width = 20 * charWidth + 'px' - component.measureScrollView() + component.measureHeightAndWidth() lineNumberNode = componentNode.querySelectorAll('.line-number')[1] wheelEvent = new WheelEvent('mousewheel', wheelDeltaX: 0, wheelDeltaY: -500) @@ -1993,7 +1993,7 @@ describe "EditorComponent", -> describe "when the wrapper view has an explicit height", -> it "does not assign a height on the component node", -> wrapperNode.style.height = '200px' - component.measureScrollView() + component.measureHeightAndWidth() expect(componentNode.style.height).toBe '' describe "when the wrapper view does not have an explicit height", -> diff --git a/src/editor-component.coffee b/src/editor-component.coffee index cd51d39d6..064663a89 100644 --- a/src/editor-component.coffee +++ b/src/editor-component.coffee @@ -42,7 +42,7 @@ EditorComponent = React.createClass mouseWheelScreenRow: null mouseWheelScreenRowClearDelay: 150 scrollSensitivity: 0.4 - scrollViewMeasurementRequested: false + heightAndWidthMeasurementRequested: false measureLineHeightAndDefaultCharWidthWhenShown: false remeasureCharacterWidthsIfVisibleAfterNextUpdate: false inputEnabled: true @@ -186,7 +186,7 @@ EditorComponent = React.createClass editor.setVisible(true) @measureLineHeightAndDefaultCharWidth() - @measureScrollView() + @measureHeightAndWidth() @measureScrollbars() componentWillUnmount: -> @@ -362,7 +362,7 @@ EditorComponent = React.createClass scrollViewNode = @refs.scrollView.getDOMNode() scrollViewNode.addEventListener 'scroll', @onScrollViewScroll - window.addEventListener 'resize', @requestScrollViewMeasurement + window.addEventListener 'resize', @requestHeightAndWidthMeasurement @listenForIMEEvents() @@ -766,23 +766,23 @@ EditorComponent = React.createClass @visible = @isVisible() if @visible if wasVisible - @measureScrollView() + @measureHeightAndWidth() else @requestUpdate() - requestScrollViewMeasurement: -> - return if @scrollViewMeasurementRequested + requestHeightAndWidthMeasurement: -> + return if @heightAndWidthMeasurementRequested - @scrollViewMeasurementRequested = true + @heightAndWidthMeasurementRequested = true requestAnimationFrame => - @scrollViewMeasurementRequested = false - @measureScrollView() + @heightAndWidthMeasurementRequested = false + @measureHeightAndWidth() # Measure explicitly-styled height and width and relay them to the model. If # these values aren't explicitly styled, we assume the editor is unconstrained # and use the scrollHeight / scrollWidth as its height and width in # calculations. - measureScrollView: -> + measureHeightAndWidth: -> return unless @isMounted() {editor, parentView} = @props From 99704517bbedf91e6d306075694aea8c3297caf5 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Mon, 14 Jul 2014 12:44:18 -0600 Subject: [PATCH 30/61] Remove animation frame batching of mousewheel events This doesn't seem to adversely affect the scroll experience, and it's much simpler. I want to avoid preventing the default action of mousewheel events if they don't actually lead to scrolling, and making the behavior synchronous will make that a lot easier. --- spec/editor-component-spec.coffee | 11 +++++++++++ src/editor-component.coffee | 16 +++------------- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/spec/editor-component-spec.coffee b/spec/editor-component-spec.coffee index d7acac7b3..00f71c3ed 100644 --- a/spec/editor-component-spec.coffee +++ b/spec/editor-component-spec.coffee @@ -1581,30 +1581,36 @@ describe "EditorComponent", -> expect(horizontalScrollbarNode.scrollLeft).toBe 0 componentNode.dispatchEvent(new WheelEvent('mousewheel', wheelDeltaX: -5, wheelDeltaY: -10)) + runSetImmediateCallbacks() expect(verticalScrollbarNode.scrollTop).toBe 10 expect(horizontalScrollbarNode.scrollLeft).toBe 0 componentNode.dispatchEvent(new WheelEvent('mousewheel', wheelDeltaX: -15, wheelDeltaY: -5)) + runSetImmediateCallbacks() expect(verticalScrollbarNode.scrollTop).toBe 10 expect(horizontalScrollbarNode.scrollLeft).toBe 15 it "updates the scrollLeft or scrollTop according to the scroll sensitivity", -> atom.config.set('editor.scrollSensitivity', 50) componentNode.dispatchEvent(new WheelEvent('mousewheel', wheelDeltaX: -5, wheelDeltaY: -10)) + runSetImmediateCallbacks() expect(horizontalScrollbarNode.scrollLeft).toBe 0 componentNode.dispatchEvent(new WheelEvent('mousewheel', wheelDeltaX: -15, wheelDeltaY: -5)) + runSetImmediateCallbacks() expect(verticalScrollbarNode.scrollTop).toBe 5 expect(horizontalScrollbarNode.scrollLeft).toBe 7 it "uses the previous scrollSensitivity when the value is not an int", -> atom.config.set('editor.scrollSensitivity', 'nope') componentNode.dispatchEvent(new WheelEvent('mousewheel', wheelDeltaX: 0, wheelDeltaY: -10)) + runSetImmediateCallbacks() expect(verticalScrollbarNode.scrollTop).toBe 10 it "parses negative scrollSensitivity values as positive", -> atom.config.set('editor.scrollSensitivity', -50) componentNode.dispatchEvent(new WheelEvent('mousewheel', wheelDeltaX: 0, wheelDeltaY: -10)) + runSetImmediateCallbacks() expect(verticalScrollbarNode.scrollTop).toBe 5 describe "when the mousewheel event's target is a line", -> @@ -1617,6 +1623,7 @@ describe "EditorComponent", -> wheelEvent = new WheelEvent('mousewheel', wheelDeltaX: 0, wheelDeltaY: -500) Object.defineProperty(wheelEvent, 'target', get: -> lineNode) componentNode.dispatchEvent(wheelEvent) + runSetImmediateCallbacks() expect(componentNode.contains(lineNode)).toBe true @@ -1629,6 +1636,7 @@ describe "EditorComponent", -> wheelEvent = new WheelEvent('mousewheel', wheelDeltaX: 10, wheelDeltaY: 0) Object.defineProperty(wheelEvent, 'target', get: -> lineNode) componentNode.dispatchEvent(wheelEvent) + runSetImmediateCallbacks() expect(component.mouseWheelScreenRow).toBe null @@ -1641,6 +1649,7 @@ describe "EditorComponent", -> wheelEvent = new WheelEvent('mousewheel', wheelDeltaX: 0, wheelDeltaY: 10) Object.defineProperty(wheelEvent, 'target', get: -> lineNode) componentNode.dispatchEvent(wheelEvent) + runSetImmediateCallbacks() expect(editor.getScrollTop()).toBe 0 @@ -1657,6 +1666,7 @@ describe "EditorComponent", -> 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) + runSetImmediateCallbacks() expect(component.mouseWheelScreenRow).toBe 0 editor.insertText("hello") @@ -1673,6 +1683,7 @@ describe "EditorComponent", -> wheelEvent = new WheelEvent('mousewheel', wheelDeltaX: 0, wheelDeltaY: -500) Object.defineProperty(wheelEvent, 'target', get: -> lineNumberNode) componentNode.dispatchEvent(wheelEvent) + runSetImmediateCallbacks() expect(componentNode.contains(lineNumberNode)).toBe true diff --git a/src/editor-component.coffee b/src/editor-component.coffee index 064663a89..ea281a24d 100644 --- a/src/editor-component.coffee +++ b/src/editor-component.coffee @@ -37,8 +37,6 @@ EditorComponent = React.createClass gutterWidth: 0 refreshingScrollbars: false measuringScrollbars: true - pendingVerticalScrollDelta: 0 - pendingHorizontalScrollDelta: 0 mouseWheelScreenRow: null mouseWheelScreenRowClearDelay: 150 scrollSensitivity: 0.4 @@ -570,27 +568,19 @@ EditorComponent = React.createClass onMouseWheel: (event) -> event.preventDefault() - animationFramePending = @pendingHorizontalScrollDelta isnt 0 or @pendingVerticalScrollDelta isnt 0 + {editor} = @props # Only scroll in one direction at a time {wheelDeltaX, wheelDeltaY} = event if Math.abs(wheelDeltaX) > Math.abs(wheelDeltaY) # Scrolling horizontally - @pendingHorizontalScrollDelta -= Math.round(wheelDeltaX * @scrollSensitivity) + editor.setScrollLeft(editor.getScrollLeft() - Math.round(wheelDeltaX * @scrollSensitivity)) else # Scrolling vertically - @pendingVerticalScrollDelta -= Math.round(wheelDeltaY * @scrollSensitivity) @mouseWheelScreenRow = @screenRowForNode(event.target) @clearMouseWheelScreenRowAfterDelay ?= debounce(@clearMouseWheelScreenRow, @mouseWheelScreenRowClearDelay) @clearMouseWheelScreenRowAfterDelay() - - unless animationFramePending - @requestAnimationFrame => - {editor} = @props - editor.setScrollTop(editor.getScrollTop() + @pendingVerticalScrollDelta) - editor.setScrollLeft(editor.getScrollLeft() + @pendingHorizontalScrollDelta) - @pendingVerticalScrollDelta = 0 - @pendingHorizontalScrollDelta = 0 + editor.setScrollTop(editor.getScrollTop() - Math.round(wheelDeltaY * @scrollSensitivity)) onScrollViewScroll: -> if @isMounted() From 0346e5809aab88d3c14e232a52e326a409982e97 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Mon, 14 Jul 2014 13:06:14 -0600 Subject: [PATCH 31/61] Only prevent default on mousewheel events if editor actually scrolls This prevents mini editors from capturing scroll events. --- spec/editor-component-spec.coffee | 36 +++++++++++++++++++++++++++++++ src/editor-component.coffee | 9 +++++--- 2 files changed, 42 insertions(+), 3 deletions(-) diff --git a/spec/editor-component-spec.coffee b/spec/editor-component-spec.coffee index 00f71c3ed..c7ef4a726 100644 --- a/spec/editor-component-spec.coffee +++ b/spec/editor-component-spec.coffee @@ -1687,6 +1687,42 @@ describe "EditorComponent", -> expect(componentNode.contains(lineNumberNode)).toBe true + it "only prevents the default action of the mousewheel event if it actually lead to scrolling", -> + spyOn(WheelEvent::, 'preventDefault').andCallThrough() + + wrapperNode.style.height = 4.5 * lineHeightInPixels + 'px' + wrapperNode.style.width = 20 * charWidth + 'px' + component.measureHeightAndWidth() + runSetImmediateCallbacks() + + componentNode.dispatchEvent(new WheelEvent('mousewheel', wheelDeltaX: 0, wheelDeltaY: 50)) + expect(editor.getScrollTop()).toBe 0 + expect(WheelEvent::preventDefault).not.toHaveBeenCalled() + + componentNode.dispatchEvent(new WheelEvent('mousewheel', wheelDeltaX: 0, wheelDeltaY: -3000)) + runSetImmediateCallbacks() + expect(editor.getScrollTop()).toBe editor.getScrollHeight() - editor.getHeight() + 15 + expect(WheelEvent::preventDefault).toHaveBeenCalled() + WheelEvent::preventDefault.reset() + + componentNode.dispatchEvent(new WheelEvent('mousewheel', wheelDeltaX: 0, wheelDeltaY: -30)) + expect(editor.getScrollTop()).toBe editor.getScrollHeight() - editor.getHeight() + 15 + expect(WheelEvent::preventDefault).not.toHaveBeenCalled() + + componentNode.dispatchEvent(new WheelEvent('mousewheel', wheelDeltaX: 50, wheelDeltaY: 0)) + expect(editor.getScrollLeft()).toBe 0 + expect(WheelEvent::preventDefault).not.toHaveBeenCalled() + + componentNode.dispatchEvent(new WheelEvent('mousewheel', wheelDeltaX: -3000, wheelDeltaY: 0)) + runSetImmediateCallbacks() + expect(editor.getScrollLeft()).toBe editor.getScrollWidth() - editor.getWidth() + 15 + expect(WheelEvent::preventDefault).toHaveBeenCalled() + WheelEvent::preventDefault.reset() + + componentNode.dispatchEvent(new WheelEvent('mousewheel', wheelDeltaX: -30, wheelDeltaY: 0)) + expect(editor.getScrollLeft()).toBe editor.getScrollWidth() - editor.getWidth() + 15 + expect(WheelEvent::preventDefault).not.toHaveBeenCalled() + describe "input events", -> inputNode = null diff --git a/src/editor-component.coffee b/src/editor-component.coffee index ea281a24d..993dbe8c7 100644 --- a/src/editor-component.coffee +++ b/src/editor-component.coffee @@ -567,20 +567,23 @@ EditorComponent = React.createClass @pendingScrollLeft = null onMouseWheel: (event) -> - event.preventDefault() {editor} = @props # Only scroll in one direction at a time {wheelDeltaX, wheelDeltaY} = event if Math.abs(wheelDeltaX) > Math.abs(wheelDeltaY) # Scrolling horizontally - editor.setScrollLeft(editor.getScrollLeft() - Math.round(wheelDeltaX * @scrollSensitivity)) + previousScrollLeft = editor.getScrollLeft() + editor.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() - editor.setScrollTop(editor.getScrollTop() - Math.round(wheelDeltaY * @scrollSensitivity)) + previousScrollTop = editor.getScrollTop() + editor.setScrollTop(previousScrollTop - Math.round(wheelDeltaY * @scrollSensitivity)) + event.preventDefault() unless previousScrollTop is editor.getScrollTop() onScrollViewScroll: -> if @isMounted() From 3206fdce9ea52bfc0ee921787ba5ae46a02faba1 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Mon, 14 Jul 2014 13:14:44 -0600 Subject: [PATCH 32/61] Add ReactEditorView::setPlaceholderText shim --- src/react-editor-view.coffee | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/react-editor-view.coffee b/src/react-editor-view.coffee index ecb5b24b7..f06a93238 100644 --- a/src/react-editor-view.coffee +++ b/src/react-editor-view.coffee @@ -223,3 +223,9 @@ class ReactEditorView extends View resetDisplay: -> # No-op shim for package specs redraw: -> # No-op shim + + setPlaceholderText: (placeholderText) -> + if @component? + @component.setProps({placeholderText}) + else + @props.placeholderText = placeholderText From c4177aba3ec8e21b56b0571004f32fc6f580d794 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Mon, 14 Jul 2014 13:15:05 -0600 Subject: [PATCH 33/61] Handle 'attributes' param to ReactEditorView --- src/react-editor-view.coffee | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/react-editor-view.coffee b/src/react-editor-view.coffee index f06a93238..568388043 100644 --- a/src/react-editor-view.coffee +++ b/src/react-editor-view.coffee @@ -7,7 +7,10 @@ EditorComponent = require './editor-component' module.exports = class ReactEditorView extends View - @content: -> @div class: 'editor react' + @content: (params) -> + attributes = params.attributes ? {} + attributes.class = 'editor react' + @div attributes focusOnAttach: false From 4a8ac85ffb5bfaad24e32141e9d8367cf865835d Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Mon, 14 Jul 2014 13:22:38 -0600 Subject: [PATCH 34/61] Restrict ReactEditorView::getPane implementation --- src/react-editor-view.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/react-editor-view.coffee b/src/react-editor-view.coffee index 568388043..413ad6d75 100644 --- a/src/react-editor-view.coffee +++ b/src/react-editor-view.coffee @@ -140,7 +140,7 @@ class ReactEditorView extends View pane?.splitDown(pane?.copyActiveItem()).activeView getPane: -> - @closest('.pane').view() + @parent('.item-views').parents('.pane').view() focus: -> if @component? From ca1220a682f2ff75fcdf0ed7b1aab301b85d7a42 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 17 Jul 2014 17:56:06 -0700 Subject: [PATCH 35/61] Delay initial measurement until the editor becomes visible Previously, these measurements were always performed when the editor component was mounted. This didn't work in situations where the component was mounted in a non-visible state. This commit includes a visibility check in the resize polling we were already doing, kicking off the measurement process as soon as the editor is visible. --- spec/editor-component-spec.coffee | 24 +++++++++++++++++++ src/cursors-component.coffee | 4 ++-- src/editor-component.coffee | 39 +++++++++++++++++++------------ src/highlights-component.coffee | 2 +- src/lines-component.coffee | 10 +++++--- 5 files changed, 58 insertions(+), 21 deletions(-) diff --git a/spec/editor-component-spec.coffee b/spec/editor-component-spec.coffee index c7ef4a726..69ee93c4c 100644 --- a/spec/editor-component-spec.coffee +++ b/spec/editor-component-spec.coffee @@ -1529,6 +1529,8 @@ describe "EditorComponent", -> expect(scrollbarCornerNode.offsetWidth).toBe 8 expect(scrollbarCornerNode.offsetHeight).toBe 8 + atom.themes.removeStylesheet('test') + it "assigns the bottom/right of the scrollbars to the width of the opposite scrollbar if it is visible", -> scrollbarCornerNode = componentNode.querySelector('.scrollbar-corner') @@ -1858,6 +1860,28 @@ describe "EditorComponent", -> expect(event.abortKeyBinding).toHaveBeenCalled() describe "hiding and showing the editor", -> + describe "when the editor is hidden when it is mounted", -> + it "defers measurement and rendering until the editor becomes visible", -> + wrapperView.remove() + + hiddenParent = document.createElement('div') + hiddenParent.style.display = 'none' + contentNode.appendChild(hiddenParent) + + wrapperView = new ReactEditorView(editor, {lineOverdrawMargin}) + wrapperNode = wrapperView.element + wrapperView.appendTo(hiddenParent) + + {component} = wrapperView + componentNode = component.getDOMNode() + expect(componentNode.querySelectorAll('.line').length).toBe 0 + + hiddenParent.style.display = 'block' + advanceClock(component.domPollingInterval) + runSetImmediateCallbacks() + + expect(componentNode.querySelectorAll('.line').length).toBeGreaterThan 0 + describe "when the lineHeight changes while the editor is hidden", -> it "does not attempt to measure the lineHeightInPixels until the editor becomes visible again", -> wrapperView.hide() diff --git a/src/cursors-component.coffee b/src/cursors-component.coffee index b33e231e5..503a8fe9b 100644 --- a/src/cursors-component.coffee +++ b/src/cursors-component.coffee @@ -12,14 +12,14 @@ CursorsComponent = React.createClass cursorBlinkIntervalHandle: null render: -> - {cursorPixelRects, scrollTop, scrollLeft, defaultCharWidth, useHardwareAcceleration} = @props + {performedInitialMeasurement, cursorPixelRects, scrollTop, scrollLeft, defaultCharWidth, useHardwareAcceleration} = @props {blinkOff} = @state className = 'cursors' className += ' blink-off' if blinkOff div {className}, - if @isMounted() + if performedInitialMeasurement for key, pixelRect of cursorPixelRects CursorComponent({key, pixelRect, scrollTop, scrollLeft, defaultCharWidth, useHardwareAcceleration}) diff --git a/src/editor-component.coffee b/src/editor-component.coffee index 993dbe8c7..e4ffa3692 100644 --- a/src/editor-component.coffee +++ b/src/editor-component.coffee @@ -58,7 +58,7 @@ EditorComponent = React.createClass style = {fontSize, fontFamily} style.lineHeight = lineHeight unless mini - if @isMounted() + if @performedInitialMeasurement renderedRowRange = @getRenderedRowRange() [renderedStartRow, renderedEndRow] = renderedRowRange cursorPixelRects = @getCursorPixelRects(renderedRowRange) @@ -109,7 +109,8 @@ EditorComponent = React.createClass CursorsComponent { scrollTop, scrollLeft, cursorPixelRects, cursorBlinkPeriod, cursorBlinkResumeDelay, - lineHeightInPixels, defaultCharWidth, @scopedCharacterWidthsChangeCount, @useHardwareAcceleration + lineHeightInPixels, defaultCharWidth, @scopedCharacterWidthsChangeCount, @useHardwareAcceleration, + @performedInitialMeasurement } LinesComponent { ref: 'lines', @@ -117,7 +118,7 @@ EditorComponent = React.createClass showIndentGuide, renderedRowRange, @pendingChanges, scrollTop, scrollLeft, @scrollingVertically, scrollHeight, scrollWidth, mouseWheelScreenRow, invisibles, visible, scrollViewHeight, @scopedCharacterWidthsChangeCount, lineWidth, @useHardwareAcceleration, - placeholderText + placeholderText, @performedInitialMeasurement } ScrollbarComponent @@ -181,11 +182,8 @@ EditorComponent = React.createClass @subscribe atom.themes, 'stylesheet-added stylsheet-removed', @onStylesheetsChanged @subscribe scrollbarStyle.changes, @refreshScrollbars - editor.setVisible(true) - - @measureLineHeightAndDefaultCharWidth() - @measureHeightAndWidth() - @measureScrollbars() + if @visible = @isVisible() + @performInitialMeasurement() componentWillUnmount: -> @props.parentView.trigger 'editor:will-be-removed', [@props.parentView] @@ -209,9 +207,20 @@ EditorComponent = React.createClass @props.parentView.trigger 'editor:display-updated' @visible = @isVisible() - @measureScrollbars() if @measuringScrollbars - @measureLineHeightAndDefaultCharWidthIfNeeded(prevState) - @remeasureCharacterWidthsIfNeeded(prevState) + if @performedInitialMeasurement + @measureScrollbars() if @measuringScrollbars + @measureLineHeightAndDefaultCharWidthIfNeeded(prevState) + @remeasureCharacterWidthsIfNeeded(prevState) + + performInitialMeasurement: -> + @updatesPaused = true + @measureLineHeightAndDefaultCharWidth() + @measureHeightAndWidth() + @measureScrollbars() + @props.editor.setVisible(true) + @updatesPaused = false + @performedInitialMeasurement = true + @requestUpdate() requestUpdate: -> if @updatesPaused @@ -740,7 +749,7 @@ EditorComponent = React.createClass isVisible: -> node = @getDOMNode() - node.offsetHeight > 0 and node.offsetWidth > 0 + node.offsetHeight > 0 or node.offsetWidth > 0 pauseDOMPolling: -> @domPollingPaused = true @@ -756,12 +765,11 @@ EditorComponent = React.createClass return if @domPollingPaused or not @isMounted() wasVisible = @visible - @visible = @isVisible() - if @visible + if @visible = @isVisible() if wasVisible @measureHeightAndWidth() else - @requestUpdate() + @performInitialMeasurement() requestHeightAndWidthMeasurement: -> return if @heightAndWidthMeasurementRequested @@ -830,6 +838,7 @@ EditorComponent = React.createClass @requestUpdate() measureScrollbars: -> + return unless @visible @measuringScrollbars = false {editor} = @props diff --git a/src/highlights-component.coffee b/src/highlights-component.coffee index 2cadcb289..dd0749ffd 100644 --- a/src/highlights-component.coffee +++ b/src/highlights-component.coffee @@ -9,7 +9,7 @@ HighlightsComponent = React.createClass render: -> div className: 'highlights', - @renderHighlights() if @isMounted() + @renderHighlights() if @props.performedInitialMeasurement renderHighlights: -> {editor, highlightDecorations, lineHeightInPixels} = @props diff --git a/src/lines-component.coffee b/src/lines-component.coffee index eeee69f01..9704aaf35 100644 --- a/src/lines-component.coffee +++ b/src/lines-component.coffee @@ -16,7 +16,9 @@ LinesComponent = React.createClass displayName: 'LinesComponent' render: -> - if @isMounted() + {performedInitialMeasurement} = @props + + if performedInitialMeasurement {editor, highlightDecorations, scrollHeight, scrollWidth, placeholderText} = @props {lineHeightInPixels, defaultCharWidth, scrollViewHeight, scopedCharacterWidthsChangeCount} = @props style = @@ -28,7 +30,7 @@ LinesComponent = React.createClass # background to avoid sub-pixel anti-aliasing problems on the GPU div {className: 'lines editor-colors', style}, div className: 'placeholder-text', placeholderText if placeholderText? - HighlightsComponent({editor, highlightDecorations, lineHeightInPixels, defaultCharWidth, scopedCharacterWidthsChangeCount}) + HighlightsComponent({editor, highlightDecorations, lineHeightInPixels, defaultCharWidth, scopedCharacterWidthsChangeCount, performedInitialMeasurement}) getTransform: -> {scrollTop, scrollLeft, useHardwareAcceleration} = @props @@ -50,10 +52,12 @@ LinesComponent = React.createClass 'renderedRowRange', 'lineDecorations', 'highlightDecorations', 'lineHeightInPixels', 'defaultCharWidth', 'scrollTop', 'scrollLeft', 'showIndentGuide', 'scrollingVertically', 'invisibles', 'visible', 'scrollViewHeight', 'mouseWheelScreenRow', 'scopedCharacterWidthsChangeCount', 'lineWidth', 'useHardwareAcceleration', - 'placeholderText' + 'placeholderText', 'performedInitialMeasurement' ) {renderedRowRange, pendingChanges} = newProps + return false unless renderedRowRange? + [renderedStartRow, renderedEndRow] = renderedRowRange for change in pendingChanges if change.screenDelta is 0 From df7f816c883d5e81e6ae010cd2236e30291e1397 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Mon, 21 Jul 2014 13:43:00 -0700 Subject: [PATCH 36/61] Move 'editor-colors' to wrapper view to support padding on mini editors --- src/editor-component.coffee | 2 +- src/react-editor-view.coffee | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/editor-component.coffee b/src/editor-component.coffee index e4ffa3692..ac7187017 100644 --- a/src/editor-component.coffee +++ b/src/editor-component.coffee @@ -87,7 +87,7 @@ EditorComponent = React.createClass style.height = scrollViewHeight if @autoHeight - className = 'editor-contents editor-colors' + className = 'editor-contents' className += ' is-focused' if focused className += ' has-selection' if hasSelection diff --git a/src/react-editor-view.coffee b/src/react-editor-view.coffee index 413ad6d75..d806f71c8 100644 --- a/src/react-editor-view.coffee +++ b/src/react-editor-view.coffee @@ -9,7 +9,7 @@ module.exports = class ReactEditorView extends View @content: (params) -> attributes = params.attributes ? {} - attributes.class = 'editor react' + attributes.class = 'editor react editor-colors' @div attributes focusOnAttach: false From 38b286f9896b6371c378a175e3fd5bc7fff8bcb3 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Mon, 21 Jul 2014 13:50:58 -0700 Subject: [PATCH 37/61] Remove 'editor-colors' class from lines and gutter for mini editors Having the editor-colors class on these elements was causing the theme to be applied to lines in mini editors in the settings view, which caused a black inset box to appear with dark syntax themes. This was added to give the lines an opaque background which was supposed to enable sub pixel anti-aliasing despite being on the GPU, but it didn't seem to be working. Perhaps we can revisit this issue after the Chrome 35 upgrade to see if sub pixel antialiasing works with opaque backgrounds afterward. --- src/gutter-component.coffee | 4 +--- src/lines-component.coffee | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/gutter-component.coffee b/src/gutter-component.coffee index 9dc6fb3e7..826723575 100644 --- a/src/gutter-component.coffee +++ b/src/gutter-component.coffee @@ -19,9 +19,7 @@ GutterComponent = React.createClass {scrollHeight, scrollViewHeight, onMouseDown} = @props div className: 'gutter', onClick: @onClick, onMouseDown: onMouseDown, - # The line-numbers div must have the 'editor-colors' class so it has an - # opaque background to avoid sub-pixel anti-aliasing problems on the GPU - div className: 'gutter line-numbers editor-colors', ref: 'lineNumbers', style: + div className: 'gutter line-numbers', ref: 'lineNumbers', style: height: Math.max(scrollHeight, scrollViewHeight) WebkitTransform: @getTransform() diff --git a/src/lines-component.coffee b/src/lines-component.coffee index 9704aaf35..d1b1aba94 100644 --- a/src/lines-component.coffee +++ b/src/lines-component.coffee @@ -26,9 +26,7 @@ LinesComponent = React.createClass width: scrollWidth WebkitTransform: @getTransform() - # The lines div must have the 'editor-colors' class so it has an opaque - # background to avoid sub-pixel anti-aliasing problems on the GPU - div {className: 'lines editor-colors', style}, + div {className: 'lines', style}, div className: 'placeholder-text', placeholderText if placeholderText? HighlightsComponent({editor, highlightDecorations, lineHeightInPixels, defaultCharWidth, scopedCharacterWidthsChangeCount, performedInitialMeasurement}) From 08ecba72e657d02d9c12dadac613b71e1b572565 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Mon, 21 Jul 2014 13:52:28 -0700 Subject: [PATCH 38/61] Upgrade to pathwatcher 1.5 Closes atom/tree-view#194 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 336878512..e03e7635e 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,7 @@ "nslog": "0.5.0", "oniguruma": "^1.0.6", "optimist": "0.4.0", - "pathwatcher": "^1.3.2", + "pathwatcher": "^1.5", "property-accessors": "^1", "q": "^1.0.1", "random-words": "0.0.1", From 6377c7ebf46b9dea3729d8a9576a449e1f1c70c7 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Mon, 21 Jul 2014 13:54:17 -0700 Subject: [PATCH 39/61] Upgrade to fs-plus 2.2.4 Closes #2313 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e03e7635e..8d6a25ffb 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,7 @@ "delegato": "^1", "emissary": "^1.2.1", "first-mate": "^1.7.1", - "fs-plus": "^2.2.3", + "fs-plus": "^2.2.4", "fstream": "0.1.24", "fuzzaldrin": "^1.1", "git-utils": "^1.3", From 0c4da92d6b37f51e98789ee551a51fa12a2d2c2c Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Mon, 21 Jul 2014 14:09:16 -0700 Subject: [PATCH 40/61] Handle hide and show entirely in the wrapper view Enable sync updates and manually invoke the pollDOM function on the component when we call hide/show on the wrapper view. This ensures that we perform initial measurements when showing the editor for the first time. --- src/editor-component.coffee | 10 +--------- src/react-editor-view.coffee | 11 +++++++++-- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/editor-component.coffee b/src/editor-component.coffee index ac7187017..fa3939cb6 100644 --- a/src/editor-component.coffee +++ b/src/editor-component.coffee @@ -22,7 +22,7 @@ EditorComponent = React.createClass statics: performSyncUpdates: false - visible: true + visible: false autoHeight: false pendingScrollTop: null pendingScrollLeft: null @@ -895,14 +895,6 @@ EditorComponent = React.createClass node = node.parentNode null - hide: -> - @visible = false - - show: -> - unless @visible - @visible = true - @forceUpdate() - getFontSize: -> @state.fontSize diff --git a/src/react-editor-view.coffee b/src/react-editor-view.coffee index d806f71c8..80f21ef57 100644 --- a/src/react-editor-view.coffee +++ b/src/react-editor-view.coffee @@ -150,11 +150,18 @@ class ReactEditorView extends View hide: -> super - @component?.hide() + @pollComponentDOM() show: -> super - @component?.show() + @pollComponentDOM() + + pollComponentDOM: -> + return unless @component? + valueToRestore = @component.performSyncUpdates + @component.performSyncUpdates = true + @component.pollDOM() + @component.performSyncUpdates = valueToRestore pageDown: -> @editor.pageDown() From f9d866fa3218b4816a0a9881a9f96d8df61b2988 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Mon, 21 Jul 2014 14:12:00 -0700 Subject: [PATCH 41/61] Upgrade to tree-view@0.111 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 8d6a25ffb..781dc0dae 100644 --- a/package.json +++ b/package.json @@ -102,7 +102,7 @@ "symbols-view": "0.60.0", "tabs": "0.45.0", "timecop": "0.21.0", - "tree-view": "0.110.0", + "tree-view": "0.111.0", "update-package-dependencies": "0.6.0", "welcome": "0.17.0", "whitespace": "0.25.0", From fb4361e976ba46fe7a803f0cdf988f352bf4d3e2 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Mon, 21 Jul 2014 14:13:05 -0700 Subject: [PATCH 42/61] Guard React mini-editors with core.useReactMiniEditors feature flag I'm not adding a default for this one so it won't show up in the settings view unless the user sets it in their config explicitly. I'm hoping it won't need to be flagged for very long. --- exports/atom.coffee | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/exports/atom.coffee b/exports/atom.coffee index b852d68a7..7b6318c7a 100644 --- a/exports/atom.coffee +++ b/exports/atom.coffee @@ -15,7 +15,10 @@ unless process.env.ATOM_SHELL_INTERNAL_RUN_AS_NODE module.exports.$ = $ module.exports.$$ = $$ module.exports.$$$ = $$$ - module.exports.EditorView = require '../src/react-editor-view' + if atom.config.get('core.useReactMiniEditors') + module.exports.EditorView = require '../src/react-editor-view' + else + module.exports.EditorView = require '../src/editor-view' module.exports.ScrollView = require '../src/scroll-view' module.exports.SelectListView = require '../src/select-list-view' module.exports.Task = require '../src/task' From a68b9a793f5cb61b0195d14053d0243efcd9242d Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Mon, 21 Jul 2014 14:28:48 -0700 Subject: [PATCH 43/61] Don't update the GutterComponent if there's no renderedRowRange --- src/gutter-component.coffee | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/gutter-component.coffee b/src/gutter-component.coffee index 826723575..223b5a077 100644 --- a/src/gutter-component.coffee +++ b/src/gutter-component.coffee @@ -51,6 +51,8 @@ GutterComponent = React.createClass ) {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 From ba21f0b0d8677583b33baa00d8282b31d64214d9 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Mon, 21 Jul 2014 14:56:09 -0700 Subject: [PATCH 44/61] Eliminate duplicate 'gutter' class This was previously needed when we applied 'editor-colors' to the line numbers to give them an opaque background in hopes of supporting sub pixel AA, but that dream is dead for now anyway. This makes the gutter harder to style, so I'm nixing it. --- src/gutter-component.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gutter-component.coffee b/src/gutter-component.coffee index 223b5a077..ae2e9f9f2 100644 --- a/src/gutter-component.coffee +++ b/src/gutter-component.coffee @@ -19,7 +19,7 @@ GutterComponent = React.createClass {scrollHeight, scrollViewHeight, onMouseDown} = @props div className: 'gutter', onClick: @onClick, onMouseDown: onMouseDown, - div className: 'gutter line-numbers', ref: 'lineNumbers', style: + div className: 'line-numbers', ref: 'lineNumbers', style: height: Math.max(scrollHeight, scrollViewHeight) WebkitTransform: @getTransform() From 43c9e21f1d4b256cb72f9359097dcaed17ca4767 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Mon, 21 Jul 2014 15:04:44 -0700 Subject: [PATCH 45/61] Make setEditorHeightInLines/WidthInChars spec helpers work with React Fixes #3019 --- spec/editor-component-spec.coffee | 7 +++++++ spec/spec-helper.coffee | 10 +++++++--- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/spec/editor-component-spec.coffee b/spec/editor-component-spec.coffee index 69ee93c4c..f75220b85 100644 --- a/spec/editor-component-spec.coffee +++ b/spec/editor-component-spec.coffee @@ -2116,6 +2116,13 @@ describe "EditorComponent", -> expect(callingOrder).toEqual ['screen-lines-changed', 'editor:display-updated'] + it "works with the ::setEditorHeightInLines and ::setEditorWidthInChars helpers", -> + setEditorHeightInLines(wrapperView, 7) + expect(componentNode.offsetHeight).toBe lineHeightInPixels * 7 + + setEditorWidthInChars(wrapperView, 10) + expect(componentNode.querySelector('.scroll-view').offsetWidth).toBe charWidth * 10 + buildMouseEvent = (type, properties...) -> properties = extend({bubbles: true, cancelable: true}, properties...) properties.detail ?= 1 diff --git a/spec/spec-helper.coffee b/spec/spec-helper.coffee index 0010393fc..407cbb8a9 100644 --- a/spec/spec-helper.coffee +++ b/spec/spec-helper.coffee @@ -318,9 +318,13 @@ window.setEditorWidthInChars = (editorView, widthInChars, charWidth=editorView.c editorView.width(charWidth * widthInChars + editorView.gutter.outerWidth()) $(window).trigger 'resize' # update width of editor view's on-screen lines -window.setEditorHeightInLines = (editorView, heightInChars, charHeight=editorView.lineHeight) -> - editorView.height(charHeight * heightInChars + editorView.renderedLines.position().top) - $(window).trigger 'resize' # update editor view's on-screen lines +window.setEditorHeightInLines = (editorView, heightInLines, lineHeight=editorView.lineHeight) -> + if editorView.hasClass('react') + editorView.height(editorView.getEditor().getLineHeightInPixels() * heightInLines) + editorView.component?.measureHeightAndWidth() + else + editorView.height(lineHeight * heightInLines + editorView.renderedLines.position().top) + $(window).trigger 'resize' # update editor view's on-screen lines $.fn.resultOfTrigger = (type) -> event = $.Event(type) From 73354f56f459c835e1076ed3811a018ca83f414d Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Mon, 21 Jul 2014 15:16:59 -0700 Subject: [PATCH 46/61] Upgrade to language-yaml@0.13 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 781dc0dae..1c7859c82 100644 --- a/package.json +++ b/package.json @@ -136,7 +136,7 @@ "language-todo": "0.10.0", "language-toml": "0.12.0", "language-xml": "0.15.0", - "language-yaml": "0.12.0" + "language-yaml": "0.13.0" }, "private": true, "scripts": { From 8a6753905cc843e56ef9c41be1aeed83c572ceb0 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Mon, 21 Jul 2014 15:28:13 -0700 Subject: [PATCH 47/61] Upgrade to language-sass@0.14 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 1c7859c82..3ecdf856f 100644 --- a/package.json +++ b/package.json @@ -128,7 +128,7 @@ "language-python": "0.18.0", "language-ruby": "0.33.0", "language-ruby-on-rails": "0.15.0", - "language-sass": "0.13.0", + "language-sass": "0.14.0", "language-shellscript": "0.8.0", "language-source": "0.7.0", "language-sql": "0.9.0", From 10bad42e7a7ca5181e214088512415adac8033e0 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Mon, 21 Jul 2014 15:28:55 -0700 Subject: [PATCH 48/61] Upgrade to language-less@0.13 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3ecdf856f..1ae47fa6e 100644 --- a/package.json +++ b/package.json @@ -119,7 +119,7 @@ "language-java": "0.11.0", "language-javascript": "0.36.0", "language-json": "0.8.0", - "language-less": "0.12.0", + "language-less": "0.13.0", "language-make": "0.10.0", "language-objective-c": "0.11.0", "language-perl": "0.9.0", From a22cf44b4964cb0ceef0ed790974f781fc3ab856 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Mon, 21 Jul 2014 16:07:47 -0700 Subject: [PATCH 49/61] Upgrade to git-utils 1.6 Closes atom/tree-view#53 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 1ae47fa6e..59a1faa9e 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,7 @@ "fs-plus": "^2.2.4", "fstream": "0.1.24", "fuzzaldrin": "^1.1", - "git-utils": "^1.3", + "git-utils": "^1.6", "grim": "0.11.0", "guid": "0.0.10", "jasmine-tagged": "^1.1.2", From 162d5a0e0dd6460fe11219729e68a916e18248cf Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Mon, 21 Jul 2014 16:09:41 -0700 Subject: [PATCH 50/61] Map ctrl-shift-z to core:redo on Windows Closes #2988 --- keymaps/win32.cson | 1 + 1 file changed, 1 insertion(+) diff --git a/keymaps/win32.cson b/keymaps/win32.cson index 774300cd6..01408fb38 100644 --- a/keymaps/win32.cson +++ b/keymaps/win32.cson @@ -29,6 +29,7 @@ 'ctrl-w': 'core:close' 'ctrl-f4': 'core:close' 'ctrl-z': 'core:undo' + 'ctrl-shift-z': 'core:redo' 'ctrl-y': 'core:redo' 'ctrl-x': 'core:cut' 'ctrl-c': 'core:copy' From 4a8a741ef087e13c8471ff3463bb0558ba239969 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Mon, 21 Jul 2014 16:30:27 -0700 Subject: [PATCH 51/61] Write Atom.desktop file directly from template Previously this was written to resources/linux/Atom.desktop which would create issues when running `sudo script/grunt install` followed by `script/grunt mkdeb` --- build/tasks/install-task.coffee | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/build/tasks/install-task.coffee b/build/tasks/install-task.coffee index d3a3aa010..4ef52a40b 100644 --- a/build/tasks/install-task.coffee +++ b/build/tasks/install-task.coffee @@ -4,11 +4,6 @@ _ = require 'underscore-plus' fs = require 'fs-plus' runas = null -fillTemplate = (filePath, data) -> - template = _.template(String(fs.readFileSync(filePath + '.in'))) - filled = template(data) - fs.writeFileSync(filePath, filled) - module.exports = (grunt) -> {cp, mkdir, rm} = require('./task-helpers')(grunt) @@ -32,7 +27,6 @@ module.exports = (grunt) -> shareDir = path.join(installDir, 'share', 'atom') iconName = path.join(shareDir,'resources','app','resources','atom.png') - desktopFile = path.join('resources', 'linux', 'Atom.desktop') mkdir binDir cp 'atom.sh', path.join(binDir, 'atom') @@ -42,13 +36,17 @@ module.exports = (grunt) -> # Create Atom.desktop if installation not in temporary folder tmpDir = if process.env.TMPDIR? then process.env.TMPDIR else '/tmp' - desktopInstallFile = path.join(installDir,'share','applications','Atom.desktop') + desktopInstallFile = path.join(installDir, 'share', 'applications', 'Atom.desktop') if installDir.indexOf(tmpDir) isnt 0 mkdir path.dirname(desktopInstallFile) {description} = grunt.file.readJSON('package.json') installDir = path.join(installDir,'.') # To prevent "Exec=/usr/local//share/atom/atom" - fillTemplate(desktopFile, {description, installDir, iconName}) - cp desktopFile, desktopInstallFile + + desktopFile = path.join('resources', 'linux', 'Atom.desktop.in') + template = _.template(String(fs.readFileSync(desktopFile))) + filled = template({description, installDir, iconName}) + + grunt.file.write(desktopInstallFile, filled) # Create relative symbol link for apm. process.chdir(binDir) From 4b07b803b3e6dce8b5b02d12729a8e15c9154f0d Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Mon, 21 Jul 2014 16:37:42 -0700 Subject: [PATCH 52/61] Move install file var under if block --- build/tasks/install-task.coffee | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/build/tasks/install-task.coffee b/build/tasks/install-task.coffee index 4ef52a40b..726366716 100644 --- a/build/tasks/install-task.coffee +++ b/build/tasks/install-task.coffee @@ -36,13 +36,14 @@ module.exports = (grunt) -> # Create Atom.desktop if installation not in temporary folder tmpDir = if process.env.TMPDIR? then process.env.TMPDIR else '/tmp' - desktopInstallFile = path.join(installDir, 'share', 'applications', 'Atom.desktop') if installDir.indexOf(tmpDir) isnt 0 mkdir path.dirname(desktopInstallFile) {description} = grunt.file.readJSON('package.json') installDir = path.join(installDir,'.') # To prevent "Exec=/usr/local//share/atom/atom" + desktopInstallFile = path.join(installDir, 'share', 'applications', 'Atom.desktop') desktopFile = path.join('resources', 'linux', 'Atom.desktop.in') + template = _.template(String(fs.readFileSync(desktopFile))) filled = template({description, installDir, iconName}) From 74992b13975fa8952c144798e08b97317e4d7c63 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Mon, 21 Jul 2014 16:38:46 -0700 Subject: [PATCH 53/61] Group template variables together --- build/tasks/install-task.coffee | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/build/tasks/install-task.coffee b/build/tasks/install-task.coffee index 726366716..f34972ddc 100644 --- a/build/tasks/install-task.coffee +++ b/build/tasks/install-task.coffee @@ -37,13 +37,14 @@ module.exports = (grunt) -> # Create Atom.desktop if installation not in temporary folder tmpDir = if process.env.TMPDIR? then process.env.TMPDIR else '/tmp' if installDir.indexOf(tmpDir) isnt 0 - mkdir path.dirname(desktopInstallFile) - {description} = grunt.file.readJSON('package.json') - installDir = path.join(installDir,'.') # To prevent "Exec=/usr/local//share/atom/atom" - - desktopInstallFile = path.join(installDir, 'share', 'applications', 'Atom.desktop') desktopFile = path.join('resources', 'linux', 'Atom.desktop.in') + desktopInstallFile = path.join(installDir, 'share', 'applications', 'Atom.desktop') + mkdir path.dirname(desktopInstallFile) + + {description} = grunt.file.readJSON('package.json') + iconName = path.join(shareDir,'resources','app','resources','atom.png') + installDir = path.join(installDir, '.') # To prevent "Exec=/usr/local//share/atom/atom" template = _.template(String(fs.readFileSync(desktopFile))) filled = template({description, installDir, iconName}) From f5951425c3083ba3f8fb08b90c1d0d53d886fa9d Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Mon, 21 Jul 2014 16:39:23 -0700 Subject: [PATCH 54/61] grunt.file.write creates parent dirs --- build/tasks/install-task.coffee | 2 -- 1 file changed, 2 deletions(-) diff --git a/build/tasks/install-task.coffee b/build/tasks/install-task.coffee index f34972ddc..3dd67260f 100644 --- a/build/tasks/install-task.coffee +++ b/build/tasks/install-task.coffee @@ -40,8 +40,6 @@ module.exports = (grunt) -> desktopFile = path.join('resources', 'linux', 'Atom.desktop.in') desktopInstallFile = path.join(installDir, 'share', 'applications', 'Atom.desktop') - mkdir path.dirname(desktopInstallFile) - {description} = grunt.file.readJSON('package.json') iconName = path.join(shareDir,'resources','app','resources','atom.png') installDir = path.join(installDir, '.') # To prevent "Exec=/usr/local//share/atom/atom" From ab980d78d2277b32337c72539ba76b4e538be21b Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Mon, 21 Jul 2014 16:39:46 -0700 Subject: [PATCH 55/61] :lipstick: --- build/tasks/install-task.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/tasks/install-task.coffee b/build/tasks/install-task.coffee index 3dd67260f..cca951bf0 100644 --- a/build/tasks/install-task.coffee +++ b/build/tasks/install-task.coffee @@ -41,7 +41,7 @@ module.exports = (grunt) -> desktopInstallFile = path.join(installDir, 'share', 'applications', 'Atom.desktop') {description} = grunt.file.readJSON('package.json') - iconName = path.join(shareDir,'resources','app','resources','atom.png') + iconName = path.join(shareDir, 'resources', 'app', 'resources', 'atom.png') installDir = path.join(installDir, '.') # To prevent "Exec=/usr/local//share/atom/atom" template = _.template(String(fs.readFileSync(desktopFile))) filled = template({description, installDir, iconName}) From ccd631a93465b7328df8e6bef2f30d8184e7dfd4 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Mon, 21 Jul 2014 16:45:59 -0700 Subject: [PATCH 56/61] Write control/desktop files to temp folder --- .gitignore | 2 -- build/package.json | 9 +++++---- build/tasks/mkdeb-task.coffee | 18 +++++++++++------- 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/.gitignore b/.gitignore index 7103ce579..1257ab371 100644 --- a/.gitignore +++ b/.gitignore @@ -13,5 +13,3 @@ debug.log docs/output docs/includes spec/fixtures/evil-files/ -resources/linux/Atom.desktop -resources/linux/debian/control diff --git a/build/package.json b/build/package.json index 91a7e30f9..a7dc2b2b1 100644 --- a/build/package.json +++ b/build/package.json @@ -14,8 +14,8 @@ "grunt": "~0.4.1", "grunt-cli": "~0.1.9", "grunt-coffeelint": "git+https://github.com/atom/grunt-coffeelint.git", - "grunt-contrib-csslint": "~0.1.2", "grunt-contrib-coffee": "~0.9.0", + "grunt-contrib-csslint": "~0.1.2", "grunt-contrib-less": "~0.8.0", "grunt-cson": "0.8.0", "grunt-download-atom-shell": "~0.8.0", @@ -27,15 +27,16 @@ "json-front-matter": "~0.1.3", "legal-eagle": "~0.4.0", "minidump": "~0.7", - "read-package-json": "1.1.8", "normalize-package-data": "0.2.12", + "npm": "~1.4.5", "rcedit": "~0.1.2", + "read-package-json": "1.1.8", "request": "~2.27.0", "rimraf": "~2.2.2", "runas": "0.5.x", + "temp": "^0.8.0", "underscore-plus": "1.x", "unzip": "~0.1.9", - "vm-compatibility-layer": "~0.1.0", - "npm": "~1.4.5" + "vm-compatibility-layer": "~0.1.0" } } diff --git a/build/tasks/mkdeb-task.coffee b/build/tasks/mkdeb-task.coffee index 090168446..6fb4edab4 100644 --- a/build/tasks/mkdeb-task.coffee +++ b/build/tasks/mkdeb-task.coffee @@ -1,11 +1,17 @@ fs = require 'fs' path = require 'path' _ = require 'underscore-plus' +temp = require 'temp' + +tempResourcesFolder = temp.mkdirSync('atom-resources-') fillTemplate = (filePath, data) -> - template = _.template(String(fs.readFileSync(filePath + '.in'))) + template = _.template(String(fs.readFileSync("#{filePath}.in"))) filled = template(data) - fs.writeFileSync(filePath, filled) + + outputPath = path.join(tempResourcesFolder, path.basename(filePath)) + fs.writeFileSync(outputPath, filled) + outputPath module.exports = (grunt) -> {spawn} = require('./task-helpers')(grunt) @@ -27,13 +33,11 @@ module.exports = (grunt) -> iconName = 'atom' data = {name, version, description, section, arch, maintainer, installDir, iconName} - control = path.join('resources', 'linux', 'debian', 'control') - fillTemplate(control, data) - desktop = path.join('resources', 'linux', 'Atom.desktop') - fillTemplate(desktop, data) + controlFilePath = fillTemplate(path.join('resources', 'linux', 'debian', 'control'), data) + desktopFilePath = fillTemplate(path.join('resources', 'linux', 'Atom.desktop'), data) icon = path.join('resources', 'atom.png') buildDir = grunt.config.get('atom.buildDir') cmd = path.join('script', 'mkdeb') - args = [version, arch, control, desktop, icon, buildDir] + args = [version, arch, controlFilePath, desktopFilePath, icon, buildDir] spawn({cmd, args}, done) From a26cb6023e3edb047cd42a631645dbc92f2550fe Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Mon, 21 Jul 2014 16:47:08 -0700 Subject: [PATCH 57/61] Track temp folders --- build/tasks/mkdeb-task.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/build/tasks/mkdeb-task.coffee b/build/tasks/mkdeb-task.coffee index 6fb4edab4..98f1c797b 100644 --- a/build/tasks/mkdeb-task.coffee +++ b/build/tasks/mkdeb-task.coffee @@ -3,6 +3,7 @@ path = require 'path' _ = require 'underscore-plus' temp = require 'temp' +temp.track() tempResourcesFolder = temp.mkdirSync('atom-resources-') fillTemplate = (filePath, data) -> From 1757ff18f28fa43c58d9258f03f051592982737b Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Mon, 21 Jul 2014 16:52:20 -0700 Subject: [PATCH 58/61] Write control/desktop files to build dir --- build/package.json | 1 - build/tasks/mkdeb-task.coffee | 20 ++++++++------------ 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/build/package.json b/build/package.json index a7dc2b2b1..8b2d55027 100644 --- a/build/package.json +++ b/build/package.json @@ -34,7 +34,6 @@ "request": "~2.27.0", "rimraf": "~2.2.2", "runas": "0.5.x", - "temp": "^0.8.0", "underscore-plus": "1.x", "unzip": "~0.1.9", "vm-compatibility-layer": "~0.1.0" diff --git a/build/tasks/mkdeb-task.coffee b/build/tasks/mkdeb-task.coffee index 98f1c797b..502b797c1 100644 --- a/build/tasks/mkdeb-task.coffee +++ b/build/tasks/mkdeb-task.coffee @@ -1,22 +1,18 @@ fs = require 'fs' path = require 'path' _ = require 'underscore-plus' -temp = require 'temp' - -temp.track() -tempResourcesFolder = temp.mkdirSync('atom-resources-') - -fillTemplate = (filePath, data) -> - template = _.template(String(fs.readFileSync("#{filePath}.in"))) - filled = template(data) - - outputPath = path.join(tempResourcesFolder, path.basename(filePath)) - fs.writeFileSync(outputPath, filled) - outputPath module.exports = (grunt) -> {spawn} = require('./task-helpers')(grunt) + fillTemplate = (filePath, data) -> + template = _.template(String(fs.readFileSync("#{filePath}.in"))) + filled = template(data) + + outputPath = path.join(grunt.config.get('atom.buildDir'), path.basename(filePath)) + fs.writeFileSync(outputPath, filled) + outputPath + grunt.registerTask 'mkdeb', 'Create debian package', -> done = @async() From b9658e23f473ade07d4b90b41a9c56e7fcc74481 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Mon, 21 Jul 2014 16:53:27 -0700 Subject: [PATCH 59/61] Write file using grunt API --- build/tasks/mkdeb-task.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/tasks/mkdeb-task.coffee b/build/tasks/mkdeb-task.coffee index 502b797c1..ccf20b790 100644 --- a/build/tasks/mkdeb-task.coffee +++ b/build/tasks/mkdeb-task.coffee @@ -10,7 +10,7 @@ module.exports = (grunt) -> filled = template(data) outputPath = path.join(grunt.config.get('atom.buildDir'), path.basename(filePath)) - fs.writeFileSync(outputPath, filled) + grunt.file.write(outputPath, filled) outputPath grunt.registerTask 'mkdeb', 'Create debian package', -> From 89733300a55ccf70914b236566d34c7952459ae4 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Mon, 21 Jul 2014 17:00:53 -0700 Subject: [PATCH 60/61] :lipstick: --- build/tasks/install-task.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/build/tasks/install-task.coffee b/build/tasks/install-task.coffee index cca951bf0..13d349a50 100644 --- a/build/tasks/install-task.coffee +++ b/build/tasks/install-task.coffee @@ -10,6 +10,7 @@ module.exports = (grunt) -> grunt.registerTask 'install', 'Install the built application', -> installDir = grunt.config.get('atom.installDir') shellAppDir = grunt.config.get('atom.shellAppDir') + if process.platform is 'win32' runas ?= require 'runas' copyFolder = path.resolve 'script', 'copy-folder.cmd' From 2f82fb2ceb5501f2df7e839af467fbfd4bec47ad Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Mon, 21 Jul 2014 18:02:02 -0700 Subject: [PATCH 61/61] Upgrade find-and-replace to fold results --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 59a1faa9e..b1d26062b 100644 --- a/package.json +++ b/package.json @@ -81,7 +81,7 @@ "dev-live-reload": "0.31.0", "exception-reporting": "0.18.0", "feedback": "0.33.0", - "find-and-replace": "0.126.0", + "find-and-replace": "0.127.0", "fuzzy-finder": "0.57.0", "git-diff": "0.37.0", "go-to-line": "0.23.0",