mirror of
https://github.com/atom/atom.git
synced 2026-01-24 06:18:03 -05:00
Merge branch 'master' of https://github.com/atom/atom into upstream
This commit is contained in:
@@ -48,7 +48,7 @@ For more information on how to work with Atom's official packages, see
|
||||
[JavaScript](https://github.com/styleguide/javascript),
|
||||
and [CSS](https://github.com/styleguide/css) styleguides.
|
||||
* Include thoughtfully-worded, well-structured
|
||||
[Jasmine](http://jasmine.github.io/) specs in the `./spec` folder. Run them using `apm test`.
|
||||
[Jasmine](http://jasmine.github.io/) specs in the `./spec` folder. Run them using `apm test`. See the [Specs Styleguide](#specs-styleguide) below.
|
||||
* Document new code based on the
|
||||
[Documentation Styleguide](#documentation-styleguide)
|
||||
* End files with a newline.
|
||||
@@ -108,6 +108,24 @@ For more information on how to work with Atom's official packages, see
|
||||
* Add an explicit `return` when your function ends with a `for`/`while` loop and
|
||||
you don't want it to return a collected array.
|
||||
|
||||
## Specs Styleguide
|
||||
|
||||
- Include thoughtfully-worded, well-structured
|
||||
[Jasmine](http://jasmine.github.io/) specs in the `./spec` folder.
|
||||
- treat `describe` as a noun or situation.
|
||||
- treat `it` as a statement about state or how an operation changes state.
|
||||
|
||||
### Example
|
||||
|
||||
```coffee
|
||||
describe 'a dog', ->
|
||||
it 'barks', ->
|
||||
# spec here
|
||||
describe 'when the dog is happy', ->
|
||||
it 'wags its tail', ->
|
||||
# spec here
|
||||
```
|
||||
|
||||
## Documentation Styleguide
|
||||
|
||||
* Use [AtomDoc](https://github.com/atom/atomdoc).
|
||||
|
||||
@@ -6,6 +6,6 @@
|
||||
"url": "https://github.com/atom/atom.git"
|
||||
},
|
||||
"dependencies": {
|
||||
"atom-package-manager": "0.152.0"
|
||||
"atom-package-manager": "0.157.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
"fs-plus": "2.x",
|
||||
"github-releases": "~0.2.0",
|
||||
"grunt": "~0.4.1",
|
||||
"grunt-atom-shell-installer": "^0.25.0",
|
||||
"grunt-atom-shell-installer": "^0.28.0",
|
||||
"grunt-cli": "~0.1.9",
|
||||
"grunt-coffeelint": "git+https://github.com/atom/grunt-coffeelint.git#cfb99aa99811d52687969532bd5a98011ed95bfe",
|
||||
"grunt-contrib-coffee": "~0.12.0",
|
||||
|
||||
28
package.json
28
package.json
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "atom",
|
||||
"productName": "Atom",
|
||||
"version": "0.189.0",
|
||||
"version": "0.190.0",
|
||||
"description": "A hackable text editor for the 21st Century.",
|
||||
"main": "./src/browser/main.js",
|
||||
"repository": {
|
||||
@@ -17,7 +17,7 @@
|
||||
"url": "http://github.com/atom/atom/raw/master/LICENSE.md"
|
||||
}
|
||||
],
|
||||
"atomShellVersion": "0.22.2",
|
||||
"atomShellVersion": "0.22.3",
|
||||
"dependencies": {
|
||||
"async": "0.2.6",
|
||||
"atom-keymap": "^5",
|
||||
@@ -90,30 +90,30 @@
|
||||
"bookmarks": "0.35.0",
|
||||
"bracket-matcher": "0.73.0",
|
||||
"command-palette": "0.34.0",
|
||||
"deprecation-cop": "0.38.0",
|
||||
"deprecation-cop": "0.39.0",
|
||||
"dev-live-reload": "0.45.0",
|
||||
"encoding-selector": "0.19.0",
|
||||
"exception-reporting": "0.24.0",
|
||||
"feedback": "0.36.0",
|
||||
"feedback": "0.38.0",
|
||||
"find-and-replace": "0.159.0",
|
||||
"fuzzy-finder": "0.72.0",
|
||||
"git-diff": "0.54.0",
|
||||
"go-to-line": "0.30.0",
|
||||
"grammar-selector": "0.46.0",
|
||||
"image-view": "0.53.0",
|
||||
"image-view": "0.54.0",
|
||||
"incompatible-packages": "0.24.0",
|
||||
"keybinding-resolver": "0.29.0",
|
||||
"link": "0.30.0",
|
||||
"markdown-preview": "0.145.0",
|
||||
"markdown-preview": "0.146.0",
|
||||
"metrics": "0.45.0",
|
||||
"notifications": "0.35.0",
|
||||
"open-on-github": "0.36.0",
|
||||
"package-generator": "0.38.0",
|
||||
"release-notes": "0.52.0",
|
||||
"settings-view": "0.186.0",
|
||||
"snippets": "0.86.0",
|
||||
"settings-view": "0.187.0",
|
||||
"snippets": "0.87.0",
|
||||
"spell-check": "0.55.0",
|
||||
"status-bar": "0.64.0",
|
||||
"status-bar": "0.66.0",
|
||||
"styleguide": "0.44.0",
|
||||
"symbols-view": "0.93.0",
|
||||
"tabs": "0.67.0",
|
||||
@@ -131,19 +131,19 @@
|
||||
"language-gfm": "0.67.0",
|
||||
"language-git": "0.10.0",
|
||||
"language-go": "0.22.0",
|
||||
"language-html": "0.30.0",
|
||||
"language-html": "0.31.0",
|
||||
"language-hyperlink": "0.12.2",
|
||||
"language-java": "0.14.0",
|
||||
"language-javascript": "0.64.0",
|
||||
"language-javascript": "0.67.0",
|
||||
"language-json": "0.14.0",
|
||||
"language-less": "0.25.0",
|
||||
"language-make": "0.14.0",
|
||||
"language-mustache": "0.11.0",
|
||||
"language-objective-c": "0.15.0",
|
||||
"language-perl": "0.21.0",
|
||||
"language-php": "0.21.0",
|
||||
"language-perl": "0.22.0",
|
||||
"language-php": "0.22.0",
|
||||
"language-property-list": "0.8.0",
|
||||
"language-python": "0.32.0",
|
||||
"language-python": "0.33.0",
|
||||
"language-ruby": "0.50.0",
|
||||
"language-ruby-on-rails": "0.21.0",
|
||||
"language-sass": "0.36.0",
|
||||
|
||||
@@ -46,8 +46,38 @@ function removeNodeModules() {
|
||||
}
|
||||
}
|
||||
|
||||
function removeTempFolders() {
|
||||
var fsPlus;
|
||||
try {
|
||||
fsPlus = require('fs-plus');
|
||||
} catch (error) {
|
||||
return;
|
||||
}
|
||||
|
||||
var temp = require('os').tmpdir();
|
||||
if (!fsPlus.isDirectorySync(temp))
|
||||
return;
|
||||
|
||||
var deletedFolders = 0;
|
||||
|
||||
fsPlus.readdirSync(temp).filter(function(folderName) {
|
||||
return folderName.indexOf('npm-') === 0;
|
||||
}).forEach(function(folderName) {
|
||||
try {
|
||||
fsPlus.removeSync(path.join(temp, folderName));
|
||||
deletedFolders++;
|
||||
} catch (error) {
|
||||
console.error("Failed to delete npm temp folder: " + error.message);
|
||||
}
|
||||
});
|
||||
|
||||
if (deletedFolders > 0)
|
||||
console.log("Deleted " + deletedFolders + " npm folders from temp directory");
|
||||
}
|
||||
|
||||
readEnvironmentVariables();
|
||||
removeNodeModules();
|
||||
removeTempFolders();
|
||||
cp.safeExec.bind(global, 'npm install npm --loglevel error', {cwd: path.resolve(__dirname, '..', 'build')}, function() {
|
||||
cp.safeExec.bind(global, 'node script/bootstrap', function(error) {
|
||||
if (error)
|
||||
|
||||
@@ -370,7 +370,7 @@ window.setEditorWidthInChars = (editorView, widthInChars, charWidth=editorView.c
|
||||
|
||||
window.setEditorHeightInLines = (editorView, heightInLines, lineHeight=editorView.lineHeight) ->
|
||||
editorView.height(editorView.getEditor().getLineHeightInPixels() * heightInLines)
|
||||
editorView.component?.measureHeightAndWidth()
|
||||
editorView.component?.measureDimensions()
|
||||
|
||||
$.fn.resultOfTrigger = (type) ->
|
||||
event = $.Event(type)
|
||||
|
||||
@@ -50,7 +50,7 @@ describe "TextEditorComponent", ->
|
||||
verticalScrollbarNode = componentNode.querySelector('.vertical-scrollbar')
|
||||
horizontalScrollbarNode = componentNode.querySelector('.horizontal-scrollbar')
|
||||
|
||||
component.measureHeightAndWidth()
|
||||
component.measureDimensions()
|
||||
nextAnimationFrame()
|
||||
|
||||
afterEach ->
|
||||
@@ -70,7 +70,7 @@ describe "TextEditorComponent", ->
|
||||
describe "line rendering", ->
|
||||
it "renders the currently-visible lines plus the overdraw margin", ->
|
||||
wrapperNode.style.height = 4.5 * lineHeightInPixels + 'px'
|
||||
component.measureHeightAndWidth()
|
||||
component.measureDimensions()
|
||||
nextAnimationFrame()
|
||||
|
||||
linesNode = componentNode.querySelector('.lines')
|
||||
@@ -113,7 +113,7 @@ describe "TextEditorComponent", ->
|
||||
|
||||
it "updates the lines when lines are inserted or removed above the rendered row range", ->
|
||||
wrapperNode.style.height = 4.5 * lineHeightInPixels + 'px'
|
||||
component.measureHeightAndWidth()
|
||||
component.measureDimensions()
|
||||
nextAnimationFrame()
|
||||
verticalScrollbarNode.scrollTop = 5 * lineHeightInPixels
|
||||
verticalScrollbarNode.dispatchEvent(new UIEvent('scroll'))
|
||||
@@ -163,7 +163,7 @@ describe "TextEditorComponent", ->
|
||||
it "renders the .lines div at the full height of the editor if there aren't enough lines to scroll vertically", ->
|
||||
editor.setText('')
|
||||
wrapperNode.style.height = '300px'
|
||||
component.measureHeightAndWidth()
|
||||
component.measureDimensions()
|
||||
nextAnimationFrame()
|
||||
|
||||
linesNode = componentNode.querySelector('.lines')
|
||||
@@ -175,7 +175,7 @@ describe "TextEditorComponent", ->
|
||||
lineNodes = componentNode.querySelectorAll('.line')
|
||||
|
||||
componentNode.style.width = gutterWidth + (30 * charWidth) + 'px'
|
||||
component.measureHeightAndWidth()
|
||||
component.measureDimensions()
|
||||
nextAnimationFrame()
|
||||
expect(editor.getScrollWidth()).toBeGreaterThan scrollViewNode.offsetWidth
|
||||
|
||||
@@ -187,7 +187,7 @@ describe "TextEditorComponent", ->
|
||||
expect(lineNode.style.width).toBe editor.getScrollWidth() + 'px'
|
||||
|
||||
componentNode.style.width = gutterWidth + editor.getScrollWidth() + 100 + 'px'
|
||||
component.measureHeightAndWidth()
|
||||
component.measureDimensions()
|
||||
nextAnimationFrame()
|
||||
scrollViewWidth = scrollViewNode.offsetWidth
|
||||
|
||||
@@ -339,7 +339,7 @@ describe "TextEditorComponent", ->
|
||||
editor.setSoftWrapped(true)
|
||||
nextAnimationFrame()
|
||||
componentNode.style.width = 16 * charWidth + editor.getVerticalScrollbarWidth() + 'px'
|
||||
component.measureHeightAndWidth()
|
||||
component.measureDimensions()
|
||||
nextAnimationFrame()
|
||||
|
||||
it "doesn't show end of line invisibles at the end of wrapped lines", ->
|
||||
@@ -480,7 +480,7 @@ describe "TextEditorComponent", ->
|
||||
describe "gutter rendering", ->
|
||||
it "renders the currently-visible line numbers", ->
|
||||
wrapperNode.style.height = 4.5 * lineHeightInPixels + 'px'
|
||||
component.measureHeightAndWidth()
|
||||
component.measureDimensions()
|
||||
nextAnimationFrame()
|
||||
|
||||
expect(componentNode.querySelectorAll('.line-number').length).toBe 6 + 2 + 1 # line overdraw margin below + dummy line number
|
||||
@@ -524,7 +524,7 @@ describe "TextEditorComponent", ->
|
||||
editor.setSoftWrapped(true)
|
||||
wrapperNode.style.height = 4.5 * lineHeightInPixels + 'px'
|
||||
wrapperNode.style.width = 30 * charWidth + 'px'
|
||||
component.measureHeightAndWidth()
|
||||
component.measureDimensions()
|
||||
nextAnimationFrame()
|
||||
|
||||
expect(componentNode.querySelectorAll('.line-number').length).toBe 6 + lineOverdrawMargin + 1 # 1 dummy line componentNode
|
||||
@@ -562,7 +562,7 @@ describe "TextEditorComponent", ->
|
||||
|
||||
it "renders the .line-numbers div at the full height of the editor even if it's taller than its content", ->
|
||||
wrapperNode.style.height = componentNode.offsetHeight + 100 + 'px'
|
||||
component.measureHeightAndWidth()
|
||||
component.measureDimensions()
|
||||
nextAnimationFrame()
|
||||
expect(componentNode.querySelector('.line-numbers').offsetHeight).toBe componentNode.offsetHeight
|
||||
|
||||
@@ -653,7 +653,7 @@ describe "TextEditorComponent", ->
|
||||
editor.setSoftWrapped(true)
|
||||
nextAnimationFrame()
|
||||
componentNode.style.width = 16 * charWidth + editor.getVerticalScrollbarWidth() + 'px'
|
||||
component.measureHeightAndWidth()
|
||||
component.measureDimensions()
|
||||
nextAnimationFrame()
|
||||
|
||||
it "doesn't add the foldable class for soft-wrapped lines", ->
|
||||
@@ -697,7 +697,7 @@ describe "TextEditorComponent", ->
|
||||
|
||||
wrapperNode.style.height = 4.5 * lineHeightInPixels + 'px'
|
||||
wrapperNode.style.width = 20 * lineHeightInPixels + 'px'
|
||||
component.measureHeightAndWidth()
|
||||
component.measureDimensions()
|
||||
nextAnimationFrame()
|
||||
|
||||
cursorNodes = componentNode.querySelectorAll('.cursor')
|
||||
@@ -992,7 +992,7 @@ describe "TextEditorComponent", ->
|
||||
|
||||
# Shrink editor vertically
|
||||
wrapperNode.style.height = 4.5 * lineHeightInPixels + 'px'
|
||||
component.measureHeightAndWidth()
|
||||
component.measureDimensions()
|
||||
nextAnimationFrame()
|
||||
|
||||
# Add decorations that are out of range
|
||||
@@ -1016,7 +1016,7 @@ describe "TextEditorComponent", ->
|
||||
editor.setText("a line that wraps, ok")
|
||||
editor.setSoftWrapped(true)
|
||||
componentNode.style.width = 16 * charWidth + 'px'
|
||||
component.measureHeightAndWidth()
|
||||
component.measureDimensions()
|
||||
nextAnimationFrame()
|
||||
|
||||
marker.destroy()
|
||||
@@ -1132,7 +1132,7 @@ describe "TextEditorComponent", ->
|
||||
|
||||
it "does not render highlights for off-screen lines until they come on-screen", ->
|
||||
wrapperNode.style.height = 2.5 * lineHeightInPixels + 'px'
|
||||
component.measureHeightAndWidth()
|
||||
component.measureDimensions()
|
||||
nextAnimationFrame()
|
||||
|
||||
marker = editor.displayBuffer.markBufferRange([[9, 2], [9, 4]], invalidate: 'inside')
|
||||
@@ -1279,10 +1279,12 @@ describe "TextEditorComponent", ->
|
||||
expect(componentNode.querySelector('.new-test-highlight')).toBeTruthy()
|
||||
|
||||
describe "overlay decoration rendering", ->
|
||||
[item] = []
|
||||
[item, gutterWidth] = []
|
||||
beforeEach ->
|
||||
item = document.createElement('div')
|
||||
item.classList.add 'overlay-test'
|
||||
item.style.background = 'red'
|
||||
gutterWidth = componentNode.querySelector('.gutter').offsetWidth
|
||||
|
||||
describe "when the marker is empty", ->
|
||||
it "renders an overlay decoration when added and removes the overlay when the decoration is destroyed", ->
|
||||
@@ -1299,71 +1301,29 @@ describe "TextEditorComponent", ->
|
||||
overlay = component.getTopmostDOMNode().querySelector('atom-overlay .overlay-test')
|
||||
expect(overlay).toBe null
|
||||
|
||||
it "renders in the correct position on initial display and when the marker moves", ->
|
||||
editor.setCursorBufferPosition([2, 5])
|
||||
|
||||
marker = editor.getLastCursor().getMarker()
|
||||
decoration = editor.decorateMarker(marker, {type: 'overlay', item})
|
||||
nextAnimationFrame()
|
||||
|
||||
position = wrapperNode.pixelPositionForBufferPosition([2, 5])
|
||||
|
||||
overlay = component.getTopmostDOMNode().querySelector('atom-overlay')
|
||||
expect(overlay.style.left).toBe position.left + 'px'
|
||||
expect(overlay.style.top).toBe position.top + editor.getLineHeightInPixels() + 'px'
|
||||
|
||||
editor.moveRight()
|
||||
editor.moveRight()
|
||||
nextAnimationFrame()
|
||||
|
||||
position = wrapperNode.pixelPositionForBufferPosition([2, 7])
|
||||
|
||||
expect(overlay.style.left).toBe position.left + 'px'
|
||||
expect(overlay.style.top).toBe position.top + editor.getLineHeightInPixels() + 'px'
|
||||
|
||||
describe "when the marker is not empty", ->
|
||||
it "renders at the head of the marker by default", ->
|
||||
marker = editor.displayBuffer.markBufferRange([[2, 5], [2, 10]], invalidate: 'never')
|
||||
decoration = editor.decorateMarker(marker, {type: 'overlay', item})
|
||||
nextAnimationFrame()
|
||||
nextAnimationFrame()
|
||||
|
||||
position = wrapperNode.pixelPositionForBufferPosition([2, 10])
|
||||
|
||||
overlay = component.getTopmostDOMNode().querySelector('atom-overlay')
|
||||
expect(overlay.style.left).toBe position.left + 'px'
|
||||
expect(overlay.style.top).toBe position.top + editor.getLineHeightInPixels() + 'px'
|
||||
|
||||
it "renders at the head of the marker when the marker is reversed", ->
|
||||
marker = editor.displayBuffer.markBufferRange([[2, 5], [2, 10]], invalidate: 'never', reversed: true)
|
||||
decoration = editor.decorateMarker(marker, {type: 'overlay', item})
|
||||
nextAnimationFrame()
|
||||
|
||||
position = wrapperNode.pixelPositionForBufferPosition([2, 5])
|
||||
|
||||
overlay = component.getTopmostDOMNode().querySelector('atom-overlay')
|
||||
expect(overlay.style.left).toBe position.left + 'px'
|
||||
expect(overlay.style.top).toBe position.top + editor.getLineHeightInPixels() + 'px'
|
||||
|
||||
it "renders at the tail of the marker when the 'position' option is 'tail'", ->
|
||||
marker = editor.displayBuffer.markBufferRange([[2, 5], [2, 10]], invalidate: 'never')
|
||||
decoration = editor.decorateMarker(marker, {type: 'overlay', position: 'tail', item})
|
||||
nextAnimationFrame()
|
||||
|
||||
position = wrapperNode.pixelPositionForBufferPosition([2, 5])
|
||||
|
||||
overlay = component.getTopmostDOMNode().querySelector('atom-overlay')
|
||||
expect(overlay.style.left).toBe position.left + 'px'
|
||||
expect(overlay.style.left).toBe position.left + gutterWidth + 'px'
|
||||
expect(overlay.style.top).toBe position.top + editor.getLineHeightInPixels() + 'px'
|
||||
|
||||
describe "positioning the overlay when near the edge of the editor", ->
|
||||
[itemWidth, itemHeight] = []
|
||||
[itemWidth, itemHeight, windowWidth, windowHeight] = []
|
||||
beforeEach ->
|
||||
atom.storeWindowDimensions()
|
||||
|
||||
itemWidth = 4 * editor.getDefaultCharWidth()
|
||||
itemHeight = 4 * editor.getLineHeightInPixels()
|
||||
|
||||
gutterWidth = componentNode.querySelector('.gutter').offsetWidth
|
||||
windowWidth = gutterWidth + 30 * editor.getDefaultCharWidth()
|
||||
windowHeight = 9 * editor.getLineHeightInPixels()
|
||||
windowHeight = 10 * editor.getLineHeightInPixels()
|
||||
|
||||
item.style.width = itemWidth + 'px'
|
||||
item.style.height = itemHeight + 'px'
|
||||
@@ -1371,139 +1331,39 @@ describe "TextEditorComponent", ->
|
||||
wrapperNode.style.width = windowWidth + 'px'
|
||||
wrapperNode.style.height = windowHeight + 'px'
|
||||
|
||||
component.measureHeightAndWidth()
|
||||
atom.setWindowDimensions({width: windowWidth, height: windowHeight})
|
||||
|
||||
component.measureDimensions()
|
||||
component.measureWindowSize()
|
||||
nextAnimationFrame()
|
||||
|
||||
it "flips horizontally when near the right edge", ->
|
||||
afterEach ->
|
||||
atom.restoreWindowDimensions()
|
||||
|
||||
it "slides horizontally left when near the right edge", ->
|
||||
marker = editor.displayBuffer.markBufferRange([[0, 26], [0, 26]], invalidate: 'never')
|
||||
decoration = editor.decorateMarker(marker, {type: 'overlay', item})
|
||||
nextAnimationFrame()
|
||||
nextAnimationFrame()
|
||||
|
||||
position = wrapperNode.pixelPositionForBufferPosition([0, 26])
|
||||
|
||||
overlay = component.getTopmostDOMNode().querySelector('atom-overlay')
|
||||
expect(overlay.style.left).toBe position.left + 'px'
|
||||
expect(overlay.style.left).toBe position.left + gutterWidth + 'px'
|
||||
expect(overlay.style.top).toBe position.top + editor.getLineHeightInPixels() + 'px'
|
||||
|
||||
editor.insertText('a')
|
||||
nextAnimationFrame()
|
||||
|
||||
position = wrapperNode.pixelPositionForBufferPosition([0, 27])
|
||||
|
||||
expect(overlay.style.left).toBe position.left - itemWidth + 'px'
|
||||
expect(overlay.style.left).toBe windowWidth - itemWidth + 'px'
|
||||
expect(overlay.style.top).toBe position.top + editor.getLineHeightInPixels() + 'px'
|
||||
|
||||
it "flips vertically when near the bottom edge", ->
|
||||
marker = editor.displayBuffer.markBufferRange([[4, 0], [4, 0]], invalidate: 'never')
|
||||
decoration = editor.decorateMarker(marker, {type: 'overlay', item})
|
||||
editor.insertText('b')
|
||||
nextAnimationFrame()
|
||||
|
||||
position = wrapperNode.pixelPositionForBufferPosition([4, 0])
|
||||
|
||||
overlay = component.getTopmostDOMNode().querySelector('atom-overlay')
|
||||
expect(overlay.style.left).toBe position.left + 'px'
|
||||
expect(overlay.style.left).toBe windowWidth - itemWidth + 'px'
|
||||
expect(overlay.style.top).toBe position.top + editor.getLineHeightInPixels() + 'px'
|
||||
|
||||
editor.insertNewline()
|
||||
nextAnimationFrame()
|
||||
|
||||
position = wrapperNode.pixelPositionForBufferPosition([5, 0])
|
||||
|
||||
expect(overlay.style.left).toBe position.left + 'px'
|
||||
expect(overlay.style.top).toBe position.top - itemHeight + 'px'
|
||||
|
||||
describe "when the editor is very small", ->
|
||||
beforeEach ->
|
||||
gutterWidth = componentNode.querySelector('.gutter').offsetWidth
|
||||
windowWidth = gutterWidth + 6 * editor.getDefaultCharWidth()
|
||||
windowHeight = 6 * editor.getLineHeightInPixels()
|
||||
|
||||
wrapperNode.style.width = windowWidth + 'px'
|
||||
wrapperNode.style.height = windowHeight + 'px'
|
||||
|
||||
component.measureHeightAndWidth()
|
||||
nextAnimationFrame()
|
||||
|
||||
it "does not flip horizontally and force the overlay to have a negative left", ->
|
||||
marker = editor.displayBuffer.markBufferRange([[0, 2], [0, 2]], invalidate: 'never')
|
||||
decoration = editor.decorateMarker(marker, {type: 'overlay', item})
|
||||
nextAnimationFrame()
|
||||
|
||||
position = wrapperNode.pixelPositionForBufferPosition([0, 2])
|
||||
|
||||
overlay = component.getTopmostDOMNode().querySelector('atom-overlay')
|
||||
expect(overlay.style.left).toBe position.left + 'px'
|
||||
expect(overlay.style.top).toBe position.top + editor.getLineHeightInPixels() + 'px'
|
||||
|
||||
editor.insertText('a')
|
||||
nextAnimationFrame()
|
||||
|
||||
position = wrapperNode.pixelPositionForBufferPosition([0, 3])
|
||||
|
||||
expect(overlay.style.left).toBe position.left + 'px'
|
||||
expect(overlay.style.top).toBe position.top + editor.getLineHeightInPixels() + 'px'
|
||||
|
||||
it "does not flip vertically and force the overlay to have a negative top", ->
|
||||
marker = editor.displayBuffer.markBufferRange([[1, 0], [1, 0]], invalidate: 'never')
|
||||
decoration = editor.decorateMarker(marker, {type: 'overlay', item})
|
||||
nextAnimationFrame()
|
||||
|
||||
position = wrapperNode.pixelPositionForBufferPosition([1, 0])
|
||||
|
||||
overlay = component.getTopmostDOMNode().querySelector('atom-overlay')
|
||||
expect(overlay.style.left).toBe position.left + 'px'
|
||||
expect(overlay.style.top).toBe position.top + editor.getLineHeightInPixels() + 'px'
|
||||
|
||||
editor.insertNewline()
|
||||
nextAnimationFrame()
|
||||
|
||||
position = wrapperNode.pixelPositionForBufferPosition([2, 0])
|
||||
|
||||
expect(overlay.style.left).toBe position.left + 'px'
|
||||
expect(overlay.style.top).toBe position.top + editor.getLineHeightInPixels() + 'px'
|
||||
|
||||
|
||||
describe "when editor scroll position is not 0", ->
|
||||
it "flips horizontally when near the right edge", ->
|
||||
editor.setScrollLeft(2 * editor.getDefaultCharWidth())
|
||||
marker = editor.displayBuffer.markBufferRange([[0, 28], [0, 28]], invalidate: 'never')
|
||||
decoration = editor.decorateMarker(marker, {type: 'overlay', item})
|
||||
nextAnimationFrame()
|
||||
|
||||
position = wrapperNode.pixelPositionForBufferPosition([0, 28])
|
||||
|
||||
overlay = component.getTopmostDOMNode().querySelector('atom-overlay')
|
||||
expect(overlay.style.left).toBe position.left + 'px'
|
||||
expect(overlay.style.top).toBe position.top + editor.getLineHeightInPixels() + 'px'
|
||||
|
||||
editor.insertText('a')
|
||||
nextAnimationFrame()
|
||||
|
||||
position = wrapperNode.pixelPositionForBufferPosition([0, 29])
|
||||
|
||||
expect(overlay.style.left).toBe position.left - itemWidth + 'px'
|
||||
expect(overlay.style.top).toBe position.top + editor.getLineHeightInPixels() + 'px'
|
||||
|
||||
it "flips vertically when near the bottom edge", ->
|
||||
editor.setScrollTop(2 * editor.getLineHeightInPixels())
|
||||
marker = editor.displayBuffer.markBufferRange([[6, 0], [6, 0]], invalidate: 'never')
|
||||
decoration = editor.decorateMarker(marker, {type: 'overlay', item})
|
||||
nextAnimationFrame()
|
||||
|
||||
position = wrapperNode.pixelPositionForBufferPosition([6, 0])
|
||||
|
||||
overlay = component.getTopmostDOMNode().querySelector('atom-overlay')
|
||||
expect(overlay.style.left).toBe position.left + 'px'
|
||||
expect(overlay.style.top).toBe position.top + editor.getLineHeightInPixels() + 'px'
|
||||
|
||||
editor.insertNewline()
|
||||
nextAnimationFrame()
|
||||
|
||||
position = wrapperNode.pixelPositionForBufferPosition([7, 0])
|
||||
|
||||
expect(overlay.style.left).toBe position.left + 'px'
|
||||
expect(overlay.style.top).toBe position.top - itemHeight + 'px'
|
||||
|
||||
describe "hidden input field", ->
|
||||
it "renders the hidden input field at the position of the last cursor if the cursor is on screen and the editor is focused", ->
|
||||
editor.setVerticalScrollMargin(0)
|
||||
@@ -1512,7 +1372,7 @@ describe "TextEditorComponent", ->
|
||||
inputNode = componentNode.querySelector('.hidden-input')
|
||||
wrapperNode.style.height = 5 * lineHeightInPixels + 'px'
|
||||
wrapperNode.style.width = 10 * charWidth + 'px'
|
||||
component.measureHeightAndWidth()
|
||||
component.measureDimensions()
|
||||
nextAnimationFrame()
|
||||
|
||||
expect(editor.getCursorScreenPosition()).toEqual [0, 0]
|
||||
@@ -1582,7 +1442,7 @@ describe "TextEditorComponent", ->
|
||||
height = 4.5 * lineHeightInPixels
|
||||
wrapperNode.style.height = height + 'px'
|
||||
wrapperNode.style.width = 10 * charWidth + 'px'
|
||||
component.measureHeightAndWidth()
|
||||
component.measureDimensions()
|
||||
nextAnimationFrame()
|
||||
|
||||
coordinates = clientCoordinatesForScreenPosition([0, 2])
|
||||
@@ -1596,7 +1456,7 @@ describe "TextEditorComponent", ->
|
||||
it "moves the cursor to the nearest screen position", ->
|
||||
wrapperNode.style.height = 4.5 * lineHeightInPixels + 'px'
|
||||
wrapperNode.style.width = 10 * charWidth + 'px'
|
||||
component.measureHeightAndWidth()
|
||||
component.measureDimensions()
|
||||
editor.setScrollTop(3.5 * lineHeightInPixels)
|
||||
editor.setScrollLeft(2 * charWidth)
|
||||
nextAnimationFrame()
|
||||
@@ -1879,7 +1739,7 @@ describe "TextEditorComponent", ->
|
||||
editor.setSoftWrapped(true)
|
||||
nextAnimationFrame()
|
||||
componentNode.style.width = 21 * charWidth + editor.getVerticalScrollbarWidth() + 'px'
|
||||
component.measureHeightAndWidth()
|
||||
component.measureDimensions()
|
||||
nextAnimationFrame()
|
||||
|
||||
describe "when the gutter is clicked", ->
|
||||
@@ -2045,7 +1905,7 @@ describe "TextEditorComponent", ->
|
||||
describe "scrolling", ->
|
||||
it "updates the vertical scrollbar when the scrollTop is changed in the model", ->
|
||||
wrapperNode.style.height = 4.5 * lineHeightInPixels + 'px'
|
||||
component.measureHeightAndWidth()
|
||||
component.measureDimensions()
|
||||
nextAnimationFrame()
|
||||
|
||||
expect(verticalScrollbarNode.scrollTop).toBe 0
|
||||
@@ -2056,7 +1916,7 @@ describe "TextEditorComponent", ->
|
||||
|
||||
it "updates the horizontal scrollbar and the x transform of the lines based on the scrollLeft of the model", ->
|
||||
componentNode.style.width = 30 * charWidth + 'px'
|
||||
component.measureHeightAndWidth()
|
||||
component.measureDimensions()
|
||||
nextAnimationFrame()
|
||||
|
||||
linesNode = componentNode.querySelector('.lines')
|
||||
@@ -2070,7 +1930,7 @@ describe "TextEditorComponent", ->
|
||||
|
||||
it "updates the scrollLeft of the model when the scrollLeft of the horizontal scrollbar changes", ->
|
||||
componentNode.style.width = 30 * charWidth + 'px'
|
||||
component.measureHeightAndWidth()
|
||||
component.measureDimensions()
|
||||
nextAnimationFrame()
|
||||
|
||||
expect(editor.getScrollLeft()).toBe 0
|
||||
@@ -2083,7 +1943,7 @@ describe "TextEditorComponent", ->
|
||||
it "does not obscure the last line with the horizontal scrollbar", ->
|
||||
wrapperNode.style.height = 4.5 * lineHeightInPixels + 'px'
|
||||
wrapperNode.style.width = 10 * charWidth + 'px'
|
||||
component.measureHeightAndWidth()
|
||||
component.measureDimensions()
|
||||
editor.setScrollBottom(editor.getScrollHeight())
|
||||
nextAnimationFrame()
|
||||
lastLineNode = component.lineNodeForScreenRow(editor.getLastScreenRow())
|
||||
@@ -2093,7 +1953,7 @@ describe "TextEditorComponent", ->
|
||||
|
||||
# Scroll so there's no space below the last line when the horizontal scrollbar disappears
|
||||
wrapperNode.style.width = 100 * charWidth + 'px'
|
||||
component.measureHeightAndWidth()
|
||||
component.measureDimensions()
|
||||
nextAnimationFrame()
|
||||
bottomOfLastLine = lastLineNode.getBoundingClientRect().bottom
|
||||
bottomOfEditor = componentNode.getBoundingClientRect().bottom
|
||||
@@ -2102,7 +1962,7 @@ describe "TextEditorComponent", ->
|
||||
it "does not obscure the last character of the longest line with the vertical scrollbar", ->
|
||||
wrapperNode.style.height = 7 * lineHeightInPixels + 'px'
|
||||
wrapperNode.style.width = 10 * charWidth + 'px'
|
||||
component.measureHeightAndWidth()
|
||||
component.measureDimensions()
|
||||
editor.setScrollLeft(Infinity)
|
||||
nextAnimationFrame()
|
||||
|
||||
@@ -2116,21 +1976,21 @@ describe "TextEditorComponent", ->
|
||||
|
||||
wrapperNode.style.height = 4.5 * lineHeightInPixels + 'px'
|
||||
wrapperNode.style.width = '1000px'
|
||||
component.measureHeightAndWidth()
|
||||
component.measureDimensions()
|
||||
nextAnimationFrame()
|
||||
|
||||
expect(verticalScrollbarNode.style.display).toBe ''
|
||||
expect(horizontalScrollbarNode.style.display).toBe 'none'
|
||||
|
||||
componentNode.style.width = 10 * charWidth + 'px'
|
||||
component.measureHeightAndWidth()
|
||||
component.measureDimensions()
|
||||
nextAnimationFrame()
|
||||
|
||||
expect(verticalScrollbarNode.style.display).toBe ''
|
||||
expect(horizontalScrollbarNode.style.display).toBe ''
|
||||
|
||||
wrapperNode.style.height = 20 * lineHeightInPixels + 'px'
|
||||
component.measureHeightAndWidth()
|
||||
component.measureDimensions()
|
||||
nextAnimationFrame()
|
||||
|
||||
expect(verticalScrollbarNode.style.display).toBe 'none'
|
||||
@@ -2139,7 +1999,7 @@ describe "TextEditorComponent", ->
|
||||
it "makes the dummy scrollbar divs only as tall/wide as the actual scrollbars", ->
|
||||
wrapperNode.style.height = 4 * lineHeightInPixels + 'px'
|
||||
wrapperNode.style.width = 10 * charWidth + 'px'
|
||||
component.measureHeightAndWidth()
|
||||
component.measureDimensions()
|
||||
nextAnimationFrame()
|
||||
|
||||
atom.styles.addStyleSheet """
|
||||
@@ -2168,21 +2028,21 @@ describe "TextEditorComponent", ->
|
||||
|
||||
wrapperNode.style.height = 4.5 * lineHeightInPixels + 'px'
|
||||
wrapperNode.style.width = '1000px'
|
||||
component.measureHeightAndWidth()
|
||||
component.measureDimensions()
|
||||
nextAnimationFrame()
|
||||
expect(verticalScrollbarNode.style.bottom).toBe '0px'
|
||||
expect(horizontalScrollbarNode.style.right).toBe verticalScrollbarNode.offsetWidth + 'px'
|
||||
expect(scrollbarCornerNode.style.display).toBe 'none'
|
||||
|
||||
componentNode.style.width = 10 * charWidth + 'px'
|
||||
component.measureHeightAndWidth()
|
||||
component.measureDimensions()
|
||||
nextAnimationFrame()
|
||||
expect(verticalScrollbarNode.style.bottom).toBe horizontalScrollbarNode.offsetHeight + 'px'
|
||||
expect(horizontalScrollbarNode.style.right).toBe verticalScrollbarNode.offsetWidth + 'px'
|
||||
expect(scrollbarCornerNode.style.display).toBe ''
|
||||
|
||||
wrapperNode.style.height = 20 * lineHeightInPixels + 'px'
|
||||
component.measureHeightAndWidth()
|
||||
component.measureDimensions()
|
||||
nextAnimationFrame()
|
||||
expect(verticalScrollbarNode.style.bottom).toBe horizontalScrollbarNode.offsetHeight + 'px'
|
||||
expect(horizontalScrollbarNode.style.right).toBe '0px'
|
||||
@@ -2191,7 +2051,7 @@ describe "TextEditorComponent", ->
|
||||
it "accounts for the width of the gutter in the scrollWidth of the horizontal scrollbar", ->
|
||||
gutterNode = componentNode.querySelector('.gutter')
|
||||
componentNode.style.width = 10 * charWidth + 'px'
|
||||
component.measureHeightAndWidth()
|
||||
component.measureDimensions()
|
||||
nextAnimationFrame()
|
||||
|
||||
expect(horizontalScrollbarNode.scrollWidth).toBe editor.getScrollWidth()
|
||||
@@ -2205,7 +2065,7 @@ describe "TextEditorComponent", ->
|
||||
beforeEach ->
|
||||
wrapperNode.style.height = 4.5 * lineHeightInPixels + 'px'
|
||||
wrapperNode.style.width = 20 * charWidth + 'px'
|
||||
component.measureHeightAndWidth()
|
||||
component.measureDimensions()
|
||||
nextAnimationFrame()
|
||||
|
||||
it "updates the scrollLeft or scrollTop on mousewheel events depending on which delta is greater (x or y)", ->
|
||||
@@ -2249,7 +2109,7 @@ describe "TextEditorComponent", ->
|
||||
it "keeps the line on the DOM if it is scrolled off-screen", ->
|
||||
wrapperNode.style.height = 4.5 * lineHeightInPixels + 'px'
|
||||
wrapperNode.style.width = 20 * charWidth + 'px'
|
||||
component.measureHeightAndWidth()
|
||||
component.measureDimensions()
|
||||
|
||||
lineNode = componentNode.querySelector('.line')
|
||||
wheelEvent = new WheelEvent('mousewheel', wheelDeltaX: 0, wheelDeltaY: -500)
|
||||
@@ -2262,7 +2122,7 @@ describe "TextEditorComponent", ->
|
||||
it "does not set the mouseWheelScreenRow if scrolling horizontally", ->
|
||||
wrapperNode.style.height = 4.5 * lineHeightInPixels + 'px'
|
||||
wrapperNode.style.width = 20 * charWidth + 'px'
|
||||
component.measureHeightAndWidth()
|
||||
component.measureDimensions()
|
||||
|
||||
lineNode = componentNode.querySelector('.line')
|
||||
wheelEvent = new WheelEvent('mousewheel', wheelDeltaX: 10, wheelDeltaY: 0)
|
||||
@@ -2305,7 +2165,7 @@ describe "TextEditorComponent", ->
|
||||
it "keeps the line number on the DOM if it is scrolled off-screen", ->
|
||||
wrapperNode.style.height = 4.5 * lineHeightInPixels + 'px'
|
||||
wrapperNode.style.width = 20 * charWidth + 'px'
|
||||
component.measureHeightAndWidth()
|
||||
component.measureDimensions()
|
||||
|
||||
lineNumberNode = componentNode.querySelectorAll('.line-number')[1]
|
||||
wheelEvent = new WheelEvent('mousewheel', wheelDeltaX: 0, wheelDeltaY: -500)
|
||||
@@ -2320,7 +2180,7 @@ describe "TextEditorComponent", ->
|
||||
|
||||
wrapperNode.style.height = 4.5 * lineHeightInPixels + 'px'
|
||||
wrapperNode.style.width = 20 * charWidth + 'px'
|
||||
component.measureHeightAndWidth()
|
||||
component.measureDimensions()
|
||||
nextAnimationFrame()
|
||||
|
||||
# try to scroll past the top, which is impossible
|
||||
@@ -2390,6 +2250,7 @@ describe "TextEditorComponent", ->
|
||||
expect(editor.lineTextForBufferRow(0)).toBe 'üvar quicksort = function () {'
|
||||
|
||||
it "does not handle input events when input is disabled", ->
|
||||
nextAnimationFrame = noAnimationFrame # This spec is flaky on the build machine, so this.
|
||||
component.setInputEnabled(false)
|
||||
componentNode.dispatchEvent(buildTextInputEvent(data: 'x', target: inputNode))
|
||||
expect(nextAnimationFrame).toBe noAnimationFrame
|
||||
@@ -2716,7 +2577,7 @@ describe "TextEditorComponent", ->
|
||||
describe "when the wrapper view has an explicit height", ->
|
||||
it "does not assign a height on the component node", ->
|
||||
wrapperNode.style.height = '200px'
|
||||
component.measureHeightAndWidth()
|
||||
component.measureDimensions()
|
||||
nextAnimationFrame()
|
||||
expect(componentNode.style.height).toBe ''
|
||||
|
||||
|
||||
@@ -29,6 +29,9 @@ describe "TextEditorPresenter", ->
|
||||
model: editor
|
||||
explicitHeight: 130
|
||||
contentFrameWidth: 500
|
||||
windowWidth: 500
|
||||
windowHeight: 130
|
||||
boundingClientRect: {left: 0, top: 0, width: 500, height: 130}
|
||||
lineHeight: 10
|
||||
baseCharacterWidth: 10
|
||||
horizontalScrollbarHeight: 10
|
||||
@@ -1485,11 +1488,11 @@ describe "TextEditorPresenter", ->
|
||||
}
|
||||
|
||||
describe ".overlays", ->
|
||||
[item] = []
|
||||
stateForOverlay = (presenter, decoration) ->
|
||||
presenter.getState().content.overlays[decoration.id]
|
||||
|
||||
it "contains state for overlay decorations both initially and when their markers move", ->
|
||||
item = {}
|
||||
marker = editor.markBufferPosition([2, 13], invalidate: 'touch')
|
||||
decoration = editor.decorateMarker(marker, {type: 'overlay', item})
|
||||
presenter = buildPresenter(explicitHeight: 30, scrollTop: 20)
|
||||
@@ -1497,14 +1500,14 @@ describe "TextEditorPresenter", ->
|
||||
# Initial state
|
||||
expectValues stateForOverlay(presenter, decoration), {
|
||||
item: item
|
||||
pixelPosition: {top: 2 * 10, left: 13 * 10}
|
||||
pixelPosition: {top: 3 * 10 - presenter.state.content.scrollTop, left: 13 * 10}
|
||||
}
|
||||
|
||||
# Change range
|
||||
expectStateUpdate presenter, -> marker.setBufferRange([[2, 13], [4, 6]])
|
||||
expectValues stateForOverlay(presenter, decoration), {
|
||||
item: item
|
||||
pixelPosition: {top: 4 * 10, left: 6 * 10}
|
||||
pixelPosition: {top: 5 * 10 - presenter.state.content.scrollTop, left: 6 * 10}
|
||||
}
|
||||
|
||||
# Valid -> invalid
|
||||
@@ -1515,14 +1518,14 @@ describe "TextEditorPresenter", ->
|
||||
expectStateUpdate presenter, -> editor.undo()
|
||||
expectValues stateForOverlay(presenter, decoration), {
|
||||
item: item
|
||||
pixelPosition: {top: 4 * 10, left: 6 * 10}
|
||||
pixelPosition: {top: 5 * 10 - presenter.state.content.scrollTop, left: 6 * 10}
|
||||
}
|
||||
|
||||
# Reverse direction
|
||||
expectStateUpdate presenter, -> marker.setBufferRange([[2, 13], [4, 6]], reversed: true)
|
||||
expectValues stateForOverlay(presenter, decoration), {
|
||||
item: item
|
||||
pixelPosition: {top: 2 * 10, left: 13 * 10}
|
||||
pixelPosition: {top: 3 * 10 - presenter.state.content.scrollTop, left: 13 * 10}
|
||||
}
|
||||
|
||||
# Destroy
|
||||
@@ -1533,69 +1536,237 @@ describe "TextEditorPresenter", ->
|
||||
decoration2 = editor.decorateMarker(marker, {type: 'overlay', item})
|
||||
expectValues stateForOverlay(presenter, decoration2), {
|
||||
item: item
|
||||
pixelPosition: {top: 2 * 10, left: 13 * 10}
|
||||
pixelPosition: {top: 3 * 10 - presenter.state.content.scrollTop, left: 13 * 10}
|
||||
}
|
||||
|
||||
it "updates when ::baseCharacterWidth changes", ->
|
||||
item = {}
|
||||
scrollTop = 20
|
||||
marker = editor.markBufferPosition([2, 13], invalidate: 'touch')
|
||||
decoration = editor.decorateMarker(marker, {type: 'overlay', item})
|
||||
presenter = buildPresenter(explicitHeight: 30, scrollTop: 20)
|
||||
presenter = buildPresenter({explicitHeight: 30, scrollTop})
|
||||
|
||||
expectValues stateForOverlay(presenter, decoration), {
|
||||
item: item
|
||||
pixelPosition: {top: 2 * 10, left: 13 * 10}
|
||||
pixelPosition: {top: 3 * 10 - scrollTop, left: 13 * 10}
|
||||
}
|
||||
|
||||
expectStateUpdate presenter, -> presenter.setBaseCharacterWidth(5)
|
||||
|
||||
expectValues stateForOverlay(presenter, decoration), {
|
||||
item: item
|
||||
pixelPosition: {top: 2 * 10, left: 13 * 5}
|
||||
pixelPosition: {top: 3 * 10 - scrollTop, left: 13 * 5}
|
||||
}
|
||||
|
||||
it "updates when ::lineHeight changes", ->
|
||||
item = {}
|
||||
scrollTop = 20
|
||||
marker = editor.markBufferPosition([2, 13], invalidate: 'touch')
|
||||
decoration = editor.decorateMarker(marker, {type: 'overlay', item})
|
||||
presenter = buildPresenter(explicitHeight: 30, scrollTop: 20)
|
||||
presenter = buildPresenter({explicitHeight: 30, scrollTop})
|
||||
|
||||
expectValues stateForOverlay(presenter, decoration), {
|
||||
item: item
|
||||
pixelPosition: {top: 2 * 10, left: 13 * 10}
|
||||
pixelPosition: {top: 3 * 10 - scrollTop, left: 13 * 10}
|
||||
}
|
||||
|
||||
expectStateUpdate presenter, -> presenter.setLineHeight(5)
|
||||
|
||||
expectValues stateForOverlay(presenter, decoration), {
|
||||
item: item
|
||||
pixelPosition: {top: 2 * 5, left: 13 * 10}
|
||||
pixelPosition: {top: 3 * 5 - scrollTop, left: 13 * 10}
|
||||
}
|
||||
|
||||
it "honors the 'position' option on overlay decorations", ->
|
||||
item = {}
|
||||
scrollTop = 20
|
||||
marker = editor.markBufferRange([[2, 13], [4, 14]], invalidate: 'touch')
|
||||
decoration = editor.decorateMarker(marker, {type: 'overlay', position: 'tail', item})
|
||||
presenter = buildPresenter(explicitHeight: 30, scrollTop: 20)
|
||||
presenter = buildPresenter({explicitHeight: 30, scrollTop})
|
||||
expectValues stateForOverlay(presenter, decoration), {
|
||||
item: item
|
||||
pixelPosition: {top: 2 * 10, left: 13 * 10}
|
||||
pixelPosition: {top: 3 * 10 - scrollTop, left: 13 * 10}
|
||||
}
|
||||
|
||||
it "is empty until all of the required measurements are assigned", ->
|
||||
item = {}
|
||||
marker = editor.markBufferRange([[2, 13], [4, 14]], invalidate: 'touch')
|
||||
decoration = editor.decorateMarker(marker, {type: 'overlay', position: 'tail', item})
|
||||
|
||||
presenter = buildPresenter(baseCharacterWidth: null, lineHeight: null)
|
||||
presenter = buildPresenter(baseCharacterWidth: null, lineHeight: null, windowWidth: null, windowHeight: null, boundingClientRect: null)
|
||||
expect(presenter.getState().content.overlays).toEqual({})
|
||||
|
||||
presenter.setBaseCharacterWidth(10)
|
||||
expect(presenter.getState().content.overlays).toEqual({})
|
||||
|
||||
presenter.setLineHeight(10)
|
||||
expect(presenter.getState().content.overlays).toEqual({})
|
||||
|
||||
presenter.setWindowSize(500, 100)
|
||||
expect(presenter.getState().content.overlays).toEqual({})
|
||||
|
||||
presenter.setBoundingClientRect({top: 0, left: 0, height: 100, width: 500})
|
||||
expect(presenter.getState().content.overlays).not.toEqual({})
|
||||
|
||||
describe "when the overlay has been measured", ->
|
||||
[gutterWidth, windowWidth, windowHeight, itemWidth, itemHeight, contentMargin, boundingClientRect, contentFrameWidth] = []
|
||||
beforeEach ->
|
||||
item = {}
|
||||
gutterWidth = 5 * 10 # 5 chars wide
|
||||
contentFrameWidth = 30 * 10
|
||||
windowWidth = gutterWidth + contentFrameWidth
|
||||
windowHeight = 9 * 10
|
||||
|
||||
itemWidth = 4 * 10
|
||||
itemHeight = 4 * 10
|
||||
contentMargin = 0
|
||||
|
||||
boundingClientRect =
|
||||
top: 0
|
||||
left: 0,
|
||||
width: windowWidth
|
||||
height: windowHeight
|
||||
|
||||
it "slides horizontally left when near the right edge", ->
|
||||
scrollLeft = 20
|
||||
marker = editor.markBufferPosition([0, 26], invalidate: 'never')
|
||||
decoration = editor.decorateMarker(marker, {type: 'overlay', item})
|
||||
|
||||
presenter = buildPresenter({scrollLeft, windowWidth, windowHeight, contentFrameWidth, boundingClientRect})
|
||||
expectStateUpdate presenter, ->
|
||||
presenter.setOverlayDimensions(decoration.id, itemWidth, itemHeight, contentMargin)
|
||||
|
||||
expectValues stateForOverlay(presenter, decoration), {
|
||||
item: item
|
||||
pixelPosition: {top: 1 * 10, left: 26 * 10 + gutterWidth - scrollLeft}
|
||||
}
|
||||
|
||||
expectStateUpdate presenter, -> editor.insertText('a')
|
||||
expectValues stateForOverlay(presenter, decoration), {
|
||||
item: item
|
||||
pixelPosition: {top: 1 * 10, left: windowWidth - itemWidth}
|
||||
}
|
||||
|
||||
expectStateUpdate presenter, -> editor.insertText('b')
|
||||
expectValues stateForOverlay(presenter, decoration), {
|
||||
item: item
|
||||
pixelPosition: {top: 1 * 10, left: windowWidth - itemWidth}
|
||||
}
|
||||
|
||||
it "flips vertically when near the bottom edge", ->
|
||||
scrollTop = 10
|
||||
marker = editor.markBufferPosition([5, 0], invalidate: 'never')
|
||||
decoration = editor.decorateMarker(marker, {type: 'overlay', item})
|
||||
|
||||
presenter = buildPresenter({scrollTop, windowWidth, windowHeight, contentFrameWidth, boundingClientRect})
|
||||
expectStateUpdate presenter, ->
|
||||
presenter.setOverlayDimensions(decoration.id, itemWidth, itemHeight, contentMargin)
|
||||
|
||||
expectValues stateForOverlay(presenter, decoration), {
|
||||
item: item
|
||||
pixelPosition: {top: 6 * 10 - scrollTop, left: gutterWidth}
|
||||
}
|
||||
|
||||
expectStateUpdate presenter, ->
|
||||
editor.insertNewline()
|
||||
editor.setScrollTop(scrollTop) # I'm fighting the editor
|
||||
expectValues stateForOverlay(presenter, decoration), {
|
||||
item: item
|
||||
pixelPosition: {top: 6 * 10 - scrollTop - itemHeight, left: gutterWidth}
|
||||
}
|
||||
|
||||
describe "when the overlay item has a margin", ->
|
||||
beforeEach ->
|
||||
itemWidth = 12 * 10
|
||||
contentMargin = -(gutterWidth + 2 * 10)
|
||||
|
||||
it "slides horizontally right when near the left edge with margin", ->
|
||||
editor.setCursorBufferPosition([0, 3])
|
||||
cursor = editor.getLastCursor()
|
||||
marker = cursor.marker
|
||||
decoration = editor.decorateMarker(marker, {type: 'overlay', item})
|
||||
|
||||
presenter = buildPresenter({windowWidth, windowHeight, contentFrameWidth, boundingClientRect})
|
||||
expectStateUpdate presenter, ->
|
||||
presenter.setOverlayDimensions(decoration.id, itemWidth, itemHeight, contentMargin)
|
||||
|
||||
expectValues stateForOverlay(presenter, decoration), {
|
||||
item: item
|
||||
pixelPosition: {top: 1 * 10, left: 3 * 10 + gutterWidth}
|
||||
}
|
||||
|
||||
expectStateUpdate presenter, -> cursor.moveLeft()
|
||||
expectValues stateForOverlay(presenter, decoration), {
|
||||
item: item
|
||||
pixelPosition: {top: 1 * 10, left: -contentMargin}
|
||||
}
|
||||
|
||||
expectStateUpdate presenter, -> cursor.moveLeft()
|
||||
expectValues stateForOverlay(presenter, decoration), {
|
||||
item: item
|
||||
pixelPosition: {top: 1 * 10, left: -contentMargin}
|
||||
}
|
||||
|
||||
describe "when the editor is very small", ->
|
||||
beforeEach ->
|
||||
windowWidth = gutterWidth + 6 * 10
|
||||
windowHeight = 6 * 10
|
||||
contentFrameWidth = windowWidth - gutterWidth
|
||||
boundingClientRect.width = windowWidth
|
||||
boundingClientRect.height = windowHeight
|
||||
|
||||
it "does not flip vertically and force the overlay to have a negative top", ->
|
||||
marker = editor.markBufferPosition([1, 0], invalidate: 'never')
|
||||
decoration = editor.decorateMarker(marker, {type: 'overlay', item})
|
||||
|
||||
presenter = buildPresenter({windowWidth, windowHeight, contentFrameWidth, boundingClientRect})
|
||||
expectStateUpdate presenter, ->
|
||||
presenter.setOverlayDimensions(decoration.id, itemWidth, itemHeight, contentMargin)
|
||||
|
||||
expectValues stateForOverlay(presenter, decoration), {
|
||||
item: item
|
||||
pixelPosition: {top: 2 * 10, left: 0 * 10 + gutterWidth}
|
||||
}
|
||||
|
||||
expectStateUpdate presenter, -> editor.insertNewline()
|
||||
expectValues stateForOverlay(presenter, decoration), {
|
||||
item: item
|
||||
pixelPosition: {top: 3 * 10, left: gutterWidth}
|
||||
}
|
||||
|
||||
it "does not adjust horizontally and force the overlay to have a negative left", ->
|
||||
itemWidth = 6 * 10
|
||||
|
||||
marker = editor.markBufferPosition([0, 0], invalidate: 'never')
|
||||
decoration = editor.decorateMarker(marker, {type: 'overlay', item})
|
||||
|
||||
presenter = buildPresenter({windowWidth, windowHeight, contentFrameWidth, boundingClientRect})
|
||||
expectStateUpdate presenter, ->
|
||||
presenter.setOverlayDimensions(decoration.id, itemWidth, itemHeight, contentMargin)
|
||||
|
||||
expectValues stateForOverlay(presenter, decoration), {
|
||||
item: item
|
||||
pixelPosition: {top: 10, left: gutterWidth}
|
||||
}
|
||||
|
||||
windowWidth = gutterWidth + 5 * 10
|
||||
expectStateUpdate presenter, -> presenter.setWindowSize(windowWidth, windowHeight)
|
||||
expectValues stateForOverlay(presenter, decoration), {
|
||||
item: item
|
||||
pixelPosition: {top: 10, left: windowWidth - itemWidth}
|
||||
}
|
||||
|
||||
windowWidth = gutterWidth + 1 * 10
|
||||
expectStateUpdate presenter, -> presenter.setWindowSize(windowWidth, windowHeight)
|
||||
expectValues stateForOverlay(presenter, decoration), {
|
||||
item: item
|
||||
pixelPosition: {top: 10, left: 0}
|
||||
}
|
||||
|
||||
windowWidth = gutterWidth
|
||||
expectStateUpdate presenter, -> presenter.setWindowSize(windowWidth, windowHeight)
|
||||
expectValues stateForOverlay(presenter, decoration), {
|
||||
item: item
|
||||
pixelPosition: {top: 10, left: 0}
|
||||
}
|
||||
|
||||
|
||||
describe ".gutter", ->
|
||||
describe ".scrollHeight", ->
|
||||
it "is initialized based on ::lineHeight, the number of lines, and ::explicitHeight", ->
|
||||
|
||||
@@ -137,6 +137,21 @@ describe "ViewRegistry", ->
|
||||
advanceClock(registry.documentPollingInterval)
|
||||
expect(events).toEqual ['write', 'read', 'poll', 'poll']
|
||||
|
||||
it "polls the document after updating when ::pollAfterNextUpdate() has been called", ->
|
||||
events = []
|
||||
registry.pollDocument -> events.push('poll')
|
||||
registry.updateDocument -> events.push('write')
|
||||
registry.readDocument -> events.push('read')
|
||||
frameRequests.shift()()
|
||||
expect(events).toEqual ['write', 'read']
|
||||
|
||||
events = []
|
||||
registry.pollAfterNextUpdate()
|
||||
registry.updateDocument -> events.push('write')
|
||||
registry.readDocument -> events.push('read')
|
||||
frameRequests.shift()()
|
||||
expect(events).toEqual ['write', 'read', 'poll']
|
||||
|
||||
describe "::pollDocument(fn)", ->
|
||||
it "calls all registered reader functions on an interval until they are disabled via a returned disposable", ->
|
||||
spyOn(window, 'setInterval').andCallFake(fakeSetInterval)
|
||||
|
||||
@@ -94,8 +94,6 @@ class Cursor extends Model
|
||||
Grim.deprecate("Use Cursor::onDidChangePosition instead")
|
||||
when 'destroyed'
|
||||
Grim.deprecate("Use Cursor::onDidDestroy instead")
|
||||
when 'destroyed'
|
||||
Grim.deprecate("Use Cursor::onDidDestroy instead")
|
||||
else
|
||||
Grim.deprecate("::on is no longer supported. Use the event subscription methods instead")
|
||||
super
|
||||
|
||||
@@ -20,7 +20,7 @@ nextId = -> idCounter++
|
||||
# decoration = editor.decorateMarker(marker, {type: 'line', class: 'my-line-class'})
|
||||
# ```
|
||||
#
|
||||
# Best practice for destorying the decoration is by destroying the {Marker}.
|
||||
# Best practice for destroying the decoration is by destroying the {Marker}.
|
||||
#
|
||||
# ```coffee
|
||||
# marker.destroy()
|
||||
@@ -56,7 +56,6 @@ class Decoration
|
||||
@properties.id = @id
|
||||
@flashQueue = null
|
||||
@destroyed = false
|
||||
|
||||
@markerDestroyDisposable = @marker.onDidDestroy => @destroy()
|
||||
|
||||
# Essential: Destroy this marker.
|
||||
|
||||
@@ -4,7 +4,6 @@ _ = require 'underscore-plus'
|
||||
|
||||
CursorsComponent = require './cursors-component'
|
||||
HighlightsComponent = require './highlights-component'
|
||||
OverlayManager = require './overlay-manager'
|
||||
|
||||
DummyLineNode = $$(-> @div className: 'line', style: 'position: absolute; visibility: hidden;', => @span 'x')[0]
|
||||
AcceptFilter = {acceptNode: -> NodeFilter.FILTER_ACCEPT}
|
||||
@@ -40,13 +39,6 @@ class LinesComponent
|
||||
insertionPoint.setAttribute('select', '.overlayer')
|
||||
@domNode.appendChild(insertionPoint)
|
||||
|
||||
insertionPoint = document.createElement('content')
|
||||
insertionPoint.setAttribute('select', 'atom-overlay')
|
||||
@overlayManager = new OverlayManager(@presenter, @hostElement)
|
||||
@domNode.appendChild(insertionPoint)
|
||||
else
|
||||
@overlayManager = new OverlayManager(@presenter, @domNode)
|
||||
|
||||
updateSync: (state) ->
|
||||
@newState = state.content
|
||||
@oldState ?= {lines: {}}
|
||||
@@ -82,8 +74,6 @@ class LinesComponent
|
||||
@cursorsComponent.updateSync(state)
|
||||
@highlightsComponent.updateSync(state)
|
||||
|
||||
@overlayManager?.render(state)
|
||||
|
||||
@oldState.indentGuidesVisible = @newState.indentGuidesVisible
|
||||
@oldState.scrollWidth = @newState.scrollWidth
|
||||
|
||||
|
||||
@@ -1,39 +1,44 @@
|
||||
module.exports =
|
||||
class OverlayManager
|
||||
constructor: (@presenter, @container) ->
|
||||
@overlayNodesById = {}
|
||||
@overlaysById = {}
|
||||
|
||||
render: (state) ->
|
||||
for decorationId, {pixelPosition, item} of state.content.overlays
|
||||
@renderOverlay(state, decorationId, item, pixelPosition)
|
||||
for decorationId, overlay of state.content.overlays
|
||||
if @shouldUpdateOverlay(decorationId, overlay)
|
||||
@renderOverlay(state, decorationId, overlay)
|
||||
|
||||
for id, overlayNode of @overlayNodesById
|
||||
for id, {overlayNode} of @overlaysById
|
||||
unless state.content.overlays.hasOwnProperty(id)
|
||||
delete @overlayNodesById[id]
|
||||
delete @overlaysById[id]
|
||||
overlayNode.remove()
|
||||
|
||||
return
|
||||
shouldUpdateOverlay: (decorationId, overlay) ->
|
||||
cachedOverlay = @overlaysById[decorationId]
|
||||
return true unless cachedOverlay?
|
||||
cachedOverlay.pixelPosition?.top isnt overlay.pixelPosition?.top or
|
||||
cachedOverlay.pixelPosition?.left isnt overlay.pixelPosition?.left
|
||||
|
||||
renderOverlay: (state, decorationId, item, pixelPosition) ->
|
||||
item = atom.views.getView(item)
|
||||
unless overlayNode = @overlayNodesById[decorationId]
|
||||
overlayNode = @overlayNodesById[decorationId] = document.createElement('atom-overlay')
|
||||
overlayNode.appendChild(item)
|
||||
measureOverlays: ->
|
||||
for decorationId, {itemView} of @overlaysById
|
||||
@measureOverlay(decorationId, itemView)
|
||||
|
||||
measureOverlay: (decorationId, itemView) ->
|
||||
contentMargin = parseInt(getComputedStyle(itemView)['margin-left']) ? 0
|
||||
@presenter.setOverlayDimensions(decorationId, itemView.offsetWidth, itemView.offsetHeight, contentMargin)
|
||||
|
||||
renderOverlay: (state, decorationId, {item, pixelPosition}) ->
|
||||
itemView = atom.views.getView(item)
|
||||
cachedOverlay = @overlaysById[decorationId]
|
||||
unless overlayNode = cachedOverlay?.overlayNode
|
||||
overlayNode = document.createElement('atom-overlay')
|
||||
@container.appendChild(overlayNode)
|
||||
@overlaysById[decorationId] = cachedOverlay = {overlayNode, itemView}
|
||||
|
||||
itemWidth = item.offsetWidth
|
||||
itemHeight = item.offsetHeight
|
||||
# The same node may be used in more than one overlay. This steals the node
|
||||
# back if it has been displayed in another overlay.
|
||||
overlayNode.appendChild(itemView) if overlayNode.childNodes.length == 0
|
||||
|
||||
|
||||
{scrollTop, scrollLeft} = state.content
|
||||
|
||||
left = pixelPosition.left
|
||||
if left + itemWidth - scrollLeft > @presenter.contentFrameWidth and left - itemWidth >= scrollLeft
|
||||
left -= itemWidth
|
||||
|
||||
top = pixelPosition.top + @presenter.lineHeight
|
||||
if top + itemHeight - scrollTop > @presenter.height and top - itemHeight - @presenter.lineHeight >= scrollTop
|
||||
top -= itemHeight + @presenter.lineHeight
|
||||
|
||||
overlayNode.style.top = top + 'px'
|
||||
overlayNode.style.left = left + 'px'
|
||||
cachedOverlay.pixelPosition = pixelPosition
|
||||
overlayNode.style.top = pixelPosition.top + 'px'
|
||||
overlayNode.style.left = pixelPosition.left + 'px'
|
||||
|
||||
@@ -11,6 +11,7 @@ InputComponent = require './input-component'
|
||||
LinesComponent = require './lines-component'
|
||||
ScrollbarComponent = require './scrollbar-component'
|
||||
ScrollbarCornerComponent = require './scrollbar-corner-component'
|
||||
OverlayManager = require './overlay-manager'
|
||||
|
||||
module.exports =
|
||||
class TextEditorComponent
|
||||
@@ -56,8 +57,14 @@ class TextEditorComponent
|
||||
@domNode = document.createElement('div')
|
||||
if @useShadowDOM
|
||||
@domNode.classList.add('editor-contents--private')
|
||||
|
||||
insertionPoint = document.createElement('content')
|
||||
insertionPoint.setAttribute('select', 'atom-overlay')
|
||||
@domNode.appendChild(insertionPoint)
|
||||
@overlayManager = new OverlayManager(@presenter, @hostElement)
|
||||
else
|
||||
@domNode.classList.add('editor-contents')
|
||||
@overlayManager = new OverlayManager(@presenter, @domNode)
|
||||
|
||||
@scrollViewNode = document.createElement('div')
|
||||
@scrollViewNode.classList.add('scroll-view')
|
||||
@@ -140,6 +147,8 @@ class TextEditorComponent
|
||||
@verticalScrollbarComponent.updateSync(@newState)
|
||||
@scrollbarCornerComponent.updateSync(@newState)
|
||||
|
||||
@overlayManager?.render(@newState)
|
||||
|
||||
if @editor.isAlive()
|
||||
@updateParentViewFocusedClassIfNeeded()
|
||||
@updateParentViewMiniClass()
|
||||
@@ -149,6 +158,7 @@ class TextEditorComponent
|
||||
|
||||
readAfterUpdateSync: =>
|
||||
@linesComponent.measureCharactersInNewLines() if @isVisible() and not @newState.content.scrollingVertically
|
||||
@overlayManager?.measureOverlays()
|
||||
|
||||
mountGutterComponent: ->
|
||||
@gutterComponent = new GutterComponent({@editor, onMouseDown: @onGutterMouseDown})
|
||||
@@ -159,7 +169,8 @@ class TextEditorComponent
|
||||
@measureScrollbars() if @measureScrollbarsWhenShown
|
||||
@sampleFontStyling()
|
||||
@sampleBackgroundColors()
|
||||
@measureHeightAndWidth()
|
||||
@measureWindowSize()
|
||||
@measureDimensions()
|
||||
@measureLineHeightAndDefaultCharWidth() if @measureLineHeightAndDefaultCharWidthWhenShown
|
||||
@remeasureCharacterWidths() if @remeasureCharacterWidthsWhenShown
|
||||
@editor.setVisible(true)
|
||||
@@ -556,8 +567,9 @@ class TextEditorComponent
|
||||
pollDOM: =>
|
||||
unless @checkForVisibilityChange()
|
||||
@sampleBackgroundColors()
|
||||
@measureHeightAndWidth()
|
||||
@measureDimensions()
|
||||
@sampleFontStyling()
|
||||
@overlayManager?.measureOverlays()
|
||||
|
||||
checkForVisibilityChange: ->
|
||||
if @isVisible()
|
||||
@@ -575,13 +587,14 @@ class TextEditorComponent
|
||||
@heightAndWidthMeasurementRequested = true
|
||||
requestAnimationFrame =>
|
||||
@heightAndWidthMeasurementRequested = false
|
||||
@measureHeightAndWidth()
|
||||
@measureDimensions()
|
||||
@measureWindowSize()
|
||||
|
||||
# Measure explicitly-styled height and width and relay them to the model. If
|
||||
# these values aren't explicitly styled, we assume the editor is unconstrained
|
||||
# and use the scrollHeight / scrollWidth as its height and width in
|
||||
# calculations.
|
||||
measureHeightAndWidth: ->
|
||||
measureDimensions: ->
|
||||
return unless @mounted
|
||||
|
||||
{position} = getComputedStyle(@hostElement)
|
||||
@@ -602,6 +615,12 @@ class TextEditorComponent
|
||||
if clientWidth > 0
|
||||
@presenter.setContentFrameWidth(clientWidth)
|
||||
|
||||
@presenter.setBoundingClientRect(@hostElement.getBoundingClientRect())
|
||||
|
||||
measureWindowSize: ->
|
||||
return unless @mounted
|
||||
@presenter.setWindowSize(window.innerWidth, window.innerHeight)
|
||||
|
||||
sampleFontStyling: =>
|
||||
oldFontSize = @fontSize
|
||||
oldFontFamily = @fontFamily
|
||||
|
||||
@@ -9,9 +9,10 @@ class TextEditorPresenter
|
||||
stoppedScrollingTimeoutId: null
|
||||
mouseWheelScreenRow: null
|
||||
scopedCharacterWidthsChangeCount: 0
|
||||
overlayDimensions: {}
|
||||
|
||||
constructor: (params) ->
|
||||
{@model, @autoHeight, @explicitHeight, @contentFrameWidth, @scrollTop, @scrollLeft} = params
|
||||
{@model, @autoHeight, @explicitHeight, @contentFrameWidth, @scrollTop, @scrollLeft, @boundingClientRect, @windowWidth, @windowHeight} = params
|
||||
{horizontalScrollbarHeight, verticalScrollbarWidth} = params
|
||||
{@lineHeight, @baseCharacterWidth, @lineOverdrawMargin, @backgroundColor, @gutterBackgroundColor} = params
|
||||
{@cursorBlinkPeriod, @cursorBlinkResumeDelay, @stoppedScrollingDelay, @focused} = params
|
||||
@@ -314,7 +315,7 @@ class TextEditorPresenter
|
||||
@emitDidUpdateState()
|
||||
|
||||
updateOverlaysState: -> @batch "shouldUpdateOverlaysState", ->
|
||||
return unless @hasPixelRectRequirements()
|
||||
return unless @hasOverlayPositionRequirements()
|
||||
|
||||
visibleDecorationIds = {}
|
||||
|
||||
@@ -327,13 +328,39 @@ class TextEditorPresenter
|
||||
else
|
||||
screenPosition = decoration.getMarker().getHeadScreenPosition()
|
||||
|
||||
pixelPosition = @pixelPositionForScreenPosition(screenPosition)
|
||||
|
||||
{scrollTop, scrollLeft} = @state.content
|
||||
gutterWidth = @boundingClientRect.width - @contentFrameWidth
|
||||
|
||||
top = pixelPosition.top + @lineHeight - scrollTop
|
||||
left = pixelPosition.left + gutterWidth - scrollLeft
|
||||
|
||||
if overlayDimensions = @overlayDimensions[decoration.id]
|
||||
{itemWidth, itemHeight, contentMargin} = overlayDimensions
|
||||
|
||||
rightDiff = left + @boundingClientRect.left + itemWidth + contentMargin - @windowWidth
|
||||
left -= rightDiff if rightDiff > 0
|
||||
|
||||
leftDiff = left + @boundingClientRect.left + contentMargin
|
||||
left -= leftDiff if leftDiff < 0
|
||||
|
||||
if top + @boundingClientRect.top + itemHeight > @windowHeight and top - (itemHeight + @lineHeight) >= 0
|
||||
top -= itemHeight + @lineHeight
|
||||
|
||||
pixelPosition.top = top
|
||||
pixelPosition.left = left
|
||||
|
||||
@state.content.overlays[decoration.id] ?= {item}
|
||||
@state.content.overlays[decoration.id].pixelPosition = @pixelPositionForScreenPosition(screenPosition)
|
||||
@state.content.overlays[decoration.id].pixelPosition = pixelPosition
|
||||
visibleDecorationIds[decoration.id] = true
|
||||
|
||||
for id of @state.content.overlays
|
||||
delete @state.content.overlays[id] unless visibleDecorationIds[id]
|
||||
|
||||
for id of @overlayDimensions
|
||||
delete @overlayDimensions[id] unless visibleDecorationIds[id]
|
||||
|
||||
return
|
||||
|
||||
updateGutterState: -> @batch "shouldUpdateGutterState", ->
|
||||
@@ -566,6 +593,7 @@ class TextEditorPresenter
|
||||
@updateLinesState()
|
||||
@updateCursorsState()
|
||||
@updateLineNumbersState()
|
||||
@updateOverlaysState()
|
||||
|
||||
didStartScrolling: ->
|
||||
if @stoppedScrollingTimeoutId?
|
||||
@@ -593,6 +621,7 @@ class TextEditorPresenter
|
||||
@updateHorizontalScrollState()
|
||||
@updateHiddenInputState()
|
||||
@updateCursorsState() unless oldScrollLeft?
|
||||
@updateOverlaysState()
|
||||
|
||||
setHorizontalScrollbarHeight: (horizontalScrollbarHeight) ->
|
||||
unless @measuredHorizontalScrollbarHeight is horizontalScrollbarHeight
|
||||
@@ -657,6 +686,24 @@ class TextEditorPresenter
|
||||
@updateLinesState()
|
||||
@updateCursorsState() unless oldContentFrameWidth?
|
||||
|
||||
setBoundingClientRect: (boundingClientRect) ->
|
||||
unless @clientRectsEqual(@boundingClientRect, boundingClientRect)
|
||||
@boundingClientRect = boundingClientRect
|
||||
@updateOverlaysState()
|
||||
|
||||
clientRectsEqual: (clientRectA, clientRectB) ->
|
||||
clientRectA? and clientRectB? and
|
||||
clientRectA.top is clientRectB.top and
|
||||
clientRectA.left is clientRectB.left and
|
||||
clientRectA.width is clientRectB.width and
|
||||
clientRectA.height is clientRectB.height
|
||||
|
||||
setWindowSize: (width, height) ->
|
||||
if @windowWidth isnt width or @windowHeight isnt height
|
||||
@windowWidth = width
|
||||
@windowHeight = height
|
||||
@updateOverlaysState()
|
||||
|
||||
setBackgroundColor: (backgroundColor) ->
|
||||
unless @backgroundColor is backgroundColor
|
||||
@backgroundColor = backgroundColor
|
||||
@@ -777,6 +824,9 @@ class TextEditorPresenter
|
||||
hasPixelRectRequirements: ->
|
||||
@hasPixelPositionRequirements() and @scrollWidth?
|
||||
|
||||
hasOverlayPositionRequirements: ->
|
||||
@hasPixelRectRequirements() and @boundingClientRect? and @windowWidth and @windowHeight
|
||||
|
||||
pixelRectForScreenRange: (screenRange) ->
|
||||
if screenRange.end.row > screenRange.start.row
|
||||
top = @pixelPositionForScreenPosition(screenRange.start).top
|
||||
@@ -994,6 +1044,18 @@ class TextEditorPresenter
|
||||
|
||||
regions
|
||||
|
||||
setOverlayDimensions: (decorationId, itemWidth, itemHeight, contentMargin) ->
|
||||
@overlayDimensions[decorationId] ?= {}
|
||||
overlayState = @overlayDimensions[decorationId]
|
||||
dimensionsAreEqual = overlayState.itemWidth is itemWidth and
|
||||
overlayState.itemHeight is itemHeight and
|
||||
overlayState.contentMargin is contentMargin
|
||||
unless dimensionsAreEqual
|
||||
overlayState.itemWidth = itemWidth
|
||||
overlayState.itemHeight = itemHeight
|
||||
overlayState.contentMargin = contentMargin
|
||||
@updateOverlaysState()
|
||||
|
||||
observeCursor: (cursor) ->
|
||||
didChangePositionDisposable = cursor.onDidChangePosition =>
|
||||
@updateHiddenInputState() if cursor.isLastCursor()
|
||||
|
||||
@@ -178,6 +178,9 @@ class ViewRegistry
|
||||
@documentPollers = @documentPollers.filter (poller) -> poller isnt fn
|
||||
@stopPollingDocument() if @documentPollers.length is 0
|
||||
|
||||
pollAfterNextUpdate: ->
|
||||
@performDocumentPollAfterUpdate = true
|
||||
|
||||
clearDocumentRequests: ->
|
||||
@documentReaders = []
|
||||
@documentWriters = []
|
||||
@@ -194,6 +197,7 @@ class ViewRegistry
|
||||
writer() while writer = @documentWriters.shift()
|
||||
reader() while reader = @documentReaders.shift()
|
||||
@performDocumentPoll() if @performDocumentPollAfterUpdate
|
||||
@performDocumentPollAfterUpdate = false
|
||||
|
||||
startPollingDocument: ->
|
||||
@pollIntervalHandle = window.setInterval(@performDocumentPoll, @documentPollingInterval)
|
||||
@@ -205,6 +209,5 @@ class ViewRegistry
|
||||
if @documentUpdateRequested
|
||||
@performDocumentPollAfterUpdate = true
|
||||
else
|
||||
@performDocumentPollAfterUpdate = false
|
||||
poller() for poller in @documentPollers
|
||||
return
|
||||
|
||||
@@ -24,7 +24,7 @@ atom-pane-container {
|
||||
display: -webkit-flex;
|
||||
-webkit-flex: 1;
|
||||
-webkit-flex-direction: column;
|
||||
overflow: hidden;
|
||||
overflow: visible;
|
||||
|
||||
.item-views {
|
||||
-webkit-flex: 1;
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
|
||||
.editor-contents--private {
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
cursor: text;
|
||||
display: -webkit-flex;
|
||||
-webkit-user-select: none;
|
||||
|
||||
@@ -28,3 +28,16 @@
|
||||
@syntax-color-modified: orange;
|
||||
@syntax-color-removed: red;
|
||||
@syntax-color-renamed: blue;
|
||||
|
||||
// For language entity colors
|
||||
@syntax-color-variable: #DF6A73;
|
||||
@syntax-color-constant: #DF6A73;
|
||||
@syntax-color-property: #DF6A73;
|
||||
@syntax-color-value: #D29B67;
|
||||
@syntax-color-function: #61AEEF;
|
||||
@syntax-color-method: @syntax-color-function;
|
||||
@syntax-color-class: #E5C17C;
|
||||
@syntax-color-keyword: #555;
|
||||
@syntax-color-tag: #555;
|
||||
@syntax-color-import: #97C378;
|
||||
@syntax-color-snippet: #97C378;
|
||||
|
||||
Reference in New Issue
Block a user