Comparar commits

..

3 Commits

Autor SHA1 Mensagem Data
Ben Ogle ec5cd6a68e Add method organization docs 2014-08-19 19:09:56 -06:00
Ben Ogle 0fc94faf29 Changes based on feedback 2014-08-01 11:03:13 -07:00
Ben Ogle de2d9739c8 Initial API guidelines doc 2014-07-31 12:33:59 -07:00
26 arquivos alterados com 580 adições e 1672 exclusões
+1 -1
Ver Arquivo
@@ -6,6 +6,6 @@
"url": "https://github.com/atom/atom.git"
},
"dependencies": {
"atom-package-manager": "0.88.0"
"atom-package-manager": "0.87.0"
}
}
+3
Ver Arquivo
@@ -148,6 +148,9 @@ 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: 'Atom'
LegalCopyright: 'Copyright (C) 2014 GitHub, Inc. All rights reserved'
FileDescription: 'The hackable editor'
LegalCopyright: 'Copyright (C) 2013 GitHub, Inc. All rights reserved'
ProductName: 'Atom'
ProductVersion: version
+112
Ver Arquivo
@@ -0,0 +1,112 @@
# 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](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)
* git
* [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).
* development headers for [GNOME Keyring](https://wiki.gnome.org/Projects/GnomeKeyring)
* libgnome-keyring-dev
### 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 Node.js installed, or node isn't identified as Node.js on your machine.
have nodejs installed, or node isn't identified as nodejs 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,19 +66,6 @@ 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.
+18 -17
Ver Arquivo
@@ -17,16 +17,16 @@
"url": "http://github.com/atom/atom/raw/master/LICENSE.md"
}
],
"atomShellVersion": "0.15.3",
"atomShellVersion": "0.15.0",
"dependencies": {
"async": "0.2.6",
"atom-keymap": "^1.0.2",
"atom-keymap": "^1.0.0",
"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.2",
"emissary": "^1.2.1",
"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.2",
"react-atom-fork": "^0.11.1",
"reactionary-atom-fork": "^1.0.0",
"runas": "1.0.1",
"scandal": "1.0.0",
"scoped-property-store": "^0.9.0",
"scrollbar-style": "^1.0.2",
"scrollbar-style": "^1.0.1",
"season": "^1.0.2",
"semver": "1.1.4",
"serializable": "^1",
@@ -82,37 +82,38 @@
"dev-live-reload": "0.33.0",
"exception-reporting": "0.19.0",
"feedback": "0.33.0",
"find-and-replace": "0.128.0",
"find-and-replace": "0.127.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.5.0",
"keybinding-resolver": "0.19.0",
"incompatible-packages": "0.3.0",
"keybinding-resolver": "0.18.0",
"link": "0.25.0",
"markdown-preview": "0.95.0",
"markdown-preview": "0.94.0",
"metrics": "0.33.0",
"open-on-github": "0.29.0",
"package-generator": "0.31.0",
"release-notes": "0.36.0",
"settings-view": "0.138.0",
"snippets": "0.51.0",
"settings-view": "0.137.0",
"snippets": "0.50.0",
"spell-check": "0.40.0",
"status-bar": "0.42.0",
"status-bar": "0.41.0",
"styleguide": "0.29.0",
"symbols-view": "0.63.0",
"tabs": "0.49.0",
"tabs": "0.48.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.29.0",
"language-coffee-script": "0.28.0",
"language-css": "0.17.0",
"language-gfm": "0.46.0",
"language-gfm": "0.44.0",
"language-git": "0.9.0",
"language-go": "0.16.0",
"language-html": "0.22.0",
@@ -127,7 +128,7 @@
"language-php": "0.15.0",
"language-property-list": "0.7.0",
"language-python": "0.18.0",
"language-ruby": "0.34.0",
"language-ruby": "0.33.0",
"language-ruby-on-rails": "0.15.0",
"language-sass": "0.14.0",
"language-shellscript": "0.8.0",
@@ -137,7 +138,7 @@
"language-todo": "0.10.0",
"language-toml": "0.12.0",
"language-xml": "0.17.0",
"language-yaml": "0.14.0"
"language-yaml": "0.13.0"
},
"private": true,
"scripts": {
Arquivo binário não exibido.
Arquivo binário não exibido.
-8
Ver Arquivo
@@ -1167,11 +1167,3 @@ 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
@@ -1,581 +0,0 @@
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
+3 -8
Ver Arquivo
@@ -3301,24 +3301,19 @@ describe "Editor", ->
expect(editor.getText()).toBe ' '
describe ".scrollToCursorPosition()", ->
it "scrolls the last cursor into view, centering around the cursor if possible and the 'center' option isn't false", ->
it "scrolls the last cursor into view", ->
editor.setCursorScreenPosition([8, 8])
editor.setLineHeightInPixels(10)
editor.setDefaultCharWidth(10)
editor.setHeight(60)
editor.setHeight(50)
editor.setWidth(50)
editor.setHorizontalScrollbarHeight(0)
expect(editor.getScrollTop()).toBe 0
expect(editor.getScrollLeft()).toBe 0
editor.scrollToCursorPosition()
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
expect(editor.getScrollRight()).toBe (9 + editor.getHorizontalScrollMargin()) * 10
describe ".pageUp/Down()", ->
it "scrolls one screen height up or down and moves the cursor one page length", ->
-3
Ver Arquivo
@@ -5,9 +5,6 @@ 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: (options) ->
@editor.scrollToScreenRange(@getScreenRange(), options)
autoscroll: ->
@editor.scrollToScreenRange(@getScreenRange())
# 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,
@wasValid, isValid,
textChanged
textChanged,
isValid
}
@oldHeadBufferPosition = newHeadBufferPosition
+1 -33
Ver Arquivo
@@ -401,45 +401,13 @@ 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, inclusive.
# endScreenRow - The screen row {Number} to end at (default: the last screen row)
#
# 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
+10 -29
Ver Arquivo
@@ -10,7 +10,6 @@ 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
@@ -51,8 +50,6 @@ 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()
@@ -94,8 +91,7 @@ EditorComponent = React.createClass
div {className, style, tabIndex: -1},
if @shouldRenderGutter()
GutterComponent {
ref: 'gutter', gutterPresenter
onMouseDown: @onGutterMouseDown, lineDecorations,
ref: 'gutter', onMouseDown: @onGutterMouseDown, lineDecorations,
defaultCharWidth, editor, renderedRowRange, maxLineNumberDigits, scrollViewHeight,
scrollTop, scrollHeight, lineHeightInPixels, @pendingChanges, mouseWheelScreenRow,
@useHardwareAcceleration, @performedInitialMeasurement, @backgroundColor, @gutterBackgroundColor
@@ -110,7 +106,7 @@ EditorComponent = React.createClass
onBlur: @onInputBlurred
LinesComponent {
ref: 'lines', contentPresenter,
ref: 'lines',
editor, lineHeightInPixels, defaultCharWidth, lineDecorations, highlightDecorations,
showIndentGuide, renderedRowRange, @pendingChanges, scrollTop, scrollLeft,
@scrollingVertically, scrollHeight, scrollWidth, mouseWheelScreenRow, invisibles,
@@ -171,9 +167,6 @@ EditorComponent = React.createClass
@observeConfig()
@setScrollSensitivity(atom.config.get('editor.scrollSensitivity'))
@presenter = new EditorPresenter(@props.editor)
@subscribe @presenter, 'did-change', @requestUpdate
componentDidMount: ->
{editor} = @props
@@ -193,7 +186,6 @@ EditorComponent = React.createClass
componentWillUnmount: ->
@props.parentView.trigger 'editor:will-be-removed', [@props.parentView]
@unsubscribe()
window.removeEventListener 'resize', @requestHeightAndWidthMeasurement
clearInterval(@domPollingIntervalId)
@domPollingIntervalId = null
@@ -221,21 +213,19 @@ 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
@@ -303,9 +293,7 @@ EditorComponent = React.createClass
{cursor} = selection
screenRange = cursor.getScreenRange()
if renderedStartRow <= screenRange.start.row < renderedEndRow
pixelRect = editor.pixelRectForScreenRange(screenRange)
pixelRect.startRow = screenRange.start.row
cursorPixelRects[cursor.id] = pixelRect
cursorPixelRects[cursor.id] = editor.pixelRectForScreenRange(screenRange)
cursorPixelRects
getLineDecorations: (decorationsByMarkerId) ->
@@ -536,14 +524,7 @@ 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
@@ -558,6 +539,9 @@ 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)
@@ -843,20 +827,17 @@ EditorComponent = React.createClass
@remeasureCharacterWidths()
sampleBackgroundColors: (suppressUpdate) ->
{editor, parentView} = @props
{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
@@ -993,8 +974,8 @@ EditorComponent = React.createClass
{clientX, clientY} = event
linesClientRect = @refs.lines.getDOMNode().getBoundingClientRect()
top = clientY - linesClientRect.top + @presenter.scrollTop
left = clientX - linesClientRect.left + @presenter.scrollLeft
top = clientY - linesClientRect.top
left = clientX - linesClientRect.left
{top, left}
getModel: ->
-331
Ver Arquivo
@@ -1,331 +0,0 @@
{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
@@ -1,347 +0,0 @@
{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 if it isn't current on screen.
# cursor.
#
# The editor is centered around the cursor's position if possible.
# The editor is also centered.
scrollToCursorPosition: ->
@scrollToBufferPosition(@editor.getCursorBufferPosition(), center: true)
+2 -21
Ver Arquivo
@@ -152,10 +152,6 @@ class Editor extends Model
updateBatchDepth: 0
selectionFlashDuration: 500
@properties
backgroundColor: null
gutterBackgroundColor: null
@delegatesMethods 'suggestedIndentForBufferRow', 'autoIndentBufferRow', 'autoIndentBufferRows',
'autoDecreaseIndentForBufferRow', 'toggleLineCommentForBufferRow', 'toggleLineCommentsForBufferRows',
toProperty: 'languageMode'
@@ -595,9 +591,6 @@ 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
@@ -1595,14 +1588,8 @@ class Editor extends Model
moveCursorToBeginningOfPreviousParagraph: ->
@moveCursors (cursor) -> cursor.moveToBeginningOfPreviousParagraph()
# 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)
scrollToCursorPosition: ->
@getCursor().autoscroll()
pageUp: ->
newScrollTop = @getScrollTop() - @getHeight()
@@ -2032,12 +2019,6 @@ 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)
+174 -35
Ver Arquivo
@@ -4,7 +4,6 @@ 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')
@@ -17,51 +16,196 @@ GutterComponent = React.createClass
measuredWidth: null
render: ->
div className: 'gutter', onClick: @onClick, onMouseDown: @props.onMouseDown
{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)"
componentWillMount: ->
@tileComponentsByStartRow = {}
@lineNumberNodesById = {}
@lineNumberIdsByScreenRow = {}
@screenRowsByLineNumberId = {}
@renderedDecorationsByLineNumberId = {}
componentDidMount: ->
@appendDummyTile()
@getDOMNode().addEventListener 'mousewheel', @onMouseWheel
@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
componentDidUpdate: (oldProps) ->
if @props.performedInitialMeasurement
@updateTiles()
return unless @props.performedInitialMeasurement
updateTiles: ->
{gutterPresenter} = @props
unless isEqualForProperties(oldProps, @props, 'maxLineNumberDigits')
@updateDummyLineNumber()
@removeLineNumberNodes()
domNode = @getDOMNode()
@clearScreenRowCaches() unless oldProps.lineHeightInPixels is @props.lineHeightInPixels
@updateLineNumbers()
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)
clearScreenRowCaches: ->
@lineNumberIdsByScreenRow = {}
@screenRowsByLineNumberId = {}
# This dummy line number element holds the gutter to the appropriate width,
# since the real line numbers are absolutely positioned for performance reasons.
appendDummyTile: ->
@dummyTileComponent = new GutterTileComponent(@props.gutterPresenter.dummyTile)
@getDOMNode().appendChild(@dummyTileComponent.domNode)
appendDummyLineNumber: ->
{maxLineNumberDigits} = @props
WrapperDiv.innerHTML = @buildLineNumberHTML(-1, false, maxLineNumberDigits)
@dummyLineNumberNode = WrapperDiv.children[0]
@refs.lineNumbers.getDOMNode().appendChild(@dummyLineNumberNode)
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
@@ -82,8 +226,3 @@ 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
@@ -1,166 +0,0 @@
{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
+236 -55
Ver Arquivo
@@ -7,7 +7,6 @@ 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}
@@ -17,22 +16,48 @@ module.exports =
LinesComponent = React.createClass
displayName: 'LinesComponent'
tileSize: 5
preservedTileNode: null
render: ->
div className: 'lines'
{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)"
componentWillMount: ->
@measuredLines = new WeakSet
@tileComponentsByStartRow = {}
componentDidMount: ->
@getDOMNode().addEventListener 'mousewheel', @onMouseWheel
@lineNodesByLineId = {}
@screenRowsByLineId = {}
@lineIdsByScreenRow = {}
@renderedDecorationsByLineId = {}
shouldComponentUpdate: (newProps) ->
return true
return true unless isEqualForProperties(newProps, @props,
'renderedRowRange', 'lineDecorations', 'highlightDecorations', 'lineHeightInPixels', 'defaultCharWidth',
'scrollTop', 'scrollLeft', 'showIndentGuide', 'scrollingVertically', 'invisibles', 'visible',
@@ -53,19 +78,209 @@ LinesComponent = React.createClass
false
componentDidUpdate: (prevProps) ->
{performedInitialMeasurement, visible, scrollingVertically} = @props
{visible, scrollingVertically, performedInitialMeasurement} = @props
return unless performedInitialMeasurement
@clearTiles() unless isEqualForProperties(prevProps, @props, 'showIndentGuide', 'invisibles')
@updateTiles()
@clearScreenRowCaches() unless prevProps.lineHeightInPixels is @props.lineHeightInPixels
@removeLineNodes() unless isEqualForProperties(prevProps, @props, 'showIndentGuide', 'invisibles')
@updateLines(@props.lineWidth isnt prevProps.lineWidth)
@measureCharactersInNewLines() if visible and not scrollingVertically
lineNodeForScreenRow: (screenRow) ->
tileComponent = @tileComponentsByStartRow[@tileStartRowForScreenRow(screenRow)]
tileComponent?.lineNodeForScreenRow(screenRow)
clearScreenRowCaches: ->
@screenRowsByLineId = {}
@lineIdsByScreenRow = {}
tileStartRowForScreenRow: (screenRow) ->
screenRow - (screenRow % @tileSize)
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]]
measureLineHeightAndDefaultCharWidth: ->
node = @getDOMNode()
@@ -78,48 +293,19 @@ 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, i in editor.linesForScreenRows(visibleStartRow, visibleEndRow - 1)
screenRow = visibleStartRow + i
for tokenizedLine in editor.linesForScreenRows(visibleStartRow, visibleEndRow - 1)
unless @measuredLines.has(tokenizedLine)
lineNode = @lineNodeForScreenRow(screenRow)
lineNode = @lineNodesByLineId[tokenizedLine.id]
@measureCharactersInLine(tokenizedLine, lineNode)
return
@@ -161,8 +347,3 @@ LinesComponent = React.createClass
clearScopedCharWidths: ->
@measuredLines.clear()
@props.editor.clearScopedCharWidths()
onMouseWheel: (event) ->
node = event.target
node = node.parentNode until node.dataset.tile
@preservedTileNode = node
+3 -4
Ver Arquivo
@@ -371,10 +371,9 @@ class Package
# to minimize the impact on startup time.
getIncompatibleNativeModules: ->
localStorageKey = "installed-packages:#{@name}:#{@metadata.version}"
unless atom.inDevMode()
try
{incompatibleNativeModules} = JSON.parse(global.localStorage.getItem(localStorageKey)) ? {}
return incompatibleNativeModules if incompatibleNativeModules?
try
{incompatibleNativeModules} = JSON.parse(global.localStorage.getItem(localStorageKey)) ? {}
return incompatibleNativeModules if incompatibleNativeModules?
incompatibleNativeModules = []
for nativeModulePath in @getNativeModuleDependencyPaths()
+2 -5
Ver Arquivo
@@ -57,7 +57,8 @@ class WindowEventHandler
@subscribeToCommand $(document), 'core:focus-previous', @focusPrevious
document.addEventListener 'keydown', @onKeydown
@subscribe $(document), 'keydown', (event) ->
atom.keymaps.handleKeyboardEvent(event.originalEvent)
@subscribe $(document), 'drop', (e) ->
e.preventDefault()
@@ -94,10 +95,6 @@ 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)