mirror of
https://github.com/atom/atom.git
synced 2026-01-24 14:28:14 -05:00
Merge branch 'master' into mb-fix-undo-stack-on-reload-again
This commit is contained in:
@@ -234,6 +234,7 @@ Please open an issue on `atom/atom` if you have suggestions for new labels, and
|
||||
| `installer` | [search][search-atom-repo-label-installer] | [search][search-atom-org-label-installer] | Related to the Atom installers for different OSes. |
|
||||
| `auto-updater` | [search][search-atom-repo-label-auto-updater] | [search][search-atom-org-label-auto-updater] | Related to the auto-updater for different OSes. |
|
||||
| `deprecation-help` | [search][search-atom-repo-label-deprecation-help] | [search][search-atom-org-label-deprecation-help] | Issues for helping package authors remove usage of deprecated APIs in packages. |
|
||||
| `electron` | [search][search-atom-repo-label-electron] | [search][search-atom-org-label-electron] | Issues that require changes to [Electron](https://electron.atom.io) to fix or implement. |
|
||||
|
||||
#### Core Team Project Management
|
||||
|
||||
@@ -329,6 +330,8 @@ Please open an issue on `atom/atom` if you have suggestions for new labels, and
|
||||
[search-atom-org-label-auto-updater]: https://github.com/issues?q=is%3Aopen+is%3Aissue+user%3Aatom+label%3Aauto-updater
|
||||
[search-atom-repo-label-deprecation-help]: https://github.com/issues?q=is%3Aopen+is%3Aissue+repo%3Aatom%2Fatom+label%3Adeprecation-help
|
||||
[search-atom-org-label-deprecation-help]: https://github.com/issues?q=is%3Aopen+is%3Aissue+user%3Aatom+label%3Adeprecation-help
|
||||
[search-atom-repo-label-electron]: https://github.com/issues?q=is%3Aissue+repo%3Aatom%2Fatom+is%3Aopen+label%3Aelectron
|
||||
[search-atom-org-label-electron]: https://github.com/issues?q=is%3Aopen+is%3Aissue+user%3Aatom+label%3Aelectron
|
||||
[search-atom-repo-label-on-deck]: https://github.com/issues?q=is%3Aopen+is%3Aissue+repo%3Aatom%2Fatom+label%3Aon-deck
|
||||
[search-atom-org-label-on-deck]: https://github.com/issues?q=is%3Aopen+is%3Aissue+user%3Aatom+label%3Aon-deck
|
||||
[search-atom-repo-label-in-progress]: https://github.com/issues?q=is%3Aopen+is%3Aissue+repo%3Aatom%2Fatom+label%3Ain-progress
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
"donna": "1.0.10",
|
||||
"formidable": "~1.0.14",
|
||||
"fs-plus": "2.x",
|
||||
"github-releases": "~0.2.0",
|
||||
"github-releases": "~0.3.0",
|
||||
"glob": "^5.0.14",
|
||||
"grunt": "~0.4.1",
|
||||
"grunt-babel": "^5.0.1",
|
||||
@@ -22,7 +22,7 @@
|
||||
"grunt-contrib-less": "~0.8.0",
|
||||
"grunt-cson": "0.15.0",
|
||||
"grunt-download-electron": "^2.1.1",
|
||||
"grunt-electron-installer": "1.0.0",
|
||||
"grunt-electron-installer": "1.0.3-0",
|
||||
"grunt-lesslint": "0.17.0",
|
||||
"grunt-peg": "~1.1.0",
|
||||
"grunt-shell": "~0.3.1",
|
||||
|
||||
@@ -24,6 +24,13 @@ module.exports = (grunt) ->
|
||||
return done("Unsupported arch #{process.arch}")
|
||||
|
||||
{name, version, description} = grunt.file.readJSON('package.json')
|
||||
|
||||
# RPM versions can't have dashes in them.
|
||||
# * http://www.rpm.org/max-rpm/ch-rpm-file-format.html
|
||||
# * https://github.com/mojombo/semver/issues/145
|
||||
version = version.replace(/-beta$/, "~beta")
|
||||
version = version.replace(/-dev$/, "~dev")
|
||||
|
||||
buildDir = grunt.config.get('atom.buildDir')
|
||||
|
||||
rpmDir = path.join(buildDir, 'rpm')
|
||||
|
||||
@@ -22,7 +22,7 @@ module.exports = (gruntObject) ->
|
||||
grunt.registerTask 'publish-build', 'Publish the built app', ->
|
||||
tasks = []
|
||||
tasks.push('build-docs', 'prepare-docs') if process.platform is 'darwin'
|
||||
tasks.push('upload-assets') if process.env.JANKY_SHA1 and process.env.JANKY_BRANCH is 'master'
|
||||
tasks.push('upload-assets')
|
||||
grunt.task.run(tasks)
|
||||
|
||||
grunt.registerTask 'prepare-docs', 'Move api.json to atom-api.json', ->
|
||||
@@ -31,6 +31,14 @@ module.exports = (gruntObject) ->
|
||||
cp path.join(docsOutputDir, 'api.json'), path.join(buildDir, 'atom-api.json')
|
||||
|
||||
grunt.registerTask 'upload-assets', 'Upload the assets to a GitHub release', ->
|
||||
switch process.env.JANKY_BRANCH
|
||||
when 'stable'
|
||||
isPrerelease = false
|
||||
when 'beta'
|
||||
isPrerelease = true
|
||||
else
|
||||
return
|
||||
|
||||
doneCallback = @async()
|
||||
startTime = Date.now()
|
||||
done = (args...) ->
|
||||
@@ -46,7 +54,7 @@ module.exports = (gruntObject) ->
|
||||
|
||||
zipAssets buildDir, assets, (error) ->
|
||||
return done(error) if error?
|
||||
getAtomDraftRelease (error, release) ->
|
||||
getAtomDraftRelease isPrerelease, (error, release) ->
|
||||
return done(error) if error?
|
||||
assetNames = (asset.assetName for asset in assets)
|
||||
deleteExistingAssets release, assetNames, (error) ->
|
||||
@@ -120,9 +128,9 @@ zipAssets = (buildDir, assets, callback) ->
|
||||
tasks.push(zip.bind(this, buildDir, sourcePath, assetName))
|
||||
async.parallel(tasks, callback)
|
||||
|
||||
getAtomDraftRelease = (callback) ->
|
||||
getAtomDraftRelease = (isPrerelease, callback) ->
|
||||
atomRepo = new GitHub({repo: 'atom/atom', token})
|
||||
atomRepo.getReleases (error, releases=[]) ->
|
||||
atomRepo.getReleases {prerelease: isPrerelease}, (error, releases=[]) ->
|
||||
if error?
|
||||
logError('Fetching atom/atom releases failed', error, releases)
|
||||
callback(error)
|
||||
@@ -142,9 +150,9 @@ getAtomDraftRelease = (callback) ->
|
||||
firstDraft.assets = assets
|
||||
callback(null, firstDraft)
|
||||
else
|
||||
createAtomDraftRelease(callback)
|
||||
createAtomDraftRelease(isPrerelease, callback)
|
||||
|
||||
createAtomDraftRelease = (callback) ->
|
||||
createAtomDraftRelease = (isPrerelease, callback) ->
|
||||
{version} = require('../../package.json')
|
||||
options =
|
||||
uri: 'https://api.github.com/repos/atom/atom/releases'
|
||||
@@ -152,6 +160,7 @@ createAtomDraftRelease = (callback) ->
|
||||
headers: defaultHeaders
|
||||
json:
|
||||
tag_name: "v#{version}"
|
||||
prerelease: isPrerelease
|
||||
name: version
|
||||
draft: true
|
||||
body: """
|
||||
|
||||
@@ -5,7 +5,7 @@ module.exports = (grunt) ->
|
||||
{spawn} = require('./task-helpers')(grunt)
|
||||
|
||||
getVersion = (callback) ->
|
||||
onBuildMachine = process.env.JANKY_SHA1 and process.env.JANKY_BRANCH is 'master'
|
||||
onBuildMachine = process.env.JANKY_SHA1 and process.env.JANKY_BRANCH in ['stable', 'beta']
|
||||
inRepository = fs.existsSync(path.resolve(__dirname, '..', '..', '.git'))
|
||||
{version} = require(path.join(grunt.config.get('atom.appDir'), 'package.json'))
|
||||
if onBuildMachine or not inRepository
|
||||
|
||||
@@ -14,19 +14,19 @@
|
||||
If it is installed elsewhere, you can create a symbolic link to the
|
||||
directory containing the python.exe using:
|
||||
`mklink /d %SystemDrive%\Python27 D:\elsewhere\Python27`
|
||||
* [GitHub for Windows](http://windows.github.com/)
|
||||
* [GitHub Desktop](http://desktop.github.com/)
|
||||
|
||||
### On Windows 8 or 10
|
||||
* [Visual Studio Express 2013 or 2015 for Windows Desktop](http://www.visualstudio.com/en-us/downloads/download-visual-studio-vs#DownloadFamilies_2)
|
||||
* [node.js](http://nodejs.org/download/) (0.10.x or 0.12.x) or [io.js](https://iojs.org) (1.x or 2.x)
|
||||
* [Python](https://www.python.org/downloads/) v2.7.x (required by [node-gyp](https://github.com/TooTallNate/node-gyp))
|
||||
* [GitHub for Windows](http://windows.github.com/)
|
||||
* [GitHub Desktop](http://desktop.github.com/)
|
||||
|
||||
## Instructions
|
||||
|
||||
```bash
|
||||
# Use the `Git Shell` program which was installed by GitHub for Windows.
|
||||
# Also make sure that you are logged into GitHub for Windows.
|
||||
# Use the `Git Shell` program which was installed by GitHub Desktop.
|
||||
# Also make sure that you are logged into GitHub Desktop.
|
||||
cd C:\
|
||||
git clone https://github.com/atom/atom/
|
||||
cd atom
|
||||
@@ -40,15 +40,14 @@ We will assume the git shell for these instructions.
|
||||
* `--build-dir` - Build the application in this directory.
|
||||
* `--verbose` - Verbose mode. A lot more information output.
|
||||
|
||||
## Why do I have to use GitHub for Windows?
|
||||
## Why do I have to use GitHub Desktop?
|
||||
|
||||
You don't. You can use your existing Git! GitHub for Windows's Git Shell is just
|
||||
easier to set up.
|
||||
You don't. You can use your existing Git! GitHub Desktop's Git Shell is just easier to set up.
|
||||
|
||||
If you _prefer_ using your existing Git installation, make sure git's cmd directory is in your PATH env variable (e.g. `C:\Program Files (x86)\Git\cmd`) before you open your powershell or command window.
|
||||
Note that you may have to open your command window as administrator. For powershell that doesn't seem to always be the case, though.
|
||||
|
||||
If none of this works, do install Github for Windows and use its Git shell. Makes life easier.
|
||||
If none of this works, do install Github Desktop and use its Git shell. Makes life easier.
|
||||
|
||||
|
||||
## Troubleshooting
|
||||
@@ -59,7 +58,6 @@ If none of this works, do install Github for Windows and use its Git shell. Make
|
||||
* If you just installed node, you'll need to restart your computer before node is
|
||||
available on your Path.
|
||||
|
||||
|
||||
* `script/build` outputs only the Node and Python versions before returning
|
||||
|
||||
* Try moving the repository to `C:\atom`. Most likely, the path is too long.
|
||||
@@ -73,7 +71,7 @@ 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.'
|
||||
* `script/build` stops at installing runas with 'Failed at the runas@x.y.z install script.'
|
||||
|
||||
See the next item.
|
||||
|
||||
|
||||
16
package.json
16
package.json
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "atom",
|
||||
"productName": "Atom",
|
||||
"version": "1.0.12",
|
||||
"version": "1.0.12-dev",
|
||||
"description": "A hackable text editor for the 21st Century.",
|
||||
"main": "./src/browser/main.js",
|
||||
"repository": {
|
||||
@@ -92,7 +92,7 @@
|
||||
"dev-live-reload": "0.46.0",
|
||||
"encoding-selector": "0.21.0",
|
||||
"exception-reporting": "0.36.0",
|
||||
"find-and-replace": "0.180.0",
|
||||
"find-and-replace": "0.181.0",
|
||||
"fuzzy-finder": "0.88.0",
|
||||
"git-diff": "0.56.0",
|
||||
"go-to-line": "0.30.0",
|
||||
@@ -124,7 +124,7 @@
|
||||
"language-c": "0.47.1",
|
||||
"language-clojure": "0.16.0",
|
||||
"language-coffee-script": "0.41.0",
|
||||
"language-csharp": "0.9.0",
|
||||
"language-csharp": "0.10.0",
|
||||
"language-css": "0.34.0",
|
||||
"language-gfm": "0.81.0",
|
||||
"language-git": "0.10.0",
|
||||
@@ -132,20 +132,20 @@
|
||||
"language-html": "0.41.2",
|
||||
"language-hyperlink": "0.14.0",
|
||||
"language-java": "0.16.0",
|
||||
"language-javascript": "0.93.0",
|
||||
"language-javascript": "0.94.0",
|
||||
"language-json": "0.16.0",
|
||||
"language-less": "0.28.2",
|
||||
"language-make": "0.17.0",
|
||||
"language-mustache": "0.12.0",
|
||||
"language-objective-c": "0.15.0",
|
||||
"language-perl": "0.28.0",
|
||||
"language-php": "0.29.0",
|
||||
"language-perl": "0.29.0",
|
||||
"language-php": "0.30.0",
|
||||
"language-property-list": "0.8.0",
|
||||
"language-python": "0.40.0",
|
||||
"language-ruby": "0.57.0",
|
||||
"language-ruby": "0.58.0",
|
||||
"language-ruby-on-rails": "0.22.0",
|
||||
"language-sass": "0.41.0",
|
||||
"language-shellscript": "0.16.0",
|
||||
"language-shellscript": "0.17.0",
|
||||
"language-source": "0.9.0",
|
||||
"language-sql": "0.17.0",
|
||||
"language-text": "0.7.0",
|
||||
|
||||
89
script/railcar
Executable file
89
script/railcar
Executable file
@@ -0,0 +1,89 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
var fs = require('fs')
|
||||
var exec = require('child_process').exec
|
||||
var series = require('async').series
|
||||
var semver = require('semver')
|
||||
|
||||
series([
|
||||
section('Preparing to roll the railcars'),
|
||||
checkCleanWorkingTree,
|
||||
run('git fetch origin master:master beta:beta stable:stable'),
|
||||
run('git fetch origin --tags'),
|
||||
|
||||
section('Updating stable branch'),
|
||||
run('git checkout stable'),
|
||||
run('git merge --ff-only origin/stable'),
|
||||
run('git merge --ff-only origin/beta'),
|
||||
bumpStableVersion,
|
||||
|
||||
section('Updating beta branch'),
|
||||
run('git checkout beta'),
|
||||
run('git merge --ff-only origin/beta'),
|
||||
run('git merge --ff-only origin/master'),
|
||||
run('git merge --strategy ours origin/stable'),
|
||||
bumpBetaVersion,
|
||||
|
||||
section('Updating master branch'),
|
||||
run('git checkout master'),
|
||||
run('git merge --ff-only origin/master'),
|
||||
run('git merge --strategy ours origin/beta'),
|
||||
bumpDevVersion,
|
||||
|
||||
section('Pushing changes upstream'),
|
||||
run('git push origin master:master beta:beta stable:stable'),
|
||||
run('git push origin --tags')
|
||||
], finish)
|
||||
|
||||
function checkCleanWorkingTree (next) {
|
||||
run('git status --porcelain')(function (error, output) {
|
||||
if (error) return next(error)
|
||||
if (output.trim().length > 0) return next(new Error('Cannot run the railcars with a dirty working tree'))
|
||||
next()
|
||||
})
|
||||
}
|
||||
|
||||
function bumpStableVersion (next) {
|
||||
run('npm version patch')(next)
|
||||
}
|
||||
|
||||
function bumpBetaVersion (next) {
|
||||
var newVersion = semver.inc(getCurrentVersion(), 'prerelease', 'beta')
|
||||
run('npm version ' + newVersion)(next)
|
||||
}
|
||||
|
||||
function bumpDevVersion (next) {
|
||||
var newVersion = semver.inc(getCurrentVersion(), 'preminor', 'dev')
|
||||
series([
|
||||
run('npm --no-git-tag-version version ' + newVersion),
|
||||
run('git commit -am "' + newVersion + '"')
|
||||
], next)
|
||||
}
|
||||
|
||||
function finish (error) {
|
||||
if (error) {
|
||||
console.log('Error: ' + error.message)
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
console.log('OK, now just wait for all CI builds to pass on beta and stable')
|
||||
}
|
||||
|
||||
function getCurrentVersion () {
|
||||
return JSON.parse(fs.readFileSync(require.resolve('../package.json'))).version
|
||||
}
|
||||
|
||||
function run (command) {
|
||||
return function (next) {
|
||||
console.log('>', command)
|
||||
exec(command, next)
|
||||
}
|
||||
}
|
||||
|
||||
function section (message) {
|
||||
return function (next) {
|
||||
console.log()
|
||||
console.log(message)
|
||||
next()
|
||||
}
|
||||
}
|
||||
@@ -108,7 +108,7 @@ describe "TextEditorComponent", ->
|
||||
component.measureDimensions()
|
||||
nextAnimationFrame()
|
||||
|
||||
tilesNodes = componentNode.querySelector(".lines").querySelectorAll(".tile")
|
||||
tilesNodes = component.tileNodesForLines()
|
||||
|
||||
expect(tilesNodes[0].style.zIndex).toBe("2")
|
||||
expect(tilesNodes[1].style.zIndex).toBe("1")
|
||||
@@ -118,7 +118,7 @@ describe "TextEditorComponent", ->
|
||||
verticalScrollbarNode.dispatchEvent(new UIEvent('scroll'))
|
||||
nextAnimationFrame()
|
||||
|
||||
tilesNodes = componentNode.querySelector(".lines").querySelectorAll(".tile")
|
||||
tilesNodes = component.tileNodesForLines()
|
||||
|
||||
expect(tilesNodes[0].style.zIndex).toBe("3")
|
||||
expect(tilesNodes[1].style.zIndex).toBe("2")
|
||||
@@ -130,7 +130,7 @@ describe "TextEditorComponent", ->
|
||||
component.measureDimensions()
|
||||
nextAnimationFrame()
|
||||
|
||||
tilesNodes = componentNode.querySelector(".lines").querySelectorAll(".tile")
|
||||
tilesNodes = component.tileNodesForLines()
|
||||
|
||||
expect(tilesNodes.length).toBe(3)
|
||||
|
||||
@@ -158,7 +158,7 @@ describe "TextEditorComponent", ->
|
||||
verticalScrollbarNode.dispatchEvent(new UIEvent('scroll'))
|
||||
nextAnimationFrame()
|
||||
|
||||
tilesNodes = componentNode.querySelector(".lines").querySelectorAll(".tile")
|
||||
tilesNodes = component.tileNodesForLines()
|
||||
|
||||
expect(component.lineNodeForScreenRow(2)).toBeUndefined()
|
||||
expect(tilesNodes.length).toBe(3)
|
||||
@@ -187,7 +187,7 @@ describe "TextEditorComponent", ->
|
||||
editor.getBuffer().deleteRows(0, 1)
|
||||
nextAnimationFrame()
|
||||
|
||||
tilesNodes = componentNode.querySelector(".lines").querySelectorAll(".tile")
|
||||
tilesNodes = component.tileNodesForLines()
|
||||
|
||||
expect(tilesNodes[0].style['-webkit-transform']).toBe "translate3d(0px, 0px, 0px)"
|
||||
expectTileContainsRow(tilesNodes[0], 0, top: 0 * lineHeightInPixels)
|
||||
@@ -202,7 +202,7 @@ describe "TextEditorComponent", ->
|
||||
editor.getBuffer().insert([0, 0], '\n\n')
|
||||
nextAnimationFrame()
|
||||
|
||||
tilesNodes = componentNode.querySelector(".lines").querySelectorAll(".tile")
|
||||
tilesNodes = component.tileNodesForLines()
|
||||
|
||||
expect(tilesNodes[0].style['-webkit-transform']).toBe "translate3d(0px, 0px, 0px)"
|
||||
expectTileContainsRow(tilesNodes[0], 0, top: 0 * lineHeightInPixels)
|
||||
@@ -294,7 +294,7 @@ describe "TextEditorComponent", ->
|
||||
editorFullWidth = editor.getScrollWidth() + editor.getVerticalScrollbarWidth()
|
||||
|
||||
for lineNode in lineNodes
|
||||
expect(lineNode.style.width).toBe editorFullWidth + 'px'
|
||||
expect(lineNode.getBoundingClientRect().width).toBe(editorFullWidth)
|
||||
|
||||
componentNode.style.width = gutterWidth + editor.getScrollWidth() + 100 + 'px'
|
||||
component.measureDimensions()
|
||||
@@ -302,7 +302,7 @@ describe "TextEditorComponent", ->
|
||||
scrollViewWidth = scrollViewNode.offsetWidth
|
||||
|
||||
for lineNode in lineNodes
|
||||
expect(lineNode.style.width).toBe scrollViewWidth + 'px'
|
||||
expect(lineNode.getBoundingClientRect().width).toBe(scrollViewWidth)
|
||||
|
||||
it "renders an nbsp on empty lines when no line-ending character is defined", ->
|
||||
atom.config.set("editor.showInvisibles", false)
|
||||
@@ -313,7 +313,7 @@ describe "TextEditorComponent", ->
|
||||
backgroundColor = getComputedStyle(wrapperNode).backgroundColor
|
||||
expect(linesNode.style.backgroundColor).toBe backgroundColor
|
||||
|
||||
for tileNode in linesNode.querySelectorAll(".tile")
|
||||
for tileNode in component.tileNodesForLines()
|
||||
expect(tileNode.style.backgroundColor).toBe(backgroundColor)
|
||||
|
||||
wrapperNode.style.backgroundColor = 'rgb(255, 0, 0)'
|
||||
@@ -322,7 +322,7 @@ describe "TextEditorComponent", ->
|
||||
advanceClock(atom.views.documentPollingInterval)
|
||||
nextAnimationFrame()
|
||||
expect(linesNode.style.backgroundColor).toBe 'rgb(255, 0, 0)'
|
||||
for tileNode in linesNode.querySelectorAll(".tile")
|
||||
for tileNode in component.tileNodesForLines()
|
||||
expect(tileNode.style.backgroundColor).toBe("rgb(255, 0, 0)")
|
||||
|
||||
|
||||
@@ -606,7 +606,7 @@ describe "TextEditorComponent", ->
|
||||
component.measureDimensions()
|
||||
nextAnimationFrame()
|
||||
|
||||
tilesNodes = componentNode.querySelector(".line-numbers").querySelectorAll(".tile")
|
||||
tilesNodes = component.tileNodesForLineNumbers()
|
||||
|
||||
expect(tilesNodes[0].style.zIndex).toBe("2")
|
||||
expect(tilesNodes[1].style.zIndex).toBe("1")
|
||||
@@ -616,33 +616,13 @@ describe "TextEditorComponent", ->
|
||||
verticalScrollbarNode.dispatchEvent(new UIEvent('scroll'))
|
||||
nextAnimationFrame()
|
||||
|
||||
tilesNodes = componentNode.querySelector(".line-numbers").querySelectorAll(".tile")
|
||||
tilesNodes = component.tileNodesForLineNumbers()
|
||||
|
||||
expect(tilesNodes[0].style.zIndex).toBe("3")
|
||||
expect(tilesNodes[1].style.zIndex).toBe("2")
|
||||
expect(tilesNodes[2].style.zIndex).toBe("1")
|
||||
expect(tilesNodes[3].style.zIndex).toBe("0")
|
||||
|
||||
it "renders higher line numbers in front of lower ones", ->
|
||||
wrapperNode.style.height = 6.5 * lineHeightInPixels + 'px'
|
||||
component.measureDimensions()
|
||||
nextAnimationFrame()
|
||||
|
||||
# Tile 0
|
||||
expect(component.lineNumberNodeForScreenRow(0).style.zIndex).toBe("2")
|
||||
expect(component.lineNumberNodeForScreenRow(1).style.zIndex).toBe("1")
|
||||
expect(component.lineNumberNodeForScreenRow(2).style.zIndex).toBe("0")
|
||||
|
||||
# Tile 1
|
||||
expect(component.lineNumberNodeForScreenRow(3).style.zIndex).toBe("2")
|
||||
expect(component.lineNumberNodeForScreenRow(4).style.zIndex).toBe("1")
|
||||
expect(component.lineNumberNodeForScreenRow(5).style.zIndex).toBe("0")
|
||||
|
||||
# Tile 2
|
||||
expect(component.lineNumberNodeForScreenRow(6).style.zIndex).toBe("2")
|
||||
expect(component.lineNumberNodeForScreenRow(7).style.zIndex).toBe("1")
|
||||
expect(component.lineNumberNodeForScreenRow(8).style.zIndex).toBe("0")
|
||||
|
||||
it "gives the line numbers container the same height as the wrapper node", ->
|
||||
linesNode = componentNode.querySelector(".line-numbers")
|
||||
|
||||
@@ -663,7 +643,7 @@ describe "TextEditorComponent", ->
|
||||
component.measureDimensions()
|
||||
nextAnimationFrame()
|
||||
|
||||
tilesNodes = componentNode.querySelector(".line-numbers").querySelectorAll(".tile")
|
||||
tilesNodes = component.tileNodesForLineNumbers()
|
||||
|
||||
expect(tilesNodes.length).toBe(3)
|
||||
expect(tilesNodes[0].style['-webkit-transform']).toBe "translate3d(0px, 0px, 0px)"
|
||||
@@ -689,7 +669,7 @@ describe "TextEditorComponent", ->
|
||||
verticalScrollbarNode.dispatchEvent(new UIEvent('scroll'))
|
||||
nextAnimationFrame()
|
||||
|
||||
tilesNodes = componentNode.querySelector(".line-numbers").querySelectorAll(".tile")
|
||||
tilesNodes = component.tileNodesForLineNumbers()
|
||||
|
||||
expect(component.lineNumberNodeForScreenRow(2)).toBeUndefined()
|
||||
expect(tilesNodes.length).toBe(3)
|
||||
@@ -791,7 +771,7 @@ describe "TextEditorComponent", ->
|
||||
lineNumbersNode = gutterNode.querySelector('.line-numbers')
|
||||
{backgroundColor} = getComputedStyle(wrapperNode)
|
||||
expect(lineNumbersNode.style.backgroundColor).toBe backgroundColor
|
||||
for tileNode in lineNumbersNode.querySelectorAll(".tile")
|
||||
for tileNode in component.tileNodesForLineNumbers()
|
||||
expect(tileNode.style.backgroundColor).toBe(backgroundColor)
|
||||
|
||||
# favor gutter color if it's assigned
|
||||
@@ -800,7 +780,7 @@ describe "TextEditorComponent", ->
|
||||
|
||||
nextAnimationFrame()
|
||||
expect(lineNumbersNode.style.backgroundColor).toBe 'rgb(255, 0, 0)'
|
||||
for tileNode in lineNumbersNode.querySelectorAll(".tile")
|
||||
for tileNode in component.tileNodesForLineNumbers()
|
||||
expect(tileNode.style.backgroundColor).toBe("rgb(255, 0, 0)")
|
||||
|
||||
it "hides or shows the gutter based on the '::isLineNumberGutterVisible' property on the model and the global 'editor.showLineNumbers' config setting", ->
|
||||
@@ -1133,7 +1113,7 @@ describe "TextEditorComponent", ->
|
||||
it "renders 2 regions for 2-line selections", ->
|
||||
editor.setSelectedScreenRange([[1, 6], [2, 10]])
|
||||
nextAnimationFrame()
|
||||
tileNode = componentNode.querySelector(".lines").querySelectorAll(".tile")[0]
|
||||
tileNode = component.tileNodesForLines()[0]
|
||||
regions = tileNode.querySelectorAll('.selection .region')
|
||||
expect(regions.length).toBe 2
|
||||
|
||||
@@ -1154,7 +1134,7 @@ describe "TextEditorComponent", ->
|
||||
nextAnimationFrame()
|
||||
|
||||
# Tile 0
|
||||
tileNode = componentNode.querySelector(".lines").querySelectorAll(".tile")[0]
|
||||
tileNode = component.tileNodesForLines()[0]
|
||||
regions = tileNode.querySelectorAll('.selection .region')
|
||||
expect(regions.length).toBe(3)
|
||||
|
||||
@@ -1177,7 +1157,7 @@ describe "TextEditorComponent", ->
|
||||
expect(region3Rect.right).toBe tileNode.getBoundingClientRect().right
|
||||
|
||||
# Tile 3
|
||||
tileNode = componentNode.querySelector(".lines").querySelectorAll(".tile")[1]
|
||||
tileNode = component.tileNodesForLines()[1]
|
||||
regions = tileNode.querySelectorAll('.selection .region')
|
||||
expect(regions.length).toBe(3)
|
||||
|
||||
@@ -2408,7 +2388,7 @@ describe "TextEditorComponent", ->
|
||||
component.measureDimensions()
|
||||
nextAnimationFrame()
|
||||
|
||||
tilesNodes = componentNode.querySelector(".lines").querySelectorAll(".tile")
|
||||
tilesNodes = component.tileNodesForLines()
|
||||
|
||||
top = 0
|
||||
for tileNode in tilesNodes
|
||||
|
||||
@@ -2035,15 +2035,10 @@ describe "TextEditorPresenter", ->
|
||||
lineNumberStateForScreenRow = (presenter, screenRow) ->
|
||||
editor = presenter.model
|
||||
tileRow = presenter.tileForRow(screenRow)
|
||||
bufferRow = editor.bufferRowForScreenRow(screenRow)
|
||||
wrapCount = screenRow - editor.screenRowForBufferRow(bufferRow)
|
||||
if wrapCount > 0
|
||||
key = bufferRow + '-' + wrapCount
|
||||
else
|
||||
key = bufferRow
|
||||
line = editor.tokenizedLineForScreenRow(screenRow)
|
||||
|
||||
gutterState = getLineNumberGutterState(presenter)
|
||||
gutterState.content.tiles[tileRow]?.lineNumbers[key]
|
||||
gutterState.content.tiles[tileRow]?.lineNumbers[line?.id]
|
||||
|
||||
tiledContentContract (presenter) -> getLineNumberGutterState(presenter).content
|
||||
|
||||
|
||||
35
spec/token-iterator-spec.coffee
Normal file
35
spec/token-iterator-spec.coffee
Normal file
@@ -0,0 +1,35 @@
|
||||
TextBuffer = require 'text-buffer'
|
||||
TokenizedBuffer = require '../src/tokenized-buffer'
|
||||
|
||||
describe "TokenIterator", ->
|
||||
it "correctly terminates scopes at the beginning of the line (regression)", ->
|
||||
grammar = atom.grammars.createGrammar('test', {
|
||||
'scopeName': 'text.broken'
|
||||
'name': 'Broken grammar'
|
||||
'patterns': [
|
||||
{
|
||||
'begin': 'start'
|
||||
'end': '(?=end)'
|
||||
'name': 'blue.broken'
|
||||
}
|
||||
{
|
||||
'match': '.'
|
||||
'name': 'yellow.broken'
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
buffer = new TextBuffer(text: """
|
||||
start x
|
||||
end x
|
||||
x
|
||||
""")
|
||||
tokenizedBuffer = new TokenizedBuffer({buffer})
|
||||
tokenizedBuffer.setGrammar(grammar)
|
||||
|
||||
tokenIterator = tokenizedBuffer.tokenizedLineForRow(1).getTokenIterator()
|
||||
tokenIterator.next()
|
||||
|
||||
expect(tokenIterator.getBufferStart()).toBe 0
|
||||
expect(tokenIterator.getScopeEnds()).toEqual []
|
||||
expect(tokenIterator.getScopeStarts()).toEqual ['text.broken', 'yellow.broken']
|
||||
@@ -9,7 +9,6 @@ class LineNumbersTileComponent
|
||||
constructor: ({@id}) ->
|
||||
@lineNumberNodesById = {}
|
||||
@domNode = document.createElement("div")
|
||||
@domNode.classList.add("tile")
|
||||
@domNode.style.position = "absolute"
|
||||
@domNode.style.display = "block"
|
||||
@domNode.style.top = 0 # Cover the space occupied by a dummy lineNumber
|
||||
@@ -75,28 +74,32 @@ class LineNumbersTileComponent
|
||||
newLineNumbersHTML += @buildLineNumberHTML(lineNumberState)
|
||||
@oldTileState.lineNumbers[id] = _.clone(lineNumberState)
|
||||
|
||||
if newLineNumberIds?
|
||||
WrapperDiv.innerHTML = newLineNumbersHTML
|
||||
newLineNumberNodes = _.toArray(WrapperDiv.children)
|
||||
return unless newLineNumberIds?
|
||||
|
||||
node = @domNode
|
||||
for id, i in newLineNumberIds
|
||||
lineNumberNode = newLineNumberNodes[i]
|
||||
@lineNumberNodesById[id] = lineNumberNode
|
||||
node.appendChild(lineNumberNode)
|
||||
WrapperDiv.innerHTML = newLineNumbersHTML
|
||||
newLineNumberNodes = _.toArray(WrapperDiv.children)
|
||||
|
||||
for id, i in newLineNumberIds
|
||||
lineNumberNode = newLineNumberNodes[i]
|
||||
@lineNumberNodesById[id] = lineNumberNode
|
||||
if nextNode = @findNodeNextTo(lineNumberNode)
|
||||
@domNode.insertBefore(lineNumberNode, nextNode)
|
||||
else
|
||||
@domNode.appendChild(lineNumberNode)
|
||||
|
||||
findNodeNextTo: (node) ->
|
||||
for nextNode in @domNode.children
|
||||
return nextNode if @screenRowForNode(node) < @screenRowForNode(nextNode)
|
||||
return
|
||||
|
||||
screenRowForNode: (node) -> parseInt(node.dataset.screenRow)
|
||||
|
||||
buildLineNumberHTML: (lineNumberState) ->
|
||||
{screenRow, bufferRow, softWrapped, top, decorationClasses, zIndex} = lineNumberState
|
||||
if screenRow?
|
||||
style = "position: absolute; top: #{top}px; z-index: #{zIndex};"
|
||||
else
|
||||
style = "visibility: hidden;"
|
||||
{screenRow, bufferRow, softWrapped, top, decorationClasses} = lineNumberState
|
||||
className = @buildLineNumberClassName(lineNumberState)
|
||||
innerHTML = @buildLineNumberInnerHTML(bufferRow, softWrapped)
|
||||
|
||||
"<div class=\"#{className}\" style=\"#{style}\" data-buffer-row=\"#{bufferRow}\" data-screen-row=\"#{screenRow}\">#{innerHTML}</div>"
|
||||
"<div class=\"#{className}\" data-buffer-row=\"#{bufferRow}\" data-screen-row=\"#{screenRow}\">#{innerHTML}</div>"
|
||||
|
||||
buildLineNumberInnerHTML: (bufferRow, softWrapped) ->
|
||||
{maxLineNumberDigits} = @newState
|
||||
@@ -119,18 +122,15 @@ class LineNumbersTileComponent
|
||||
oldLineNumberState.foldable = newLineNumberState.foldable
|
||||
oldLineNumberState.decorationClasses = _.clone(newLineNumberState.decorationClasses)
|
||||
|
||||
unless oldLineNumberState.top is newLineNumberState.top
|
||||
node.style.top = newLineNumberState.top + 'px'
|
||||
unless oldLineNumberState.screenRow is newLineNumberState.screenRow and oldLineNumberState.bufferRow is newLineNumberState.bufferRow
|
||||
node.innerHTML = @buildLineNumberInnerHTML(newLineNumberState.bufferRow, newLineNumberState.softWrapped)
|
||||
node.dataset.screenRow = newLineNumberState.screenRow
|
||||
oldLineNumberState.top = newLineNumberState.top
|
||||
node.dataset.bufferRow = newLineNumberState.bufferRow
|
||||
oldLineNumberState.screenRow = newLineNumberState.screenRow
|
||||
|
||||
unless oldLineNumberState.zIndex is newLineNumberState.zIndex
|
||||
node.style.zIndex = newLineNumberState.zIndex
|
||||
oldLineNumberState.zIndex = newLineNumberState.zIndex
|
||||
oldLineNumberState.bufferRow = newLineNumberState.bufferRow
|
||||
|
||||
buildLineNumberClassName: ({bufferRow, foldable, decorationClasses, softWrapped}) ->
|
||||
className = "line-number line-number-#{bufferRow}"
|
||||
className = "line-number"
|
||||
className += " " + decorationClasses.join(' ') if decorationClasses?
|
||||
className += " foldable" if foldable and not softWrapped
|
||||
className
|
||||
|
||||
@@ -21,7 +21,6 @@ class LinesTileComponent
|
||||
@screenRowsByLineId = {}
|
||||
@lineIdsByScreenRow = {}
|
||||
@domNode = document.createElement("div")
|
||||
@domNode.classList.add("tile")
|
||||
@domNode.style.position = "absolute"
|
||||
@domNode.style.display = "block"
|
||||
|
||||
@@ -110,10 +109,19 @@ class LinesTileComponent
|
||||
for id, i in newLineIds
|
||||
lineNode = newLineNodes[i]
|
||||
@lineNodesByLineId[id] = lineNode
|
||||
@domNode.appendChild(lineNode)
|
||||
if nextNode = @findNodeNextTo(lineNode)
|
||||
@domNode.insertBefore(lineNode, nextNode)
|
||||
else
|
||||
@domNode.appendChild(lineNode)
|
||||
|
||||
findNodeNextTo: (node) ->
|
||||
for nextNode, index in @domNode.children
|
||||
continue if index is 0 # skips highlights node
|
||||
return nextNode if @screenRowForNode(node) < @screenRowForNode(nextNode)
|
||||
return
|
||||
|
||||
screenRowForNode: (node) -> parseInt(node.dataset.screenRow)
|
||||
|
||||
buildLineHTML: (id) ->
|
||||
{width} = @newState
|
||||
{screenRow, tokens, text, top, lineEnding, fold, isSoftWrapped, indentLevel, decorationClasses} = @newTileState.lines[id]
|
||||
@@ -124,7 +132,7 @@ class LinesTileComponent
|
||||
classes += decorationClass + ' '
|
||||
classes += 'line'
|
||||
|
||||
lineHTML = "<div class=\"#{classes}\" style=\"position: absolute; top: #{top}px; width: #{width}px;\" data-screen-row=\"#{screenRow}\">"
|
||||
lineHTML = "<div class=\"#{classes}\" data-screen-row=\"#{screenRow}\">"
|
||||
|
||||
if text is ""
|
||||
lineHTML += @buildEmptyLineInnerHTML(id)
|
||||
@@ -284,9 +292,6 @@ class LinesTileComponent
|
||||
|
||||
lineNode = @lineNodesByLineId[id]
|
||||
|
||||
if @newState.width isnt @oldState.width
|
||||
lineNode.style.width = @newState.width + 'px'
|
||||
|
||||
newDecorationClasses = newLineState.decorationClasses
|
||||
oldDecorationClasses = oldLineState.decorationClasses
|
||||
|
||||
@@ -302,10 +307,6 @@ class LinesTileComponent
|
||||
|
||||
oldLineState.decorationClasses = newLineState.decorationClasses
|
||||
|
||||
if newLineState.top isnt oldLineState.top
|
||||
lineNode.style.top = newLineState.top + 'px'
|
||||
oldLineState.top = newLineState.top
|
||||
|
||||
if newLineState.screenRow isnt oldLineState.screenRow
|
||||
lineNode.dataset.screenRow = newLineState.screenRow
|
||||
oldLineState.screenRow = newLineState.screenRow
|
||||
|
||||
@@ -749,6 +749,13 @@ class TextEditorComponent
|
||||
|
||||
tileComponent?.lineNumberNodeForScreenRow(screenRow)
|
||||
|
||||
tileNodesForLines: ->
|
||||
@linesComponent.getTiles()
|
||||
|
||||
tileNodesForLineNumbers: ->
|
||||
gutterComponent = @gutterContainerComponent.getLineNumberGutterComponent()
|
||||
gutterComponent.getTiles()
|
||||
|
||||
screenRowForNode: (node) ->
|
||||
while node?
|
||||
if screenRow = node.dataset.screenRow
|
||||
|
||||
@@ -20,7 +20,7 @@ class TextEditorPresenter
|
||||
@measuredHorizontalScrollbarHeight = horizontalScrollbarHeight
|
||||
@measuredVerticalScrollbarWidth = verticalScrollbarWidth
|
||||
@gutterWidth ?= 0
|
||||
@tileSize ?= 12
|
||||
@tileSize ?= 6
|
||||
|
||||
@disposables = new CompositeDisposable
|
||||
@emitter = new Emitter
|
||||
@@ -584,22 +584,15 @@ class TextEditorPresenter
|
||||
if startRow > 0
|
||||
rowBeforeStartRow = startRow - 1
|
||||
lastBufferRow = @model.bufferRowForScreenRow(rowBeforeStartRow)
|
||||
wrapCount = rowBeforeStartRow - @model.screenRowForBufferRow(lastBufferRow)
|
||||
else
|
||||
lastBufferRow = null
|
||||
wrapCount = 0
|
||||
|
||||
if endRow > startRow
|
||||
bufferRows = @model.bufferRowsForScreenRows(startRow, endRow - 1)
|
||||
zIndex = bufferRows.length - 1
|
||||
for bufferRow, i in bufferRows
|
||||
if bufferRow is lastBufferRow
|
||||
wrapCount++
|
||||
id = bufferRow + '-' + wrapCount
|
||||
softWrapped = true
|
||||
else
|
||||
id = bufferRow
|
||||
wrapCount = 0
|
||||
lastBufferRow = bufferRow
|
||||
softWrapped = false
|
||||
|
||||
@@ -607,10 +600,10 @@ class TextEditorPresenter
|
||||
top = (screenRow - startRow) * @lineHeight
|
||||
decorationClasses = @lineNumberDecorationClassesForRow(screenRow)
|
||||
foldable = @model.isFoldableAtScreenRow(screenRow)
|
||||
id = @model.tokenizedLineForScreenRow(screenRow).id
|
||||
|
||||
tileState.lineNumbers[id] = {screenRow, bufferRow, softWrapped, top, decorationClasses, foldable, zIndex}
|
||||
tileState.lineNumbers[id] = {screenRow, bufferRow, softWrapped, top, decorationClasses, foldable}
|
||||
visibleLineNumberIds[id] = true
|
||||
zIndex--
|
||||
|
||||
for id of tileState.lineNumbers
|
||||
delete tileState.lineNumbers[id] unless visibleLineNumberIds[id]
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
{values} = require 'underscore-plus'
|
||||
|
||||
cloneObject = (object) ->
|
||||
clone = {}
|
||||
clone[key] = value for key, value of object
|
||||
@@ -49,3 +51,6 @@ class TiledComponent
|
||||
|
||||
getComponentForTile: (tileRow) ->
|
||||
@componentsByTileId[tileRow]
|
||||
|
||||
getTiles: ->
|
||||
values(@componentsByTileId).map (component) -> component.getDomNode()
|
||||
|
||||
@@ -31,11 +31,14 @@ class TokenIterator
|
||||
while @index < tags.length
|
||||
tag = tags[@index]
|
||||
if tag < 0
|
||||
scope = atom.grammars.scopeForId(tag)
|
||||
if tag % 2 is 0
|
||||
@scopeEnds.push(atom.grammars.scopeForId(tag + 1))
|
||||
if @scopeStarts[@scopeStarts.length - 1] is scope
|
||||
@scopeStarts.pop()
|
||||
else
|
||||
@scopeEnds.push(scope)
|
||||
@scopes.pop()
|
||||
else
|
||||
scope = atom.grammars.scopeForId(tag)
|
||||
@scopeStarts.push(scope)
|
||||
@scopes.push(scope)
|
||||
@index++
|
||||
|
||||
@@ -506,6 +506,15 @@ class Workspace extends Model
|
||||
#
|
||||
# Returns a {Disposable} on which `.dispose()` can be called to remove the
|
||||
# opener.
|
||||
#
|
||||
# Note that the opener will be called if and only if the URI is not already open
|
||||
# in the current pane. The searchAllPanes flag expands the search from the
|
||||
# current pane to all panes. If you wish to open a view of a different type for
|
||||
# a file that is already open, consider changing the protocol of the URI. For
|
||||
# example, perhaps you wish to preview a rendered version of the file `/foo/bar/baz.quux`
|
||||
# that is already open in a text editor view. You could signal this by calling
|
||||
# {Workspace::open} on the URI `quux-preview://foo/bar/baz.quux`. Then your opener
|
||||
# can check the protocol for quux-preview and only handle those URIs that match.
|
||||
addOpener: (opener) ->
|
||||
if includeDeprecatedAPIs
|
||||
packageName = @getCallingPackageName()
|
||||
|
||||
@@ -28,3 +28,4 @@
|
||||
@import "text";
|
||||
@import "utilities";
|
||||
@import "octicons";
|
||||
@import "cursors";
|
||||
|
||||
26
static/cursors.less
Normal file
26
static/cursors.less
Normal file
@@ -0,0 +1,26 @@
|
||||
@import "./variables/syntax-variables";
|
||||
@import "syntax-variables";
|
||||
|
||||
@import "./variables/ui-variables";
|
||||
@import "ui-variables";
|
||||
|
||||
@ibeam-1x: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAQAAAC1+jfqAAAAL0lEQVQoz2NgCD3x//9/BhBYBWdhgFVAiVW4JBFKGIa4AqD0//9D3pt4I4tAdAMAHTQ/j5Zom30AAAAASUVORK5CYII=');
|
||||
@ibeam-2x: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAQAAADZc7J/AAAAz0lEQVRIx2NgYGBY/R8I/vx5eelX3n82IJ9FxGf6tksvf/8FiTMQAcAGQMDvSwu09abffY8QYSAScNk45G198eX//yev73/4///701eh//kZSARckrNBRvz//+8+6ZohwCzjGNjdgQxkAg7B9WADeBjIBqtJCbhRA0YNoIkBSNmaPEMoNmA0FkYNoFKhapJ6FGyAH3nauaSmPfwI0v/3OukVi0CIZ+F25KrtYcx/CTIy0e+rC7R1Z4KMICVTQQ14feVXIbR695u14+Ir4gwAAD49E54wc1kWAAAAAElFTkSuQmCC');
|
||||
|
||||
.cursor-white() {
|
||||
cursor: -webkit-image-set(@ibeam-1x 1x, @ibeam-2x 2x) 5 8, text;
|
||||
}
|
||||
|
||||
// Editors
|
||||
& when ( lightness(@syntax-background-color) < 50% ) {
|
||||
.platform-darwin atom-text-editor:not([mini])::shadow .editor-contents--private {
|
||||
.cursor-white();
|
||||
}
|
||||
}
|
||||
|
||||
// Mini Editors
|
||||
& when ( lightness(@input-background-color) < 50% ) {
|
||||
.platform-darwin atom-text-editor[mini]::shadow .editor-contents--private {
|
||||
.cursor-white();
|
||||
}
|
||||
}
|
||||
@@ -46,6 +46,7 @@ atom-text-editor {
|
||||
}
|
||||
|
||||
.line-number {
|
||||
position: relative;
|
||||
white-space: nowrap;
|
||||
padding-left: .5em;
|
||||
opacity: 0.6;
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
}
|
||||
|
||||
.line-number {
|
||||
position: relative;
|
||||
white-space: nowrap;
|
||||
padding-left: .5em;
|
||||
opacity: 0.6;
|
||||
|
||||
Reference in New Issue
Block a user