Comparar commits

..

78 Commits

Autor SHA1 Mensagem Data
Nathan Sobo 0d587028af Preserve tiles that are the target of mousewheel events
This allows velocity-style mouse wheel scrolling to continue once the
tile has been scrolled off screen.
2014-08-08 14:39:25 -06:00
Nathan Sobo d4207c04d6 Render content and gutter tiles with an opaque background color 2014-08-07 18:40:57 -06:00
Nathan Sobo da864b6eec Account for scroll position when translating mouse clicks to positions 2014-08-07 15:22:47 -06:00
Nathan Sobo ce75ee011a Maintain scrollTop and scrollLeft as top-level presenter properties 2014-08-07 15:22:22 -06:00
Nathan Sobo 71b7fb3b5d Render line number decorations based on presenter state 2014-08-07 15:17:05 -06:00
Nathan Sobo f413a21a11 Add line number decorations to the presenter 2014-08-07 15:01:22 -06:00
Nathan Sobo 544cd54a41 Start rendering tiled line numbers. Still rough. 2014-08-07 15:00:35 -06:00
Nathan Sobo 8b1eec39b5 Add a dummy tile to the gutter presenter with the maxLineNumberDigits
This will be used to hold open the gutter's width despite the absolute
positioned tiles.
2014-08-07 09:02:14 -06:00
Nathan Sobo 02b955f823 Break out 'content' and 'gutter' presenters
For now they're just an extra level in the hash, but we'll need to store
the maxLineNumberDigits in the gutter presenter for maintenance of the
dummy line node.
2014-08-07 08:48:00 -06:00
Nathan Sobo 30d386527d Update maxLineNumberDigits in gutter tiles 2014-08-07 07:19:10 -06:00
Nathan Sobo 0af6dfc94c Decide how to handle screen line changes in each tile presenter 2014-08-07 07:19:10 -06:00
Nathan Sobo ef7a6b94b2 Combine specs for line and gutter tiles 2014-08-07 07:19:10 -06:00
Nathan Sobo 4d6be72f56 Start adding gutter tiles to EditorPresenter 2014-08-07 07:19:09 -06:00
Nathan Sobo 3ded5ec0f8 Return dot-separated line numbers to indicate soft wraps 2014-08-07 07:19:09 -06:00
Nathan Sobo 2f15f72139 Add DisplayBuffer::lineNumbersForScreenRows 2014-08-07 07:19:09 -06:00
Nathan Sobo 6f9fa39ad2 Remove DisplayStateManager. Using EditorPresenter instead. 2014-08-07 07:19:09 -06:00
Nathan Sobo f2eeb0629f Avoid occasional blank lines at the top and bottom of the editor 2014-08-07 07:19:09 -06:00
Nathan Sobo 1fdb4dbeda Un-f 2014-08-07 07:19:09 -06:00
Nathan Sobo 54c2139992 Add line decorations to EditorPresenter 2014-08-07 07:19:09 -06:00
Nathan Sobo 6e7dedb198 Explicit model tile height in presenter 2014-08-07 07:19:09 -06:00
Nathan Sobo 1fc33e1e21 Render and update lines based on mutable presenter 2014-08-07 07:19:09 -06:00
Nathan Sobo 583096a916 Handle scrollLeft changes 2014-08-07 07:19:09 -06:00
Nathan Sobo 4d88acd756 Start on a mutable EditorPresenter object 2014-08-07 07:19:09 -06:00
Nathan Sobo a3afaed950 Add proper line decorations to initial state 2014-08-07 07:19:09 -06:00
Nathan Sobo 716219b9a3 Handle 'onlyNonEmpty' option for line decorations 2014-08-07 07:19:08 -06:00
Nathan Sobo bc558194e9 Handle 'onlyEmpty' option for line decorations 2014-08-07 07:19:08 -06:00
Nathan Sobo c1ee2f420b Support 'onlyHead' option in line decorations 2014-08-07 07:19:08 -06:00
Nathan Sobo 58d9304c22 Refactor line decoration updates 2014-08-07 07:19:08 -06:00
Nathan Sobo 3226f54b97 Improve custom matcher to express omission of values 2014-08-07 07:19:08 -06:00
Nathan Sobo 63969a6467 Use line decorations to update display
Basic line decorations are working, but we still need to handle special
decoration options.
2014-08-07 07:19:08 -06:00
Nathan Sobo d5f7aca0ad Build line decorations into initial state 2014-08-07 07:19:08 -06:00
Nathan Sobo 94160044ad Fix fat finger 2014-08-07 07:19:08 -06:00
Nathan Sobo 4dcb35152f Only handle line decorations for now 2014-08-07 07:19:08 -06:00
Nathan Sobo 45ac962681 Update the display state on when line decorations are updated or removed 2014-08-07 07:19:08 -06:00
Nathan Sobo c5d502ab88 Add line decorations to display state when they're added 2014-08-07 07:19:08 -06:00
Nathan Sobo c75fc8aa01 Make ::updateTiles an iterator for operation-specific updates
Previously, I was trying to update everything with the same method. Now
I'm performing updates that are specifically tailored to each type of
operation on the model.
2014-08-07 07:19:07 -06:00
Nathan Sobo e954430599 Update tile states when display buffer changes 2014-08-07 07:19:07 -06:00
Nathan Sobo 44795da767 WIP: Base editor updates on immutable display state 2014-08-07 07:19:07 -06:00
Nathan Sobo 11df5c2855 Render cursors 2014-08-07 07:19:07 -06:00
Nathan Sobo 11189692b7 Break lines out into manually-updated tiles 2014-08-07 07:19:07 -06:00
Nathan Sobo a7fb07ccfa Avoid React handling of keydown/textInput to save ~1ms on keystrokes 2014-08-07 07:19:07 -06:00
Nathan Sobo a8df20271f WIP 2014-08-07 07:19:07 -06:00
Nathan Sobo 84064a811c :non_potable_water: Unsubscribe from window resize in EditorComponent
This fixes a major memory leak
2014-08-07 04:59:02 -06:00
Cheng Zhao 3c932d6d91 Upgrade to atom-shell@0.15.3 2014-08-07 00:11:40 +08:00
Ben Ogle a24d1d1af7 Upgrade find-and-replace 2014-08-05 11:31:58 -07:00
Ben Ogle edb5b43d64 Upgrade settings view for #3176 2014-08-05 11:20:38 -07:00
Ben Ogle ac496e1fa4 Upgrade tabs to fix hanging in #1663 2014-08-05 10:54:57 -07:00
Ben Ogle 103f3f8597 Merge pull request #3185 from sryze/patch-1
Add build fix from #2435
2014-08-05 10:17:24 -07:00
Kevin Sawicki 9842baedce Use Atom as FileDescription
This is the text presented when the app is presented in a select list.

Closes #3179
2014-08-05 07:59:02 -07:00
Kevin Sawicki aaa916f78d Upgrade to language-coffee-script@0.29 2014-08-05 07:52:49 -07:00
Sergey Zolotarev 7f6a4cccaf Add build fix from #2435 2014-08-05 18:00:25 +07:00
Kevin Sawicki 87edff1e42 Upgrade to language-gfm@0.46 2014-08-04 11:09:42 -07:00
Kevin Sawicki 96f35d3cde Upgrade to language-ruby@0.34 2014-08-04 11:09:41 -07:00
Nathan Sobo 7e45ffa4c3 Center around the cursor in Editor::scrollToCursorPosition by default
Fixes #3131
2014-08-04 11:58:07 -06:00
Kevin Sawicki 6af69b0fc7 Merge pull request #3164 from Bengt/patch-1
Correct Node.js' spelling, link Git and GNOME Keyring
2014-08-04 10:39:12 -07:00
Cheng Zhao 99e02570d1 Upgrade to atom-shell@0.15.2 2014-08-04 22:12:44 +08:00
Bengt Lüers 823cfcac57 Correct Node.js' spelling, link Git and GNOME Keyring 2014-08-03 16:06:18 +02:00
Nathan Sobo de6ccd8c08 Merge pull request #3146 from atom/ns-latency
Improve cursor movement and typing latency a bit
2014-08-02 09:21:51 -07:00
Ben Ogle 2135d3be83 Update statusbar to add toggle 2014-08-01 10:22:14 -07:00
Nathan Sobo 1c3720c160 Upgrade keybinding-resolver for spec fix 2014-07-31 15:23:58 -06:00
Nathan Sobo 6c72b13adc Upgrade keymap to avoid temp objects in keystrokeForKeyboardEvent 2014-07-31 15:21:37 -06:00
Kevin Sawicki 1404904d24 Upgrade to language-gfm@0.45 2014-07-31 14:15:01 -07:00
Nathan Sobo db243936b4 Update emissary for Emitter::emit optimization 2014-07-31 15:11:25 -06:00
Nathan Sobo 6e72627e9e Stop propagation of keydown/textInput events to prevent React handler
React's global synthetic event handler is somewhat expensive. This
prevents it from being invoked on every keystroke, saving ~1ms.
2014-07-31 15:11:06 -06:00
Kevin Sawicki 3d36ba7ecc Upgrade to scrollbar-style 1.0.2 2014-07-31 13:30:27 -07:00
Kevin Sawicki a7c0d6073f Upgrade to markdown-preview@0.95 2014-07-31 13:24:18 -07:00
Kevin Sawicki f25b468272 Upgrade to apm 0.88 2014-07-31 13:07:44 -07:00
Kevin Sawicki 2d0fb8ee6b Upgrade to incompatible-packages@0.5 2014-07-31 09:19:15 -07:00
Kevin Sawicki d875becc7a Upgrade to snippets@0.51 2014-07-31 09:06:38 -07:00
Kevin Sawicki cb72af63fd Upgrade to language-yaml@0.14 2014-07-31 08:57:09 -07:00
Kevin Sawicki f7187f1d5a Spy on atom.inDevMode() 2014-07-31 08:42:31 -07:00
Kevin Sawicki 700acdc5a2 Upgrade to incompatible-packages@0.4 2014-07-31 08:37:18 -07:00
Kevin Sawicki 18016ae9df 💄 Use unless instead of if not 2014-07-31 08:33:36 -07:00
Kevin Sawicki a30faa5bea Merge pull request #3139 from maschs/ms-incompatibleModulesDev
In devmode do not load incompatible modules from cache
2014-07-31 08:32:58 -07:00
Kevin Sawicki 05a113bb7a Merge pull request #3120 from atom/ks-remove-vendored-dlls
Remove vendored dlls
2014-07-31 08:32:53 -07:00
Kevin Sawicki f5d4ece9cd Remove vendored dlls
These are now provided by atom-shell
2014-07-31 08:20:33 -07:00
Cheng Zhao 3bda37c56c Upgrade to atom-shell@0.15.1 2014-07-31 23:19:50 +08:00
Maximilian Schüßler 62b52cb70a In devmode do not load incompatible from cache 2014-07-31 16:24:29 +02:00
26 arquivos alterados com 1672 adições e 580 exclusões
+1 -1
Ver Arquivo
@@ -6,6 +6,6 @@
"url": "https://github.com/atom/atom.git"
},
"dependencies": {
"atom-package-manager": "0.87.0"
"atom-package-manager": "0.88.0"
}
}
-3
Ver Arquivo
@@ -148,9 +148,6 @@ module.exports = (grunt) ->
grunt.file.copy(sourcePath, path.resolve(appDir, '..', subDirectory, filename))
if process.platform is 'win32'
cp path.join('resources', 'win', 'msvcp100.dll'), path.join(shellAppDir, 'msvcp100.dll')
cp path.join('resources', 'win', 'msvcr100.dll'), path.join(shellAppDir, 'msvcr100.dll')
# Set up chocolatey ignore and gui files
fs.writeFileSync path.join(appDir, 'apm', 'node_modules', 'atom-package-manager', 'bin', 'node.exe.ignore'), ''
fs.writeFileSync path.join(appDir, 'node_modules', 'symbols-view', 'vendor', 'ctags-win32.exe.ignore'), ''
+2 -2
Ver Arquivo
@@ -45,8 +45,8 @@ module.exports = (grunt) ->
strings =
CompanyName: 'GitHub, Inc.'
FileDescription: 'The hackable editor'
LegalCopyright: 'Copyright (C) 2013 GitHub, Inc. All rights reserved'
FileDescription: 'Atom'
LegalCopyright: 'Copyright (C) 2014 GitHub, Inc. All rights reserved'
ProductName: 'Atom'
ProductVersion: version
-112
Ver Arquivo
@@ -1,112 +0,0 @@
# API Guidelines
__Note__: These are not all in practice yet. We are still sorting this out, and plan to move the entire API this way.
## General Guidelines
We should strive to have only one way to do something, and make it clear what that way is.
__Bad__
* Several ways to get the `Editor` model from the view.
* `EditorView.editor`
* `EditorView.getModel()`
* `EditorView.getEditor()`
* Delegated methods on the views when there is a model counterpart
* `Editor.toggleSoftTabs()`
* `EditorView.toggleSoftTabs()`
__Good__
* One way to get the `Editor` model from the view
* `EditorView.getModel()`
* Use the method on the model
* `Editor.toggleSoftTabs()`
* One clear way to subscribe to events
* `subscription = @subscribe thing, 'event', -> ...`
* One clear way to unsubscribe to events
* `subscription.off()` only; not `thing.off 'event', -> ...`
## Essential vs Extended
There are two groups of classes / methods. We break them up to facilitate a gentle introduction into the API and help authors build a knowledge foundation more quickly.
### Essential
These are classes, methods, and concepts nearly every package author will need to know about. Need to create commands? Subscribe to atom events? Get a reference to all the editors? Highlight a line in the editor? Patterns and methods for these things will be explained in the essential API.
We want to keep the essential API minimal and focused.
### Extended
The extended API contains The Power. Need to move one cursor independent of the others? Want to do some processing on the markers? You can do it with the extended API.
## View / Model
Operations on Views should be limited to DOM manipulation only. A package author should only need access to, say, the `EditorView` when it needs to directly modify the `EditorView`'s DOM.
## Properties
No public properties. Use methods instead.
## Methods
### Naming
We strive to fit the [Objective C][naming] naming conventions for the sake of readability.
* Be descriptive, always write out the whole word. `selection` not `sel`, `cursor` not `cur`.
* Describe the arguments names in the method name eg. `decorationsForMarker(marker)`, `objectAtIndex(index)`
* Use `get` prefix only when there are no arguments `getCursors()`, `getLastCursor()`, `cursorForMarker(marker)`
* Prefix bool methods with `is` eg. `isDefault()`
Array accessor methods would be written as follows
```coffee
getObjects()
addObject(object)
removeObject(object)
removeObjectAtIndex(index)
objectAtIndex(index)
objectsForThing(thing)
objectForThing(thing)
```
## Events
There will be no `off()` method on objects. `on()` will return a subscription object which contains the `off()` method.
* Events should be emitted with one event object as an argument, rather than a bunch of arguments.
* If an event is cancelable, it will provide a `cancel` function in the event object.
### Naming
Past tense. ???
## Documentation
Comment doc strings on methods, events, etc
* how to doc sections?
* events?
* props?
* callback args?
* option hashes?
### Method organization
* All methods in classes should be grouped into sections by usage pattern.
* Methods within a section should be ordered with the most commonly used methods at the top. `Essential` methods always go above `Extended` methods.
* Sections should be ordered in the class with the most commonly used sections at the top.
A section:
```coffee
###
Section: Reading Text
###
# Essential: Returns a {String} representing the entire contents of the editor.
getText: -> @buffer.getText()
```
[naming]:https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/CodingGuidelines/Articles/NamingMethods.html
+7 -7
Ver Arquivo
@@ -6,21 +6,21 @@ Ubuntu LTS 12.04 64-bit is the recommended platform.
* OS with 64-bit or 32-bit architecture
* C++ toolchain
* git
* [node.js](http://nodejs.org/download/) v0.10.x
* [npm](http://www.npmjs.org/) v1.4.x (bundled with node.js)
* [Git](http://git-scm.com/)
* [Node.js](http://nodejs.org/download/) v0.10.x
* [npm](http://www.npmjs.org/) v1.4.x (bundled with Node.js)
* `npm -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
* development headers for [GNOME Keyring](https://wiki.gnome.org/Projects/GnomeKeyring)
### 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).
* 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).
* 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`
@@ -78,7 +78,7 @@ See also https://github.com/atom/atom/issues/2082.
### /usr/bin/env: node: No such file or directory
If you get this notice when attempting to `script/build`, you either do not
have nodejs installed, or node isn't identified as nodejs on your machine.
have Node.js installed, or node isn't identified as Node.js on your machine.
If it's the latter, entering `sudo ln -s /usr/bin/nodejs /usr/bin/node` into
your terminal may fix the issue.
+13
Ver Arquivo
@@ -66,6 +66,19 @@ If none of this works, do install Github for Windows and use its Git shell. Make
* https://github.com/TooTallNate/node-gyp/issues/297
* https://code.google.com/p/gyp/issues/detail?id=393
* `script/build` stops at installing runas with 'Failed at the runas@0.5.4 install script.'
See the next item.
* `error MSB8020: The build tools for Visual Studio 2010 (Platform Toolset = 'v100') cannot be found.`
* If you're building atom with Visual Studio 2013 try executing the following
command in your Git shell and then re-run `script/build`:
```
$env:GYP_MSVS_VERSION=2013
```
* 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.
+17 -18
Ver Arquivo
@@ -17,16 +17,16 @@
"url": "http://github.com/atom/atom/raw/master/LICENSE.md"
}
],
"atomShellVersion": "0.15.0",
"atomShellVersion": "0.15.3",
"dependencies": {
"async": "0.2.6",
"atom-keymap": "^1.0.0",
"atom-keymap": "^1.0.2",
"bootstrap": "git+https://github.com/atom/bootstrap.git#6af81906189f1747fd6c93479e3d998ebe041372",
"clear-cut": "0.4.0",
"coffee-script": "1.7.0",
"coffeestack": "0.7.0",
"delegato": "^1",
"emissary": "^1.2.1",
"emissary": "^1.2.2",
"first-mate": "^2.0.1",
"fs-plus": "^2.2.6",
"fstream": "0.1.24",
@@ -45,12 +45,12 @@
"property-accessors": "^1",
"q": "^1.0.1",
"random-words": "0.0.1",
"react-atom-fork": "^0.11.1",
"react-atom-fork": "^0.11.2",
"reactionary-atom-fork": "^1.0.0",
"runas": "1.0.1",
"scandal": "1.0.0",
"scoped-property-store": "^0.9.0",
"scrollbar-style": "^1.0.1",
"scrollbar-style": "^1.0.2",
"season": "^1.0.2",
"semver": "1.1.4",
"serializable": "^1",
@@ -82,38 +82,37 @@
"dev-live-reload": "0.33.0",
"exception-reporting": "0.19.0",
"feedback": "0.33.0",
"find-and-replace": "0.127.0",
"find-and-replace": "0.128.0",
"fuzzy-finder": "0.57.0",
"git-diff": "0.37.0",
"go-to-line": "0.23.0",
"grammar-selector": "0.27.0",
"image-view": "0.36.0",
"incompatible-packages": "0.3.0",
"keybinding-resolver": "0.18.0",
"incompatible-packages": "0.5.0",
"keybinding-resolver": "0.19.0",
"link": "0.25.0",
"markdown-preview": "0.94.0",
"markdown-preview": "0.95.0",
"metrics": "0.33.0",
"open-on-github": "0.29.0",
"package-generator": "0.31.0",
"release-notes": "0.36.0",
"settings-view": "0.137.0",
"snippets": "0.50.0",
"settings-view": "0.138.0",
"snippets": "0.51.0",
"spell-check": "0.40.0",
"status-bar": "0.41.0",
"status-bar": "0.42.0",
"styleguide": "0.29.0",
"symbols-view": "0.63.0",
"tabs": "0.48.0",
"tabs": "0.49.0",
"timecop": "0.22.0",
"tree-view": "0.112.0",
"update-package-dependencies": "0.6.0",
"welcome": "0.17.0",
"whitespace": "0.25.0",
"wrap-guide": "0.21.0",
"language-c": "0.26.0",
"language-coffee-script": "0.28.0",
"language-coffee-script": "0.29.0",
"language-css": "0.17.0",
"language-gfm": "0.44.0",
"language-gfm": "0.46.0",
"language-git": "0.9.0",
"language-go": "0.16.0",
"language-html": "0.22.0",
@@ -128,7 +127,7 @@
"language-php": "0.15.0",
"language-property-list": "0.7.0",
"language-python": "0.18.0",
"language-ruby": "0.33.0",
"language-ruby": "0.34.0",
"language-ruby-on-rails": "0.15.0",
"language-sass": "0.14.0",
"language-shellscript": "0.8.0",
@@ -138,7 +137,7 @@
"language-todo": "0.10.0",
"language-toml": "0.12.0",
"language-xml": "0.17.0",
"language-yaml": "0.13.0"
"language-yaml": "0.14.0"
},
"private": true,
"scripts": {
Arquivo binário não exibido.
Arquivo binário não exibido.
+8
Ver Arquivo
@@ -1167,3 +1167,11 @@ describe "DisplayBuffer", ->
expect(displayBuffer.getScrollWidth()).toBe 10 * 63 + operatorWidth * 2 + cursorWidth
expect(changedSpy.callCount).toBe 1
describe "::lineNumbersForScreenRows(startRow, endRow)", ->
it "returns the line numbers for the given screen row range, inclusive of the endRow", ->
displayBuffer.createFold(4, 7)
displayBuffer.setEditorWidthInChars(30)
displayBuffer.setSoftWrap(true)
expect(displayBuffer.lineNumbersForScreenRows(6, 11)).toEqual ['4.2', '5', '9', '9.1', '9.2', '10']
+581
Ver Arquivo
@@ -0,0 +1,581 @@
TextBuffer = require 'text-buffer'
_ = require 'underscore-plus'
EditorPresenter = require '../src/editor-presenter'
Editor = require '../src/editor'
describe "DisplayStateManager", ->
[buffer, editor, presenter] = []
beforeEach ->
@addMatchers(toHaveValues: ToHaveValuesMatcher)
spyOn(EditorPresenter::, 'getContentTileSize').andReturn 5
spyOn(EditorPresenter::, 'getGutterTileSize').andReturn 5
buffer = new TextBuffer(filePath: atom.project.resolve('sample.js'))
buffer.loadSync()
buffer.insert([12, 3], '\n' + buffer.getText()) # repeat text so we have more lines
editor = new Editor({buffer})
editor.setLineHeightInPixels(10)
editor.setDefaultCharWidth(10)
editor.setHeight(100)
editor.setWidth(500)
presenter = new EditorPresenter(editor)
afterEach ->
editor.destroy()
describe "scrollPosition", ->
it "maintains the scrollTop and scrollLeft as top-level presenter properties", ->
expect(presenter.scrollTop).toBe 0
expect(presenter.scrollLeft).toBe 0
editor.setScrollTop(20)
editor.setScrollLeft(30)
expect(presenter.scrollTop).toBe 20
expect(presenter.scrollLeft).toBe 30
describe "tiles", ->
describe "initial state", ->
it "renders tiles that overlap the visible row range", ->
expect(presenter).toHaveValues
content:
tiles:
0:
startRow: 0
top: 0
width: editor.getWidth()
height: 5 * 10
lineHeightInPixels: 10
lines: editor.linesForScreenRows(0, 4)
5:
startRow: 5
top: 50
width: editor.getWidth()
height: 5 * 10
lineHeightInPixels: 10
lines: editor.linesForScreenRows(5, 9)
10:
startRow: 10
top: 100
width: editor.getWidth()
height: 5 * 10
lineHeightInPixels: 10
lines: editor.linesForScreenRows(10, 14)
gutter:
tiles:
0:
startRow: 0
top: 0
height: 5 * 10
lineHeightInPixels: 10
lineNumbers: editor.lineNumbersForScreenRows(0, 4)
5:
startRow: 5
top: 50
height: 5 * 10
lineHeightInPixels: 10
lineNumbers: editor.lineNumbersForScreenRows(5, 9)
10:
startRow: 10
top: 100
height: 5 * 10
lineHeightInPixels: 10
lineNumbers: editor.lineNumbersForScreenRows(10, 14)
it "renders a dummy gutter tile to maintain the proper gutter width", ->
expect(presenter.gutter).toHaveValues
dummyTile:
dummy: true
maxLineNumberDigits: 2
it "assigns the backgroundColor on the content and gutter tiles, favoring the gutter's background color if it's assigned", ->
editor.setBackgroundColor('#ff0')
presenter = new EditorPresenter(editor)
expect(presenter).toHaveValues
content:
tiles:
0: backgroundColor: '#ff0'
5: backgroundColor: '#ff0'
10: backgroundColor: '#ff0'
gutter:
tiles:
0: backgroundColor: '#ff0'
5: backgroundColor: '#ff0'
10: backgroundColor: '#ff0'
editor.setGutterBackgroundColor('#a00')
presenter = new EditorPresenter(editor)
expect(presenter).toHaveValues
content:
tiles:
0: backgroundColor: '#ff0'
5: backgroundColor: '#ff0'
10: backgroundColor: '#ff0'
gutter:
tiles:
0: backgroundColor: '#a00'
5: backgroundColor: '#a00'
10: backgroundColor: '#a00'
describe "when the width is changed", ->
it "updates the line tiles with the new width", ->
editor.setWidth(700)
expect(presenter.content.tiles).toHaveValues
0:
width: 700
5:
width: 700
10:
width: 700
describe "when the height is changed", ->
it "updates the rendered tiles to reflect the change", ->
editor.setHeight(160)
expect(presenter).toHaveValues
content:
tiles:
0:
startRow: 0
top: 0
5:
startRow: 5
top: 50
10:
startRow: 10
top: 100
15:
startRow: 15
top: 150
gutter:
tiles:
0:
startRow: 0
top: 0
5:
startRow: 5
top: 50
10:
startRow: 10
top: 100
15:
startRow: 15
top: 150
editor.setHeight(70)
expect(presenter).toHaveValues
content:
tiles:
0:
startRow: 0
top: 0
5:
startRow: 5
top: 50
gutter:
tiles:
0:
startRow: 0
top: 0
5:
startRow: 5
top: 50
describe "when lineHeightInPixels changes", ->
it "updates the rendered tiles to reflect the change", ->
editor.setScrollTop(10)
editor.setLineHeightInPixels(7)
expect(presenter).toHaveValues
content:
tiles:
0:
startRow: 0
top: 0 - 10
height: 5 * 7
lineHeightInPixels: 7
5:
startRow: 5
top: 7 * 5 - 10
height: 5 * 7
lineHeightInPixels: 7
10:
startRow: 10
top: 7 * 10 - 10
height: 5 * 7
lineHeightInPixels: 7
15:
startRow: 15
top: 7 * 15 - 10
height: 5 * 7
lineHeightInPixels: 7
gutter:
tiles:
0:
startRow: 0
top: 0 - 10
height: 5 * 7
lineHeightInPixels: 7
5:
startRow: 5
top: 7 * 5 - 10
height: 5 * 7
lineHeightInPixels: 7
10:
startRow: 10
top: 7 * 10 - 10
height: 5 * 7
lineHeightInPixels: 7
15:
startRow: 15
top: 7 * 15 - 10
height: 5 * 7
lineHeightInPixels: 7
describe "when scrollTop changes", ->
it "updates the rendered tiles to reflect the change", ->
editor.setScrollTop(20)
expect(presenter).toHaveValues
content:tiles:
0:
top: -20
lines: editor.linesForScreenRows(0, 4)
5:
top: 30
lines: editor.linesForScreenRows(5, 9)
10:
top: 80
lines: editor.linesForScreenRows(10, 14)
gutter:tiles:
0:
top: -20
lineNumbers: editor.lineNumbersForScreenRows(0, 4)
5:
top: 30
lineNumbers: editor.lineNumbersForScreenRows(5, 9)
10:
top: 80
lineNumbers: editor.lineNumbersForScreenRows(10, 14)
editor.setScrollTop(70)
expect(presenter.content.tiles).toHaveValues
5:
top: -20
lines: editor.linesForScreenRows(5, 9)
10:
top: 30
lines: editor.linesForScreenRows(10, 14)
15:
top: 80
lines: editor.linesForScreenRows(15, 19)
describe "when scrollLeft changes", ->
it "updates the rendered tiles to reflect the change", ->
expect(presenter.content.tiles).toHaveValues
0:
left: 0
5
left: 0
10:
left: 0
editor.setScrollLeft(30)
expect(presenter.content.tiles).toHaveValues
0:
left: -30
5:
left: -30
10:
left: -30
describe "when the backgroundColor or gutterBackgroundColor change", ->
it "updates the backgroundColor of the tiles", ->
editor.setBackgroundColor('#abe')
expect(presenter).toHaveValues
content:
tiles:
0: backgroundColor: '#abe'
5: backgroundColor: '#abe'
10: backgroundColor: '#abe'
gutter:
tiles:
0: backgroundColor: '#abe'
5: backgroundColor: '#abe'
10: backgroundColor: '#abe'
editor.setGutterBackgroundColor('#dad')
expect(presenter).toHaveValues
content:
tiles:
0: backgroundColor: '#abe'
5: backgroundColor: '#abe'
10: backgroundColor: '#abe'
gutter:
tiles:
0: backgroundColor: '#dad'
5: backgroundColor: '#dad'
10: backgroundColor: '#dad'
describe "lines", ->
describe "initial state", ->
it "breaks lines into tiles", ->
expect(presenter.content.tiles).toHaveValues
0:
startRow: 0
lines: editor.linesForScreenRows(0, 4)
5:
startRow: 5
lines: editor.linesForScreenRows(5, 9)
10:
startRow: 10
lines: editor.linesForScreenRows(10, 14)
describe "when the screen lines change", ->
it "updates the lines in the tiles to reflect the change", ->
buffer.setTextInRange([[3, 5], [7, 0]], "a\nb\nc\nd")
expect(presenter.content.tiles).toHaveValues
0:
startRow: 0
lines: editor.linesForScreenRows(0, 4)
5:
startRow: 5
lines: editor.linesForScreenRows(5, 9)
10
startRow: 10
lines: editor.linesForScreenRows(10, 14)
describe "line decorations", ->
marker = null
beforeEach ->
marker = editor.markBufferRange([[3, 4], [5, 6]], invalidate: 'touch')
describe "initial state", ->
it "renders existing line decorations on the appropriate lines", ->
decoration = editor.decorateMarker(marker, type: 'line', class: 'test')
presenter = new EditorPresenter(editor)
decorationsById = {}
decorationsById[decoration.id] = decoration.getParams()
expect(presenter.content.tiles).toHaveValues
0:
lineDecorations:
3: decorationsById
4: decorationsById
5:
lineDecorations:
5: decorationsById
describe "when a line decoration is added, updated, invalidated, or removed", ->
it "updates the presented line decorations accordingly", ->
decoration = editor.decorateMarker(marker, type: 'line', class: 'test')
decorationsById = {}
decorationsById[decoration.id] = decoration.getParams()
expect(presenter.content.tiles).toHaveValues
0:
lineDecorations:
3: decorationsById
4: decorationsById
5:
lineDecorations:
5: decorationsById
marker.setBufferRange([[8, 4], [10, 6]])
expect(presenter.content.tiles).toHaveValues
0:
lineDecorations:
3: null
4: null
5:
lineDecorations:
5: null
8: decorationsById
9: decorationsById
10:
lineDecorations:
10: decorationsById
buffer.insert([8, 5], 'invalidate marker')
expect(presenter.content.tiles).toHaveValues
5:
lineDecorations:
8: null
9: null
10:
lineDecorations:
10: null
buffer.undo()
expect(presenter.content.tiles).toHaveValues
5:
lineDecorations:
8: decorationsById
9: decorationsById
10:
lineDecorations:
10: decorationsById
marker.destroy()
expect(presenter.content.tiles).toHaveValues
5:
lineDecorations:
8: null
9: null
10:
lineDecorations:
10: null
describe "line numbers", ->
describe "when the screen lines change", ->
it "updates the line numbers to reflect the change", ->
editor.createFold(4, 7)
expect(presenter.gutter.tiles).toHaveValues
0:
lineNumbers: editor.lineNumbersForScreenRows(0, 4)
5:
lineNumbers: editor.lineNumbersForScreenRows(5, 9)
10:
lineNumbers: editor.lineNumbersForScreenRows(10, 14)
it "updates the maxLineNumberDigits if necessary", ->
buffer.setText('')
expect(presenter.gutter.dummyTile.maxLineNumberDigits).toBe 1
expect(presenter.gutter.tiles).toHaveValues
0:
maxLineNumberDigits: 1
buffer.setText([0..10].join('\n'))
expect(presenter.gutter.dummyTile.maxLineNumberDigits).toBe 2
expect(presenter.gutter.tiles).toHaveValues
0:
maxLineNumberDigits: 2
5:
maxLineNumberDigits: 2
10:
maxLineNumberDigits: 2
buffer.delete([[8, 0], [Infinity, 0]])
expect(presenter.gutter.dummyTile.maxLineNumberDigits).toBe 1
expect(presenter.gutter.tiles).toHaveValues
0:
maxLineNumberDigits: 1
5:
maxLineNumberDigits: 1
describe "line number decorations", ->
marker = null
beforeEach ->
marker = editor.markBufferRange([[3, 4], [5, 6]], invalidate: 'touch')
describe "initial state", ->
it "renders existing line number decorations on the appropriate lines", ->
decoration = editor.decorateMarker(marker, type: 'gutter', class: 'test')
presenter = new EditorPresenter(editor)
decorationsById = {}
decorationsById[decoration.id] = decoration.getParams()
expect(presenter.gutter.tiles).toHaveValues
0:
lineNumberDecorations:
3: decorationsById
4: decorationsById
5:
lineNumberDecorations:
5: decorationsById
describe "when a line number decorations is added, updated, invalidated, or removed", ->
it "updates the presented line decorations accordingly", ->
decoration = editor.decorateMarker(marker, type: 'gutter', class: 'test')
decorationsById = {}
decorationsById[decoration.id] = decoration.getParams()
expect(presenter.gutter.tiles).toHaveValues
0:
lineNumberDecorations:
3: decorationsById
4: decorationsById
5:
lineNumberDecorations:
5: decorationsById
marker.setBufferRange([[8, 4], [10, 6]])
expect(presenter.gutter.tiles).toHaveValues
0:
lineNumberDecorations:
3: null
4: null
5:
lineNumberDecorations:
5: null
8: decorationsById
9: decorationsById
10:
lineNumberDecorations:
10: decorationsById
buffer.insert([8, 5], 'invalidate marker')
expect(presenter.gutter.tiles).toHaveValues
5:
lineNumberDecorations:
8: null
9: null
10:
lineNumberDecorations:
10: null
buffer.undo()
expect(presenter.gutter.tiles).toHaveValues
5:
lineNumberDecorations:
8: decorationsById
9: decorationsById
10:
lineNumberDecorations:
10: decorationsById
marker.destroy()
expect(presenter.gutter.tiles).toHaveValues
5:
lineNumberDecorations:
8: null
9: null
10:
lineNumberDecorations:
10: null
ToHaveValuesMatcher = (expected) ->
hasAllValues = true
wrongValues = {}
checkValues = (actual, expected, keyPath=[]) ->
for key, expectedValue of expected
key = numericKey if numericKey = parseInt(key)
currentKeyPath = keyPath.concat([key])
if expectedValue?
if actual.hasOwnProperty(key)
actualValue = actual[key]
if expectedValue.constructor is Object and _.size(expectedValue) > 0
checkValues(actualValue, expectedValue, currentKeyPath)
else
unless _.isEqual(actualValue, expectedValue)
hasAllValues = false
_.setValueForKeyPath(wrongValues, currentKeyPath.join('.'), {actualValue, expectedValue})
else
hasAllValues = false
_.setValueForKeyPath(wrongValues, currentKeyPath.join('.'), {expectedValue})
else
actualValue = actual[key]
if actualValue?
hasAllValues = false
_.setValueForKeyPath(wrongValues, currentKeyPath.join('.'), {actualValue, expectedValue})
this.message = => "Object did not have expected values: #{jasmine.pp(wrongValues)}"
checkValues(@actual, expected)
console.warn "Object did not have expected values:", wrongValues unless hasAllValues
hasAllValues
+8 -3
Ver Arquivo
@@ -3301,20 +3301,25 @@ describe "Editor", ->
expect(editor.getText()).toBe ' '
describe ".scrollToCursorPosition()", ->
it "scrolls the last cursor into view", ->
it "scrolls the last cursor into view, centering around the cursor if possible and the 'center' option isn't false", ->
editor.setCursorScreenPosition([8, 8])
editor.setLineHeightInPixels(10)
editor.setDefaultCharWidth(10)
editor.setHeight(50)
editor.setHeight(60)
editor.setWidth(50)
editor.setHorizontalScrollbarHeight(0)
expect(editor.getScrollTop()).toBe 0
expect(editor.getScrollLeft()).toBe 0
editor.scrollToCursorPosition()
expect(editor.getScrollBottom()).toBe (9 + editor.getVerticalScrollMargin()) * 10
expect(editor.getScrollTop()).toBe (8.5 * 10) - 30
expect(editor.getScrollBottom()).toBe (8.5 * 10) + 30
expect(editor.getScrollRight()).toBe (9 + editor.getHorizontalScrollMargin()) * 10
editor.setScrollTop(0)
editor.scrollToCursorPosition(center: false)
expect(editor.getScrollBottom()).toBe (9 + editor.getVerticalScrollMargin()) * 10
describe ".pageUp/Down()", ->
it "scrolls one screen height up or down and moves the cursor one page length", ->
editor.manageScrollPosition = true
+3
Ver Arquivo
@@ -5,6 +5,9 @@ ThemePackage = require '../src/theme-package'
describe "Package", ->
describe "when the package contains incompatible native modules", ->
beforeEach ->
spyOn(atom, 'inDevMode').andReturn(false)
it "does not activate it", ->
packagePath = atom.project.resolve('packages/package-with-incompatible-native-module')
pack = new Package(packagePath)
+2 -2
Ver Arquivo
@@ -95,8 +95,8 @@ class Cursor extends Model
getBufferPosition: ->
@marker.getHeadBufferPosition()
autoscroll: ->
@editor.scrollToScreenRange(@getScreenRange())
autoscroll: (options) ->
@editor.scrollToScreenRange(@getScreenRange(), options)
# Public: If the marker range is empty, the cursor is marked as being visible.
updateVisibility: ->
+2 -2
Ver Arquivo
@@ -220,8 +220,8 @@ class DisplayBufferMarker
@oldTailScreenPosition, newTailScreenPosition,
@oldHeadBufferPosition, newHeadBufferPosition,
@oldTailBufferPosition, newTailBufferPosition,
textChanged,
isValid
@wasValid, isValid,
textChanged
}
@oldHeadBufferPosition = newHeadBufferPosition
+33 -1
Ver Arquivo
@@ -401,13 +401,45 @@ class DisplayBuffer extends Model
# buffer rows corresponding to every screen row in the range
#
# startScreenRow - The screen row {Number} to start at
# endScreenRow - The screen row {Number} to end at (default: the last screen row)
# endScreenRow - The screen row {Number} to end at, inclusive.
#
# Returns an {Array} of buffer rows as {Numbers}s.
bufferRowsForScreenRows: (startScreenRow, endScreenRow) ->
for screenRow in [startScreenRow..endScreenRow]
@rowMap.bufferRowRangeForScreenRow(screenRow)[0]
# Given starting and ending screen rows, this returns an array of the line
# number strings corresponding to every screen row in the range.
#
# Line numbers start at 1 as opposed to row numbers which start at 0.
# Soft wrapped lines are indicated by dot-separated strings. For example, if
# line 9 wraps twice, it will appear as '9', '9.1', '9.2'.
#
# startScreenRow - The screen row {Number} to start at
# endScreenRow - The screen row {Number} to end at, inclusive.
#
# Returns an {Array} of line numbers as {String}s.
lineNumbersForScreenRows: (startScreenRow, endScreenRow) ->
bufferRows = @bufferRowsForScreenRows(startScreenRow, endScreenRow)
# Pad the leading
leadingSoftWraps = 0
while @bufferRowForScreenRow(startScreenRow - leadingSoftWraps - 1) is bufferRows[0]
bufferRows.unshift(bufferRows[0])
leadingSoftWraps++
lineNumbers = []
for bufferRow in bufferRows
lineNumber = (bufferRow + 1).toString()
if bufferRow is lastBufferRow
lineNumber += ".#{++softWraps}"
else
lastBufferRow = bufferRow
softWraps = 0
lineNumbers.push(lineNumber)
lineNumbers[leadingSoftWraps..]
# Creates a new fold between two row numbers.
#
# startRow - The row {Number} to start folding at
+29 -10
Ver Arquivo
@@ -10,6 +10,7 @@ LinesComponent = require './lines-component'
ScrollbarComponent = require './scrollbar-component'
ScrollbarCornerComponent = require './scrollbar-corner-component'
SubscriberMixin = require './subscriber-mixin'
EditorPresenter = require './editor-presenter'
module.exports =
EditorComponent = React.createClass
@@ -50,6 +51,8 @@ EditorComponent = React.createClass
render: ->
{focused, showIndentGuide, showInvisibles, showLineNumbers, visible} = @state
{editor, mini, cursorBlinkPeriod, cursorBlinkResumeDelay} = @props
contentPresenter = @presenter.content
gutterPresenter = @presenter.gutter
maxLineNumberDigits = editor.getLineCount().toString().length
invisibles = if showInvisibles and not mini then @state.invisibles else {}
hasSelection = editor.getSelection()? and !editor.getSelection().isEmpty()
@@ -91,7 +94,8 @@ EditorComponent = React.createClass
div {className, style, tabIndex: -1},
if @shouldRenderGutter()
GutterComponent {
ref: 'gutter', onMouseDown: @onGutterMouseDown, lineDecorations,
ref: 'gutter', gutterPresenter
onMouseDown: @onGutterMouseDown, lineDecorations,
defaultCharWidth, editor, renderedRowRange, maxLineNumberDigits, scrollViewHeight,
scrollTop, scrollHeight, lineHeightInPixels, @pendingChanges, mouseWheelScreenRow,
@useHardwareAcceleration, @performedInitialMeasurement, @backgroundColor, @gutterBackgroundColor
@@ -106,7 +110,7 @@ EditorComponent = React.createClass
onBlur: @onInputBlurred
LinesComponent {
ref: 'lines',
ref: 'lines', contentPresenter,
editor, lineHeightInPixels, defaultCharWidth, lineDecorations, highlightDecorations,
showIndentGuide, renderedRowRange, @pendingChanges, scrollTop, scrollLeft,
@scrollingVertically, scrollHeight, scrollWidth, mouseWheelScreenRow, invisibles,
@@ -167,6 +171,9 @@ EditorComponent = React.createClass
@observeConfig()
@setScrollSensitivity(atom.config.get('editor.scrollSensitivity'))
@presenter = new EditorPresenter(@props.editor)
@subscribe @presenter, 'did-change', @requestUpdate
componentDidMount: ->
{editor} = @props
@@ -186,6 +193,7 @@ EditorComponent = React.createClass
componentWillUnmount: ->
@props.parentView.trigger 'editor:will-be-removed', [@props.parentView]
@unsubscribe()
window.removeEventListener 'resize', @requestHeightAndWidthMeasurement
clearInterval(@domPollingIntervalId)
@domPollingIntervalId = null
@@ -213,19 +221,21 @@ EditorComponent = React.createClass
@measureScrollbars() if @measuringScrollbars
performInitialMeasurement: ->
console.log "INITIAL MEASUREMENT"
@updatesPaused = true
@measureHeightAndWidth()
@sampleFontStyling()
@sampleBackgroundColors()
@measureScrollbars()
@measureLineHeightAndDefaultCharWidth() if @measureLineHeightAndDefaultCharWidthWhenShown
@remeasureCharacterWidths() if @remeasureCharacterWidthsWhenShown
# @remeasureCharacterWidths() if @remeasureCharacterWidthsWhenShown
@props.editor.setVisible(true)
@updatesPaused = false
@performedInitialMeasurement = true
requestUpdate: ->
return unless @isMounted()
@pauseDOMPolling()
if @updatesPaused
@updateRequestedWhilePaused = true
@@ -293,7 +303,9 @@ EditorComponent = React.createClass
{cursor} = selection
screenRange = cursor.getScreenRange()
if renderedStartRow <= screenRange.start.row < renderedEndRow
cursorPixelRects[cursor.id] = editor.pixelRectForScreenRange(screenRange)
pixelRect = editor.pixelRectForScreenRange(screenRange)
pixelRect.startRow = screenRange.start.row
cursorPixelRects[cursor.id] = pixelRect
cursorPixelRects
getLineDecorations: (decorationsByMarkerId) ->
@@ -524,7 +536,14 @@ EditorComponent = React.createClass
@refs.input.focus()
onTextInput: (event) ->
event.stopPropagation()
# If we prevent the insertion of a space character, then the browser
# interprets the spacebar keypress as a page-down command.
event.preventDefault() unless event.data is ' '
return unless @isInputEnabled()
event.reactSkipEventDispatch = true
{editor} = @props
inputNode = event.target
@@ -539,9 +558,6 @@ EditorComponent = React.createClass
editor.insertText(event.data)
inputNode.value = event.data
# If we prevent the insertion of a space character, then the browser
# interprets the spacebar keypress as a page-down command.
event.preventDefault() unless event.data is ' '
onInputFocused: ->
@setState(focused: true)
@@ -827,17 +843,20 @@ EditorComponent = React.createClass
@remeasureCharacterWidths()
sampleBackgroundColors: (suppressUpdate) ->
{parentView} = @props
{editor, parentView} = @props
{showLineNumbers} = @state
{backgroundColor} = getComputedStyle(parentView.element)
if backgroundColor isnt @backgroundColor
editor.setBackgroundColor(backgroundColor)
@backgroundColor = backgroundColor
@requestUpdate() unless suppressUpdate
if @shouldRenderGutter()
gutterBackgroundColor = getComputedStyle(@refs.gutter.getDOMNode()).backgroundColor
gutterBackgroundColor = null if gutterBackgroundColor is 'rgba(0, 0, 0, 0)'
if gutterBackgroundColor isnt @gutterBackgroundColor
editor.setGutterBackgroundColor(backgroundColor)
@gutterBackgroundColor = gutterBackgroundColor
@requestUpdate() unless suppressUpdate
@@ -974,8 +993,8 @@ EditorComponent = React.createClass
{clientX, clientY} = event
linesClientRect = @refs.lines.getDOMNode().getBoundingClientRect()
top = clientY - linesClientRect.top
left = clientX - linesClientRect.left
top = clientY - linesClientRect.top + @presenter.scrollTop
left = clientX - linesClientRect.left + @presenter.scrollLeft
{top, left}
getModel: ->
+331
Ver Arquivo
@@ -0,0 +1,331 @@
{Emitter, Subscriber} = require 'emissary'
_ = require 'underscore-plus'
module.exports =
class EditorPresenter
Emitter.includeInto(this)
Subscriber.includeInto(this)
constructor: (@editor) ->
@content = {tiles: {}}
@gutter = {tiles: {}, dummyTile: {dummy: true}}
@updateTiles()
@updateDummyGutterTile()
@scrollTop = @editor.getScrollTop()
@scrollLeft = @editor.getScrollLeft()
@subscribe @editor.$width.changes, @onWidthChanged
@subscribe @editor.$height.changes, @onHeightChanged
@subscribe @editor.$lineHeightInPixels.changes, @onLineHeightInPixelsChanged
@subscribe @editor.$scrollTop.changes, @onScrollTopChanged
@subscribe @editor.$scrollLeft.changes, @onScrollLeftChanged
@subscribe @editor.$backgroundColor.changes, @onBackgroundColorChanged
@subscribe @editor.$gutterBackgroundColor.changes, @onBackgroundColorChanged
@subscribe @editor, 'screen-lines-changed', @onScreenLinesChanged
@subscribe @editor, 'decoration-added', @onDecorationAdded
@subscribe @editor, 'decoration-removed', @onDecorationRemoved
@subscribe @editor, 'decoration-changed', @onDecorationChanged
getContentTileSize: -> 5
getGutterTileSize: -> 20
getVisibleRowRange: ->
heightInLines = Math.ceil(@editor.getHeight() / @editor.getLineHeightInPixels())
startRow = Math.floor(@editor.getScrollTop() / @editor.getLineHeightInPixels())
endRow = Math.min(@editor.getLineCount(), startRow + heightInLines)
[startRow, endRow]
contentTileStartRowForRow: (startRow) ->
startRow - (startRow % @getContentTileSize())
gutterTileStartRowForRow: (startRow) ->
startRow - (startRow % @getGutterTileSize())
getContentTileRowRange: ->
[startRow, endRow] = @getVisibleRowRange()
startRow = @contentTileStartRowForRow(startRow)
endRow = @contentTileStartRowForRow(endRow) + @getContentTileSize()
[startRow, endRow]
getGutterTileRowRange: ->
[startRow, endRow] = @getVisibleRowRange()
startRow = @gutterTileStartRowForRow(startRow)
endRow = @gutterTileStartRowForRow(endRow) + @getGutterTileSize()
[startRow, endRow]
updateTiles: (fn) ->
@updateContentTiles(fn)
@updateGutterTiles(fn)
@emit 'did-change'
updateContentTiles: (fn) ->
[startRow, endRow] = @getContentTileRowRange()
for tileStartRow of @content.tiles
delete @content.tiles[tileStartRow] unless startRow <= tileStartRow < endRow
for tileStartRow in [startRow...endRow] by @getContentTileSize()
if existingTile = @content.tiles[tileStartRow]
fn?(existingTile)
else
tileEndRow = tileStartRow + @getContentTileSize()
@content.tiles[tileStartRow] = new ContentTilePresenter(@editor, tileStartRow, tileEndRow)
updateGutterTiles: (fn) ->
[startRow, endRow] = @getGutterTileRowRange()
for tileStartRow of @gutter.tiles
delete @gutter.tiles[tileStartRow] unless startRow <= tileStartRow < endRow
for tileStartRow in [startRow...endRow] by @getGutterTileSize()
if existingTile = @gutter.tiles[tileStartRow]
fn?(existingTile)
else
tileEndRow = tileStartRow + @getGutterTileSize()
@gutter.tiles[tileStartRow] = new GutterTilePresenter(@editor, tileStartRow, tileEndRow)
updateDummyGutterTile: ->
@gutter.dummyTile.maxLineNumberDigits = @editor.getLineCount().toString().length
onWidthChanged: =>
@updateTiles (tile) -> tile.updateWidth()
onHeightChanged: =>
@updateTiles()
onLineHeightInPixelsChanged: =>
@updateTiles (tile) -> tile.updateLineHeightInPixels()
onScrollTopChanged: (@scrollTop) =>
@updateTiles (tile) -> tile.updateScrollTop()
onScrollLeftChanged: (@scrollLeft) =>
@updateTiles (tile) -> tile.updateScrollLeft()
onScreenLinesChanged: (change) =>
@updateDummyGutterTile() if change.bufferDelta isnt 0
@updateTiles (tile) -> tile.onScreenLinesChanged(change)
onBackgroundColorChanged: =>
@updateTiles (tile) -> tile.updateBackgroundColor()
onDecorationAdded: (marker, decoration) =>
@updateTiles (tile) -> tile.onDecorationAdded(decoration)
onDecorationRemoved: (marker, decoration) =>
@updateTiles (tile) -> tile.onDecorationRemoved(decoration)
onDecorationChanged: (marker, decoration, change) =>
@updateTiles (tile) -> tile.onDecorationChanged(decoration, change)
class ContentTilePresenter
constructor: (@editor, @startRow, @endRow) ->
@lineDecorations = {}
@updateWidth()
@updateLineHeightInPixels()
@updateScrollTop()
@updateScrollLeft()
@updateBackgroundColor()
@updateLines()
@populateDecorations()
updateWidth: ->
@width = @editor.getWidth()
updateHeight: ->
@height = (@endRow - @startRow) * @editor.getLineHeightInPixels()
updateLineHeightInPixels: ->
@lineHeightInPixels = @editor.getLineHeightInPixels()
@updateTop()
@updateHeight()
updateScrollTop: ->
@updateTop()
updateScrollLeft: ->
@left = 0 - @editor.getScrollLeft()
updateBackgroundColor: ->
@backgroundColor = @editor.getBackgroundColor()
updateTop: ->
@top = @startRow * @editor.getLineHeightInPixels() - @editor.getScrollTop()
updateLines: ->
@lines = @editor.linesForScreenRows(@startRow, @endRow - 1)
populateDecorations: ->
for markerId, decorations of @editor.decorationsForScreenRowRange(@startRow, @endRow)
for decoration in decorations
@onDecorationAdded(decoration)
onScreenLinesChanged: (change) ->
@updateLines() if change.start < @endRow
onDecorationAdded: (decoration) ->
if decoration.isType('line')
@onLineDecorationAdded(decoration)
onDecorationRemoved: (decoration) ->
if decoration.isType('line')
@onLineDecorationRemoved(decoration)
onDecorationChanged: (decoration, change) ->
if decoration.isType('line')
@onLineDecorationChanged(decoration, change)
onLineDecorationAdded: (decoration) ->
marker = decoration.getMarker()
headRow = marker.getHeadScreenPosition().row
tailRow = marker.getTailScreenPosition().row
valid = marker.isValid()
params = decoration.getParams()
if rowRange = @rowRangeForLineDecoration(params, headRow, tailRow, valid)
@addLineDecorations(params, rowRange...)
onLineDecorationRemoved: (decoration) ->
marker = decoration.getMarker()
headRow = marker.getHeadScreenPosition().row
tailRow = marker.getTailScreenPosition().row
valid = true # FIXME: Markers shouldn't always be invalidated when destroyed
params = decoration.getParams()
if rowRange = @rowRangeForLineDecoration(params, headRow, tailRow, valid)
@removeLineDecorations(params, rowRange...)
onLineDecorationChanged: (decoration, change) ->
params = decoration.getParams()
{oldHeadScreenPosition, oldTailScreenPosition, wasValid} = change
if rowRange = @rowRangeForLineDecoration(params, oldHeadScreenPosition.row, oldTailScreenPosition.row, wasValid)
@removeLineDecorations(params, rowRange...)
{newHeadScreenPosition, newTailScreenPosition, isValid} = change
if rowRange = @rowRangeForLineDecoration(params, newHeadScreenPosition.row, newTailScreenPosition.row, isValid)
@addLineDecorations(params, rowRange...)
addLineDecorations: (params, decorationStartRow, decorationEndRow) ->
unless decorationEndRow < @startRow or @endRow <= decorationStartRow
for row in [decorationStartRow..decorationEndRow]
@lineDecorations[row] ?= {}
@lineDecorations[row][params.id] = params
removeLineDecorations: (params, decorationStartRow, decorationEndRow) ->
unless decorationEndRow < @startRow or @endRow <= decorationStartRow
for row in [decorationStartRow..decorationEndRow]
delete @lineDecorations[row][params.id]
delete @lineDecorations[row] if _.size(@lineDecorations[row]) is 0
rowRangeForLineDecoration: (decoration, headRow, tailRow, valid) ->
return unless valid
startRow = Math.min(headRow, tailRow)
endRow = Math.max(headRow, tailRow)
[startRow, endRow]
class GutterTilePresenter
constructor: (@editor, @startRow, @endRow) ->
@lineNumberDecorations = {}
@populateDecorations()
@updateLineHeightInPixels()
@updateScrollTop()
@updateBackgroundColor()
populateDecorations: ->
for markerId, decorations of @editor.decorationsForScreenRowRange(@startRow, @endRow)
for decoration in decorations
@onDecorationAdded(decoration)
updateLineHeightInPixels: ->
@lineHeightInPixels = @editor.getLineHeightInPixels()
@updateTop()
@updateHeight()
@updateLineNumbers()
updateScrollLeft: -> # NO-OP
updateScrollTop: ->
@updateTop()
updateBackgroundColor: ->
@backgroundColor = @editor.getGutterBackgroundColor() ? @editor.getBackgroundColor()
updateTop: ->
@top = @startRow * @editor.getLineHeightInPixels() - @editor.getScrollTop()
updateWidth: -> # NO-OP
updateHeight: ->
@height = (@endRow - @startRow) * @editor.getLineHeightInPixels()
updateLineNumbers: ->
@lineNumbers = @editor.lineNumbersForScreenRows(@startRow, @endRow - 1)
@maxLineNumberDigits = @editor.getLineCount().toString().length
updateMaxLineNumberDigits: ->
@maxLineNumberDigits = @editor.getLineCount().toString().length
onScreenLinesChanged: (change) ->
if change.bufferDelta isnt 0 or change.screenDelta isnt 0
@updateMaxLineNumberDigits()
@updateLineNumbers() if change.start < @endRow
onDecorationAdded: (decoration) ->
return unless decoration.isType('gutter')
marker = decoration.getMarker()
headRow = marker.getHeadScreenPosition().row
tailRow = marker.getTailScreenPosition().row
valid = marker.isValid()
params = decoration.getParams()
if rowRange = @rowRangeForLineNumberDecoration(params, headRow, tailRow, valid)
@addLineNumberDecorations(params, rowRange...)
onDecorationRemoved: (decoration) ->
return unless decoration.isType('gutter')
marker = decoration.getMarker()
headRow = marker.getHeadScreenPosition().row
tailRow = marker.getTailScreenPosition().row
valid = true # FIXME: Markers shouldn't always be invalidated when destroyed
params = decoration.getParams()
if rowRange = @rowRangeForLineNumberDecoration(params, headRow, tailRow, valid)
@removeLineNumberDecorations(params, rowRange...)
onDecorationChanged: (decoration, change) ->
return unless decoration.isType('gutter')
params = decoration.getParams()
{oldHeadScreenPosition, oldTailScreenPosition, wasValid} = change
if rowRange = @rowRangeForLineNumberDecoration(params, oldHeadScreenPosition.row, oldTailScreenPosition.row, wasValid)
@removeLineNumberDecorations(params, rowRange...)
{newHeadScreenPosition, newTailScreenPosition, isValid} = change
if rowRange = @rowRangeForLineNumberDecoration(params, newHeadScreenPosition.row, newTailScreenPosition.row, isValid)
@addLineNumberDecorations(params, rowRange...)
addLineNumberDecorations: (params, decorationStartRow, decorationEndRow) ->
unless decorationEndRow < @startRow or @endRow <= decorationStartRow
for row in [decorationStartRow..decorationEndRow]
@lineNumberDecorations[row] ?= {}
@lineNumberDecorations[row][params.id] = params
removeLineNumberDecorations: (params, decorationStartRow, decorationEndRow) ->
unless decorationEndRow < @startRow or @endRow <= decorationStartRow
for row in [decorationStartRow..decorationEndRow]
delete @lineNumberDecorations[row][params.id]
delete @lineNumberDecorations[row] if _.size(@lineNumberDecorations[row]) is 0
rowRangeForLineNumberDecoration: (decoration, headRow, tailRow, valid) ->
return unless valid
startRow = Math.min(headRow, tailRow)
endRow = Math.max(headRow, tailRow)
[startRow, endRow]
+347
Ver Arquivo
@@ -0,0 +1,347 @@
{extend, toArray, isEqual, clone} = require 'underscore-plus'
Decoration = require './decoration'
WrapperDiv = document.createElement('div')
module.exports =
class EditorTileComponent
top: null
left: null
height: null
width: null
lineHeightInPixels: null
lineWidths: null
backgroundColor: null
preserved: false
constructor: (@presenter) ->
@lineNodesByLineId = {}
@screenRowsByLineId = {}
@lineIdsByScreenRow = {}
@lineDecorationsByLineId = {}
@cursorPixelRectsById = {}
@cursorNodesById = {}
@domNode = document.createElement('div')
@domNode.dataset.tile = true
@domNode.style.position = 'absolute'
@domNode.style.overflow = 'hidden'
@buildLines()
@update()
stateChangedForKeys: ->
for key in arguments
return true if @prevState?.get(key) isnt @state.get(key)
false
update: ->
@updateTransform()
@updateWidth()
@updateHeight()
@updateBackgroundColor()
@updateLines()
# @clearScreenRowCaches() if newProps.lineHeightInPixels isnt @props.lineHeightInPixels
# @updateCursors()
preserve: ->
return if @preserved
@domNode.style.visibility = 'hidden'
@preserved = true
revive: (@presenter) ->
@domNode.style.visibility = ''
@visible = true
updateTransform: ->
{left, top} = @presenter
unless left is @left and top is @top
@domNode.style['-webkit-transform'] = "translate3d(#{left}px, #{top}px, 0px)"
@left = left
@top = top
updateWidth: ->
{width} = @presenter
unless width is @width
@domNode.style.width = width + 'px'
@width = width
updateHeight: ->
{height} = @presenter
unless height is @height
@domNode.style.height = height + 'px'
@height = height
updateBackgroundColor: ->
{backgroundColor} = @presenter
unless backgroundColor is @backgroundColor
@domNode.style.backgroundColor = backgroundColor
@backgroundColor = backgroundColor
buildLines: ->
{startRow, lines, lineDecorations} = @presenter
linesHTML = ""
for line, i in lines
screenRow = startRow + i
linesHTML += @buildLineHTML(line, screenRow)
@domNode.innerHTML = linesHTML
for line, i in lines
screenRow = startRow + i
lineNode = @domNode.children[i]
@lineNodesByLineId[line.id] = lineNode
@screenRowsByLineId[line.id] = screenRow
@lineDecorationsByLineId[line.id] = clone(lineDecorations[screenRow])
@lineIdsByScreenRow[screenRow] = line.id
updateLines: ->
{lines, width} = @presenter
@removeLineNodes()
@appendOrUpdateVisibleLineNodes()
@lineWidths = width
removeLineNodes: () ->
lineIds = new Set
lineIds.add(line.id.toString()) for line in @presenter.lines
for lineId, lineNode of @lineNodesByLineId when not lineIds.has(lineId)
screenRow = @screenRowsByLineId[lineId]
delete @lineNodesByLineId[lineId]
delete @lineIdsByScreenRow[screenRow] if @lineIdsByScreenRow[screenRow] is lineId
delete @screenRowsByLineId[lineId]
delete @lineDecorationsByLineId[lineId]
@domNode.removeChild(lineNode)
appendOrUpdateVisibleLineNodes: ->
{startRow, lines, lineHeightInPixels, width} = @presenter
newLines = null
newLinesHTML = null
for line, index in lines
screenRow = startRow + index
if @hasLineNode(line.id)
@updateLineNode(line, screenRow)
else
newLines ?= []
newLinesHTML ?= ""
newLines.push(line)
newLinesHTML += @buildLineHTML(line, screenRow)
@screenRowsByLineId[line.id] = screenRow
@lineIdsByScreenRow[screenRow] = line.id
@lineHeightInPixels = lineHeightInPixels
@lineWidths = width
return unless newLines?
WrapperDiv.innerHTML = newLinesHTML
newLineNodes = toArray(WrapperDiv.children)
for line, i in newLines
lineNode = newLineNodes[i]
@lineNodesByLineId[line.id] = lineNode
@domNode.appendChild(lineNode)
updateLineNode: (line, screenRow) ->
{startRow, lineHeightInPixels, width} = @presenter
lineNode = @lineNodesByLineId[line.id]
unless width is @lineWidths
lineNode.style.width = width + 'px'
unless @screenRowsByLineId[line.id] is screenRow and lineHeightInPixels is @lineHeightInPixels
lineNode.style.top = (screenRow - startRow) * lineHeightInPixels + 'px'
unless @screenRowsByLineId[line.id] is screenRow
lineNode.dataset.screenRow = screenRow
@screenRowsByLineId[line.id] = screenRow
@lineIdsByScreenRow[screenRow] = line.id
@updateLineDecorations(lineNode, line, screenRow)
updateLineDecorations: (lineNode, line, screenRow) ->
desiredDecorations = @presenter.lineDecorations[screenRow]
if currentDecorations = @lineDecorationsByLineId[line.id]
for id, decoration of currentDecorations
unless desiredDecorations?[id]?
lineNode.classList.remove(decoration.class)
delete currentDecorations[id]
if desiredDecorations?
currentDecorations = (@lineDecorationsByLineId[line.id] ?= {})
for id, decoration of desiredDecorations
unless currentDecorations[id]?
lineNode.classList.add(decoration.class)
currentDecorations[id] = decoration
clearScreenRowCaches: ->
@screenRowsByLineId = {}
@lineIdsByScreenRow = {}
hasLineNode: (lineId) ->
@lineNodesByLineId.hasOwnProperty(lineId)
lineNodeForScreenRow: (screenRow) ->
@lineNodesByLineId[@lineIdsByScreenRow[screenRow]]
hasDecoration: (decorations, decoration) ->
decorations? and decorations[decoration.id] is decoration
buildLineHTML: (line, screenRow) ->
{startRow, lineHeightInPixels, width} = @presenter
{text, fold, isSoftWrapped, indentLevel} = line
classes = @getLineClasses(screenRow)
top = (screenRow - startRow) * lineHeightInPixels
style = "position: absolute; top: #{top}px; width: #{width}px;"
lineHTML = """<div class="#{classes}" style="#{style}">"""
if text is ""
lineHTML += @buildEmptyLineInnerHTML(line)
else
lineHTML += @buildLineInnerHTML(line)
lineHTML += '<span class="fold-marker"></span>' if fold?
lineHTML += "</div>"
lineHTML
getLineClasses: (screenRow) ->
classes = ''
if lineDecorationsForScreenRow = @presenter.lineDecorations[screenRow]
for id, decoration of lineDecorationsForScreenRow
classes += decoration.class + ' '
classes + 'line'
buildEmptyLineInnerHTML: (line) ->
invisibles = {}
showIndentGuide = false
# {showIndentGuide, invisibles} = @props
{cr, eol} = invisibles
{indentLevel, tabLength} = line
if showIndentGuide and indentLevel > 0
invisiblesToRender = []
invisiblesToRender.push(cr) if cr? and line.lineEnding is '\r\n'
invisiblesToRender.push(eol) if eol?
lineHTML = ''
for i in [0...indentLevel]
lineHTML += "<span class='indent-guide'>"
for j in [0...tabLength]
if invisible = invisiblesToRender.shift()
lineHTML += "<span class='invisible-character'>#{invisible}</span>"
else
lineHTML += ' '
lineHTML += "</span>"
while invisiblesToRender.length
lineHTML += "<span class='invisible-character'>#{invisiblesToRender.shift()}</span>"
lineHTML
else
# @buildEndOfLineHTML(line, @props.invisibles) or '&nbsp;'
@buildEndOfLineHTML(line, {}) or '&nbsp;'
buildLineInnerHTML: (line) ->
# {invisibles, mini, showIndentGuide} = @props
invisibles = {}
mini = false
showIndentGuide = false
{tokens, text} = line
innerHTML = ""
scopeStack = []
firstTrailingWhitespacePosition = text.search(/\s*$/)
lineIsWhitespaceOnly = firstTrailingWhitespacePosition is 0
for token in tokens
innerHTML += @updateScopeStack(scopeStack, token.scopes)
hasIndentGuide = not mini and showIndentGuide and (token.hasLeadingWhitespace or (token.hasTrailingWhitespace and lineIsWhitespaceOnly))
innerHTML += token.getValueAsHtml({invisibles, hasIndentGuide})
innerHTML += @popScope(scopeStack) while scopeStack.length > 0
innerHTML += @buildEndOfLineHTML(line, invisibles)
innerHTML
buildEndOfLineHTML: (line, invisibles) ->
# return '' if @props.mini or line.isSoftWrapped()
return '' if line.isSoftWrapped()
html = ''
# Note the lack of '?' in the character checks. A user can set the chars
# to an empty string which we will interpret as not-set
if invisibles.cr and line.lineEnding is '\r\n'
html += "<span class='invisible-character'>#{invisibles.cr}</span>"
if invisibles.eol
html += "<span class='invisible-character'>#{invisibles.eol}</span>"
html
updateScopeStack: (scopeStack, desiredScopes) ->
html = ""
# Find a common prefix
for scope, i in desiredScopes
break unless scopeStack[i] is desiredScopes[i]
# Pop scopes until we're at the common prefx
until scopeStack.length is i
html += @popScope(scopeStack)
# Push onto common prefix until scopeStack equals desiredScopes
for j in [i...desiredScopes.length]
html += @pushScope(scopeStack, desiredScopes[j])
html
popScope: (scopeStack) ->
scopeStack.pop()
"</span>"
pushScope: (scopeStack, scope) ->
scopeStack.push(scope)
"<span class=\"#{scope.replace(/\.+/g, ' ')}\">"
updateCursors: ->
return
{cursorPixelRects, startRow, lineHeightInPixels} = @props
for id of @cursorPixelRectsById
@removeCursorNode(id) unless cursorPixelRects?.hasOwnProperty(id)
if cursorPixelRects?
for id, newPixelRect of cursorPixelRects
newPixelRect.top -= startRow * lineHeightInPixels
if oldPixelRect = @cursorPixelRectsById[id]
unless isEqual(oldPixelRect, newPixelRect)
@updateCursorNode(id, newPixelRect)
else
@buildCursorNode(id, newPixelRect)
updateCursorNode: (id, pixelRect) ->
{top, left, height, width} = pixelRect
@cursorNodesById[id].style.top = top + 'px'
@cursorNodesById[id].style.left = left + 'px'
@cursorNodesById[id].style.height = height + 'px'
@cursorNodesById[id].style.width = width + 'px'
@cursorPixelRectsById[id] = pixelRect
buildCursorNode: (id, pixelRect) ->
cursorNode = document.createElement('div')
cursorNode.className = 'cursor'
cursorNode.style.position = 'absolute'
@cursorNodesById[id] = cursorNode
@cursorPixelRectsById[id] = pixelRect
@updateCursorNode(id, pixelRect)
@domNode.appendChild(cursorNode)
removeCursorNode: (id) ->
@domNode.removeChild(@cursorNodesById[id])
delete @cursorPixelRectsById[id]
delete @cursorNodesById[id]
+2 -2
Ver Arquivo
@@ -641,9 +641,9 @@ class EditorView extends View
@scrollBottom(@editor.getScreenLineCount() * @lineHeight)
# Public: Scrolls the editor to the position of the most recently added
# cursor.
# cursor if it isn't current on screen.
#
# The editor is also centered.
# The editor is centered around the cursor's position if possible.
scrollToCursorPosition: ->
@scrollToBufferPosition(@editor.getCursorBufferPosition(), center: true)
+21 -2
Ver Arquivo
@@ -152,6 +152,10 @@ class Editor extends Model
updateBatchDepth: 0
selectionFlashDuration: 500
@properties
backgroundColor: null
gutterBackgroundColor: null
@delegatesMethods 'suggestedIndentForBufferRow', 'autoIndentBufferRow', 'autoIndentBufferRows',
'autoDecreaseIndentForBufferRow', 'toggleLineCommentForBufferRow', 'toggleLineCommentsForBufferRows',
toProperty: 'languageMode'
@@ -591,6 +595,9 @@ class Editor extends Model
# {Delegates to: DisplayBuffer.bufferRowsForScreenRows}
bufferRowsForScreenRows: (startRow, endRow) -> @displayBuffer.bufferRowsForScreenRows(startRow, endRow)
# {Delegats to: DisplayBuffer.lineNumbersForScreenRows}
lineNumbersForScreenRows: (startRow, endRow) -> @displayBuffer.lineNumbersForScreenRows(startRow, endRow)
bufferRowForScreenRow: (row) -> @displayBuffer.bufferRowForScreenRow(row)
# Public: Get the syntactic scopes for the given position in buffer
@@ -1588,8 +1595,14 @@ class Editor extends Model
moveCursorToBeginningOfPreviousParagraph: ->
@moveCursors (cursor) -> cursor.moveToBeginningOfPreviousParagraph()
scrollToCursorPosition: ->
@getCursor().autoscroll()
# Public: Scroll the editor to reveal the most recently added cursor if it is
# off-screen.
#
# options - An optional hash of options.
# :center - Center the editor around the cursor if possible. Defauls to
# true.
scrollToCursorPosition: (options) ->
@getCursor().autoscroll(center: options?.center ? true)
pageUp: ->
newScrollTop = @getScrollTop() - @getHeight()
@@ -2019,6 +2032,12 @@ class Editor extends Model
getScrollHeight: -> @displayBuffer.getScrollHeight()
getScrollWidth: (scrollWidth) -> @displayBuffer.getScrollWidth(scrollWidth)
setBackgroundColor: (@backgroundColor) -> @backgroundColor
getBackgroundColor: -> @backgroundColor
setGutterBackgroundColor: (@gutterBackgroundColor) -> @gutterBackgroundColor
getGutterBackgroundColor: -> @gutterBackgroundColor
getVisibleRowRange: -> @displayBuffer.getVisibleRowRange()
intersectsVisibleRowRange: (startRow, endRow) -> @displayBuffer.intersectsVisibleRowRange(startRow, endRow)
+35 -174
Ver Arquivo
@@ -4,6 +4,7 @@ React = require 'react-atom-fork'
{isEqual, isEqualForProperties, multiplyString, toArray} = require 'underscore-plus'
Decoration = require './decoration'
SubscriberMixin = require './subscriber-mixin'
GutterTileComponent = require './gutter-tile-component'
WrapperDiv = document.createElement('div')
@@ -16,196 +17,51 @@ GutterComponent = React.createClass
measuredWidth: null
render: ->
{scrollHeight, scrollViewHeight, onMouseDown, backgroundColor, gutterBackgroundColor} = @props
if gutterBackgroundColor isnt 'rbga(0, 0, 0, 0)'
backgroundColor = gutterBackgroundColor
div className: 'gutter', onClick: @onClick, onMouseDown: onMouseDown,
div className: 'line-numbers', ref: 'lineNumbers', style:
height: Math.max(scrollHeight, scrollViewHeight)
WebkitTransform: @getTransform()
backgroundColor: backgroundColor
getTransform: ->
{scrollTop, useHardwareAcceleration} = @props
if useHardwareAcceleration
"translate3d(0px, #{-scrollTop}px, 0px)"
else
"translate(0px, #{-scrollTop}px)"
div className: 'gutter', onClick: @onClick, onMouseDown: @props.onMouseDown
componentWillMount: ->
@lineNumberNodesById = {}
@lineNumberIdsByScreenRow = {}
@screenRowsByLineNumberId = {}
@renderedDecorationsByLineNumberId = {}
@tileComponentsByStartRow = {}
componentDidMount: ->
@appendDummyLineNumber()
@updateLineNumbers() if @props.performedInitialMeasurement
# Only update the gutter if the visible row range has changed or if a
# non-zero-delta change to the screen lines has occurred within the current
# visible row range.
shouldComponentUpdate: (newProps) ->
return true unless isEqualForProperties(newProps, @props,
'renderedRowRange', 'scrollTop', 'lineHeightInPixels', 'mouseWheelScreenRow', 'lineDecorations',
'scrollViewHeight', 'useHardwareAcceleration', 'backgroundColor', 'gutterBackgroundColor'
)
{renderedRowRange, pendingChanges, lineDecorations} = newProps
return false unless renderedRowRange?
for change in pendingChanges when Math.abs(change.screenDelta) > 0 or Math.abs(change.bufferDelta) > 0
return true unless change.end <= renderedRowRange.start or renderedRowRange.end <= change.start
false
@appendDummyTile()
@getDOMNode().addEventListener 'mousewheel', @onMouseWheel
componentDidUpdate: (oldProps) ->
return unless @props.performedInitialMeasurement
if @props.performedInitialMeasurement
@updateTiles()
unless isEqualForProperties(oldProps, @props, 'maxLineNumberDigits')
@updateDummyLineNumber()
@removeLineNumberNodes()
updateTiles: ->
{gutterPresenter} = @props
@clearScreenRowCaches() unless oldProps.lineHeightInPixels is @props.lineHeightInPixels
@updateLineNumbers()
domNode = @getDOMNode()
clearScreenRowCaches: ->
@lineNumberIdsByScreenRow = {}
@screenRowsByLineNumberId = {}
for tileStartRow, tileComponent of @tileComponentsByStartRow
unless gutterPresenter.tiles[tileStartRow]?
if tileComponent.domNode is @preservedTileNode
tileComponent.preserve()
else
domNode.removeChild(tileComponent.domNode)
delete @tileComponentsByStartRow[tileStartRow]
for tileStartRow, tilePresenter of gutterPresenter.tiles
if tileComponent = @tileComponentsByStartRow[tileStartRow]
tileComponent = @tileComponentsByStartRow[tileStartRow]
tileComponent.revive(tilePresenter) if tileComponent.preserved
tileComponent.update()
else
tileComponent = new GutterTileComponent(tilePresenter)
@tileComponentsByStartRow[tileStartRow] = tileComponent
domNode.appendChild(tileComponent.domNode)
# This dummy line number element holds the gutter to the appropriate width,
# since the real line numbers are absolutely positioned for performance reasons.
appendDummyLineNumber: ->
{maxLineNumberDigits} = @props
WrapperDiv.innerHTML = @buildLineNumberHTML(-1, false, maxLineNumberDigits)
@dummyLineNumberNode = WrapperDiv.children[0]
@refs.lineNumbers.getDOMNode().appendChild(@dummyLineNumberNode)
appendDummyTile: ->
@dummyTileComponent = new GutterTileComponent(@props.gutterPresenter.dummyTile)
@getDOMNode().appendChild(@dummyTileComponent.domNode)
updateDummyLineNumber: ->
@dummyLineNumberNode.innerHTML = @buildLineNumberInnerHTML(0, false, @props.maxLineNumberDigits)
updateLineNumbers: ->
lineNumberIdsToPreserve = @appendOrUpdateVisibleLineNumberNodes()
@removeLineNumberNodes(lineNumberIdsToPreserve)
appendOrUpdateVisibleLineNumberNodes: ->
{editor, renderedRowRange, scrollTop, maxLineNumberDigits, lineDecorations} = @props
[startRow, endRow] = renderedRowRange
newLineNumberIds = null
newLineNumbersHTML = null
visibleLineNumberIds = new Set
wrapCount = 0
for bufferRow, index in editor.bufferRowsForScreenRows(startRow, endRow - 1)
screenRow = startRow + index
if bufferRow is lastBufferRow
id = "#{bufferRow}-#{wrapCount++}"
else
id = bufferRow.toString()
lastBufferRow = bufferRow
wrapCount = 0
visibleLineNumberIds.add(id)
if @hasLineNumberNode(id)
@updateLineNumberNode(id, bufferRow, screenRow, wrapCount > 0)
else
newLineNumberIds ?= []
newLineNumbersHTML ?= ""
newLineNumberIds.push(id)
newLineNumbersHTML += @buildLineNumberHTML(bufferRow, wrapCount > 0, maxLineNumberDigits, screenRow)
@screenRowsByLineNumberId[id] = screenRow
@lineNumberIdsByScreenRow[screenRow] = id
@renderedDecorationsByLineNumberId[id] = lineDecorations[screenRow]
if newLineNumberIds?
WrapperDiv.innerHTML = newLineNumbersHTML
newLineNumberNodes = toArray(WrapperDiv.children)
node = @refs.lineNumbers.getDOMNode()
for lineNumberId, i in newLineNumberIds
lineNumberNode = newLineNumberNodes[i]
@lineNumberNodesById[lineNumberId] = lineNumberNode
node.appendChild(lineNumberNode)
visibleLineNumberIds
removeLineNumberNodes: (lineNumberIdsToPreserve) ->
{mouseWheelScreenRow} = @props
node = @refs.lineNumbers.getDOMNode()
for lineNumberId, lineNumberNode of @lineNumberNodesById when not lineNumberIdsToPreserve?.has(lineNumberId)
screenRow = @screenRowsByLineNumberId[lineNumberId]
if not screenRow? or screenRow isnt mouseWheelScreenRow
delete @lineNumberNodesById[lineNumberId]
delete @lineNumberIdsByScreenRow[screenRow] if @lineNumberIdsByScreenRow[screenRow] is lineNumberId
delete @screenRowsByLineNumberId[lineNumberId]
delete @renderedDecorationsByLineNumberId[lineNumberId]
node.removeChild(lineNumberNode)
buildLineNumberHTML: (bufferRow, softWrapped, maxLineNumberDigits, screenRow) ->
{editor, lineHeightInPixels, lineDecorations} = @props
if screenRow?
style = "position: absolute; top: #{screenRow * lineHeightInPixels}px;"
else
style = "visibility: hidden;"
innerHTML = @buildLineNumberInnerHTML(bufferRow, softWrapped, maxLineNumberDigits)
classes = ''
if lineDecorations? and decorations = lineDecorations[screenRow]
for id, decoration of decorations
if Decoration.isType(decoration, 'gutter')
classes += decoration.class + ' '
classes += "foldable " if bufferRow >= 0 and editor.isFoldableAtBufferRow(bufferRow)
classes += "line-number line-number-#{bufferRow}"
"<div class=\"#{classes}\" style=\"#{style}\" data-buffer-row=\"#{bufferRow}\" data-screen-row=\"#{screenRow}\">#{innerHTML}</div>"
buildLineNumberInnerHTML: (bufferRow, softWrapped, maxLineNumberDigits) ->
if softWrapped
lineNumber = ""
else
lineNumber = (bufferRow + 1).toString()
padding = multiplyString('&nbsp;', maxLineNumberDigits - lineNumber.length)
iconHTML = '<div class="icon-right"></div>'
padding + lineNumber + iconHTML
updateLineNumberNode: (lineNumberId, bufferRow, screenRow, softWrapped) ->
{editor, lineDecorations} = @props
node = @lineNumberNodesById[lineNumberId]
if editor.isFoldableAtBufferRow(bufferRow)
node.classList.add('foldable')
else
node.classList.remove('foldable')
decorations = lineDecorations[screenRow]
previousDecorations = @renderedDecorationsByLineNumberId[lineNumberId]
if previousDecorations?
for id, decoration of previousDecorations
if Decoration.isType(decoration, 'gutter') and not @hasDecoration(decorations, decoration)
node.classList.remove(decoration.class)
if decorations?
for id, decoration of decorations
if Decoration.isType(decoration, 'gutter') and not @hasDecoration(previousDecorations, decoration)
node.classList.add(decoration.class)
unless @screenRowsByLineNumberId[lineNumberId] is screenRow
{lineHeightInPixels} = @props
node.style.top = screenRow * lineHeightInPixels + 'px'
node.dataset.screenRow = screenRow
@screenRowsByLineNumberId[lineNumberId] = screenRow
@lineNumberIdsByScreenRow[screenRow] = lineNumberId
hasDecoration: (decorations, decoration) ->
decorations? and decorations[decoration.id] is decoration
@@ -226,3 +82,8 @@ GutterComponent = React.createClass
editor.unfoldBufferRow(bufferRow)
else
editor.foldBufferRow(bufferRow)
onMouseWheel: (event) ->
node = event.target
node = node.parentNode until not node? or node.dataset.tile
@preservedTileNode = node
+166
Ver Arquivo
@@ -0,0 +1,166 @@
{toArray, multiplyString, clone} = require 'underscore-plus'
WrapperDiv = document.createElement('div')
module.exports =
class GutterTileComponent
preserved: false
constructor: (@presenter) ->
@lineNumberNodesById = {}
@screenRowsByLineNumberId = {}
@lineNumberDecorationsByLineNumberId = {}
@domNode = document.createElement('div')
@domNode.dataset.tile = true
@domNode.style.overflow = 'hidden'
if @presenter.dummy
@domNode.style.visibility = 'hidden'
else
@domNode.style.position = 'absolute'
@domNode.style.top = '0px'
@appendDummyLineNumber()
@update()
appendDummyLineNumber: ->
WrapperDiv.innerHTML = @buildLineNumberHTML('0')
@domNode.appendChild(WrapperDiv.firstChild)
update: ->
return if @presenter.dummy
@updateTransform()
@updateHeight()
@updateBackgroundColor()
@updateLineNumbers()
preserve: ->
return if @preserved
@domNode.style.visibility = 'hidden'
@preserved = true
revive: (@presenter) ->
@domNode.style.visibility = ''
@visible = true
updateTransform: ->
{top} = @presenter
unless top is @top
@domNode.style['-webkit-transform'] = "translate3d(0px, #{top}px, 0px)"
@top = top
updateHeight: ->
{height} = @presenter
unless height is @height
@domNode.style.height = height + 'px'
@height = height
updateBackgroundColor: ->
{backgroundColor} = @presenter
unless backgroundColor is @backgroundColor
@domNode.style.backgroundColor = backgroundColor
@backgroundColor = backgroundColor
updateLineNumbers: ->
{lineNumbers, lineNumberDecorations} = @presenter
lineNumbersSet = new Set
lineNumbersSet.add(lineNumber) for lineNumber in lineNumbers
for lineNumberId, lineNumberNode of @lineNumberNodesById
unless lineNumbersSet.has(lineNumberId)
delete @lineNumberNodesById[lineNumberId]
delete @screenRowsByLineNumberId[lineNumberId]
delete @lineNumberDecorationsByLineNumberId[lineNumberId]
@domNode.removeChild(lineNumberNode)
newLineNumberIds = []
newLineNumbersHTML = ""
for lineNumberId, i in lineNumbers
screenRow = @presenter.startRow + i
if @lineNumberNodesById[lineNumberId]?
@updateLineNumberNode(lineNumberId, screenRow)
else
newLineNumberIds.push(lineNumberId)
newLineNumbersHTML += @buildLineNumberHTML(lineNumberId, screenRow)
@screenRowsByLineNumberId[lineNumberId] = screenRow
# @lineNumberIdsByScreenRow[screenRow] = id
@lineNumberDecorationsByLineNumberId[lineNumberId] = clone(lineNumberDecorations[screenRow])
if newLineNumberIds.length > 0
WrapperDiv.innerHTML = newLineNumbersHTML
newLineNumberNodes = toArray(WrapperDiv.children)
for lineNumberId, i in newLineNumberIds
@lineNumberNodesById[lineNumberId] = newLineNumberNodes[i]
@lineNumberDecorationsByLineNumberId
@domNode.appendChild(newLineNumberNodes[i])
buildLineNumberHTML: (lineNumberId, screenRow) ->
{lineHeightInPixels, lineNumberDecorations} = @presenter
if screenRow?
top = (screenRow - @presenter.startRow) * lineHeightInPixels
style = "position: absolute; top: #{top}px;"
else
style = "visibility: hidden;"
innerHTML = @buildLineNumberInnerHTML(lineNumberId)
classes = ''
if decorationsById = lineNumberDecorations?[screenRow]
for id, decoration of decorationsById
classes += decoration.class + ' '
# classes += "foldable " if bufferRow >= 0 and editor.isFoldableAtBufferRow(bufferRow)
classes += "line-number line-number-#{lineNumberId.replace(/\..*/, '')}"
"<div class=\"#{classes}\" style=\"#{style}\" data-screen-row=\"#{screenRow}\">#{innerHTML}</div>"
buildLineNumberInnerHTML: (lineNumberId) ->
{maxLineNumberDigits} = @presenter
softWrapped = lineNumberId.indexOf('.') isnt -1
if softWrapped
lineNumber = ""
else
lineNumber = lineNumberId
padding = multiplyString('&nbsp;', maxLineNumberDigits - lineNumber.length)
iconHTML = '<div class="icon-right"></div>'
padding + lineNumber + iconHTML
updateLineNumberNode: (lineNumberId, screenRow) ->
{lineDecorations} = @presenter
node = @lineNumberNodesById[lineNumberId]
# if editor.isFoldableAtBufferRow(bufferRow)
# node.classList.add('foldable')
# else
# node.classList.remove('foldable')
unless @screenRowsByLineNumberId[lineNumberId] is screenRow
{lineHeightInPixels} = @presenter
node.style.top = screenRow * lineHeightInPixels + 'px'
node.dataset.screenRow = screenRow
@screenRowsByLineNumberId[lineNumberId] = screenRow
# @lineNumberIdsByScreenRow[screenRow] = lineNumberId
@updateLineNumberDecorations(node, lineNumberId, screenRow)
updateLineNumberDecorations: (lineNumberNode, lineNumberId, screenRow) ->
desiredDecorations = @presenter.lineNumberDecorations[screenRow]
if currentDecorations = @lineNumberDecorationsByLineNumberId[lineNumberId]
for id, decoration of currentDecorations
unless desiredDecorations?[id]?
lineNumberNode.classList.remove(decoration.class)
delete currentDecorations[id]
if desiredDecorations?
currentDecorations = (@lineNumberDecorationsByLineNumberId[lineNumberId] ?= {})
for id, decoration of desiredDecorations
unless currentDecorations[id]?
lineNumberNode.classList.add(decoration.class)
currentDecorations[id] = decoration
+55 -236
Ver Arquivo
@@ -7,6 +7,7 @@ React = require 'react-atom-fork'
Decoration = require './decoration'
CursorsComponent = require './cursors-component'
HighlightsComponent = require './highlights-component'
EditorTileComponent = require './editor-tile-component'
DummyLineNode = $$(-> @div className: 'line', style: 'position: absolute; visibility: hidden;', => @span 'x')[0]
AcceptFilter = {acceptNode: -> NodeFilter.FILTER_ACCEPT}
@@ -16,48 +17,22 @@ module.exports =
LinesComponent = React.createClass
displayName: 'LinesComponent'
tileSize: 5
preservedTileNode: null
render: ->
{performedInitialMeasurement, cursorBlinkPeriod, cursorBlinkResumeDelay} = @props
if performedInitialMeasurement
{editor, highlightDecorations, scrollHeight, scrollWidth, placeholderText, backgroundColor} = @props
{lineHeightInPixels, defaultCharWidth, scrollViewHeight, scopedCharacterWidthsChangeCount} = @props
{scrollTop, scrollLeft, cursorPixelRects} = @props
style =
height: Math.max(scrollHeight, scrollViewHeight)
width: scrollWidth
WebkitTransform: @getTransform()
backgroundColor: backgroundColor
div {className: 'lines', style},
div className: 'placeholder-text', placeholderText if placeholderText?
CursorsComponent {
cursorPixelRects, cursorBlinkPeriod, cursorBlinkResumeDelay, lineHeightInPixels,
defaultCharWidth, scopedCharacterWidthsChangeCount, performedInitialMeasurement
}
HighlightsComponent {
editor, highlightDecorations, lineHeightInPixels, defaultCharWidth,
scopedCharacterWidthsChangeCount, performedInitialMeasurement
}
getTransform: ->
{scrollTop, scrollLeft, useHardwareAcceleration} = @props
if useHardwareAcceleration
"translate3d(#{-scrollLeft}px, #{-scrollTop}px, 0px)"
else
"translate(#{-scrollLeft}px, #{-scrollTop}px)"
div className: 'lines'
componentWillMount: ->
@measuredLines = new WeakSet
@lineNodesByLineId = {}
@screenRowsByLineId = {}
@lineIdsByScreenRow = {}
@renderedDecorationsByLineId = {}
@tileComponentsByStartRow = {}
componentDidMount: ->
@getDOMNode().addEventListener 'mousewheel', @onMouseWheel
shouldComponentUpdate: (newProps) ->
return true
return true unless isEqualForProperties(newProps, @props,
'renderedRowRange', 'lineDecorations', 'highlightDecorations', 'lineHeightInPixels', 'defaultCharWidth',
'scrollTop', 'scrollLeft', 'showIndentGuide', 'scrollingVertically', 'invisibles', 'visible',
@@ -78,209 +53,19 @@ LinesComponent = React.createClass
false
componentDidUpdate: (prevProps) ->
{visible, scrollingVertically, performedInitialMeasurement} = @props
{performedInitialMeasurement, visible, scrollingVertically} = @props
return unless performedInitialMeasurement
@clearScreenRowCaches() unless prevProps.lineHeightInPixels is @props.lineHeightInPixels
@removeLineNodes() unless isEqualForProperties(prevProps, @props, 'showIndentGuide', 'invisibles')
@updateLines(@props.lineWidth isnt prevProps.lineWidth)
@clearTiles() unless isEqualForProperties(prevProps, @props, 'showIndentGuide', 'invisibles')
@updateTiles()
@measureCharactersInNewLines() if visible and not scrollingVertically
clearScreenRowCaches: ->
@screenRowsByLineId = {}
@lineIdsByScreenRow = {}
updateLines: (updateWidth) ->
{editor, renderedRowRange, showIndentGuide, selectionChanged, lineDecorations} = @props
[startRow, endRow] = renderedRowRange
visibleLines = editor.linesForScreenRows(startRow, endRow - 1)
@removeLineNodes(visibleLines)
@appendOrUpdateVisibleLineNodes(visibleLines, startRow, updateWidth)
removeLineNodes: (visibleLines=[]) ->
{mouseWheelScreenRow} = @props
visibleLineIds = new Set
visibleLineIds.add(line.id.toString()) for line in visibleLines
node = @getDOMNode()
for lineId, lineNode of @lineNodesByLineId when not visibleLineIds.has(lineId)
screenRow = @screenRowsByLineId[lineId]
if not screenRow? or screenRow isnt mouseWheelScreenRow
delete @lineNodesByLineId[lineId]
delete @lineIdsByScreenRow[screenRow] if @lineIdsByScreenRow[screenRow] is lineId
delete @screenRowsByLineId[lineId]
delete @renderedDecorationsByLineId[lineId]
node.removeChild(lineNode)
appendOrUpdateVisibleLineNodes: (visibleLines, startRow, updateWidth) ->
{lineDecorations} = @props
newLines = null
newLinesHTML = null
for line, index in visibleLines
screenRow = startRow + index
if @hasLineNode(line.id)
@updateLineNode(line, screenRow, updateWidth)
else
newLines ?= []
newLinesHTML ?= ""
newLines.push(line)
newLinesHTML += @buildLineHTML(line, screenRow)
@screenRowsByLineId[line.id] = screenRow
@lineIdsByScreenRow[screenRow] = line.id
@renderedDecorationsByLineId[line.id] = lineDecorations[screenRow]
return unless newLines?
WrapperDiv.innerHTML = newLinesHTML
newLineNodes = toArray(WrapperDiv.children)
node = @getDOMNode()
for line, i in newLines
lineNode = newLineNodes[i]
@lineNodesByLineId[line.id] = lineNode
node.appendChild(lineNode)
hasLineNode: (lineId) ->
@lineNodesByLineId.hasOwnProperty(lineId)
buildLineHTML: (line, screenRow) ->
{editor, mini, showIndentGuide, lineHeightInPixels, lineDecorations, lineWidth} = @props
{tokens, text, lineEnding, fold, isSoftWrapped, indentLevel} = line
classes = ''
if decorations = lineDecorations[screenRow]
for id, decoration of decorations
if Decoration.isType(decoration, 'line')
classes += decoration.class + ' '
classes += 'line'
top = screenRow * lineHeightInPixels
lineHTML = "<div class=\"#{classes}\" style=\"position: absolute; top: #{top}px; width: #{lineWidth}px;\" data-screen-row=\"#{screenRow}\">"
if text is ""
lineHTML += @buildEmptyLineInnerHTML(line)
else
lineHTML += @buildLineInnerHTML(line)
lineHTML += '<span class="fold-marker"></span>' if fold
lineHTML += "</div>"
lineHTML
buildEmptyLineInnerHTML: (line) ->
{showIndentGuide, invisibles} = @props
{cr, eol} = invisibles
{indentLevel, tabLength} = line
if showIndentGuide and indentLevel > 0
invisiblesToRender = []
invisiblesToRender.push(cr) if cr? and line.lineEnding is '\r\n'
invisiblesToRender.push(eol) if eol?
lineHTML = ''
for i in [0...indentLevel]
lineHTML += "<span class='indent-guide'>"
for j in [0...tabLength]
if invisible = invisiblesToRender.shift()
lineHTML += "<span class='invisible-character'>#{invisible}</span>"
else
lineHTML += ' '
lineHTML += "</span>"
while invisiblesToRender.length
lineHTML += "<span class='invisible-character'>#{invisiblesToRender.shift()}</span>"
lineHTML
else
@buildEndOfLineHTML(line, @props.invisibles) or '&nbsp;'
buildLineInnerHTML: (line) ->
{invisibles, mini, showIndentGuide, invisibles} = @props
{tokens, text} = line
innerHTML = ""
scopeStack = []
firstTrailingWhitespacePosition = text.search(/\s*$/)
lineIsWhitespaceOnly = firstTrailingWhitespacePosition is 0
for token in tokens
innerHTML += @updateScopeStack(scopeStack, token.scopes)
hasIndentGuide = not mini and showIndentGuide and (token.hasLeadingWhitespace or (token.hasTrailingWhitespace and lineIsWhitespaceOnly))
innerHTML += token.getValueAsHtml({invisibles, hasIndentGuide})
innerHTML += @popScope(scopeStack) while scopeStack.length > 0
innerHTML += @buildEndOfLineHTML(line, invisibles)
innerHTML
buildEndOfLineHTML: (line, invisibles) ->
return '' if @props.mini or line.isSoftWrapped()
html = ''
# Note the lack of '?' in the character checks. A user can set the chars
# to an empty string which we will interpret as not-set
if invisibles.cr and line.lineEnding is '\r\n'
html += "<span class='invisible-character'>#{invisibles.cr}</span>"
if invisibles.eol
html += "<span class='invisible-character'>#{invisibles.eol}</span>"
html
updateScopeStack: (scopeStack, desiredScopes) ->
html = ""
# Find a common prefix
for scope, i in desiredScopes
break unless scopeStack[i] is desiredScopes[i]
# Pop scopes until we're at the common prefx
until scopeStack.length is i
html += @popScope(scopeStack)
# Push onto common prefix until scopeStack equals desiredScopes
for j in [i...desiredScopes.length]
html += @pushScope(scopeStack, desiredScopes[j])
html
popScope: (scopeStack) ->
scopeStack.pop()
"</span>"
pushScope: (scopeStack, scope) ->
scopeStack.push(scope)
"<span class=\"#{scope.replace(/\.+/g, ' ')}\">"
updateLineNode: (line, screenRow, updateWidth) ->
{editor, lineHeightInPixels, lineDecorations, lineWidth} = @props
lineNode = @lineNodesByLineId[line.id]
decorations = lineDecorations[screenRow]
previousDecorations = @renderedDecorationsByLineId[line.id]
if previousDecorations?
for id, decoration of previousDecorations
if Decoration.isType(decoration, 'line') and not @hasDecoration(decorations, decoration)
lineNode.classList.remove(decoration.class)
if decorations?
for id, decoration of decorations
if Decoration.isType(decoration, 'line') and not @hasDecoration(previousDecorations, decoration)
lineNode.classList.add(decoration.class)
lineNode.style.width = lineWidth + 'px' if updateWidth
unless @screenRowsByLineId[line.id] is screenRow
lineNode.style.top = screenRow * lineHeightInPixels + 'px'
lineNode.dataset.screenRow = screenRow
@screenRowsByLineId[line.id] = screenRow
@lineIdsByScreenRow[screenRow] = line.id
hasDecoration: (decorations, decoration) ->
decorations? and decorations[decoration.id] is decoration
lineNodeForScreenRow: (screenRow) ->
@lineNodesByLineId[@lineIdsByScreenRow[screenRow]]
tileComponent = @tileComponentsByStartRow[@tileStartRowForScreenRow(screenRow)]
tileComponent?.lineNodeForScreenRow(screenRow)
tileStartRowForScreenRow: (screenRow) ->
screenRow - (screenRow % @tileSize)
measureLineHeightAndDefaultCharWidth: ->
node = @getDOMNode()
@@ -293,19 +78,48 @@ LinesComponent = React.createClass
editor.setLineHeightInPixels(lineHeightInPixels)
editor.setDefaultCharWidth(charWidth)
updateTiles: ->
{contentPresenter} = @props
domNode = @getDOMNode()
for tileStartRow, tileComponent of @tileComponentsByStartRow
unless contentPresenter.tiles[tileStartRow]?
if tileComponent.domNode is @preservedTileNode
tileComponent.preserve()
else
domNode.removeChild(tileComponent.domNode)
delete @tileComponentsByStartRow[tileStartRow]
for tileStartRow, tilePresenter of contentPresenter.tiles
if tileComponent = @tileComponentsByStartRow[tileStartRow]
tileComponent = @tileComponentsByStartRow[tileStartRow]
tileComponent.revive(tilePresenter) if tileComponent.preserved
tileComponent.update()
else
tileComponent = new EditorTileComponent(tilePresenter)
@tileComponentsByStartRow[tileStartRow] = tileComponent
domNode.appendChild(tileComponent.domNode)
clearTiles: ->
for startRow, tileComponent of @tileComponentsByStartRow
domNode.removeChild(tileComponent.domNode)
delete @tileComponentsByStartRow[startRow]
remeasureCharacterWidths: ->
@clearScopedCharWidths()
@measureCharactersInNewLines()
measureCharactersInNewLines: ->
return
{editor} = @props
[visibleStartRow, visibleEndRow] = @props.renderedRowRange
node = @getDOMNode()
editor.batchCharacterMeasurement =>
for tokenizedLine in editor.linesForScreenRows(visibleStartRow, visibleEndRow - 1)
for tokenizedLine, i in editor.linesForScreenRows(visibleStartRow, visibleEndRow - 1)
screenRow = visibleStartRow + i
unless @measuredLines.has(tokenizedLine)
lineNode = @lineNodesByLineId[tokenizedLine.id]
lineNode = @lineNodeForScreenRow(screenRow)
@measureCharactersInLine(tokenizedLine, lineNode)
return
@@ -347,3 +161,8 @@ LinesComponent = React.createClass
clearScopedCharWidths: ->
@measuredLines.clear()
@props.editor.clearScopedCharWidths()
onMouseWheel: (event) ->
node = event.target
node = node.parentNode until node.dataset.tile
@preservedTileNode = node
+4 -3
Ver Arquivo
@@ -371,9 +371,10 @@ class Package
# to minimize the impact on startup time.
getIncompatibleNativeModules: ->
localStorageKey = "installed-packages:#{@name}:#{@metadata.version}"
try
{incompatibleNativeModules} = JSON.parse(global.localStorage.getItem(localStorageKey)) ? {}
return incompatibleNativeModules if incompatibleNativeModules?
unless atom.inDevMode()
try
{incompatibleNativeModules} = JSON.parse(global.localStorage.getItem(localStorageKey)) ? {}
return incompatibleNativeModules if incompatibleNativeModules?
incompatibleNativeModules = []
for nativeModulePath in @getNativeModuleDependencyPaths()
+5 -2
Ver Arquivo
@@ -57,8 +57,7 @@ class WindowEventHandler
@subscribeToCommand $(document), 'core:focus-previous', @focusPrevious
@subscribe $(document), 'keydown', (event) ->
atom.keymaps.handleKeyboardEvent(event.originalEvent)
document.addEventListener 'keydown', @onKeydown
@subscribe $(document), 'drop', (e) ->
e.preventDefault()
@@ -95,6 +94,10 @@ class WindowEventHandler
bindCommandToAction('core:redo', 'redo:')
bindCommandToAction('core:select-all', 'selectAll:')
onKeydown: (event) ->
atom.keymaps.handleKeyboardEvent(event)
event.stopImmediatePropagation()
openLink: ({target, currentTarget}) ->
location = target?.getAttribute('href') or currentTarget?.getAttribute('href')
if location and location[0] isnt '#' and /^https?:\/\//.test(location)