Comparar commits
178 Commits
| Autor | SHA1 | Data | |
|---|---|---|---|
| 4155c53bd6 | |||
| d3538502c5 | |||
| 90a17de8bd | |||
| a82ea86a05 | |||
| 22c4992788 | |||
| 2a5d034248 | |||
| ed3c37c101 | |||
| 558e5e52c5 | |||
| 92d16a9d0a | |||
| f224a6d5f0 | |||
| 16f95a1420 | |||
| 0231d02877 | |||
| 6d2b70b3d9 | |||
| 49b825aeb3 | |||
| 357299a700 | |||
| 88df674dd6 | |||
| 31afa0abd5 | |||
| b11accec6d | |||
| defa869d5c | |||
| 8d4be6ab57 | |||
| 9976166902 | |||
| 8ea277ef77 | |||
| d85b8bfaf3 | |||
| d9e15d937e | |||
| 908ff5d3cd | |||
| 35b4ba3066 | |||
| f90e47daef | |||
| 5440dd68a7 | |||
| 11205d9eaa | |||
| 34e37fce7c | |||
| 3402c8dcd1 | |||
| 3921a63f67 | |||
| 5ecaf5dfc4 | |||
| 475f92351f | |||
| 0a5b378425 | |||
| 157774f552 | |||
| 320c12134a | |||
| 280b451835 | |||
| 4ee03fe590 | |||
| db1652f1ed | |||
| 8013ff7775 | |||
| c24475c2c8 | |||
| 580c639265 | |||
| 4c33549371 | |||
| 7be5553ba1 | |||
| 43936a1faf | |||
| e3c44bf551 | |||
| 6b0636d331 | |||
| cb0ee735be | |||
| 036dc06bac | |||
| 7155ec4b73 | |||
| 86ea4d94bb | |||
| 6a2021ac98 | |||
| 6fe05064eb | |||
| dd5c9ff6d4 | |||
| 29179d0bbc | |||
| f6400a4097 | |||
| e170b9f56b | |||
| 2ea8418c66 | |||
| c20a4a3084 | |||
| 0be64f9902 | |||
| 5300fefef4 | |||
| 2602e6ec0a | |||
| 1a22fc3c68 | |||
| 6d02861f11 | |||
| 57ed190ea3 | |||
| 436d7de817 | |||
| a9feed2e4a | |||
| 8b04e94d09 | |||
| 5c5576c39d | |||
| 2b957beeda | |||
| 832aeffd4f | |||
| 5d08ecdcb2 | |||
| 997529774c | |||
| 48d20ff1ec | |||
| 0793f291d1 | |||
| 8d479328ec | |||
| 569326e76a | |||
| 8372b84e9f | |||
| eda55156e5 | |||
| c40a526302 | |||
| 540b038ced | |||
| ce1ebec253 | |||
| 44e121c997 | |||
| 3579404bed | |||
| 1e19860409 | |||
| eab4b578a3 | |||
| 2878196e0a | |||
| 1c037411e9 | |||
| 68144681b2 | |||
| 484a516bd6 | |||
| fa6e84415b | |||
| 7117634ba8 | |||
| a108c283cf | |||
| 3dc61f4a7a | |||
| 9685e3f1db | |||
| b2c70f9e69 | |||
| d7a3ffa9de | |||
| 18ed91a402 | |||
| e991b3d10c | |||
| f1b7f9ca30 | |||
| e1e510e473 | |||
| d042d15a50 | |||
| fe9fec733d | |||
| 06095e57d7 | |||
| b83f908e28 | |||
| fad2a63a14 | |||
| 2de42303d4 | |||
| a7aed07d70 | |||
| 673b62f547 | |||
| 19835f2f66 | |||
| 7474de8f7e | |||
| 84c30ef6c5 | |||
| 0aa5fa9eeb | |||
| b5f8b159fc | |||
| 79357be899 | |||
| 12d6a90ddc | |||
| 4eb3be6f17 | |||
| af622c6b74 | |||
| df8e0a8464 | |||
| a1c1879ba6 | |||
| d1e52d4105 | |||
| 0f01840e3e | |||
| e1945fce14 | |||
| e6ecf10616 | |||
| b85ebbad2a | |||
| 38ba96a54f | |||
| 334e2ef7ab | |||
| 58efeb8a20 | |||
| 3ae6540c70 | |||
| 497ac5e5ce | |||
| 8735780473 | |||
| 888b5ab098 | |||
| c897f42d51 | |||
| 3c5312e834 | |||
| c59cbb3b25 | |||
| b3da11edfb | |||
| f56b487935 | |||
| 840abd6780 | |||
| 2e554ac819 | |||
| 35c7bc0eef | |||
| d99a9b0f3f | |||
| 7ac8b80172 | |||
| 68f2bd56f0 | |||
| e709b986cd | |||
| 74bdd5f0e8 | |||
| 48b6c24882 | |||
| e5f800ef35 | |||
| 492022fdd8 | |||
| cf7b87842e | |||
| 1838ff2502 | |||
| 505bfc28db | |||
| abbe8d2eec | |||
| bf33d96899 | |||
| a56b5eef2f | |||
| 80eb31679f | |||
| 54039e9d3b | |||
| 92c28fc44f | |||
| bc67efb72b | |||
| 0ee4d864be | |||
| 1d724339d6 | |||
| ce90b72807 | |||
| 4f356121d7 | |||
| 7b19152a58 | |||
| 15da69287e | |||
| 8d8db5142f | |||
| 6d7881bcfe | |||
| c2b7955ec6 | |||
| cc073ae462 | |||
| 3348c0e75a | |||
| 376a850507 | |||
| 168c6cdbca | |||
| 9ece33dbfe | |||
| 942041f214 | |||
| 260be2e096 | |||
| 6c8b4de986 | |||
| 9b267728d0 | |||
| 126d0d1b3c |
+12
@@ -6,10 +6,21 @@ Visit [atom.io](https://atom.io) to learn more.
|
||||
|
||||
## Installing
|
||||
|
||||
### Mac OS X
|
||||
|
||||
Download the latest [Atom release](https://github.com/atom/atom/releases/latest).
|
||||
|
||||
Atom will automatically update when a new release is available.
|
||||
|
||||
### Windows
|
||||
|
||||
Install the [Atom chocolatey package](https://chocolatey.org/packages/Atom).
|
||||
|
||||
1. Install [chocolatey](https://chocolatey.org).
|
||||
2. Close and reopen your command prompt or PowerShell window.
|
||||
3. Run `cinst Atom`
|
||||
4. In the future run `cup Atom` to upgrade to the latest release.
|
||||
|
||||
## Building
|
||||
|
||||
* [Linux](docs/build-instructions/linux.md)
|
||||
@@ -18,4 +29,5 @@ Atom will automatically update when a new release is available.
|
||||
* [Windows](docs/build-instructions/windows.md)
|
||||
|
||||
## Developing
|
||||
|
||||
Check out the [guides](https://atom.io/docs/latest) and the [API reference](https://atom.io/docs/api).
|
||||
|
||||
+1
-1
@@ -6,6 +6,6 @@
|
||||
"url": "https://github.com/atom/atom.git"
|
||||
},
|
||||
"dependencies": {
|
||||
"atom-package-manager": "0.70.0"
|
||||
"atom-package-manager": "0.77.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,14 +47,17 @@ module.exports = (grunt) ->
|
||||
contentsDir = shellAppDir
|
||||
appDir = path.join(shellAppDir, 'resources', 'app')
|
||||
installDir ?= path.join(process.env.ProgramFiles, appName)
|
||||
killCommand = 'taskkill /F /IM atom.exe'
|
||||
else if process.platform is 'darwin'
|
||||
contentsDir = path.join(shellAppDir, 'Contents')
|
||||
appDir = path.join(contentsDir, 'Resources', 'app')
|
||||
installDir ?= path.join('/Applications', appName)
|
||||
killCommand = 'pkill -9 Atom'
|
||||
else
|
||||
contentsDir = shellAppDir
|
||||
appDir = path.join(shellAppDir, 'resources', 'app')
|
||||
installDir ?= process.env.INSTALL_PREFIX ? '/usr/local'
|
||||
killCommand ='pkill -9 Atom'
|
||||
|
||||
coffeeConfig =
|
||||
glob_to_multiple:
|
||||
@@ -213,7 +216,7 @@ module.exports = (grunt) ->
|
||||
|
||||
shell:
|
||||
'kill-atom':
|
||||
command: 'pkill -9 Atom'
|
||||
command: killCommand
|
||||
options:
|
||||
stdout: false
|
||||
stderr: false
|
||||
|
||||
@@ -51,7 +51,17 @@ module.exports = (grunt) ->
|
||||
path.join('pegjs', 'examples')
|
||||
path.join('plist', 'tests')
|
||||
path.join('xmldom', 'test')
|
||||
path.join('combined-stream', 'test')
|
||||
path.join('delayed-stream', 'test')
|
||||
path.join('domhandler', 'test')
|
||||
path.join('fstream-ignore', 'test')
|
||||
path.join('harmony-collections', 'test')
|
||||
path.join('lru-cache', 'test')
|
||||
path.join('minimatch', 'test')
|
||||
path.join('normalize-package-data', 'test')
|
||||
path.join('npm', 'test')
|
||||
path.join('jasmine-reporters', 'ext')
|
||||
path.join('jasmine-node', 'node_modules', 'gaze')
|
||||
path.join('build', 'Release', 'obj.target')
|
||||
path.join('build', 'Release', 'obj')
|
||||
path.join('build', 'Release', '.deps')
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
path = require 'path'
|
||||
|
||||
module.exports = (grunt) ->
|
||||
{spawn} = require('./task-helpers')(grunt)
|
||||
|
||||
grunt.registerTask 'codesign', 'Codesign the app', ->
|
||||
return unless process.platform is 'darwin'
|
||||
|
||||
done = @async()
|
||||
|
||||
if process.env.XCODE_KEYCHAIN
|
||||
if process.platform is 'darwin' and process.env.XCODE_KEYCHAIN
|
||||
unlockKeychain (error) ->
|
||||
if error?
|
||||
done(error)
|
||||
@@ -22,6 +22,15 @@ module.exports = (grunt) ->
|
||||
spawn {cmd, args}, (error) -> callback(error)
|
||||
|
||||
signApp = (callback) ->
|
||||
cmd = 'codesign'
|
||||
args = ['-f', '-v', '-s', 'Developer ID Application: GitHub', grunt.config.get('atom.shellAppDir')]
|
||||
spawn {cmd, args}, (error) -> callback(error)
|
||||
switch process.platform
|
||||
when 'darwin'
|
||||
cmd = 'codesign'
|
||||
args = ['-f', '-v', '-s', 'Developer ID Application: GitHub', grunt.config.get('atom.shellAppDir')]
|
||||
spawn {cmd, args}, (error) -> callback(error)
|
||||
when 'win32'
|
||||
spawn {cmd: 'taskkill', args: ['/F', '/IM', 'atom.exe']}, ->
|
||||
cmd = process.env.JANKY_SIGNTOOL ? 'signtool'
|
||||
args = [path.join(grunt.config.get('atom.shellAppDir'), 'atom.exe')]
|
||||
spawn {cmd, args}, (error) -> callback(error)
|
||||
else
|
||||
callback()
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
path = require 'path'
|
||||
|
||||
module.exports = (grunt) ->
|
||||
grunt.registerTask 'output-long-paths', 'Log long paths in the built application', ->
|
||||
shellAppDir = grunt.config.get('atom.shellAppDir')
|
||||
|
||||
longPaths = []
|
||||
grunt.file.recurse shellAppDir, (absolutePath, rootPath, relativePath, fileName) ->
|
||||
if relativePath
|
||||
fullPath = path.join(relativePath, fileName)
|
||||
else
|
||||
fullPath = fileName
|
||||
longPaths.push(fullPath) if fullPath.length >= 200
|
||||
|
||||
longPaths.sort (longPath1, longPath2) -> longPath2.length - longPath1.length
|
||||
|
||||
longPaths.forEach (longPath) ->
|
||||
grunt.log.error "#{longPath.length} character path: #{longPath}"
|
||||
@@ -53,9 +53,7 @@ module.exports = (grunt) ->
|
||||
continue unless isAtomPackage(packagePath)
|
||||
packageSpecQueue.push(packagePath)
|
||||
|
||||
# TODO: Restore concurrency on Windows
|
||||
packageSpecQueue.concurrency = 1 unless process.platform is 'win32'
|
||||
|
||||
packageSpecQueue.concurrency = 1
|
||||
packageSpecQueue.drain = -> callback(null, failedPackages)
|
||||
|
||||
runCoreSpecs = (callback) ->
|
||||
@@ -105,4 +103,9 @@ module.exports = (grunt) ->
|
||||
failures.push "atom core" if coreSpecFailed
|
||||
|
||||
grunt.log.error("[Error]".red + " #{failures.join(', ')} spec(s) failed") if failures.length > 0
|
||||
done(!coreSpecFailed and failedPackages.length == 0)
|
||||
|
||||
if process.platform is 'win32' and process.env.JANKY_SHA1
|
||||
# Package specs are still flaky on Windows CI
|
||||
done(!coreSpecFailed)
|
||||
else
|
||||
done(!coreSpecFailed and failedPackages.length == 0)
|
||||
|
||||
+37
-8
@@ -1,4 +1,4 @@
|
||||
## Atom.io package and update API
|
||||
# Atom.io package and update API
|
||||
|
||||
This guide describes the web API used by [apm](https://github.com/atom/apm) and
|
||||
Atom. The vast majority of use cases are met by the `apm` command-line tool,
|
||||
@@ -8,6 +8,8 @@ and making sure you have pushed your git tag. In fact, Atom itself shells out to
|
||||
uses `apm`, see the [PackageManager class](https://github.com/atom/settings-view/blob/master/lib/package-manager.coffee)
|
||||
in the `settings-view` package.
|
||||
|
||||
*This API should be considered pre-release and is subject to change (though significant breaking changes are unlikely).*
|
||||
|
||||
### Authorization
|
||||
|
||||
For calls to the API that require authentication, provide a valid token from your
|
||||
@@ -17,9 +19,11 @@ For calls to the API that require authentication, provide a valid token from you
|
||||
|
||||
All requests that take parameters require `application/json`.
|
||||
|
||||
## Resources
|
||||
# API Resources
|
||||
|
||||
### Packages
|
||||
## Packages
|
||||
|
||||
### Listing packages
|
||||
|
||||
#### GET /api/packages
|
||||
|
||||
@@ -40,6 +44,8 @@ Returns a list of all packages in the following format:
|
||||
]
|
||||
```
|
||||
|
||||
### Showing package details
|
||||
|
||||
#### GET /api/packages/:package_name
|
||||
|
||||
Returns package details and versions for a single package
|
||||
@@ -68,6 +74,8 @@ Returns:
|
||||
}
|
||||
```
|
||||
|
||||
### Creating a package
|
||||
|
||||
#### POST /api/packages
|
||||
|
||||
Create a new package; requires authentication.
|
||||
@@ -92,6 +100,7 @@ Returns:
|
||||
- The package.json at owner/repo isn't valid
|
||||
- **409** - A package by that name already exists
|
||||
|
||||
### Deleting a package
|
||||
|
||||
#### DELETE /api/packages/:package_name
|
||||
|
||||
@@ -103,6 +112,13 @@ Returns:
|
||||
- **400** - Repository is inaccessible
|
||||
- **401** - Unauthorized
|
||||
|
||||
### Renaming a package
|
||||
|
||||
Packages are renamed by publishing a new version with the name changed in `package.json`
|
||||
See [Creating a new package version](#creating-a-new-package-version) for details.
|
||||
|
||||
Requests made to the previous name will forward to the new name.
|
||||
|
||||
### Package Versions
|
||||
|
||||
#### GET /api/packages/:package_name/versions/:version_name
|
||||
@@ -144,7 +160,9 @@ Returns `package.json` with `dist` key added for e.g. tarball download:
|
||||
|
||||
#### POST /api/packages/:package_name/versions
|
||||
|
||||
Creates a new package version from a git tag; requires authentication.
|
||||
Creates a new package version from a git tag; requires authentication. If `rename`
|
||||
is not `true`, the `name` field in `package.json` *must* match the current package
|
||||
name.
|
||||
|
||||
#### Parameters
|
||||
|
||||
@@ -152,14 +170,15 @@ Creates a new package version from a git tag; requires authentication.
|
||||
that the version name will not be taken from the tag, but from the `version`
|
||||
key in the `package.json` file at that ref. The authenticating user *must* have
|
||||
access to the package repository.
|
||||
- **rename** - Boolean indicating whether this version contains a new name for the package.
|
||||
|
||||
#### Returns
|
||||
|
||||
- **201** - Successfully created. Returns created version.
|
||||
- **400** - Git tag not found / Repository inaccessible
|
||||
- **400** - Git tag not found / Repository inaccessible / package.json invalid
|
||||
- **409** - Version exists
|
||||
|
||||
### Delete a version
|
||||
### Deleting a version
|
||||
|
||||
#### DELETE /api/packages/:package_name/versions/:version_name
|
||||
|
||||
@@ -172,7 +191,9 @@ you'll need to increment the version when republishing.
|
||||
Returns 204 No Content
|
||||
|
||||
|
||||
### Stars
|
||||
## Stars
|
||||
|
||||
### Listing user stars
|
||||
|
||||
#### GET /api/users/:login/stars
|
||||
|
||||
@@ -186,18 +207,24 @@ List the authenticated user's starred packages; requires authentication.
|
||||
|
||||
Return value is similar to **GET /api/packages**
|
||||
|
||||
### Starring a package
|
||||
|
||||
#### POST /api/packages/:name/star
|
||||
|
||||
Star a package; requires authentication.
|
||||
|
||||
Returns a package.
|
||||
|
||||
### Unstarring a package
|
||||
|
||||
#### DELETE /api/packages/:name/star
|
||||
|
||||
Unstar a package; requires authentication.
|
||||
|
||||
Returns 204 No Content.
|
||||
|
||||
### Listing a package's stargazers
|
||||
|
||||
#### GET /api/packages/:name/stargazers
|
||||
|
||||
List the users that have starred a package.
|
||||
@@ -211,7 +238,9 @@ Returns a list of user objects:
|
||||
]
|
||||
```
|
||||
|
||||
### Atom updates
|
||||
## Atom updates
|
||||
|
||||
### Listing Atom updates
|
||||
|
||||
#### GET /api/updates
|
||||
|
||||
|
||||
@@ -169,7 +169,7 @@ For example, to change the color of the cursor, you could add the following
|
||||
rule to your _~/.atom/styles.less_ file:
|
||||
|
||||
```less
|
||||
.editor .cursor {
|
||||
.editor.is-focused .cursor {
|
||||
border-color: pink;
|
||||
}
|
||||
```
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
'shift-pageup': 'core:select-page-up'
|
||||
'shift-pagedown': 'core:select-page-down'
|
||||
'delete': 'core:delete'
|
||||
'shift-delete': 'core:delete'
|
||||
'shift-delete': 'core:cut'
|
||||
'pageup': 'core:page-up'
|
||||
'pagedown': 'core:page-down'
|
||||
'backspace': 'core:backspace'
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
'ctrl-s': 'core:save'
|
||||
'ctrl-S': 'core:save-as'
|
||||
'ctrl-w': 'core:close'
|
||||
'ctrl-f4': 'core:close'
|
||||
'ctrl-z': 'core:undo'
|
||||
'ctrl-y': 'core:redo'
|
||||
'ctrl-x': 'core:cut'
|
||||
@@ -40,7 +41,7 @@
|
||||
'shift-pageup': 'core:select-page-up'
|
||||
'shift-pagedown': 'core:select-page-down'
|
||||
'delete': 'core:delete'
|
||||
'shift-delete': 'core:delete'
|
||||
'shift-delete': 'core:cut'
|
||||
'pageup': 'core:page-up'
|
||||
'pagedown': 'core:page-down'
|
||||
'backspace': 'core:backspace'
|
||||
|
||||
+25
-25
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "atom",
|
||||
"productName": "Atom",
|
||||
"version": "0.109.0",
|
||||
"version": "0.115.0",
|
||||
"description": "A hackable text editor for the 21st Century.",
|
||||
"main": "./src/browser/main.js",
|
||||
"repository": {
|
||||
@@ -62,54 +62,54 @@
|
||||
"vm-compatibility-layer": "0.1.0"
|
||||
},
|
||||
"packageDependencies": {
|
||||
"atom-dark-syntax": "0.17.0",
|
||||
"atom-dark-ui": "0.30.0",
|
||||
"atom-light-syntax": "0.18.0",
|
||||
"atom-light-ui": "0.26.0",
|
||||
"base16-tomorrow-dark-theme": "0.17.0",
|
||||
"solarized-dark-syntax": "0.18.0",
|
||||
"solarized-light-syntax": "0.9.0",
|
||||
"atom-dark-syntax": "0.19.0",
|
||||
"atom-dark-ui": "0.32.0",
|
||||
"atom-light-syntax": "0.20.0",
|
||||
"atom-light-ui": "0.28.0",
|
||||
"base16-tomorrow-dark-theme": "0.19.0",
|
||||
"solarized-dark-syntax": "0.20.0",
|
||||
"solarized-light-syntax": "0.11.0",
|
||||
"archive-view": "0.33.0",
|
||||
"autocomplete": "0.28.0",
|
||||
"autoflow": "0.17.0",
|
||||
"autosave": "0.14.0",
|
||||
"background-tips": "0.15.0",
|
||||
"bookmarks": "0.26.0",
|
||||
"bookmarks": "0.27.0",
|
||||
"bracket-matcher": "0.48.0",
|
||||
"command-palette": "0.24.0",
|
||||
"deprecation-cop": "0.7.0",
|
||||
"dev-live-reload": "0.31.0",
|
||||
"exception-reporting": "0.18.0",
|
||||
"feedback": "0.33.0",
|
||||
"find-and-replace": "0.122.0",
|
||||
"fuzzy-finder": "0.56.0",
|
||||
"git-diff": "0.34.0",
|
||||
"find-and-replace": "0.126.0",
|
||||
"fuzzy-finder": "0.57.0",
|
||||
"git-diff": "0.36.0",
|
||||
"go-to-line": "0.23.0",
|
||||
"grammar-selector": "0.27.0",
|
||||
"image-view": "0.36.0",
|
||||
"keybinding-resolver": "0.18.0",
|
||||
"link": "0.24.0",
|
||||
"markdown-preview": "0.86.0",
|
||||
"metrics": "0.32.0",
|
||||
"markdown-preview": "0.93.0",
|
||||
"metrics": "0.33.0",
|
||||
"open-on-github": "0.29.0",
|
||||
"package-generator": "0.31.0",
|
||||
"release-notes": "0.32.0",
|
||||
"settings-view": "0.129.0",
|
||||
"release-notes": "0.35.0",
|
||||
"settings-view": "0.133.0",
|
||||
"snippets": "0.47.0",
|
||||
"spell-check": "0.38.0",
|
||||
"status-bar": "0.41.0",
|
||||
"styleguide": "0.29.0",
|
||||
"symbols-view": "0.59.0",
|
||||
"tabs": "0.42.0",
|
||||
"tabs": "0.44.0",
|
||||
"timecop": "0.21.0",
|
||||
"tree-view": "0.108.0",
|
||||
"tree-view": "0.109.0",
|
||||
"update-package-dependencies": "0.6.0",
|
||||
"welcome": "0.17.0",
|
||||
"whitespace": "0.23.0",
|
||||
"wrap-guide": "0.19.0",
|
||||
"whitespace": "0.24.0",
|
||||
"wrap-guide": "0.21.0",
|
||||
|
||||
"language-c": "0.21.0",
|
||||
"language-coffee-script": "0.23.0",
|
||||
"language-coffee-script": "0.25.0",
|
||||
"language-css": "0.17.0",
|
||||
"language-gfm": "0.42.0",
|
||||
"language-git": "0.9.0",
|
||||
@@ -117,21 +117,21 @@
|
||||
"language-html": "0.22.0",
|
||||
"language-hyperlink": "0.10.0",
|
||||
"language-java": "0.11.0",
|
||||
"language-javascript": "0.32.0",
|
||||
"language-javascript": "0.35.0",
|
||||
"language-json": "0.8.0",
|
||||
"language-less": "0.11.0",
|
||||
"language-less": "0.12.0",
|
||||
"language-make": "0.10.0",
|
||||
"language-objective-c": "0.11.0",
|
||||
"language-perl": "0.9.0",
|
||||
"language-php": "0.15.0",
|
||||
"language-property-list": "0.7.0",
|
||||
"language-python": "0.18.0",
|
||||
"language-ruby": "0.30.0",
|
||||
"language-ruby": "0.32.0",
|
||||
"language-ruby-on-rails": "0.15.0",
|
||||
"language-sass": "0.13.0",
|
||||
"language-shellscript": "0.8.0",
|
||||
"language-source": "0.7.0",
|
||||
"language-sql": "0.8.0",
|
||||
"language-sql": "0.9.0",
|
||||
"language-text": "0.6.0",
|
||||
"language-todo": "0.10.0",
|
||||
"language-toml": "0.12.0",
|
||||
|
||||
+26
-5
@@ -44,23 +44,44 @@ function bootstrap() {
|
||||
var apmInstallCommand = npmPath + npmFlags + 'install';
|
||||
var apmInstallOptions = {cwd: apmInstallPath};
|
||||
var moduleInstallCommand = apmPath + ' install' + apmFlags;
|
||||
var dedupeCommand = apmPath + ' dedupe' + apmFlags;
|
||||
var dedupeApmCommand = apmPath + ' dedupe' + apmFlags;
|
||||
var dedupeNpmCommand = npmPath + npmFlags + 'dedupe';
|
||||
|
||||
if (process.argv.indexOf('--no-quiet') === -1) {
|
||||
buildInstallCommand += ' --quiet';
|
||||
apmInstallCommand += ' --quiet';
|
||||
moduleInstallCommand += ' --quiet';
|
||||
dedupeCommand += ' --quiet';
|
||||
dedupeApmCommand += ' --quiet';
|
||||
dedupeNpmCommand += ' --quiet';
|
||||
buildInstallOptions.ignoreStdout = true;
|
||||
apmInstallOptions.ignoreStdout = true;
|
||||
}
|
||||
|
||||
// apm ships with 32-bit node so make sure its native modules are compiled
|
||||
// for a 32-bit target architecture
|
||||
if (process.env.JANKY_SHA1 && process.platform === 'win32')
|
||||
apmInstallCommand += ' --arch=ia32';
|
||||
|
||||
var commands = [
|
||||
{command: buildInstallCommand, message: 'Installing build modules...', options: buildInstallOptions},
|
||||
{command: apmInstallCommand, message: 'Installing apm...', options: apmInstallOptions},
|
||||
{
|
||||
command: buildInstallCommand,
|
||||
message: 'Installing build modules...',
|
||||
options: buildInstallOptions
|
||||
},
|
||||
{
|
||||
command: apmInstallCommand,
|
||||
message: 'Installing apm...',
|
||||
options: apmInstallOptions
|
||||
},
|
||||
apmPath + ' clean' + apmFlags,
|
||||
moduleInstallCommand,
|
||||
dedupeCommand + ' ' + packagesToDedupe.join(' '),
|
||||
dedupeApmCommand + ' ' + packagesToDedupe.join(' '),
|
||||
{
|
||||
command: dedupeNpmCommand + ' request',
|
||||
options: {
|
||||
cwd: path.resolve(__dirname, '..', 'apm', 'node_modules', 'atom-package-manager')
|
||||
}
|
||||
},
|
||||
];
|
||||
|
||||
process.chdir(path.dirname(__dirname));
|
||||
|
||||
@@ -535,3 +535,18 @@ describe "the `atom` global", ->
|
||||
expect(atom.isReleasedVersion()).toBe true
|
||||
version = '36b5518'
|
||||
expect(atom.isReleasedVersion()).toBe false
|
||||
|
||||
describe "window:update-available", ->
|
||||
it "is triggered when the auto-updater sends the update-downloaded event", ->
|
||||
updateAvailableHandler = jasmine.createSpy("update-available-handler")
|
||||
atom.workspaceView.on 'window:update-available', updateAvailableHandler
|
||||
autoUpdater = require('remote').require('auto-updater')
|
||||
autoUpdater.emit 'update-downloaded', null, "notes", "version"
|
||||
|
||||
waitsFor ->
|
||||
updateAvailableHandler.callCount > 0
|
||||
|
||||
runs ->
|
||||
[event, version, notes] = updateAvailableHandler.mostRecentCall.args
|
||||
expect(notes).toBe 'notes'
|
||||
expect(version).toBe 'version'
|
||||
|
||||
@@ -215,6 +215,19 @@ describe "DisplayBuffer", ->
|
||||
displayBuffer.setEditorWidthInChars(-1)
|
||||
expect(displayBuffer.editorWidthInChars).not.toBe -1
|
||||
|
||||
it "sets ::scrollLeft to 0 and keeps it there when soft wrapping is enabled", ->
|
||||
displayBuffer.setDefaultCharWidth(10)
|
||||
displayBuffer.setWidth(50)
|
||||
displayBuffer.manageScrollPosition = true
|
||||
|
||||
displayBuffer.setSoftWrap(false)
|
||||
displayBuffer.setScrollLeft(Infinity)
|
||||
expect(displayBuffer.getScrollLeft()).toBeGreaterThan 0
|
||||
displayBuffer.setSoftWrap(true)
|
||||
expect(displayBuffer.getScrollLeft()).toBe 0
|
||||
displayBuffer.setScrollLeft(10)
|
||||
expect(displayBuffer.getScrollLeft()).toBe 0
|
||||
|
||||
describe "primitive folding", ->
|
||||
beforeEach ->
|
||||
displayBuffer.destroy()
|
||||
@@ -329,8 +342,6 @@ describe "DisplayBuffer", ->
|
||||
fold2 = displayBuffer.createFold(4, 9)
|
||||
fold1 = displayBuffer.createFold(0, 4)
|
||||
|
||||
displayBuffer.logLines(0, 20)
|
||||
|
||||
expect(displayBuffer.lineForRow(0).text).toMatch /^0/
|
||||
expect(displayBuffer.lineForRow(1).text).toMatch /^10/
|
||||
|
||||
@@ -643,6 +654,19 @@ describe "DisplayBuffer", ->
|
||||
buffer.delete([[6, 0], [6, 65]])
|
||||
expect(displayBuffer.getMaxLineLength()).toBe 62
|
||||
|
||||
it "correctly updates the location of the longest screen line when changes occur", ->
|
||||
expect(displayBuffer.getLongestScreenRow()).toBe 6
|
||||
buffer.delete([[0, 0], [2, 0]])
|
||||
expect(displayBuffer.getLongestScreenRow()).toBe 4
|
||||
buffer.delete([[4, 0], [5, 0]])
|
||||
|
||||
expect(displayBuffer.getLongestScreenRow()).toBe 1
|
||||
expect(displayBuffer.getMaxLineLength()).toBe 62
|
||||
|
||||
buffer.delete([[2, 0], [4, 0]])
|
||||
expect(displayBuffer.getLongestScreenRow()).toBe 1
|
||||
expect(displayBuffer.getMaxLineLength()).toBe 62
|
||||
|
||||
describe "::destroy()", ->
|
||||
it "unsubscribes all display buffer markers from their underlying buffer marker (regression)", ->
|
||||
marker = displayBuffer.markBufferPosition([12, 2])
|
||||
@@ -1014,15 +1038,35 @@ describe "DisplayBuffer", ->
|
||||
expect(start.left).toBe (4 * 10) + (6 * 11)
|
||||
|
||||
describe "decorations", ->
|
||||
it "can add decorations associated with markers and remove them", ->
|
||||
decoration = {type: 'gutter', class: 'one'}
|
||||
[marker, decoration, decorationParams] = []
|
||||
beforeEach ->
|
||||
marker = displayBuffer.markBufferRange([[2, 13], [3, 15]])
|
||||
decorationParams = {type: 'gutter', class: 'one'}
|
||||
decoration = displayBuffer.decorateMarker(marker, decorationParams)
|
||||
|
||||
displayBuffer.addDecorationForMarker(marker, decoration)
|
||||
it "can add decorations associated with markers and remove them", ->
|
||||
expect(decoration).toBeDefined()
|
||||
expect(decoration.getParams()).toBe decorationParams
|
||||
expect(displayBuffer.decorationForId(decoration.id)).toBe decoration
|
||||
expect(displayBuffer.decorationsForScreenRowRange(2, 3)[marker.id][0]).toBe decoration
|
||||
|
||||
displayBuffer.removeDecorationForMarker(marker, decoration)
|
||||
decoration.destroy()
|
||||
expect(displayBuffer.decorationsForScreenRowRange(2, 3)[marker.id]).not.toBeDefined()
|
||||
expect(displayBuffer.decorationForId(decoration.id)).not.toBeDefined()
|
||||
|
||||
it "will not fail if the decoration is removed twice", ->
|
||||
decoration.destroy()
|
||||
decoration.destroy()
|
||||
expect(displayBuffer.decorationForId(decoration.id)).not.toBeDefined()
|
||||
|
||||
describe "when a decoration is updated via Decoration::update()", ->
|
||||
it "emits an 'updated' event containing the new and old params", ->
|
||||
decoration.on 'updated', updatedSpy = jasmine.createSpy()
|
||||
decoration.update type: 'gutter', class: 'two'
|
||||
|
||||
{oldParams, newParams} = updatedSpy.mostRecentCall.args[0]
|
||||
expect(oldParams).toEqual decorationParams
|
||||
expect(newParams).toEqual type: 'gutter', class: 'two', id: decoration.id
|
||||
|
||||
describe "::setScrollTop", ->
|
||||
beforeEach ->
|
||||
|
||||
@@ -232,6 +232,28 @@ describe "EditorComponent", ->
|
||||
runSetImmediateCallbacks()
|
||||
expect(component.lineNodeForScreenRow(0).textContent).toBe "a line that ends with a carriage return#{invisibles.cr}#{invisibles.eol}"
|
||||
|
||||
it "renders invisible line-ending characters on empty lines", ->
|
||||
expect(component.lineNodeForScreenRow(10).textContent).toBe invisibles.eol
|
||||
|
||||
it "interleaves invisible line-ending characters with indent guides on empty lines", ->
|
||||
component.setShowIndentGuide(true)
|
||||
editor.setTextInBufferRange([[10, 0], [11, 0]], "\r\n", false)
|
||||
runSetImmediateCallbacks()
|
||||
expect(component.lineNodeForScreenRow(10).innerHTML).toBe '<span class="indent-guide"><span class="invisible-character">C</span><span class="invisible-character">E</span></span>'
|
||||
|
||||
editor.setTabLength(3)
|
||||
runSetImmediateCallbacks()
|
||||
expect(component.lineNodeForScreenRow(10).innerHTML).toBe '<span class="indent-guide"><span class="invisible-character">C</span><span class="invisible-character">E</span> </span>'
|
||||
|
||||
editor.setTabLength(1)
|
||||
runSetImmediateCallbacks()
|
||||
expect(component.lineNodeForScreenRow(10).innerHTML).toBe '<span class="indent-guide"><span class="invisible-character">C</span></span><span class="indent-guide"><span class="invisible-character">E</span></span>'
|
||||
|
||||
editor.setTextInBufferRange([[9, 0], [9, Infinity]], ' ')
|
||||
editor.setTextInBufferRange([[11, 0], [11, Infinity]], ' ')
|
||||
runSetImmediateCallbacks()
|
||||
expect(component.lineNodeForScreenRow(10).innerHTML).toBe '<span class="indent-guide"><span class="invisible-character">C</span></span><span class="invisible-character">E</span>'
|
||||
|
||||
describe "when soft wrapping is enabled", ->
|
||||
beforeEach ->
|
||||
editor.setText "a line that wraps "
|
||||
@@ -269,13 +291,11 @@ describe "EditorComponent", ->
|
||||
|
||||
line2LeafNodes = getLeafNodes(component.lineNodeForScreenRow(2))
|
||||
|
||||
expect(line2LeafNodes.length).toBe 3
|
||||
expect(line2LeafNodes.length).toBe 2
|
||||
expect(line2LeafNodes[0].textContent).toBe ' '
|
||||
expect(line2LeafNodes[0].classList.contains('indent-guide')).toBe true
|
||||
expect(line2LeafNodes[1].textContent).toBe ' '
|
||||
expect(line2LeafNodes[1].classList.contains('indent-guide')).toBe true
|
||||
expect(line2LeafNodes[2].textContent).toBe ' '
|
||||
expect(line2LeafNodes[2].classList.contains('indent-guide')).toBe true
|
||||
|
||||
it "renders indent guides correctly on lines containing only whitespace", ->
|
||||
editor.getBuffer().insert([1, Infinity], '\n ')
|
||||
@@ -303,7 +323,7 @@ describe "EditorComponent", ->
|
||||
it "updates the indent guides on empty lines preceding an indentation change", ->
|
||||
editor.getBuffer().insert([12, 0], '\n')
|
||||
runSetImmediateCallbacks()
|
||||
editor.getBuffer().insert([13, 0], ' ')
|
||||
editor.getBuffer().insert([13, 0], ' ')
|
||||
runSetImmediateCallbacks()
|
||||
|
||||
line12LeafNodes = getLeafNodes(component.lineNodeForScreenRow(12))
|
||||
@@ -315,7 +335,7 @@ describe "EditorComponent", ->
|
||||
it "updates the indent guides on empty lines following an indentation change", ->
|
||||
editor.getBuffer().insert([12, 2], '\n')
|
||||
runSetImmediateCallbacks()
|
||||
editor.getBuffer().insert([12, 0], ' ')
|
||||
editor.getBuffer().insert([12, 0], ' ')
|
||||
runSetImmediateCallbacks()
|
||||
|
||||
line13LeafNodes = getLeafNodes(component.lineNodeForScreenRow(13))
|
||||
@@ -451,6 +471,7 @@ describe "EditorComponent", ->
|
||||
expect(component.refs.gutter).not.toBeDefined()
|
||||
atom.config.set("editor.showLineNumbers", true)
|
||||
expect(component.refs.gutter).toBeDefined()
|
||||
expect(component.lineNumberNodeForScreenRow(3)).toBeDefined()
|
||||
|
||||
describe "fold decorations", ->
|
||||
describe "rendering fold decorations", ->
|
||||
@@ -587,6 +608,33 @@ describe "EditorComponent", ->
|
||||
expect(cursorRect.left).toBe rangeRect.left
|
||||
expect(cursorRect.width).toBe rangeRect.width
|
||||
|
||||
it "positions cursors correctly after character widths are changed via a stylesheet change", ->
|
||||
atom.config.set('editor.fontFamily', 'sans-serif')
|
||||
editor.setCursorScreenPosition([0, 16])
|
||||
runSetImmediateCallbacks()
|
||||
|
||||
atom.themes.applyStylesheet 'test', """
|
||||
.function.js {
|
||||
font-weight: bold;
|
||||
}
|
||||
"""
|
||||
runSetImmediateCallbacks() # re-measure characters once for a synchronous set of stylesheet changes
|
||||
runSetImmediateCallbacks() # update based on new measurements
|
||||
|
||||
cursor = node.querySelector('.cursor')
|
||||
cursorRect = cursor.getBoundingClientRect()
|
||||
|
||||
cursorLocationTextNode = component.lineNodeForScreenRow(0).querySelector('.storage.type.function.js').firstChild
|
||||
range = document.createRange()
|
||||
range.setStart(cursorLocationTextNode, 0)
|
||||
range.setEnd(cursorLocationTextNode, 1)
|
||||
rangeRect = range.getBoundingClientRect()
|
||||
|
||||
expect(cursorRect.left).toBe rangeRect.left
|
||||
expect(cursorRect.width).toBe rangeRect.width
|
||||
|
||||
atom.themes.removeStylesheet('test')
|
||||
|
||||
it "sets the cursor to the default character width at the end of a line", ->
|
||||
editor.setCursorScreenPosition([0, Infinity])
|
||||
runSetImmediateCallbacks()
|
||||
@@ -743,13 +791,26 @@ describe "EditorComponent", ->
|
||||
expect(selectionNode.offsetTop).toBe editor.getLineHeightInPixels()
|
||||
expect(selectionNode.offsetLeft).toBe editor.pixelPositionForScreenPosition([1, 6]).left
|
||||
|
||||
it "will flash the selection when flash:true is passed to editor::setSelectedBufferRange", ->
|
||||
editor.setSelectedBufferRange([[1, 6], [1, 10]], flash: true)
|
||||
runSetImmediateCallbacks()
|
||||
selectionNode = node.querySelector('.selection')
|
||||
expect(selectionNode.classList.contains('flash')).toBe true
|
||||
|
||||
advanceClock editor.selectionFlashDuration
|
||||
expect(selectionNode.classList.contains('flash')).toBe false
|
||||
|
||||
editor.setSelectedBufferRange([[1, 5], [1, 7]], flash: true)
|
||||
runSetImmediateCallbacks()
|
||||
expect(selectionNode.classList.contains('flash')).toBe true
|
||||
|
||||
describe "line decoration rendering", ->
|
||||
[marker, decoration] = []
|
||||
[marker, decoration, decorationParams] = []
|
||||
|
||||
beforeEach ->
|
||||
marker = editor.displayBuffer.markBufferRange([[2, 13], [3, 15]], invalidate: 'inside')
|
||||
decoration = {type: ['gutter', 'line'], class: 'a'}
|
||||
editor.addDecorationForMarker(marker, decoration)
|
||||
decorationParams = {type: ['gutter', 'line'], class: 'a'}
|
||||
decoration = editor.decorateMarker(marker, decorationParams)
|
||||
runSetImmediateCallbacks()
|
||||
|
||||
it "applies line decoration classes to lines and line numbers", ->
|
||||
@@ -763,7 +824,7 @@ describe "EditorComponent", ->
|
||||
|
||||
# Add decorations that are out of range
|
||||
marker2 = editor.displayBuffer.markBufferRange([[9, 0], [9, 0]])
|
||||
editor.addDecorationForMarker(marker2, type: ['gutter', 'line'], class: 'b')
|
||||
editor.decorateMarker(marker2, type: ['gutter', 'line'], class: 'b')
|
||||
runSetImmediateCallbacks()
|
||||
|
||||
# Scroll decorations into view
|
||||
@@ -786,7 +847,7 @@ describe "EditorComponent", ->
|
||||
|
||||
marker.destroy()
|
||||
marker = editor.markBufferRange([[0, 0], [0, 2]])
|
||||
editor.addDecorationForMarker(marker, type: ['gutter', 'line'], class: 'b')
|
||||
editor.decorateMarker(marker, type: ['gutter', 'line'], class: 'b')
|
||||
runSetImmediateCallbacks()
|
||||
expect(lineNumberHasClass(0, 'b')).toBe true
|
||||
expect(lineNumberHasClass(1, 'b')).toBe false
|
||||
@@ -820,7 +881,7 @@ describe "EditorComponent", ->
|
||||
|
||||
it "remove decoration classes and unsubscribes from markers decorations are removed", ->
|
||||
expect(marker.getSubscriptionCount('changed'))
|
||||
editor.removeDecorationForMarker(marker, decoration)
|
||||
decoration.destroy()
|
||||
runSetImmediateCallbacks()
|
||||
expect(lineNumberHasClass(1, 'a')).toBe false
|
||||
expect(lineNumberHasClass(2, 'a')).toBe false
|
||||
@@ -855,7 +916,7 @@ describe "EditorComponent", ->
|
||||
|
||||
describe "when the decoration's 'onlyHead' property is true", ->
|
||||
it "only applies the decoration's class to lines containing the marker's head", ->
|
||||
editor.addDecorationForMarker(marker, type: ['gutter', 'line'], class: 'only-head', onlyHead: true)
|
||||
editor.decorateMarker(marker, type: ['gutter', 'line'], class: 'only-head', onlyHead: true)
|
||||
runSetImmediateCallbacks()
|
||||
expect(lineAndLineNumberHaveClass(1, 'only-head')).toBe false
|
||||
expect(lineAndLineNumberHaveClass(2, 'only-head')).toBe false
|
||||
@@ -864,7 +925,7 @@ describe "EditorComponent", ->
|
||||
|
||||
describe "when the decoration's 'onlyEmpty' property is true", ->
|
||||
it "only applies the decoration when its marker is empty", ->
|
||||
editor.addDecorationForMarker(marker, type: ['gutter', 'line'], class: 'only-empty', onlyEmpty: true)
|
||||
editor.decorateMarker(marker, type: ['gutter', 'line'], class: 'only-empty', onlyEmpty: true)
|
||||
runSetImmediateCallbacks()
|
||||
expect(lineAndLineNumberHaveClass(2, 'only-empty')).toBe false
|
||||
expect(lineAndLineNumberHaveClass(3, 'only-empty')).toBe false
|
||||
@@ -876,7 +937,7 @@ describe "EditorComponent", ->
|
||||
|
||||
describe "when the decoration's 'onlyNonEmpty' property is true", ->
|
||||
it "only applies the decoration when its marker is non-empty", ->
|
||||
editor.addDecorationForMarker(marker, type: ['gutter', 'line'], class: 'only-non-empty', onlyNonEmpty: true)
|
||||
editor.decorateMarker(marker, type: ['gutter', 'line'], class: 'only-non-empty', onlyNonEmpty: true)
|
||||
runSetImmediateCallbacks()
|
||||
expect(lineAndLineNumberHaveClass(2, 'only-non-empty')).toBe true
|
||||
expect(lineAndLineNumberHaveClass(3, 'only-non-empty')).toBe true
|
||||
@@ -887,12 +948,12 @@ describe "EditorComponent", ->
|
||||
expect(lineAndLineNumberHaveClass(3, 'only-non-empty')).toBe false
|
||||
|
||||
describe "highlight decoration rendering", ->
|
||||
[marker, decoration, scrollViewClientLeft] = []
|
||||
[marker, decoration, decorationParams, scrollViewClientLeft] = []
|
||||
beforeEach ->
|
||||
scrollViewClientLeft = node.querySelector('.scroll-view').getBoundingClientRect().left
|
||||
marker = editor.displayBuffer.markBufferRange([[2, 13], [3, 15]], invalidate: 'inside')
|
||||
decoration = {type: 'highlight', class: 'test-highlight'}
|
||||
editor.addDecorationForMarker(marker, decoration)
|
||||
decorationParams = {type: 'highlight', class: 'test-highlight'}
|
||||
decoration = editor.decorateMarker(marker, decorationParams)
|
||||
runSetImmediateCallbacks()
|
||||
|
||||
it "does not render highlights for off-screen lines until they come on-screen", ->
|
||||
@@ -901,7 +962,7 @@ describe "EditorComponent", ->
|
||||
runSetImmediateCallbacks()
|
||||
|
||||
marker = editor.displayBuffer.markBufferRange([[9, 2], [9, 4]], invalidate: 'inside')
|
||||
editor.addDecorationForMarker(marker, type: 'highlight', class: 'some-highlight')
|
||||
editor.decorateMarker(marker, type: 'highlight', class: 'some-highlight')
|
||||
runSetImmediateCallbacks()
|
||||
|
||||
# Should not be rendering range containing the marker
|
||||
@@ -929,7 +990,7 @@ describe "EditorComponent", ->
|
||||
expect(regions.length).toBe 2
|
||||
|
||||
it "removes highlights when a decoration is removed", ->
|
||||
editor.removeDecorationForMarker(marker, decoration)
|
||||
decoration.destroy()
|
||||
runSetImmediateCallbacks()
|
||||
regions = node.querySelectorAll('.test-highlight .region')
|
||||
expect(regions.length).toBe 0
|
||||
@@ -960,6 +1021,39 @@ describe "EditorComponent", ->
|
||||
regions = node.querySelectorAll('.test-highlight .region')
|
||||
expect(regions.length).toBe 2
|
||||
|
||||
describe "when flashing a decoration via Decoration::flash()", ->
|
||||
highlightNode = null
|
||||
beforeEach ->
|
||||
highlightNode = node.querySelector('.test-highlight')
|
||||
|
||||
it "adds and removes the flash class specified in ::flash", ->
|
||||
expect(highlightNode.classList.contains('flash-class')).toBe false
|
||||
|
||||
decoration.flash('flash-class', 10)
|
||||
expect(highlightNode.classList.contains('flash-class')).toBe true
|
||||
|
||||
advanceClock(10)
|
||||
expect(highlightNode.classList.contains('flash-class')).toBe false
|
||||
|
||||
describe "when ::flash is called again before the first has finished", ->
|
||||
it "removes the class from the decoration highlight before adding it for the second ::flash call", ->
|
||||
delayAnimationFrames = true
|
||||
|
||||
decoration.flash('flash-class', 10)
|
||||
nextAnimationFrame()
|
||||
expect(highlightNode.classList.contains('flash-class')).toBe true
|
||||
advanceClock(2)
|
||||
|
||||
decoration.flash('flash-class', 10)
|
||||
# Removed for 1 frame to force CSS transition to restart
|
||||
expect(highlightNode.classList.contains('flash-class')).toBe false
|
||||
|
||||
nextAnimationFrame()
|
||||
expect(highlightNode.classList.contains('flash-class')).toBe true
|
||||
|
||||
advanceClock(10)
|
||||
expect(highlightNode.classList.contains('flash-class')).toBe false
|
||||
|
||||
describe "when a decoration's marker moves", ->
|
||||
it "moves rendered highlights when the buffer is changed", ->
|
||||
regionStyle = node.querySelector('.test-highlight .region').style
|
||||
@@ -983,6 +1077,16 @@ describe "EditorComponent", ->
|
||||
regionStyle = node.querySelector('.test-highlight .region').style
|
||||
expect(parseInt(regionStyle.top)).toBe 5 * lineHeightInPixels
|
||||
|
||||
describe "when a decoration is updated via Decoration::update", ->
|
||||
it "renders the decoration's new params", ->
|
||||
expect(node.querySelector('.test-highlight')).toBeTruthy()
|
||||
|
||||
decoration.update(type: 'highlight', class: 'new-test-highlight')
|
||||
runSetImmediateCallbacks()
|
||||
|
||||
expect(node.querySelector('.test-highlight')).toBeFalsy()
|
||||
expect(node.querySelector('.new-test-highlight')).toBeTruthy()
|
||||
|
||||
describe "hidden input field", ->
|
||||
it "renders the hidden input field at the position of the last cursor if the cursor is on screen and the editor is focused", ->
|
||||
editor.setVerticalScrollMargin(0)
|
||||
@@ -1029,7 +1133,7 @@ describe "EditorComponent", ->
|
||||
expect(inputNode.offsetTop).toBe 0
|
||||
expect(inputNode.offsetLeft).toBe 0
|
||||
|
||||
describe "mouse interactions on the scrollView", ->
|
||||
describe "mouse interactions on the lines", ->
|
||||
linesNode = null
|
||||
|
||||
beforeEach ->
|
||||
@@ -1065,34 +1169,61 @@ describe "EditorComponent", ->
|
||||
expect(editor.getSelectedScreenRanges()).toEqual [[[3, 4], [3, 4]], [[5, 6], [5, 6]]]
|
||||
|
||||
describe "when a non-folded line is double-clicked", ->
|
||||
it "selects the word containing the nearest screen position", ->
|
||||
linesNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenPosition([5, 10]), detail: 2))
|
||||
linesNode.dispatchEvent(buildMouseEvent('mouseup'))
|
||||
expect(editor.getSelectedScreenRange()).toEqual [[5, 6], [5, 13]]
|
||||
describe "when no modifier keys are held down", ->
|
||||
it "selects the word containing the nearest screen position", ->
|
||||
linesNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenPosition([5, 10]), detail: 1))
|
||||
linesNode.dispatchEvent(buildMouseEvent('mouseup'))
|
||||
linesNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenPosition([5, 10]), detail: 2))
|
||||
linesNode.dispatchEvent(buildMouseEvent('mouseup'))
|
||||
expect(editor.getSelectedScreenRange()).toEqual [[5, 6], [5, 13]]
|
||||
|
||||
linesNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenPosition([6, 6]), detail: 1))
|
||||
linesNode.dispatchEvent(buildMouseEvent('mouseup'))
|
||||
expect(editor.getSelectedScreenRange()).toEqual [[6, 6], [6, 6]]
|
||||
linesNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenPosition([6, 6]), detail: 1))
|
||||
linesNode.dispatchEvent(buildMouseEvent('mouseup'))
|
||||
expect(editor.getSelectedScreenRange()).toEqual [[6, 6], [6, 6]]
|
||||
|
||||
linesNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenPosition([8, 8]), detail: 1, shiftKey: true))
|
||||
linesNode.dispatchEvent(buildMouseEvent('mouseup'))
|
||||
expect(editor.getSelectedScreenRange()).toEqual [[6, 6], [8, 8]]
|
||||
linesNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenPosition([8, 8]), detail: 1, shiftKey: true))
|
||||
linesNode.dispatchEvent(buildMouseEvent('mouseup'))
|
||||
expect(editor.getSelectedScreenRange()).toEqual [[6, 6], [8, 8]]
|
||||
|
||||
describe "when the command key is held down", ->
|
||||
it "selects the word containing the newly-added cursor", ->
|
||||
linesNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenPosition([5, 10]), detail: 1, metaKey: true))
|
||||
linesNode.dispatchEvent(buildMouseEvent('mouseup'))
|
||||
linesNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenPosition([5, 10]), detail: 2, metaKey: true))
|
||||
linesNode.dispatchEvent(buildMouseEvent('mouseup'))
|
||||
|
||||
expect(editor.getSelectedScreenRanges()).toEqual [[[0, 0], [0, 0]], [[5, 6], [5, 13]]]
|
||||
|
||||
describe "when a non-folded line is triple-clicked", ->
|
||||
it "selects the line containing the nearest screen position", ->
|
||||
linesNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenPosition([5, 10]), detail: 3))
|
||||
linesNode.dispatchEvent(buildMouseEvent('mouseup'))
|
||||
expect(editor.getSelectedScreenRange()).toEqual [[5, 0], [6, 0]]
|
||||
describe "when no modifier keys are held down", ->
|
||||
it "selects the line containing the nearest screen position", ->
|
||||
linesNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenPosition([5, 10]), detail: 1))
|
||||
linesNode.dispatchEvent(buildMouseEvent('mouseup'))
|
||||
linesNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenPosition([5, 10]), detail: 2))
|
||||
linesNode.dispatchEvent(buildMouseEvent('mouseup'))
|
||||
linesNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenPosition([5, 10]), detail: 3))
|
||||
linesNode.dispatchEvent(buildMouseEvent('mouseup'))
|
||||
expect(editor.getSelectedScreenRange()).toEqual [[5, 0], [6, 0]]
|
||||
|
||||
linesNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenPosition([6, 6]), detail: 1, shiftKey: true))
|
||||
linesNode.dispatchEvent(buildMouseEvent('mouseup'))
|
||||
expect(editor.getSelectedScreenRange()).toEqual [[5, 0], [7, 0]]
|
||||
linesNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenPosition([6, 6]), detail: 1, shiftKey: true))
|
||||
linesNode.dispatchEvent(buildMouseEvent('mouseup'))
|
||||
expect(editor.getSelectedScreenRange()).toEqual [[5, 0], [7, 0]]
|
||||
|
||||
linesNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenPosition([7, 5]), detail: 1))
|
||||
linesNode.dispatchEvent(buildMouseEvent('mouseup'))
|
||||
linesNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenPosition([8, 8]), detail: 1, shiftKey: true))
|
||||
linesNode.dispatchEvent(buildMouseEvent('mouseup'))
|
||||
expect(editor.getSelectedScreenRange()).toEqual [[7, 5], [8, 8]]
|
||||
linesNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenPosition([7, 5]), detail: 1))
|
||||
linesNode.dispatchEvent(buildMouseEvent('mouseup'))
|
||||
linesNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenPosition([8, 8]), detail: 1, shiftKey: true))
|
||||
linesNode.dispatchEvent(buildMouseEvent('mouseup'))
|
||||
expect(editor.getSelectedScreenRange()).toEqual [[7, 5], [8, 8]]
|
||||
|
||||
describe "when the command key is held down", ->
|
||||
it "selects the line containing the newly-added cursor", ->
|
||||
linesNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenPosition([5, 10]), detail: 1, metaKey: true))
|
||||
linesNode.dispatchEvent(buildMouseEvent('mouseup'))
|
||||
linesNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenPosition([5, 10]), detail: 2, metaKey: true))
|
||||
linesNode.dispatchEvent(buildMouseEvent('mouseup'))
|
||||
linesNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenPosition([5, 10]), detail: 3, metaKey: true))
|
||||
linesNode.dispatchEvent(buildMouseEvent('mouseup'))
|
||||
expect(editor.getSelectedScreenRanges()).toEqual [[[0, 0], [0, 0]], [[5, 0], [6, 0]]]
|
||||
|
||||
describe "when the mouse is clicked and dragged", ->
|
||||
it "selects to the nearest screen position until the mouse button is released", ->
|
||||
@@ -1731,6 +1862,29 @@ describe "EditorComponent", ->
|
||||
line0Right = node.querySelector('.line > span:last-child').getBoundingClientRect().right
|
||||
expect(cursorLeft).toBe line0Right
|
||||
|
||||
describe "when stylesheets change while the editor is hidden", ->
|
||||
afterEach ->
|
||||
atom.themes.removeStylesheet('test')
|
||||
|
||||
it "does not re-measure character widths until the editor is shown again", ->
|
||||
atom.config.set('editor.fontFamily', 'sans-serif')
|
||||
|
||||
wrapperView.hide()
|
||||
atom.themes.applyStylesheet 'test', """
|
||||
.function.js {
|
||||
font-weight: bold;
|
||||
}
|
||||
"""
|
||||
runSetImmediateCallbacks()
|
||||
|
||||
wrapperView.show()
|
||||
editor.setCursorBufferPosition([0, Infinity])
|
||||
runSetImmediateCallbacks()
|
||||
|
||||
cursorLeft = node.querySelector('.cursor').getBoundingClientRect().left
|
||||
line0Right = node.querySelector('.line > span:last-child').getBoundingClientRect().right
|
||||
expect(cursorLeft).toBe line0Right
|
||||
|
||||
describe "when lines are changed while the editor is hidden", ->
|
||||
it "does not measure new characters until the editor is shown again", ->
|
||||
editor.setText('')
|
||||
@@ -1741,9 +1895,11 @@ describe "EditorComponent", ->
|
||||
wrapperView.show()
|
||||
expect(node.querySelector('.cursor').style['-webkit-transform']).toBe "translate3d(#{9 * charWidth}px, 0px, 0px)"
|
||||
|
||||
describe "when the editor component is resized", ->
|
||||
it "updates the component based on a new size", ->
|
||||
describe "soft wrapping", ->
|
||||
beforeEach ->
|
||||
editor.setSoftWrap(true)
|
||||
|
||||
it "updates the wrap location when the editor is resized", ->
|
||||
newHeight = 4 * editor.getLineHeightInPixels() + "px"
|
||||
expect(newHeight).toBeLessThan node.style.height
|
||||
node.style.height = newHeight
|
||||
@@ -1758,6 +1914,16 @@ describe "EditorComponent", ->
|
||||
runSetImmediateCallbacks()
|
||||
expect(node.querySelector('.line').textContent).toBe "var quicksort "
|
||||
|
||||
it "accounts for the scroll view's padding when determining the wrap location", ->
|
||||
scrollViewNode = node.querySelector('.scroll-view')
|
||||
scrollViewNode.style.paddingLeft = 20 + 'px'
|
||||
node.style.width = 30 * charWidth + 'px'
|
||||
|
||||
advanceClock(component.scrollViewMeasurementInterval)
|
||||
runSetImmediateCallbacks()
|
||||
|
||||
expect(component.lineNodeForScreenRow(0).textContent).toBe "var quicksort = "
|
||||
|
||||
describe "default decorations", ->
|
||||
it "applies .cursor-line decorations for line numbers overlapping selections", ->
|
||||
editor.setCursorScreenPosition([4, 4])
|
||||
@@ -1820,6 +1986,7 @@ describe "EditorComponent", ->
|
||||
|
||||
buildMouseEvent = (type, properties...) ->
|
||||
properties = extend({bubbles: true, cancelable: true}, properties...)
|
||||
properties.detail ?= 1
|
||||
event = new MouseEvent(type, properties)
|
||||
Object.defineProperty(event, 'which', get: -> properties.which) if properties.which?
|
||||
if properties.target?
|
||||
|
||||
@@ -785,6 +785,19 @@ describe "Editor", ->
|
||||
editor.moveCursorLeft()
|
||||
expect(editor.getScrollLeft()).toBe 58 * 10
|
||||
|
||||
it "scrolls down when inserting lines makes the document longer than the editor's height", ->
|
||||
editor.setCursorScreenPosition([13, Infinity])
|
||||
editor.insertNewline()
|
||||
expect(editor.getScrollBottom()).toBe 14 * 10
|
||||
editor.insertNewline()
|
||||
expect(editor.getScrollBottom()).toBe 15 * 10
|
||||
|
||||
it "autoscrolls to the cursor when it moves due to undo", ->
|
||||
editor.insertText('abc')
|
||||
editor.setScrollTop(Infinity)
|
||||
editor.undo()
|
||||
expect(editor.getScrollTop()).toBe 0
|
||||
|
||||
describe "selection", ->
|
||||
selection = null
|
||||
|
||||
@@ -1227,6 +1240,8 @@ describe "Editor", ->
|
||||
expect(editor.getSelectedBufferRanges()).toEqual [[[0, 0], [0, 0]], [[3, 4], [5, 6]]]
|
||||
|
||||
it "autoscrolls to the added selection if needed", ->
|
||||
editor.manageScrollPosition = true
|
||||
|
||||
editor.setLineHeightInPixels(10)
|
||||
editor.setDefaultCharWidth(10)
|
||||
editor.setHeight(50)
|
||||
|
||||
@@ -208,13 +208,16 @@ describe "Project", ->
|
||||
describe "when a file doesn't exist", ->
|
||||
it "calls back with an error", ->
|
||||
errors = []
|
||||
missingPath = path.resolve('/not-a-file.js')
|
||||
expect(fs.existsSync(missingPath)).toBeFalsy()
|
||||
|
||||
waitsForPromise ->
|
||||
atom.project.replace /items/gi, 'items', ['/not-a-file.js'], (result, error) ->
|
||||
atom.project.replace /items/gi, 'items', [missingPath], (result, error) ->
|
||||
errors.push(error)
|
||||
|
||||
runs ->
|
||||
expect(errors).toHaveLength 1
|
||||
expect(errors[0].path).toBe '/not-a-file.js'
|
||||
expect(errors[0].path).toBe missingPath
|
||||
|
||||
describe "when called with unopened files", ->
|
||||
it "replaces properly", ->
|
||||
|
||||
@@ -37,7 +37,7 @@ jasmine.getEnv().addEqualityTester(_.isEqual) # Use underscore's definition of e
|
||||
|
||||
if process.platform is 'win32' and process.env.JANKY_SHA1
|
||||
# Use longer timeout on Windows CI
|
||||
jasmine.getEnv().defaultTimeoutInterval = 30000
|
||||
jasmine.getEnv().defaultTimeoutInterval = 60000
|
||||
else
|
||||
jasmine.getEnv().defaultTimeoutInterval = 5000
|
||||
|
||||
|
||||
@@ -684,8 +684,8 @@ describe "TokenizedBuffer", ->
|
||||
|
||||
expect(tokenizedBuffer.lineForScreenRow(5).indentLevel).toBe 3
|
||||
expect(tokenizedBuffer.lineForScreenRow(6).indentLevel).toBe 3
|
||||
expect(tokenizedBuffer.lineForScreenRow(9).indentLevel).toBe 2
|
||||
expect(tokenizedBuffer.lineForScreenRow(10).indentLevel).toBe 2
|
||||
expect(tokenizedBuffer.lineForScreenRow(9).indentLevel).toBe 3
|
||||
expect(tokenizedBuffer.lineForScreenRow(10).indentLevel).toBe 3
|
||||
expect(tokenizedBuffer.lineForScreenRow(11).indentLevel).toBe 2
|
||||
|
||||
tokenizedBuffer.on "changed", changeHandler = jasmine.createSpy('changeHandler')
|
||||
@@ -693,12 +693,12 @@ describe "TokenizedBuffer", ->
|
||||
buffer.setTextInRange([[7, 0], [8, 65]], ' one\n two\n three\n four')
|
||||
|
||||
delete changeHandler.argsForCall[0][0].bufferChange
|
||||
expect(changeHandler).toHaveBeenCalledWith(start: 5, end: 8, delta: 2)
|
||||
expect(changeHandler).toHaveBeenCalledWith(start: 5, end: 10, delta: 2)
|
||||
|
||||
expect(tokenizedBuffer.lineForScreenRow(5).indentLevel).toBe 4
|
||||
expect(tokenizedBuffer.lineForScreenRow(6).indentLevel).toBe 4
|
||||
expect(tokenizedBuffer.lineForScreenRow(11).indentLevel).toBe 2
|
||||
expect(tokenizedBuffer.lineForScreenRow(12).indentLevel).toBe 2
|
||||
expect(tokenizedBuffer.lineForScreenRow(11).indentLevel).toBe 4
|
||||
expect(tokenizedBuffer.lineForScreenRow(12).indentLevel).toBe 4
|
||||
expect(tokenizedBuffer.lineForScreenRow(13).indentLevel).toBe 2
|
||||
|
||||
it "updates the indentLevel of empty lines surrounding a change that removes lines", ->
|
||||
@@ -711,7 +711,7 @@ describe "TokenizedBuffer", ->
|
||||
buffer.setTextInRange([[7, 0], [8, 65]], ' ok')
|
||||
|
||||
delete changeHandler.argsForCall[0][0].bufferChange
|
||||
expect(changeHandler).toHaveBeenCalledWith(start: 5, end: 8, delta: -1)
|
||||
expect(changeHandler).toHaveBeenCalledWith(start: 5, end: 10, delta: -1)
|
||||
|
||||
expect(tokenizedBuffer.lineForScreenRow(5).indentLevel).toBe 2
|
||||
expect(tokenizedBuffer.lineForScreenRow(6).indentLevel).toBe 2
|
||||
|
||||
+19
-7
@@ -225,9 +225,15 @@ class Atom extends Model
|
||||
else
|
||||
@center()
|
||||
|
||||
# Returns true if the dimensions are useable, false if they should be ignored.
|
||||
# Work around for https://github.com/atom/atom-shell/issues/473
|
||||
isValidDimensions: ({x, y, width, height}={}) ->
|
||||
width > 0 and height > 0 and x + width > 0 and y + height > 0
|
||||
|
||||
storeDefaultWindowDimensions: ->
|
||||
dimensions = JSON.stringify(atom.getWindowDimensions())
|
||||
localStorage.setItem("defaultWindowDimensions", dimensions)
|
||||
dimensions = @getWindowDimensions()
|
||||
if @isValidDimensions(dimensions)
|
||||
localStorage.setItem("defaultWindowDimensions", JSON.stringify(dimensions))
|
||||
|
||||
getDefaultWindowDimensions: ->
|
||||
{windowDimensions} = @getLoadSettings()
|
||||
@@ -240,15 +246,21 @@ class Atom extends Model
|
||||
console.warn "Error parsing default window dimensions", error
|
||||
localStorage.removeItem("defaultWindowDimensions")
|
||||
|
||||
{width, height} = screen.getPrimaryDisplay().workAreaSize
|
||||
dimensions ? {x: 0, y: 0, width: Math.min(1024, width), height: height}
|
||||
if @isValidDimensions(dimensions)
|
||||
dimensions
|
||||
else
|
||||
{width, height} = screen.getPrimaryDisplay().workAreaSize
|
||||
{x: 0, y: 0, width: Math.min(1024, width), height}
|
||||
|
||||
restoreWindowDimensions: ->
|
||||
windowDimensions = @state.windowDimensions ? @getDefaultWindowDimensions()
|
||||
@setWindowDimensions(windowDimensions)
|
||||
dimensions = @state.windowDimensions
|
||||
unless @isValidDimensions(dimensions)
|
||||
dimensions = @getDefaultWindowDimensions()
|
||||
@setWindowDimensions(dimensions)
|
||||
|
||||
storeWindowDimensions: ->
|
||||
@state.windowDimensions = @getWindowDimensions()
|
||||
dimensions = @getWindowDimensions()
|
||||
@state.windowDimensions = dimensions if @isValidDimensions(dimensions)
|
||||
|
||||
# Public: Get the load settings for the current window.
|
||||
#
|
||||
|
||||
@@ -138,6 +138,7 @@ class AtomWindow
|
||||
when 'window:reload' then @reload()
|
||||
when 'window:toggle-dev-tools' then @toggleDevTools()
|
||||
when 'window:close' then @close()
|
||||
when 'window:update-available' then @sendCommandToBrowserWindow(command, args...) # For spec testing
|
||||
else if @isWebViewFocused()
|
||||
@sendCommandToBrowserWindow(command, args...)
|
||||
else
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
https = require 'https'
|
||||
autoUpdater = require 'auto-updater'
|
||||
dialog = require 'dialog'
|
||||
_ = require 'underscore-plus'
|
||||
@@ -16,11 +17,12 @@ class AutoUpdateManager
|
||||
|
||||
constructor: (@version) ->
|
||||
@state = IDLE_STATE
|
||||
@feedUrl = "https://atom.io/api/updates?version=#{@version}"
|
||||
|
||||
# Only released versions should check for updates.
|
||||
return if /\w{7}/.test(@version)
|
||||
if process.platform is 'win32'
|
||||
autoUpdater.checkForUpdates = => @checkForUpdatesShim()
|
||||
|
||||
autoUpdater.setFeedUrl "https://atom.io/api/updates?version=#{@version}"
|
||||
autoUpdater.setFeedUrl @feedUrl
|
||||
|
||||
autoUpdater.on 'checking-for-update', =>
|
||||
@setState(CHECKING_STATE)
|
||||
@@ -39,7 +41,25 @@ class AutoUpdateManager
|
||||
@setState(UPDATE_AVAILABLE_STATE)
|
||||
@emitUpdateAvailableEvent(@getWindows()...)
|
||||
|
||||
@check(hidePopups: true)
|
||||
# Only released versions should check for updates.
|
||||
unless /\w{7}/.test(@version)
|
||||
@check(hidePopups: true)
|
||||
|
||||
# Windows doesn't have an auto-updater, so use this method to shim the events.
|
||||
checkForUpdatesShim: ->
|
||||
autoUpdater.emit 'checking-for-update'
|
||||
request = https.get @feedUrl, (response) ->
|
||||
if response.statusCode == 200
|
||||
body = ""
|
||||
response.on 'data', (chunk) -> body += chunk
|
||||
response.on 'end', ->
|
||||
{notes, name} = JSON.parse(body)
|
||||
autoUpdater.emit 'update-downloaded', null, notes, name
|
||||
else
|
||||
autoUpdater.emit 'update-not-available'
|
||||
|
||||
request.on 'error', (error) ->
|
||||
autoUpdater.emit 'error', null, error.message
|
||||
|
||||
emitUpdateAvailableEvent: (windows...) ->
|
||||
return unless @releaseVersion? and @releaseNotes
|
||||
|
||||
@@ -45,7 +45,10 @@ class BufferedProcess
|
||||
# Related to joyent/node#2318
|
||||
if process.platform is "win32"
|
||||
# Quote all arguments and escapes inner quotes
|
||||
cmdArgs = args.map (arg) -> "\"#{arg.replace(/"/g, '\\"')}\""
|
||||
if args?
|
||||
cmdArgs = args.map (arg) -> "\"#{arg.replace(/"/g, '\\"')}\""
|
||||
else
|
||||
cmdArgs = []
|
||||
cmdArgs.unshift("\"#{command}\"")
|
||||
cmdArgs = ['/s', '/c', "\"#{cmdArgs.join(' ')}\""]
|
||||
cmdOptions = _.clone(options)
|
||||
|
||||
@@ -28,6 +28,7 @@ class Cursor extends Model
|
||||
|
||||
# Supports old editor view
|
||||
@needsAutoscroll ?= @isLastCursor() and !textChanged
|
||||
@autoscroll() if @editor.manageScrollPosition and @isLastCursor() and textChanged
|
||||
|
||||
@goalColumn = null
|
||||
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
_ = require 'underscore-plus'
|
||||
{Subscriber, Emitter} = require 'emissary'
|
||||
|
||||
idCounter = 0
|
||||
nextId = -> idCounter++
|
||||
|
||||
# Public: Represents a decoration that follows a {Marker}. A decoration is
|
||||
# basically a visual representation of a marker. It allows you to add CSS
|
||||
# classes to line numbers in the gutter, lines, and add selection-line regions
|
||||
# around marked ranges of text.
|
||||
#
|
||||
# {Decoration} objects are not meant to be created directly, but created with
|
||||
# {Editor::decorateMarker}. eg.
|
||||
#
|
||||
# ```coffee
|
||||
# range = editor.getSelectedBufferRange() # any range you like
|
||||
# marker = editor.markBufferRange(range)
|
||||
# decoration = editor.decorateMarker(marker, {type: 'line', class: 'my-line-class'})
|
||||
# ```
|
||||
#
|
||||
# Best practice for destorying the decoration is by destroying the {Marker}.
|
||||
#
|
||||
# ```
|
||||
# marker.destroy()
|
||||
# ```
|
||||
#
|
||||
# You should only use {Decoration::destroy} when you still need or do not own
|
||||
# the marker.
|
||||
#
|
||||
# ### IDs
|
||||
# Each {Decoration} has a unique ID available via `decoration.id`.
|
||||
#
|
||||
# ### Events
|
||||
# A couple of events are emitted:
|
||||
#
|
||||
# * `destroyed`: When the {Decoration} is destroyed
|
||||
# * `updated`: When the {Decoration} is updated via {Decoration::update}.
|
||||
# Event object has properties `oldParams` and `newParams`
|
||||
#
|
||||
module.exports =
|
||||
class Decoration
|
||||
Emitter.includeInto(this)
|
||||
|
||||
@isType: (decorationParams, type) ->
|
||||
if _.isArray(decorationParams.type)
|
||||
type in decorationParams.type
|
||||
else
|
||||
type is decorationParams.type
|
||||
|
||||
constructor: (@marker, @displayBuffer, @params) ->
|
||||
@id = nextId()
|
||||
@params.id = @id
|
||||
@flashQueue = null
|
||||
@isDestroyed = false
|
||||
|
||||
# Public: Destroy this marker.
|
||||
#
|
||||
# If you own the marker, you should use {Marker:destroy} which will destroy
|
||||
# this decoration.
|
||||
destroy: ->
|
||||
return if @isDestroyed
|
||||
@isDestroyed = true
|
||||
@displayBuffer.removeDecoration(this)
|
||||
@emit 'destoryed'
|
||||
|
||||
# Public: Update the marker with new params. Allows you to change the decoration's class.
|
||||
#
|
||||
# ```
|
||||
# decoration.update({type: 'gutter', class: 'my-new-class'})
|
||||
# ```
|
||||
update: (newParams) ->
|
||||
return if @isDestroyed
|
||||
oldParams = @params
|
||||
@params = newParams
|
||||
@params.id = @id
|
||||
@displayBuffer.decorationUpdated(this)
|
||||
@emit 'updated', {oldParams, newParams}
|
||||
|
||||
# Public: Returns the marker associated with this {Decoration}
|
||||
getMarker: -> @marker
|
||||
|
||||
# Public: Returns the {Decoration}'s params.
|
||||
getParams: -> @params
|
||||
|
||||
# Public: Check if this decoration is of type `type`
|
||||
#
|
||||
# type - A {String} type like `'gutter'`
|
||||
#
|
||||
# Returns a {Boolean}
|
||||
isType: (type) ->
|
||||
Decoration.isType(@params, type)
|
||||
|
||||
matchesPattern: (decorationPattern) ->
|
||||
return false unless decorationPattern?
|
||||
for key, value of decorationPattern
|
||||
return false if @params[key] != value
|
||||
true
|
||||
|
||||
flash: (klass, duration=500) ->
|
||||
flashObject = {class: klass, duration}
|
||||
@flashQueue ?= []
|
||||
@flashQueue.push(flashObject)
|
||||
@emit 'flash'
|
||||
|
||||
consumeNextFlash: ->
|
||||
return @flashQueue.shift() if @flashQueue?.length > 0
|
||||
null
|
||||
+43
-39
@@ -8,6 +8,7 @@ TokenizedBuffer = require './tokenized-buffer'
|
||||
RowMap = require './row-map'
|
||||
Fold = require './fold'
|
||||
Token = require './token'
|
||||
Decoration = require './decoration'
|
||||
DisplayBufferMarker = require './display-buffer-marker'
|
||||
|
||||
class BufferToScreenConversionError extends Error
|
||||
@@ -45,6 +46,7 @@ class DisplayBuffer extends Model
|
||||
@charWidthsByScope = {}
|
||||
@markers = {}
|
||||
@foldsByMarkerId = {}
|
||||
@decorationsById = {}
|
||||
@decorationsByMarkerId = {}
|
||||
@decorationMarkerChangedSubscriptions = {}
|
||||
@decorationMarkerDestroyedSubscriptions = {}
|
||||
@@ -202,6 +204,9 @@ class DisplayBuffer extends Model
|
||||
else
|
||||
@scrollLeft = Math.round(scrollLeft)
|
||||
|
||||
getMaxScrollLeft: ->
|
||||
@getScrollWidth() - @getClientWidth()
|
||||
|
||||
getScrollRight: -> @scrollLeft + @width
|
||||
setScrollRight: (scrollRight) ->
|
||||
@setScrollLeft(scrollRight - @width)
|
||||
@@ -593,6 +598,12 @@ class DisplayBuffer extends Model
|
||||
getMaxLineLength: ->
|
||||
@maxLineLength
|
||||
|
||||
# Gets the row number of the longest screen line.
|
||||
#
|
||||
# Return a {}
|
||||
getLongestScreenRow: ->
|
||||
@longestScreenRow
|
||||
|
||||
# Given a buffer position, this converts it into a screen position.
|
||||
#
|
||||
# bufferPosition - An object that represents a buffer position. It can be either
|
||||
@@ -746,31 +757,17 @@ class DisplayBuffer extends Model
|
||||
rangeForAllLines: ->
|
||||
new Range([0, 0], @clipScreenPosition([Infinity, Infinity]))
|
||||
|
||||
decorationForId: (id) ->
|
||||
@decorationsById[id]
|
||||
|
||||
decorationsForScreenRowRange: (startScreenRow, endScreenRow) ->
|
||||
decorationsByMarkerId = {}
|
||||
|
||||
for marker in @findMarkers(intersectsScreenRowRange: [startScreenRow, endScreenRow])
|
||||
if decorations = @decorationsByMarkerId[marker.id]
|
||||
decorationsByMarkerId[marker.id] = decorations
|
||||
decorationsByMarkerId
|
||||
|
||||
decorationMatchesType: (decoration, type) ->
|
||||
if _.isArray(decoration.type)
|
||||
type in decoration.type
|
||||
else
|
||||
type is decoration.type
|
||||
|
||||
decorationMatchesPattern: (decoration, decorationPattern) ->
|
||||
return false unless decoration? and decorationPattern?
|
||||
for key, value of decorationPattern
|
||||
return false if decoration[key] != value
|
||||
true
|
||||
|
||||
addDecorationForMarker: (marker, decoration) ->
|
||||
unless marker?
|
||||
console.warn 'A null marker cannot be decorated'
|
||||
return
|
||||
|
||||
decorateMarker: (marker, decorationParams) ->
|
||||
marker = @getMarker(marker.id)
|
||||
|
||||
@decorationMarkerDestroyedSubscriptions[marker.id] ?= @subscribe marker, 'destroyed', =>
|
||||
@@ -785,24 +782,23 @@ class DisplayBuffer extends Model
|
||||
for decoration in decorations
|
||||
@emit 'decoration-changed', marker, decoration, event
|
||||
|
||||
decoration = new Decoration(marker, this, decorationParams)
|
||||
@decorationsByMarkerId[marker.id] ?= []
|
||||
@decorationsByMarkerId[marker.id].push(decoration)
|
||||
@decorationsById[decoration.id] = decoration
|
||||
@emit 'decoration-added', marker, decoration
|
||||
decoration
|
||||
|
||||
removeDecorationForMarker: (marker, decorationPattern) ->
|
||||
unless marker?
|
||||
console.warn 'A decoration cannot be removed from a null marker'
|
||||
return
|
||||
|
||||
removeDecoration: (decoration) ->
|
||||
{marker} = decoration
|
||||
return unless decorations = @decorationsByMarkerId[marker.id]
|
||||
index = decorations.indexOf(decoration)
|
||||
|
||||
for i in [decorations.length - 1..0]
|
||||
decoration = decorations[i]
|
||||
if @decorationMatchesPattern(decoration, decorationPattern)
|
||||
decorations.splice(i, 1)
|
||||
@emit 'decoration-removed', marker, decoration
|
||||
|
||||
@removedAllMarkerDecorations(marker) if decorations.length is 0
|
||||
if index > -1
|
||||
decorations.splice(index, 1)
|
||||
delete @decorationsById[decoration.id]
|
||||
@emit 'decoration-removed', marker, decoration
|
||||
@removedAllMarkerDecorations(marker) if decorations.length is 0
|
||||
|
||||
removeAllDecorationsForMarker: (marker) ->
|
||||
decorations = @decorationsByMarkerId[marker.id].slice()
|
||||
@@ -818,6 +814,9 @@ class DisplayBuffer extends Model
|
||||
delete @decorationMarkerChangedSubscriptions[marker.id]
|
||||
delete @decorationMarkerDestroyedSubscriptions[marker.id]
|
||||
|
||||
decorationUpdated: (decoration) ->
|
||||
@emit 'decoration-updated', decoration
|
||||
|
||||
# Retrieves a {DisplayBufferMarker} based on its id.
|
||||
#
|
||||
# id - A {Number} representing a marker id
|
||||
@@ -971,7 +970,7 @@ class DisplayBuffer extends Model
|
||||
for marker in @getMarkers()
|
||||
marker.notifyObservers(textChanged: false)
|
||||
|
||||
destroy: ->
|
||||
destroyed: ->
|
||||
marker.unsubscribe() for marker in @getMarkers()
|
||||
@tokenizedBuffer.destroy()
|
||||
@unsubscribe()
|
||||
@@ -991,18 +990,19 @@ class DisplayBuffer extends Model
|
||||
endBufferRow = @rowMap.bufferRowRangeForBufferRow(endBufferRow - 1)[1]
|
||||
startScreenRow = @rowMap.screenRowRangeForBufferRow(startBufferRow)[0]
|
||||
endScreenRow = @rowMap.screenRowRangeForBufferRow(endBufferRow - 1)[1]
|
||||
|
||||
{screenLines, regions} = @buildScreenLines(startBufferRow, endBufferRow + bufferDelta)
|
||||
screenDelta = screenLines.length - (endScreenRow - startScreenRow)
|
||||
|
||||
@screenLines[startScreenRow...endScreenRow] = screenLines
|
||||
@rowMap.spliceRegions(startBufferRow, endBufferRow - startBufferRow, regions)
|
||||
@findMaxLineLength(startScreenRow, endScreenRow, screenLines)
|
||||
@findMaxLineLength(startScreenRow, endScreenRow, screenLines, screenDelta)
|
||||
|
||||
return if options.suppressChangeEvent
|
||||
|
||||
changeEvent =
|
||||
start: startScreenRow
|
||||
end: endScreenRow - 1
|
||||
screenDelta: screenLines.length - (endScreenRow - startScreenRow)
|
||||
screenDelta: screenDelta
|
||||
bufferDelta: bufferDelta
|
||||
|
||||
if options.delayChangeEvent
|
||||
@@ -1057,7 +1057,7 @@ class DisplayBuffer extends Model
|
||||
|
||||
{screenLines, regions}
|
||||
|
||||
findMaxLineLength: (startScreenRow, endScreenRow, newScreenLines) ->
|
||||
findMaxLineLength: (startScreenRow, endScreenRow, newScreenLines, screenDelta) ->
|
||||
oldMaxLineLength = @maxLineLength
|
||||
|
||||
if startScreenRow <= @longestScreenRow < endScreenRow
|
||||
@@ -1066,19 +1066,23 @@ class DisplayBuffer extends Model
|
||||
maxLengthCandidatesStartRow = 0
|
||||
maxLengthCandidates = @screenLines
|
||||
else
|
||||
@longestScreenRow += screenDelta if endScreenRow < @longestScreenRow
|
||||
maxLengthCandidatesStartRow = startScreenRow
|
||||
maxLengthCandidates = newScreenLines
|
||||
|
||||
for screenLine, screenRow in maxLengthCandidates
|
||||
for screenLine, i in maxLengthCandidates
|
||||
screenRow = maxLengthCandidatesStartRow + i
|
||||
length = screenLine.text.length
|
||||
if length > @maxLineLength
|
||||
@longestScreenRow = maxLengthCandidatesStartRow + screenRow
|
||||
@longestScreenRow = screenRow
|
||||
@maxLineLength = length
|
||||
|
||||
@computeScrollWidth() if oldMaxLineLength isnt @maxLineLength
|
||||
|
||||
computeScrollWidth: ->
|
||||
@scrollWidth = @pixelPositionForScreenPosition([@longestScreenRow, @maxLineLength]).left + 1
|
||||
@scrollWidth = @pixelPositionForScreenPosition([@longestScreenRow, @maxLineLength]).left
|
||||
@scrollWidth += 1 unless @getSoftWrap()
|
||||
@setScrollLeft(Math.min(@getScrollLeft(), @getMaxScrollLeft()))
|
||||
|
||||
handleBufferMarkersUpdated: =>
|
||||
if event = @pendingChangeEvent
|
||||
@@ -1090,7 +1094,7 @@ class DisplayBuffer extends Model
|
||||
@emit 'marker-created', @getMarker(marker.id)
|
||||
|
||||
createFoldForMarker: (marker) ->
|
||||
@addDecorationForMarker(marker, type: 'gutter', class: 'folded')
|
||||
@decorateMarker(marker, type: 'gutter', class: 'folded')
|
||||
new Fold(this, marker)
|
||||
|
||||
foldForMarker: (marker) ->
|
||||
|
||||
@@ -42,7 +42,7 @@ EditorComponent = React.createClass
|
||||
scrollSensitivity: 0.4
|
||||
scrollViewMeasurementRequested: false
|
||||
measureLineHeightAndDefaultCharWidthWhenShown: false
|
||||
remeasureCharacterWidthsWhenShown: false
|
||||
remeasureCharacterWidthsIfVisibleAfterNextUpdate: false
|
||||
inputEnabled: true
|
||||
scrollViewMeasurementInterval: 100
|
||||
scopedCharacterWidthsChangeCount: null
|
||||
@@ -182,6 +182,7 @@ EditorComponent = React.createClass
|
||||
@measureScrollbars()
|
||||
|
||||
componentWillUnmount: ->
|
||||
@props.parentView.trigger 'editor:will-be-removed', [@props.parentView]
|
||||
@unsubscribe()
|
||||
clearInterval(@scrollViewMeasurementIntervalId)
|
||||
@scrollViewMeasurementIntervalId = null
|
||||
@@ -280,21 +281,22 @@ EditorComponent = React.createClass
|
||||
headScreenRow = null
|
||||
if marker.isValid()
|
||||
for decoration in decorations
|
||||
if editor.decorationMatchesType(decoration, 'gutter') or editor.decorationMatchesType(decoration, 'line')
|
||||
if decoration.isType('gutter') or decoration.isType('line')
|
||||
decorationParams = decoration.getParams()
|
||||
screenRange ?= marker.getScreenRange()
|
||||
headScreenRow ?= marker.getHeadScreenPosition().row
|
||||
startRow = screenRange.start.row
|
||||
endRow = screenRange.end.row
|
||||
endRow-- if not screenRange.isEmpty() and screenRange.end.column == 0
|
||||
for screenRow in [startRow..endRow]
|
||||
continue if decoration.onlyHead and screenRow isnt headScreenRow
|
||||
continue if decorationParams.onlyHead and screenRow isnt headScreenRow
|
||||
if screenRange.isEmpty()
|
||||
continue if decoration.onlyNonEmpty
|
||||
continue if decorationParams.onlyNonEmpty
|
||||
else
|
||||
continue if decoration.onlyEmpty
|
||||
continue if decorationParams.onlyEmpty
|
||||
|
||||
decorationsByScreenRow[screenRow] ?= []
|
||||
decorationsByScreenRow[screenRow].push decoration
|
||||
decorationsByScreenRow[screenRow] ?= {}
|
||||
decorationsByScreenRow[screenRow][decoration.id] = decorationParams
|
||||
|
||||
decorationsByScreenRow
|
||||
|
||||
@@ -306,13 +308,14 @@ EditorComponent = React.createClass
|
||||
screenRange = marker.getScreenRange()
|
||||
if marker.isValid() and not screenRange.isEmpty()
|
||||
for decoration in decorations
|
||||
if editor.decorationMatchesType(decoration, 'highlight')
|
||||
if decoration.isType('highlight')
|
||||
decorationParams = decoration.getParams()
|
||||
filteredDecorations[markerId] ?=
|
||||
id: markerId
|
||||
startPixelPosition: editor.pixelPositionForScreenPosition(screenRange.start)
|
||||
endPixelPosition: editor.pixelPositionForScreenPosition(screenRange.end)
|
||||
decorations: []
|
||||
filteredDecorations[markerId].decorations.push decoration
|
||||
filteredDecorations[markerId].decorations.push decorationParams
|
||||
|
||||
# At least in Chromium 31, removing the last highlight causes a rendering
|
||||
# artifact where chunks of the lines disappear, so we always leave this
|
||||
@@ -330,6 +333,7 @@ EditorComponent = React.createClass
|
||||
@subscribe editor, 'decoration-added', @onDecorationChanged
|
||||
@subscribe editor, 'decoration-removed', @onDecorationChanged
|
||||
@subscribe editor, 'decoration-changed', @onDecorationChanged
|
||||
@subscribe editor, 'decoration-updated', @onDecorationChanged
|
||||
@subscribe editor, 'character-widths-changed', @onCharacterWidthsChanged
|
||||
@subscribe editor.$scrollTop.changes, @onScrollTopChanged
|
||||
@subscribe editor.$scrollLeft.changes, @requestUpdate
|
||||
@@ -458,7 +462,7 @@ EditorComponent = React.createClass
|
||||
'editor:unfold-all': => editor.unfoldAll()
|
||||
'editor:fold-current-row': => editor.foldCurrentRow()
|
||||
'editor:unfold-current-row': => editor.unfoldCurrentRow()
|
||||
'editor:fold-selection': => neditor.foldSelectedLines()
|
||||
'editor:fold-selection': => editor.foldSelectedLines()
|
||||
'editor:fold-at-indent-level-1': => editor.foldAllAtIndentLevel(0)
|
||||
'editor:fold-at-indent-level-2': => editor.foldAllAtIndentLevel(1)
|
||||
'editor:fold-at-indent-level-3': => editor.foldAllAtIndentLevel(2)
|
||||
@@ -595,15 +599,18 @@ EditorComponent = React.createClass
|
||||
editor.unfoldBufferRow(bufferRow)
|
||||
return
|
||||
|
||||
if shiftKey
|
||||
editor.selectToScreenPosition(screenPosition)
|
||||
else if metaKey or (ctrlKey and process.platform isnt 'darwin')
|
||||
editor.addCursorAtScreenPosition(screenPosition)
|
||||
else
|
||||
editor.setCursorScreenPosition(screenPosition)
|
||||
switch detail
|
||||
when 2 then editor.selectWord()
|
||||
when 3 then editor.selectLine()
|
||||
switch detail
|
||||
when 1
|
||||
if shiftKey
|
||||
editor.selectToScreenPosition(screenPosition)
|
||||
else if metaKey or (ctrlKey and process.platform isnt 'darwin')
|
||||
editor.addCursorAtScreenPosition(screenPosition)
|
||||
else
|
||||
editor.setCursorScreenPosition(screenPosition)
|
||||
when 2
|
||||
editor.getLastSelection().selectWord()
|
||||
when 3
|
||||
editor.getLastSelection().selectLine()
|
||||
|
||||
@handleDragUntilMouseUp event, (screenPosition) ->
|
||||
editor.selectToScreenPosition(screenPosition)
|
||||
@@ -648,6 +655,8 @@ EditorComponent = React.createClass
|
||||
|
||||
onStylesheetsChanged: (stylesheet) ->
|
||||
@refreshScrollbars() if @containsScrollbarSelector(stylesheet)
|
||||
@remeasureCharacterWidthsIfVisibleAfterNextUpdate = true
|
||||
@requestUpdate() if @state.visible
|
||||
|
||||
onScreenLinesChanged: (change) ->
|
||||
{editor} = @props
|
||||
@@ -762,6 +771,8 @@ EditorComponent = React.createClass
|
||||
|
||||
if position is 'absolute' or width
|
||||
clientWidth = scrollViewNode.clientWidth
|
||||
paddingLeft = parseInt(getComputedStyle(scrollViewNode).paddingLeft)
|
||||
clientWidth -= paddingLeft
|
||||
editor.setWidth(clientWidth) if clientWidth > 0
|
||||
|
||||
measureLineHeightAndCharWidthsIfNeeded: (prevState) ->
|
||||
@@ -782,12 +793,12 @@ EditorComponent = React.createClass
|
||||
if @state.visible
|
||||
@remeasureCharacterWidths()
|
||||
else
|
||||
@remeasureCharacterWidthsWhenShown = true
|
||||
else if @remeasureCharacterWidthsWhenShown and @state.visible and not prevState.visible
|
||||
@remeasureCharacterWidthsIfVisibleAfterNextUpdate = true
|
||||
else if @remeasureCharacterWidthsIfVisibleAfterNextUpdate and @state.visible
|
||||
@remeasureCharacterWidthsIfVisibleAfterNextUpdate = false
|
||||
@remeasureCharacterWidths()
|
||||
|
||||
remeasureCharacterWidths: ->
|
||||
@remeasureCharacterWidthsWhenShown = false
|
||||
@refs.lines.remeasureCharacterWidths()
|
||||
|
||||
onGutterWidthChanged: (@gutterWidth) ->
|
||||
|
||||
+52
-62
@@ -131,6 +131,10 @@ TextMateScopeSelector = require('first-mate').ScopeSelector
|
||||
# - {::markScreenRange}
|
||||
# - {::getMarker}
|
||||
# - {::findMarkers}
|
||||
#
|
||||
# ### Decorations
|
||||
# - {::decorateMarker}
|
||||
# - {::decorationsForScreenRowRange}
|
||||
module.exports =
|
||||
class Editor extends Model
|
||||
Serializable.includeInto(this)
|
||||
@@ -146,6 +150,7 @@ class Editor extends Model
|
||||
selections: null
|
||||
suppressSelectionMerging: false
|
||||
updateBatchDepth: 0
|
||||
selectionFlashDuration: 500
|
||||
|
||||
@delegatesMethods 'suggestedIndentForBufferRow', 'autoIndentBufferRow', 'autoIndentBufferRows',
|
||||
'autoDecreaseIndentForBufferRow', 'toggleLineCommentForBufferRow', 'toggleLineCommentsForBufferRows',
|
||||
@@ -218,6 +223,7 @@ class Editor extends Model
|
||||
@subscribe @displayBuffer, "decoration-added", (args...) => @emit 'decoration-added', args...
|
||||
@subscribe @displayBuffer, "decoration-removed", (args...) => @emit 'decoration-removed', args...
|
||||
@subscribe @displayBuffer, "decoration-changed", (args...) => @emit 'decoration-changed', args...
|
||||
@subscribe @displayBuffer, "decoration-updated", (args...) => @emit 'decoration-updated', args...
|
||||
@subscribe @displayBuffer, "character-widths-changed", (changeCount) => @emit 'character-widths-changed', changeCount
|
||||
|
||||
getViewClass: ->
|
||||
@@ -1078,8 +1084,10 @@ class Editor extends Model
|
||||
# startScreenRow - the {Number} beginning screen row
|
||||
# endScreenRow - the {Number} end screen row (inclusive)
|
||||
#
|
||||
# Returns an {Object} of decorations in the form `{1: [{type: 'gutter', class: 'someclass'}], 2: ...}`
|
||||
# where the keys are markerIds, and the values are an array of decoration objects attached to the marker.
|
||||
# Returns an {Object} of decorations in the form
|
||||
# `{1: [{id: 10, type: 'gutter', class: 'someclass'}], 2: ...}`
|
||||
# where the keys are {Marker} IDs, and the values are an array of decoration
|
||||
# params objects attached to the marker.
|
||||
# Returns an empty object when no decorations are found
|
||||
decorationsForScreenRowRange: (startScreenRow, endScreenRow) ->
|
||||
@displayBuffer.decorationsForScreenRowRange(startScreenRow, endScreenRow)
|
||||
@@ -1088,62 +1096,45 @@ class Editor extends Model
|
||||
# is invalidated, or is destroyed, the decoration will be updated to reflect
|
||||
# the marker's state.
|
||||
#
|
||||
# There are three types of supported decorations:
|
||||
# * `line`: Adds your CSS `class` to the line nodes within the range
|
||||
# marked by the marker
|
||||
# * `gutter`: Adds your CSS `class` to the line number nodes within the
|
||||
# range marked by the marker
|
||||
# * `highlight`: Adds a new highlight div to the editor surrounding the
|
||||
# range marked by the marker. When the user selects text, the selection is
|
||||
# visualized with a highlight decoration internally. The structure of this
|
||||
# highlight will be:
|
||||
# ```html
|
||||
# <div class="highlight <your-class>">
|
||||
# <!-- Will be one region for each row in the range. Spans 2 lines? There will be 2 regions. -->
|
||||
# <div class="region"></div>
|
||||
# </div>
|
||||
# ```
|
||||
#
|
||||
# marker - A {Marker} you want this decoration to follow.
|
||||
# decoration - An {Object} representing the decoration eg. `{type: 'gutter', class: 'linter-error'}`
|
||||
# The decoration can contain the following keys:
|
||||
# * type: There are a few supported decoration types:
|
||||
# * `gutter`: Applies the decoration to the line numbers spanned by the
|
||||
# marker.
|
||||
# * `line`: Applies the decoration to the lines spanned by the marker.
|
||||
# * `highlight`: Applies the decoration to a "highlight" behind the
|
||||
# marked range. When the user selects text, the selection is
|
||||
# visualized with a highlight decoration internally.
|
||||
# * class: This CSS class will be applied to the decorated line number,
|
||||
# line, or highlight.
|
||||
# * onlyHead: If `true`, the decoration will only be applied to the head
|
||||
# of the marker. Only applicable to the `line` and `gutter` types.
|
||||
# * onlyEmpty: If `true`, the decoration will only be applied if the
|
||||
# associated marker is empty. Only applicable to the `line` and
|
||||
# `gutter` types.
|
||||
# * onlyNonEmpty: If `true`, the decoration will only be applied if the
|
||||
# associated marker is non-empty. Only applicable to the `line` and
|
||||
# gutter types.
|
||||
addDecorationForMarker: (marker, decoration) ->
|
||||
@displayBuffer.addDecorationForMarker(marker, decoration)
|
||||
# decorationParams - An {Object} representing the decoration eg. `{type: 'gutter', class: 'linter-error'}`
|
||||
# :type - There are a few supported decoration types:
|
||||
# * `gutter`: Applies the decoration to the line numbers spanned by the marker.
|
||||
# * `line`: Applies the decoration to the lines spanned by the marker.
|
||||
# * `highlight`: Applies the decoration to a "highlight" behind the marked range.
|
||||
# :class - This CSS class will be applied to the decorated line number,
|
||||
# line, or highlight.
|
||||
# :onlyHead - If `true`, the decoration will only be applied to the head
|
||||
# of the marker. Only applicable to the `line` and `gutter` types.
|
||||
# :onlyEmpty - If `true`, the decoration will only be applied if the
|
||||
# associated marker is empty. Only applicable to the `line` and
|
||||
# `gutter` types.
|
||||
# :onlyNonEmpty - If `true`, the decoration will only be applied if the
|
||||
# associated marker is non-empty. Only applicable to the `line` and
|
||||
# gutter types.
|
||||
#
|
||||
# Returns a {Decoration} object
|
||||
decorateMarker: (marker, decorationParams) ->
|
||||
@displayBuffer.decorateMarker(marker, decorationParams)
|
||||
|
||||
# Public: Removes all decorations associated with a {Marker} that match a
|
||||
# `decorationPattern` and stop tracking the {Marker}.
|
||||
#
|
||||
# ```coffee
|
||||
# marker = editor.markBufferRange([[4, 13], [5, 17]])
|
||||
# editor.removeDecorationForMarker(marker, {type: 'gutter', class: 'linter-error'})
|
||||
# ```
|
||||
#
|
||||
# All decorations matching a pattern will be removed. For example, you might
|
||||
# have decorations with a namespace like this attached to a row:
|
||||
#
|
||||
# ```coffee
|
||||
# [
|
||||
# {type: 'gutter', namespace: 'myns', class: 'something'},
|
||||
# {type: 'gutter', namespace: 'myns', class: 'something-else'}
|
||||
# ]
|
||||
# ```
|
||||
#
|
||||
# You can remove both with:
|
||||
#
|
||||
# ```coffee
|
||||
# editor.removeDecorationForMarker(marker, {namespace: 'myns'})
|
||||
# ```
|
||||
#
|
||||
# marker - the {Marker} to detach from
|
||||
# decorationPattern - the {Object} decoration type to filter by eg. `{type: 'gutter', class: 'linter-error'}`
|
||||
#
|
||||
# Returns nothing
|
||||
removeDecorationForMarker: (marker, decorationPattern) ->
|
||||
@displayBuffer.removeDecorationForMarker(marker, decorationPattern)
|
||||
|
||||
decorationMatchesType: (decoration, type) ->
|
||||
@displayBuffer.decorationMatchesType(decoration, type)
|
||||
decorationForId: (id) ->
|
||||
@displayBuffer.decorationForId(id)
|
||||
|
||||
# Public: Get the {DisplayBufferMarker} for the given marker id.
|
||||
getMarker: (id) ->
|
||||
@@ -1250,9 +1241,9 @@ class Editor extends Model
|
||||
addCursor: (marker) ->
|
||||
cursor = new Cursor(editor: this, marker: marker)
|
||||
@cursors.push(cursor)
|
||||
@addDecorationForMarker(marker, type: 'gutter', class: 'cursor-line')
|
||||
@addDecorationForMarker(marker, type: 'gutter', class: 'cursor-line-no-selection', onlyHead: true, onlyEmpty: true)
|
||||
@addDecorationForMarker(marker, type: 'line', class: 'cursor-line', onlyEmpty: true)
|
||||
@decorateMarker(marker, type: 'gutter', class: 'cursor-line')
|
||||
@decorateMarker(marker, type: 'gutter', class: 'cursor-line-no-selection', onlyHead: true, onlyEmpty: true)
|
||||
@decorateMarker(marker, type: 'line', class: 'cursor-line', onlyEmpty: true)
|
||||
@emit 'cursor-added', cursor
|
||||
cursor
|
||||
|
||||
@@ -1279,7 +1270,6 @@ class Editor extends Model
|
||||
if selection.intersectsBufferRange(selectionBufferRange)
|
||||
return selection
|
||||
else
|
||||
@addDecorationForMarker(marker, type: 'highlight', class: 'selection')
|
||||
@emit 'selection-added', selection
|
||||
selection
|
||||
|
||||
@@ -1294,7 +1284,7 @@ class Editor extends Model
|
||||
addSelectionForBufferRange: (bufferRange, options={}) ->
|
||||
@markBufferRange(bufferRange, _.defaults(@getSelectionMarkerAttributes(), options))
|
||||
selection = @getLastSelection()
|
||||
selection.autoscroll()
|
||||
selection.autoscroll() if @manageScrollPosition
|
||||
selection
|
||||
|
||||
# Public: Set the selected range in buffer coordinates. If there are multiple
|
||||
@@ -1502,7 +1492,7 @@ class Editor extends Model
|
||||
# text - A {String}
|
||||
#
|
||||
# Returns the {Range} of the newly-inserted text.
|
||||
setTextInBufferRange: (range, text) -> @getBuffer().setTextInRange(range, text)
|
||||
setTextInBufferRange: (range, text, normalizeLineEndings) -> @getBuffer().setTextInRange(range, text, normalizeLineEndings)
|
||||
|
||||
# Public: Get the {Range} of the paragraph surrounding the most recently added
|
||||
# cursor.
|
||||
|
||||
@@ -2,6 +2,7 @@ _ = require 'underscore-plus'
|
||||
React = require 'react-atom-fork'
|
||||
{div} = require 'reactionary-atom-fork'
|
||||
{isEqual, isEqualForProperties, multiplyString, toArray} = require 'underscore-plus'
|
||||
Decoration = require './decoration'
|
||||
SubscriberMixin = require './subscriber-mixin'
|
||||
|
||||
WrapperDiv = document.createElement('div')
|
||||
@@ -40,6 +41,7 @@ GutterComponent = React.createClass
|
||||
|
||||
componentDidMount: ->
|
||||
@appendDummyLineNumber()
|
||||
@updateLineNumbers() if @props.renderedRowRange?
|
||||
|
||||
# Only update the gutter if the visible row range has changed or if a
|
||||
# non-zero-delta change to the screen lines has occurred within the current
|
||||
@@ -153,8 +155,8 @@ GutterComponent = React.createClass
|
||||
|
||||
classes = ''
|
||||
if lineDecorations? and decorations = lineDecorations[screenRow]
|
||||
for decoration in decorations
|
||||
if editor.decorationMatchesType(decoration, 'gutter')
|
||||
for id, decoration of decorations
|
||||
if Decoration.isType(decoration, 'gutter')
|
||||
classes += decoration.class + ' '
|
||||
|
||||
classes += "foldable " if bufferRow >= 0 and editor.isFoldableAtBufferRow(bufferRow)
|
||||
@@ -185,12 +187,13 @@ GutterComponent = React.createClass
|
||||
previousDecorations = @renderedDecorationsByLineNumberId[lineNumberId]
|
||||
|
||||
if previousDecorations?
|
||||
for decoration in previousDecorations
|
||||
node.classList.remove(decoration.class) if editor.decorationMatchesType(decoration, 'gutter') and not _.deepContains(decorations, decoration)
|
||||
for id, decoration of previousDecorations
|
||||
if Decoration.isType(decoration, 'gutter') and not @hasDecoration(decorations, decoration)
|
||||
node.classList.remove(decoration.class)
|
||||
|
||||
if decorations?
|
||||
for decoration in decorations
|
||||
if editor.decorationMatchesType(decoration, 'gutter') and not _.deepContains(previousDecorations, decoration)
|
||||
for id, decoration of decorations
|
||||
if Decoration.isType(decoration, 'gutter') and not @hasDecoration(previousDecorations, decoration)
|
||||
node.classList.add(decoration.class)
|
||||
|
||||
unless @screenRowsByLineNumberId[lineNumberId] is screenRow
|
||||
@@ -200,6 +203,9 @@ GutterComponent = React.createClass
|
||||
@screenRowsByLineNumberId[lineNumberId] = screenRow
|
||||
@lineNumberIdsByScreenRow[screenRow] = lineNumberId
|
||||
|
||||
hasDecoration: (decorations, decoration) ->
|
||||
decorations? and decorations[decoration.id] is decoration
|
||||
|
||||
hasLineNumberNode: (lineNumberId) ->
|
||||
@lineNumberNodesById.hasOwnProperty(lineNumberId)
|
||||
|
||||
|
||||
@@ -11,12 +11,35 @@ HighlightComponent = React.createClass
|
||||
|
||||
className = 'highlight'
|
||||
className += " #{decoration.class}" if decoration.class?
|
||||
|
||||
div {className},
|
||||
if endPixelPosition.top is startPixelPosition.top
|
||||
@renderSingleLineRegions()
|
||||
else
|
||||
@renderMultiLineRegions()
|
||||
|
||||
componentDidMount: ->
|
||||
{editor, decoration} = @props
|
||||
if decoration.id?
|
||||
@decoration = editor.decorationForId(decoration.id)
|
||||
@decoration.on 'flash', @startFlashAnimation
|
||||
@startFlashAnimation()
|
||||
|
||||
componentWillUnmount: ->
|
||||
@decoration?.off 'flash', @startFlashAnimation
|
||||
|
||||
startFlashAnimation: ->
|
||||
return unless flash = @decoration.consumeNextFlash()
|
||||
|
||||
node = @getDOMNode()
|
||||
node.classList.remove(flash.class)
|
||||
|
||||
requestAnimationFrame =>
|
||||
node.classList.add(flash.class)
|
||||
clearTimeout(@flashTimeoutId)
|
||||
removeFlashClass = -> node.classList.remove(flash.class)
|
||||
@flashTimeoutId = setTimeout(removeFlashClass, flash.duration)
|
||||
|
||||
renderSingleLineRegions: ->
|
||||
{startPixelPosition, endPixelPosition, lineHeightInPixels} = @props
|
||||
|
||||
|
||||
@@ -12,12 +12,12 @@ HighlightsComponent = React.createClass
|
||||
@renderHighlights() if @isMounted()
|
||||
|
||||
renderHighlights: ->
|
||||
{highlightDecorations, lineHeightInPixels} = @props
|
||||
{editor, highlightDecorations, lineHeightInPixels} = @props
|
||||
|
||||
highlightComponents = []
|
||||
for markerId, {startPixelPosition, endPixelPosition, decorations} of highlightDecorations
|
||||
for decoration in decorations
|
||||
highlightComponents.push(HighlightComponent({key: "#{markerId}-#{decoration.class}", startPixelPosition, endPixelPosition, decoration, lineHeightInPixels}))
|
||||
highlightComponents.push(HighlightComponent({editor, key: "#{markerId}-#{decoration.class}", startPixelPosition, endPixelPosition, decoration, lineHeightInPixels}))
|
||||
|
||||
highlightComponents
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ React = require 'react-atom-fork'
|
||||
{debounce, isEqual, isEqualForProperties, multiplyString, toArray} = require 'underscore-plus'
|
||||
{$$} = require 'space-pen'
|
||||
|
||||
Decoration = require './decoration'
|
||||
HighlightsComponent = require './highlights-component'
|
||||
|
||||
DummyLineNode = $$(-> @div className: 'line', style: 'position: absolute; visibility: hidden;', => @span 'x')[0]
|
||||
@@ -134,8 +135,8 @@ LinesComponent = React.createClass
|
||||
|
||||
classes = ''
|
||||
if decorations = lineDecorations[screenRow]
|
||||
for decoration in decorations
|
||||
if editor.decorationMatchesType(decoration, 'line')
|
||||
for id, decoration of decorations
|
||||
if Decoration.isType(decoration, 'line')
|
||||
classes += decoration.class + ' '
|
||||
classes += 'line'
|
||||
|
||||
@@ -152,14 +153,31 @@ LinesComponent = React.createClass
|
||||
lineHTML
|
||||
|
||||
buildEmptyLineInnerHTML: (line) ->
|
||||
{showIndentGuide} = @props
|
||||
{showIndentGuide, invisibles} = @props
|
||||
{cr, eol} = invisibles
|
||||
{indentLevel, tabLength} = line
|
||||
|
||||
if showIndentGuide and indentLevel > 0
|
||||
indentSpan = "<span class='indent-guide'>#{multiplyString(' ', tabLength)}</span>"
|
||||
multiplyString(indentSpan, indentLevel + 1)
|
||||
invisiblesToRender = []
|
||||
invisiblesToRender.push(cr) if cr? and line.lineEnding is '\r\n'
|
||||
invisiblesToRender.push(eol) if eol?
|
||||
|
||||
lineHTML = ''
|
||||
for i in [0...indentLevel]
|
||||
lineHTML += "<span class='indent-guide'>"
|
||||
for j in [0...tabLength]
|
||||
if invisible = invisiblesToRender.shift()
|
||||
lineHTML += "<span class='invisible-character'>#{invisible}</span>"
|
||||
else
|
||||
lineHTML += ' '
|
||||
lineHTML += "</span>"
|
||||
|
||||
while invisiblesToRender.length
|
||||
lineHTML += "<span class='invisible-character'>#{invisiblesToRender.shift()}</span>"
|
||||
|
||||
lineHTML
|
||||
else
|
||||
" "
|
||||
@buildEndOfLineHTML(line, @props.invisibles)
|
||||
|
||||
buildLineInnerHTML: (line) ->
|
||||
{invisibles, mini, showIndentGuide, invisibles} = @props
|
||||
@@ -222,12 +240,13 @@ LinesComponent = React.createClass
|
||||
previousDecorations = @renderedDecorationsByLineId[line.id]
|
||||
|
||||
if previousDecorations?
|
||||
for decoration in previousDecorations
|
||||
lineNode.classList.remove(decoration.class) if editor.decorationMatchesType(decoration, 'line') and not _.deepContains(decorations, decoration)
|
||||
for id, decoration of previousDecorations
|
||||
if Decoration.isType(decoration, 'line') and not @hasDecoration(decorations, decoration)
|
||||
lineNode.classList.remove(decoration.class)
|
||||
|
||||
if decorations?
|
||||
for decoration in decorations
|
||||
if editor.decorationMatchesType(decoration, 'line') and not _.deepContains(previousDecorations, decoration)
|
||||
for id, decoration of decorations
|
||||
if Decoration.isType(decoration, 'line') and not @hasDecoration(previousDecorations, decoration)
|
||||
lineNode.classList.add(decoration.class)
|
||||
|
||||
lineNode.style.width = lineWidth + 'px' if updateWidth
|
||||
@@ -238,6 +257,9 @@ LinesComponent = React.createClass
|
||||
@screenRowsByLineId[line.id] = screenRow
|
||||
@lineIdsByScreenRow[screenRow] = line.id
|
||||
|
||||
hasDecoration: (decorations, decoration) ->
|
||||
decorations? and decorations[decoration.id] is decoration
|
||||
|
||||
lineNodeForScreenRow: (screenRow) ->
|
||||
@lineNodesByLineId[@lineIdsByScreenRow[screenRow]]
|
||||
|
||||
|
||||
@@ -8,6 +8,8 @@ class PaneContainer extends Model
|
||||
atom.deserializers.add(this)
|
||||
Serializable.includeInto(this)
|
||||
|
||||
@version: 1
|
||||
|
||||
@properties
|
||||
root: -> new Pane
|
||||
activePane: null
|
||||
@@ -27,10 +29,12 @@ class PaneContainer extends Model
|
||||
deserializeParams: (params) ->
|
||||
params.root = atom.deserializers.deserialize(params.root, container: this)
|
||||
params.destroyEmptyPanes = atom.config.get('core.destroyEmptyPanes')
|
||||
params.activePane = params.root.getPanes().find (pane) -> pane.id is params.activePaneId
|
||||
params
|
||||
|
||||
serializeParams: (params) ->
|
||||
root: @root?.serialize()
|
||||
activePaneId: @activePane.id
|
||||
|
||||
replaceChild: (oldChild, newChild) ->
|
||||
throw new Error("Replacing non-existent child") if oldChild isnt @root
|
||||
|
||||
+1
-3
@@ -40,14 +40,12 @@ class Pane extends Model
|
||||
@subscribe @items.onRemoval (item, index) =>
|
||||
@unsubscribe item if typeof item.on is 'function'
|
||||
|
||||
@activate() if params?.active
|
||||
|
||||
# Called by the Serializable mixin during serialization.
|
||||
serializeParams: ->
|
||||
id: @id
|
||||
items: compact(@items.map((item) -> item.serialize?()))
|
||||
activeItemUri: @activeItem?.getUri?()
|
||||
focused: @focused
|
||||
active: @active
|
||||
|
||||
# Called by the Serializable mixin during deserialization.
|
||||
deserializeParams: (params) ->
|
||||
|
||||
@@ -38,17 +38,20 @@ class ReactEditorView extends View
|
||||
@overlayer = $(node).find('.lines').addClass('overlayer')
|
||||
@hiddenInput = $(node).find('.hidden-input')
|
||||
|
||||
@gutter = $(node).find('.gutter')
|
||||
@gutter.removeClassFromAllLines = (klass) =>
|
||||
@gutter.find('.line-number').removeClass(klass)
|
||||
# FIXME: there should be a better way to deal with the gutter element
|
||||
@subscribe atom.config.observe 'editor.showLineNumbers', =>
|
||||
@gutter = $(node).find('.gutter')
|
||||
|
||||
@gutter.getLineNumberElement = (bufferRow) =>
|
||||
@gutter.find("[data-buffer-row='#{bufferRow}']")
|
||||
@gutter.removeClassFromAllLines = (klass) =>
|
||||
@gutter.find('.line-number').removeClass(klass)
|
||||
|
||||
@gutter.addClassToLine = (bufferRow, klass) =>
|
||||
lines = @gutter.find("[data-buffer-row='#{bufferRow}']")
|
||||
lines.addClass(klass)
|
||||
lines.length > 0
|
||||
@gutter.getLineNumberElement = (bufferRow) =>
|
||||
@gutter.find("[data-buffer-row='#{bufferRow}']")
|
||||
|
||||
@gutter.addClassToLine = (bufferRow, klass) =>
|
||||
lines = @gutter.find("[data-buffer-row='#{bufferRow}']")
|
||||
lines.addClass(klass)
|
||||
lines.length > 0
|
||||
|
||||
@focus() if @focusOnAttach
|
||||
|
||||
|
||||
@@ -15,6 +15,8 @@ class Selection extends Model
|
||||
constructor: ({@cursor, @marker, @editor, id}) ->
|
||||
@assignId(id)
|
||||
@cursor.selection = this
|
||||
@decoration = @editor.decorateMarker(@marker, type: 'highlight', class: 'selection')
|
||||
|
||||
@marker.on 'changed', => @screenRangeChanged()
|
||||
@marker.on 'destroyed', =>
|
||||
@destroyed = true
|
||||
@@ -76,9 +78,12 @@ class Selection extends Model
|
||||
options.reversed ?= @isReversed()
|
||||
@editor.destroyFoldsIntersectingBufferRange(bufferRange) unless options.preserveFolds
|
||||
@modifySelection =>
|
||||
needsFlash = options.flash
|
||||
delete options.flash if options.flash?
|
||||
@cursor.needsAutoscroll = false if @needsAutoscroll?
|
||||
@marker.setBufferRange(bufferRange, options)
|
||||
@autoscroll() if @needsAutoscroll and @editor.manageScrollPosition
|
||||
@decoration.flash('flash', @editor.selectionFlashDuration) if needsFlash
|
||||
|
||||
# Public: Returns the starting and ending buffer rows the selection is
|
||||
# highlighting.
|
||||
@@ -315,12 +320,12 @@ class Selection extends Model
|
||||
wasReversed = @isReversed()
|
||||
@clear()
|
||||
@cursor.needsAutoscroll = @cursor.isLastCursor()
|
||||
@cursor.autoscroll() if @editor.manageScrollPosition and @cursor.isLastCursor()
|
||||
|
||||
if options.indentBasis? and not options.autoIndent
|
||||
text = @normalizeIndents(text, options.indentBasis)
|
||||
|
||||
newBufferRange = @editor.buffer.setTextInRange(oldBufferRange, text, pick(options, 'undo'))
|
||||
|
||||
if options.select
|
||||
@setBufferRange(newBufferRange, reversed: wasReversed)
|
||||
else
|
||||
|
||||
@@ -231,22 +231,27 @@ class TokenizedBuffer extends Model
|
||||
|
||||
indentLevelForRow: (row) ->
|
||||
line = @buffer.lineForRow(row)
|
||||
indentLevel = 0
|
||||
|
||||
if line is ''
|
||||
nextRow = row + 1
|
||||
lineCount = @getLineCount()
|
||||
while nextRow < lineCount
|
||||
nextLine = @buffer.lineForRow(nextRow)
|
||||
return @indentLevelForLine(nextLine) unless nextLine is ''
|
||||
unless nextLine is ''
|
||||
indentLevel = Math.ceil(@indentLevelForLine(nextLine))
|
||||
break
|
||||
nextRow++
|
||||
|
||||
previousRow = row - 1
|
||||
while previousRow >= 0
|
||||
previousLine = @buffer.lineForRow(previousRow)
|
||||
return @indentLevelForLine(previousLine) unless previousLine is ''
|
||||
unless previousLine is ''
|
||||
indentLevel = Math.max(Math.ceil(@indentLevelForLine(previousLine)), indentLevel)
|
||||
break
|
||||
previousRow--
|
||||
|
||||
0
|
||||
indentLevel
|
||||
else
|
||||
@indentLevelForLine(line)
|
||||
|
||||
|
||||
@@ -64,15 +64,15 @@
|
||||
}
|
||||
|
||||
.btn-toolbar {
|
||||
> .btn-group {
|
||||
> .btn-group + .btn-group, > .btn-group + .btn, > .btn + .btn {
|
||||
float: none;
|
||||
display: inline-block;
|
||||
}
|
||||
> * {
|
||||
margin-right: 0;
|
||||
margin-left: @component-padding/2;
|
||||
}
|
||||
> *:first-child {
|
||||
margin-left: 0;
|
||||
}
|
||||
> * {
|
||||
margin-right: @component-padding / 2;
|
||||
}
|
||||
> *:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -179,6 +179,11 @@
|
||||
box-shadow: inset 1px 0;
|
||||
}
|
||||
|
||||
.editor .vertical-scrollbar,
|
||||
.editor .horizontal-scrollbar {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.editor .vertical-scrollbar {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
|
||||
@@ -19,3 +19,17 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.define-selection-flash-color-if-not-defined() { @syntax-selection-flash-color: rgba(100, 255, 100, 0.7); }
|
||||
.define-selection-flash-color-if-not-defined();
|
||||
|
||||
@-webkit-keyframes flash {
|
||||
from { background-color: @syntax-selection-flash-color; }
|
||||
to { background-color: null; }
|
||||
}
|
||||
|
||||
.editor .flash.selection .region {
|
||||
-webkit-animation-name: flash;
|
||||
-webkit-animation-duration: .5s;
|
||||
-webkit-animation-iteration-count: 1;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
@syntax-text-color: #333;
|
||||
@syntax-cursor-color: #333;
|
||||
@syntax-selection-color: #69c;
|
||||
@syntax-selection-flash-color: #00f; // Color the selection is 'flashed' when you run find next
|
||||
@syntax-background-color: #fff;
|
||||
|
||||
// Guide colors
|
||||
|
||||
Referência em uma Nova Issue
Bloquear um usuário