mirror of
https://github.com/atom/atom.git
synced 2026-01-23 05:48:10 -05:00
Merge branch 'master' into ns-mb-detangle-editor
This commit is contained in:
@@ -20,6 +20,7 @@ These are just guidelines, not rules, use your best judgment and feel free to pr
|
||||
|
||||
[Styleguides](#styleguides)
|
||||
* [Git Commit Messages](#git-commit-messages)
|
||||
* [JavaScript Styleguide](#javascript-styleguide)
|
||||
* [CoffeeScript Styleguide](#coffeescript-styleguide)
|
||||
* [Specs Styleguide](#specs-styleguide)
|
||||
* [Documentation Styleguide](#documentation-styleguide)
|
||||
@@ -280,6 +281,25 @@ If you want to read about using Atom or developing packages in Atom, the [Atom F
|
||||
* :arrow_down: `:arrow_down:` when downgrading dependencies
|
||||
* :shirt: `:shirt:` when removing linter warnings
|
||||
|
||||
### JavaScript Styleguide
|
||||
|
||||
All JavaScript must adhere to [JavaScript Standard Style](http://standardjs.com/).
|
||||
|
||||
* Prefer `Object.assign()` to the object spread operator (`{...anotherObj}`)
|
||||
* Inline `export`s with expressions
|
||||
```js
|
||||
// Use this:
|
||||
export default class ClassName {
|
||||
|
||||
}
|
||||
|
||||
// Instead of:
|
||||
class ClassName {
|
||||
|
||||
}
|
||||
export default ClassName
|
||||
```
|
||||
|
||||
### CoffeeScript Styleguide
|
||||
|
||||
* Set parameter defaults without spaces around the equal sign
|
||||
|
||||
@@ -320,7 +320,7 @@ getDefaultChannelAndReleaseBranch = (version) ->
|
||||
else
|
||||
channel = 'stable'
|
||||
|
||||
minorVersion = version.match(/^\d\.\d/)[0]
|
||||
minorVersion = version.match(/^\d+\.\d+/)[0]
|
||||
releaseBranch = "#{minorVersion}-releases"
|
||||
[channel, releaseBranch]
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
"grunt-peg": "~1.1.0",
|
||||
"grunt-shell": "~0.3.1",
|
||||
"grunt-standard": "^2.0.0",
|
||||
"joanna": "0.0.6",
|
||||
"legal-eagle": "~0.13.0",
|
||||
"minidump": "~0.9",
|
||||
"npm": "3.10.5",
|
||||
|
||||
@@ -4,7 +4,9 @@ fs = require 'fs-plus'
|
||||
_ = require 'underscore-plus'
|
||||
|
||||
donna = require 'donna'
|
||||
joanna = require 'joanna'
|
||||
tello = require 'tello'
|
||||
glob = require 'glob'
|
||||
|
||||
module.exports = (grunt) ->
|
||||
getClassesToInclude = ->
|
||||
@@ -30,8 +32,16 @@ module.exports = (grunt) ->
|
||||
grunt.registerTask 'build-docs', 'Builds the API docs in src', ->
|
||||
docsOutputDir = grunt.config.get('docsOutputDir')
|
||||
|
||||
metadata = donna.generateMetadata(['.'])
|
||||
api = tello.digest(metadata)
|
||||
[coffeeMetadata] = donna.generateMetadata(['.'])
|
||||
jsMetadata = joanna(glob.sync('src/*.js'))
|
||||
|
||||
metadata = {
|
||||
repository: coffeeMetadata.repository,
|
||||
version: coffeeMetadata.version,
|
||||
files: Object.assign(coffeeMetadata.files, jsMetadata.files)
|
||||
}
|
||||
|
||||
api = tello.digest([metadata])
|
||||
_.extend(api.classes, getClassesToInclude())
|
||||
api.classes = sortClasses(api.classes)
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "atom",
|
||||
"productName": "Atom",
|
||||
"version": "1.10.0-dev",
|
||||
"version": "1.11.0-dev",
|
||||
"description": "A hackable text editor for the 21st Century.",
|
||||
"main": "./src/main-process/main.js",
|
||||
"repository": {
|
||||
@@ -58,7 +58,7 @@
|
||||
"sinon": "1.17.4",
|
||||
"source-map-support": "^0.3.2",
|
||||
"temp": "0.8.1",
|
||||
"text-buffer": "9.2.2",
|
||||
"text-buffer": "9.2.3",
|
||||
"typescript-simple": "1.0.0",
|
||||
"underscore-plus": "^1.6.6",
|
||||
"winreg": "^1.2.1",
|
||||
@@ -77,7 +77,7 @@
|
||||
"one-light-syntax": "1.3.0",
|
||||
"solarized-dark-syntax": "1.0.2",
|
||||
"solarized-light-syntax": "1.0.2",
|
||||
"about": "1.5.3",
|
||||
"about": "1.6.0",
|
||||
"archive-view": "0.61.1",
|
||||
"autocomplete-atom-api": "0.10.0",
|
||||
"autocomplete-css": "0.11.2",
|
||||
@@ -109,7 +109,7 @@
|
||||
"notifications": "0.65.0",
|
||||
"open-on-github": "1.2.0",
|
||||
"package-generator": "1.0.0",
|
||||
"settings-view": "0.241.0",
|
||||
"settings-view": "0.241.1",
|
||||
"snippets": "1.0.2",
|
||||
"spell-check": "0.67.1",
|
||||
"status-bar": "1.4.0",
|
||||
|
||||
@@ -24,14 +24,14 @@ describe "Package", ->
|
||||
mockLocalStorage()
|
||||
|
||||
it "does not activate it", ->
|
||||
packagePath = atom.project.getDirectories()[0]?.resolve('packages/package-with-incompatible-native-module')
|
||||
packagePath = atom.project.getDirectories()[0].resolve('packages/package-with-incompatible-native-module')
|
||||
pack = buildPackage(packagePath)
|
||||
expect(pack.isCompatible()).toBe false
|
||||
expect(pack.incompatibleModules[0].name).toBe 'native-module'
|
||||
expect(pack.incompatibleModules[0].path).toBe path.join(packagePath, 'node_modules', 'native-module')
|
||||
|
||||
it "utilizes _atomModuleCache if present to determine the package's native dependencies", ->
|
||||
packagePath = atom.project.getDirectories()[0]?.resolve('packages/package-with-ignored-incompatible-native-module')
|
||||
packagePath = atom.project.getDirectories()[0].resolve('packages/package-with-ignored-incompatible-native-module')
|
||||
pack = buildPackage(packagePath)
|
||||
expect(pack.getNativeModuleDependencyPaths().length).toBe(1) # doesn't see the incompatible module
|
||||
expect(pack.isCompatible()).toBe true
|
||||
@@ -41,8 +41,7 @@ describe "Package", ->
|
||||
expect(pack.isCompatible()).toBe false
|
||||
|
||||
it "caches the incompatible native modules in local storage", ->
|
||||
packagePath = atom.project.getDirectories()[0]?.resolve('packages/package-with-incompatible-native-module')
|
||||
|
||||
packagePath = atom.project.getDirectories()[0].resolve('packages/package-with-incompatible-native-module')
|
||||
expect(buildPackage(packagePath).isCompatible()).toBe false
|
||||
expect(global.localStorage.getItem.callCount).toBe 1
|
||||
expect(global.localStorage.setItem.callCount).toBe 1
|
||||
@@ -51,6 +50,18 @@ describe "Package", ->
|
||||
expect(global.localStorage.getItem.callCount).toBe 2
|
||||
expect(global.localStorage.setItem.callCount).toBe 1
|
||||
|
||||
it "logs an error to the console describing the problem", ->
|
||||
packagePath = atom.project.getDirectories()[0].resolve('packages/package-with-incompatible-native-module')
|
||||
|
||||
spyOn(console, 'warn')
|
||||
spyOn(atom.notifications, 'addFatalError')
|
||||
|
||||
buildPackage(packagePath).activateNow()
|
||||
|
||||
expect(atom.notifications.addFatalError).not.toHaveBeenCalled()
|
||||
expect(console.warn.callCount).toBe(1)
|
||||
expect(console.warn.mostRecentCall.args[0]).toContain('it requires one or more incompatible native modules (native-module)')
|
||||
|
||||
describe "::rebuild()", ->
|
||||
beforeEach ->
|
||||
mockLocalStorage()
|
||||
|
||||
@@ -1,91 +0,0 @@
|
||||
{times, random} = require 'underscore-plus'
|
||||
randomWords = require 'random-words'
|
||||
TextBuffer = require 'text-buffer'
|
||||
TextEditor = require '../src/text-editor'
|
||||
|
||||
describe "TextEditor", ->
|
||||
[editor, tokenizedBuffer, buffer, steps] = []
|
||||
|
||||
softWrapColumn = 80
|
||||
|
||||
beforeEach ->
|
||||
atom.config.set('editor.softWrapAtPreferredLineLength', true)
|
||||
atom.config.set('editor.preferredLineLength', softWrapColumn)
|
||||
|
||||
it "properly renders soft-wrapped lines when randomly mutated", ->
|
||||
times 10, (i) ->
|
||||
buffer = new TextBuffer
|
||||
editor = atom.workspace.buildTextEditor({buffer})
|
||||
editor.setEditorWidthInChars(80)
|
||||
tokenizedBuffer = editor.tokenizedBuffer
|
||||
steps = []
|
||||
|
||||
times 30, ->
|
||||
randomlyMutateEditor()
|
||||
verifyLines()
|
||||
|
||||
verifyLines = ->
|
||||
{bufferRows, screenLines} = getReferenceScreenLines()
|
||||
for referenceBufferRow, screenRow in bufferRows
|
||||
referenceScreenLine = screenLines[screenRow]
|
||||
actualBufferRow = editor.bufferRowForScreenRow(screenRow)
|
||||
unless actualBufferRow is referenceBufferRow
|
||||
logLines()
|
||||
throw new Error("Invalid buffer row #{actualBufferRow} for screen row #{screenRow}", )
|
||||
|
||||
actualScreenLine = editor.lineTextForScreenRow(screenRow)
|
||||
unless actualScreenLine is referenceScreenLine
|
||||
logLines()
|
||||
throw new Error("Invalid line text at screen row #{screenRow}")
|
||||
|
||||
logLines = ->
|
||||
console.log "==== screen lines ===="
|
||||
editor.logScreenLines()
|
||||
console.log "==== reference lines ===="
|
||||
{bufferRows, screenLines} = getReferenceScreenLines()
|
||||
for bufferRow, screenRow in bufferRows
|
||||
console.log screenRow, bufferRow, screenLines[screenRow].text
|
||||
console.log "==== steps to reproduce this failure: ==="
|
||||
for step in steps
|
||||
console.log 'editor.' + step[0] + '('+ step[1..].map((a) -> JSON.stringify(a)).join(', ') + ')'
|
||||
|
||||
randomlyMutateEditor = ->
|
||||
if Math.random() < .2
|
||||
softWrapped = not editor.isSoftWrapped()
|
||||
steps.push(['setSoftWrapped', softWrapped])
|
||||
editor.setSoftWrapped(softWrapped)
|
||||
else
|
||||
range = getRandomRange()
|
||||
text = getRandomText()
|
||||
steps.push(['setTextInBufferRange', range, text])
|
||||
editor.setTextInBufferRange(range, text)
|
||||
|
||||
getRandomRange = ->
|
||||
startRow = random(0, buffer.getLastRow())
|
||||
startColumn = random(0, buffer.lineForRow(startRow).length)
|
||||
endRow = random(startRow, buffer.getLastRow())
|
||||
endColumn = random(0, buffer.lineForRow(endRow).length)
|
||||
[[startRow, startColumn], [endRow, endColumn]]
|
||||
|
||||
getRandomText = ->
|
||||
text = []
|
||||
max = buffer.getText().split(/\s/).length * 0.75
|
||||
|
||||
times random(5, max), ->
|
||||
if Math.random() < .1
|
||||
text += '\n'
|
||||
else
|
||||
text += " " if /\w$/.test(text)
|
||||
text += randomWords(exactly: 1)
|
||||
text
|
||||
|
||||
getReferenceScreenLines = ->
|
||||
referenceEditor = atom.workspace.buildTextEditor()
|
||||
referenceEditor.setEditorWidthInChars(80)
|
||||
referenceEditor.setText(editor.getText())
|
||||
referenceEditor.setSoftWrapped(editor.isSoftWrapped())
|
||||
|
||||
screenLines = [0..referenceEditor.getLastScreenRow()].map (row) -> referenceEditor.lineTextForScreenRow(row)
|
||||
bufferRows = referenceEditor.bufferRowsForScreenRows(0, referenceEditor.getLastScreenRow())
|
||||
|
||||
{screenLines, bufferRows}
|
||||
@@ -1,104 +0,0 @@
|
||||
RowMap = require '../src/row-map'
|
||||
|
||||
describe "RowMap", ->
|
||||
map = null
|
||||
|
||||
beforeEach ->
|
||||
map = new RowMap
|
||||
|
||||
describe "::screenRowRangeForBufferRow(bufferRow)", ->
|
||||
it "returns the range of screen rows corresponding to the given buffer row", ->
|
||||
map.spliceRegions(0, 0, [
|
||||
{bufferRows: 5, screenRows: 5}
|
||||
{bufferRows: 1, screenRows: 5}
|
||||
{bufferRows: 5, screenRows: 5}
|
||||
{bufferRows: 5, screenRows: 1}
|
||||
])
|
||||
|
||||
expect(map.screenRowRangeForBufferRow(0)).toEqual [0, 1]
|
||||
expect(map.screenRowRangeForBufferRow(5)).toEqual [5, 10]
|
||||
expect(map.screenRowRangeForBufferRow(6)).toEqual [10, 11]
|
||||
expect(map.screenRowRangeForBufferRow(11)).toEqual [15, 16]
|
||||
expect(map.screenRowRangeForBufferRow(12)).toEqual [15, 16]
|
||||
expect(map.screenRowRangeForBufferRow(16)).toEqual [16, 17]
|
||||
|
||||
describe "::bufferRowRangeForScreenRow(screenRow)", ->
|
||||
it "returns the range of buffer rows corresponding to the given screen row", ->
|
||||
map.spliceRegions(0, 0, [
|
||||
{bufferRows: 5, screenRows: 5}
|
||||
{bufferRows: 1, screenRows: 5}
|
||||
{bufferRows: 5, screenRows: 5}
|
||||
{bufferRows: 5, screenRows: 1}
|
||||
])
|
||||
|
||||
expect(map.bufferRowRangeForScreenRow(0)).toEqual [0, 1]
|
||||
expect(map.bufferRowRangeForScreenRow(5)).toEqual [5, 6]
|
||||
expect(map.bufferRowRangeForScreenRow(6)).toEqual [5, 6]
|
||||
expect(map.bufferRowRangeForScreenRow(10)).toEqual [6, 7]
|
||||
expect(map.bufferRowRangeForScreenRow(14)).toEqual [10, 11]
|
||||
expect(map.bufferRowRangeForScreenRow(15)).toEqual [11, 16]
|
||||
expect(map.bufferRowRangeForScreenRow(16)).toEqual [16, 17]
|
||||
|
||||
describe "::spliceRegions(startBufferRow, bufferRowCount, regions)", ->
|
||||
it "can insert regions when empty", ->
|
||||
regions = [
|
||||
{bufferRows: 5, screenRows: 5}
|
||||
{bufferRows: 1, screenRows: 5}
|
||||
{bufferRows: 5, screenRows: 5}
|
||||
{bufferRows: 5, screenRows: 1}
|
||||
]
|
||||
map.spliceRegions(0, 0, regions)
|
||||
expect(map.getRegions()).toEqual regions
|
||||
|
||||
it "can insert wrapped lines into rectangular regions", ->
|
||||
map.spliceRegions(0, 0, [{bufferRows: 10, screenRows: 10}])
|
||||
map.spliceRegions(5, 0, [{bufferRows: 1, screenRows: 3}])
|
||||
expect(map.getRegions()).toEqual [
|
||||
{bufferRows: 5, screenRows: 5}
|
||||
{bufferRows: 1, screenRows: 3}
|
||||
{bufferRows: 5, screenRows: 5}
|
||||
]
|
||||
|
||||
it "can splice wrapped lines into rectangular regions", ->
|
||||
map.spliceRegions(0, 0, [{bufferRows: 10, screenRows: 10}])
|
||||
map.spliceRegions(5, 1, [{bufferRows: 1, screenRows: 3}])
|
||||
expect(map.getRegions()).toEqual [
|
||||
{bufferRows: 5, screenRows: 5}
|
||||
{bufferRows: 1, screenRows: 3}
|
||||
{bufferRows: 4, screenRows: 4}
|
||||
]
|
||||
|
||||
it "can splice folded lines into rectangular regions", ->
|
||||
map.spliceRegions(0, 0, [{bufferRows: 10, screenRows: 10}])
|
||||
map.spliceRegions(5, 3, [{bufferRows: 3, screenRows: 1}])
|
||||
expect(map.getRegions()).toEqual [
|
||||
{bufferRows: 5, screenRows: 5}
|
||||
{bufferRows: 3, screenRows: 1}
|
||||
{bufferRows: 2, screenRows: 2}
|
||||
]
|
||||
|
||||
it "can replace folded regions with a folded region that surrounds them", ->
|
||||
map.spliceRegions(0, 0, [
|
||||
{bufferRows: 3, screenRows: 3}
|
||||
{bufferRows: 3, screenRows: 1}
|
||||
{bufferRows: 1, screenRows: 1}
|
||||
{bufferRows: 3, screenRows: 1}
|
||||
{bufferRows: 3, screenRows: 3}
|
||||
])
|
||||
map.spliceRegions(2, 8, [{bufferRows: 8, screenRows: 1}])
|
||||
expect(map.getRegions()).toEqual [
|
||||
{bufferRows: 2, screenRows: 2}
|
||||
{bufferRows: 8, screenRows: 1}
|
||||
{bufferRows: 3, screenRows: 3}
|
||||
]
|
||||
|
||||
it "merges adjacent rectangular regions", ->
|
||||
map.spliceRegions(0, 0, [
|
||||
{bufferRows: 3, screenRows: 3}
|
||||
{bufferRows: 3, screenRows: 1}
|
||||
{bufferRows: 1, screenRows: 1}
|
||||
{bufferRows: 3, screenRows: 1}
|
||||
{bufferRows: 3, screenRows: 3}
|
||||
])
|
||||
|
||||
map.spliceRegions(3, 7, [{bufferRows: 5, screenRows: 5}])
|
||||
@@ -4,100 +4,152 @@ import TokenizedBufferIterator from '../src/tokenized-buffer-iterator'
|
||||
import {Point} from 'text-buffer'
|
||||
|
||||
describe('TokenizedBufferIterator', () => {
|
||||
it('reports two boundaries at the same position when tags close, open, then close again without a non-negative integer separating them (regression)', () => {
|
||||
const tokenizedBuffer = {
|
||||
tokenizedLineForRow () {
|
||||
return {
|
||||
tags: [-1, -2, -1, -2],
|
||||
text: '',
|
||||
openScopes: []
|
||||
}
|
||||
},
|
||||
|
||||
grammar: {
|
||||
scopeForId () {
|
||||
return 'foo'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const iterator = new TokenizedBufferIterator(tokenizedBuffer)
|
||||
|
||||
iterator.seek(Point(0, 0))
|
||||
expect(iterator.getPosition()).toEqual(Point(0, 0))
|
||||
expect(iterator.getCloseTags()).toEqual([])
|
||||
expect(iterator.getOpenTags()).toEqual(['foo'])
|
||||
|
||||
iterator.moveToSuccessor()
|
||||
expect(iterator.getPosition()).toEqual(Point(0, 0))
|
||||
expect(iterator.getCloseTags()).toEqual(['foo'])
|
||||
expect(iterator.getOpenTags()).toEqual(['foo'])
|
||||
|
||||
iterator.moveToSuccessor()
|
||||
expect(iterator.getCloseTags()).toEqual(['foo'])
|
||||
expect(iterator.getOpenTags()).toEqual([])
|
||||
})
|
||||
|
||||
it("reports a boundary at line end if the next line's open scopes don't match the containing tags for the current line", () => {
|
||||
const tokenizedBuffer = {
|
||||
tokenizedLineForRow (row) {
|
||||
if (row === 0) {
|
||||
describe('seek(position)', function () {
|
||||
it('seeks to the leftmost tag boundary at the given position, returning the containing tags', function () {
|
||||
const tokenizedBuffer = {
|
||||
tokenizedLineForRow (row) {
|
||||
return {
|
||||
tags: [-1, 3, -2, -3],
|
||||
text: 'bar',
|
||||
tags: [-1, -2, -3, -4, -5, 3, -3, -4, -6],
|
||||
text: 'foo',
|
||||
openScopes: []
|
||||
}
|
||||
} else if (row === 1) {
|
||||
return {
|
||||
tags: [3],
|
||||
text: 'baz',
|
||||
openScopes: [-1]
|
||||
}
|
||||
} else if (row === 2) {
|
||||
return {
|
||||
tags: [-2],
|
||||
text: '',
|
||||
openScopes: [-1]
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
grammar: {
|
||||
scopeForId (id) {
|
||||
if (id === -2 || id === -1) {
|
||||
return 'foo'
|
||||
} else if (id === -3) {
|
||||
return 'qux'
|
||||
grammar: {
|
||||
scopeForId (id) {
|
||||
return {
|
||||
'-1': 'foo', '-2': 'foo',
|
||||
'-3': 'bar', '-4': 'bar',
|
||||
'-5': 'baz', '-6': 'baz'
|
||||
}[id]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const iterator = new TokenizedBufferIterator(tokenizedBuffer)
|
||||
const iterator = new TokenizedBufferIterator(tokenizedBuffer)
|
||||
|
||||
iterator.seek(Point(0, 0))
|
||||
expect(iterator.getPosition()).toEqual(Point(0, 0))
|
||||
expect(iterator.getCloseTags()).toEqual([])
|
||||
expect(iterator.getOpenTags()).toEqual(['foo'])
|
||||
expect(iterator.seek(Point(0, 0))).toEqual([])
|
||||
expect(iterator.getCloseTags()).toEqual([])
|
||||
expect(iterator.getOpenTags()).toEqual(['foo'])
|
||||
|
||||
iterator.moveToSuccessor()
|
||||
expect(iterator.getPosition()).toEqual(Point(0, 3))
|
||||
expect(iterator.getCloseTags()).toEqual(['foo'])
|
||||
expect(iterator.getOpenTags()).toEqual(['qux'])
|
||||
iterator.moveToSuccessor()
|
||||
expect(iterator.getCloseTags()).toEqual(['foo'])
|
||||
expect(iterator.getOpenTags()).toEqual(['bar'])
|
||||
|
||||
iterator.moveToSuccessor()
|
||||
expect(iterator.getPosition()).toEqual(Point(0, 3))
|
||||
expect(iterator.getCloseTags()).toEqual(['qux'])
|
||||
expect(iterator.getOpenTags()).toEqual([])
|
||||
expect(iterator.seek(Point(0, 1))).toEqual(['baz'])
|
||||
expect(iterator.getCloseTags()).toEqual([])
|
||||
expect(iterator.getOpenTags()).toEqual([])
|
||||
|
||||
iterator.moveToSuccessor()
|
||||
expect(iterator.getPosition()).toEqual(Point(1, 0))
|
||||
expect(iterator.getCloseTags()).toEqual([])
|
||||
expect(iterator.getOpenTags()).toEqual(['foo'])
|
||||
iterator.moveToSuccessor()
|
||||
expect(iterator.getCloseTags()).toEqual([])
|
||||
expect(iterator.getOpenTags()).toEqual(['bar'])
|
||||
|
||||
iterator.moveToSuccessor()
|
||||
expect(iterator.getPosition()).toEqual(Point(2, 0))
|
||||
expect(iterator.getCloseTags()).toEqual(['foo'])
|
||||
expect(iterator.getOpenTags()).toEqual([])
|
||||
expect(iterator.seek(Point(0, 3))).toEqual(['baz'])
|
||||
expect(iterator.getCloseTags()).toEqual([])
|
||||
expect(iterator.getOpenTags()).toEqual(['bar'])
|
||||
|
||||
iterator.moveToSuccessor()
|
||||
expect(iterator.getCloseTags()).toEqual(['bar', 'baz'])
|
||||
expect(iterator.getOpenTags()).toEqual([])
|
||||
})
|
||||
})
|
||||
|
||||
describe('moveToSuccessor()', function () {
|
||||
it('reports two boundaries at the same position when tags close, open, then close again without a non-negative integer separating them (regression)', () => {
|
||||
const tokenizedBuffer = {
|
||||
tokenizedLineForRow () {
|
||||
return {
|
||||
tags: [-1, -2, -1, -2],
|
||||
text: '',
|
||||
openScopes: []
|
||||
}
|
||||
},
|
||||
|
||||
grammar: {
|
||||
scopeForId () {
|
||||
return 'foo'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const iterator = new TokenizedBufferIterator(tokenizedBuffer)
|
||||
|
||||
iterator.seek(Point(0, 0))
|
||||
expect(iterator.getPosition()).toEqual(Point(0, 0))
|
||||
expect(iterator.getCloseTags()).toEqual([])
|
||||
expect(iterator.getOpenTags()).toEqual(['foo'])
|
||||
|
||||
iterator.moveToSuccessor()
|
||||
expect(iterator.getPosition()).toEqual(Point(0, 0))
|
||||
expect(iterator.getCloseTags()).toEqual(['foo'])
|
||||
expect(iterator.getOpenTags()).toEqual(['foo'])
|
||||
|
||||
iterator.moveToSuccessor()
|
||||
expect(iterator.getCloseTags()).toEqual(['foo'])
|
||||
expect(iterator.getOpenTags()).toEqual([])
|
||||
})
|
||||
|
||||
it("reports a boundary at line end if the next line's open scopes don't match the containing tags for the current line", () => {
|
||||
const tokenizedBuffer = {
|
||||
tokenizedLineForRow (row) {
|
||||
if (row === 0) {
|
||||
return {
|
||||
tags: [-1, 3, -2, -3],
|
||||
text: 'bar',
|
||||
openScopes: []
|
||||
}
|
||||
} else if (row === 1) {
|
||||
return {
|
||||
tags: [3],
|
||||
text: 'baz',
|
||||
openScopes: [-1]
|
||||
}
|
||||
} else if (row === 2) {
|
||||
return {
|
||||
tags: [-2],
|
||||
text: '',
|
||||
openScopes: [-1]
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
grammar: {
|
||||
scopeForId (id) {
|
||||
if (id === -2 || id === -1) {
|
||||
return 'foo'
|
||||
} else if (id === -3) {
|
||||
return 'qux'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const iterator = new TokenizedBufferIterator(tokenizedBuffer)
|
||||
|
||||
iterator.seek(Point(0, 0))
|
||||
expect(iterator.getPosition()).toEqual(Point(0, 0))
|
||||
expect(iterator.getCloseTags()).toEqual([])
|
||||
expect(iterator.getOpenTags()).toEqual(['foo'])
|
||||
|
||||
iterator.moveToSuccessor()
|
||||
expect(iterator.getPosition()).toEqual(Point(0, 3))
|
||||
expect(iterator.getCloseTags()).toEqual(['foo'])
|
||||
expect(iterator.getOpenTags()).toEqual(['qux'])
|
||||
|
||||
iterator.moveToSuccessor()
|
||||
expect(iterator.getPosition()).toEqual(Point(0, 3))
|
||||
expect(iterator.getCloseTags()).toEqual(['qux'])
|
||||
expect(iterator.getOpenTags()).toEqual([])
|
||||
|
||||
iterator.moveToSuccessor()
|
||||
expect(iterator.getPosition()).toEqual(Point(1, 0))
|
||||
expect(iterator.getCloseTags()).toEqual([])
|
||||
expect(iterator.getOpenTags()).toEqual(['foo'])
|
||||
|
||||
iterator.moveToSuccessor()
|
||||
expect(iterator.getPosition()).toEqual(Point(2, 0))
|
||||
expect(iterator.getCloseTags()).toEqual(['foo'])
|
||||
expect(iterator.getOpenTags()).toEqual([])
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -734,7 +734,7 @@ describe "TokenizedBuffer", ->
|
||||
expect(iterator.seek(Point(0, 8))).toEqual(["source.js"])
|
||||
expect(iterator.getPosition()).toEqual(Point(0, 8))
|
||||
expect(iterator.seek(Point(1, 0))).toEqual(["source.js", "comment.block.js"])
|
||||
expect(iterator.getPosition()).toEqual(Point(1, 5))
|
||||
expect(iterator.getPosition()).toEqual(Point(1, 0))
|
||||
expect(iterator.seek(Point(1, 18))).toEqual(["source.js", "constant.numeric.decimal.js"])
|
||||
expect(iterator.getPosition()).toEqual(Point(1, 18))
|
||||
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
crypto = require 'crypto'
|
||||
clipboard = require './safe-clipboard'
|
||||
|
||||
# Extended: Represents the clipboard used for copying and pasting in Atom.
|
||||
#
|
||||
# An instance of this class is always available as the `atom.clipboard` global.
|
||||
#
|
||||
# ## Examples
|
||||
#
|
||||
# ```coffee
|
||||
# atom.clipboard.write('hello')
|
||||
#
|
||||
# console.log(atom.clipboard.read()) # 'hello'
|
||||
# ```
|
||||
module.exports =
|
||||
class Clipboard
|
||||
constructor: ->
|
||||
@reset()
|
||||
|
||||
reset: ->
|
||||
@metadata = null
|
||||
@signatureForMetadata = null
|
||||
|
||||
# Creates an `md5` hash of some text.
|
||||
#
|
||||
# * `text` A {String} to hash.
|
||||
#
|
||||
# Returns a hashed {String}.
|
||||
md5: (text) ->
|
||||
crypto.createHash('md5').update(text, 'utf8').digest('hex')
|
||||
|
||||
# Public: Write the given text to the clipboard.
|
||||
#
|
||||
# The metadata associated with the text is available by calling
|
||||
# {::readWithMetadata}.
|
||||
#
|
||||
# * `text` The {String} to store.
|
||||
# * `metadata` (optional) The additional info to associate with the text.
|
||||
write: (text, metadata) ->
|
||||
@signatureForMetadata = @md5(text)
|
||||
@metadata = metadata
|
||||
clipboard.writeText(text)
|
||||
|
||||
# Public: Read the text from the clipboard.
|
||||
#
|
||||
# Returns a {String}.
|
||||
read: ->
|
||||
clipboard.readText()
|
||||
|
||||
# Public: Read the text from the clipboard and return both the text and the
|
||||
# associated metadata.
|
||||
#
|
||||
# Returns an {Object} with the following keys:
|
||||
# * `text` The {String} clipboard text.
|
||||
# * `metadata` The metadata stored by an earlier call to {::write}.
|
||||
readWithMetadata: ->
|
||||
text = @read()
|
||||
if @signatureForMetadata is @md5(text)
|
||||
{text, @metadata}
|
||||
else
|
||||
{text}
|
||||
70
src/clipboard.js
Normal file
70
src/clipboard.js
Normal file
@@ -0,0 +1,70 @@
|
||||
/** @babel */
|
||||
|
||||
import crypto from 'crypto'
|
||||
import clipboard from './safe-clipboard'
|
||||
|
||||
// Extended: Represents the clipboard used for copying and pasting in Atom.
|
||||
//
|
||||
// An instance of this class is always available as the `atom.clipboard` global.
|
||||
//
|
||||
// ## Examples
|
||||
//
|
||||
// ```coffee
|
||||
// atom.clipboard.write('hello')
|
||||
//
|
||||
// console.log(atom.clipboard.read()) # 'hello'
|
||||
// ```
|
||||
export default class Clipboard {
|
||||
constructor () {
|
||||
this.reset()
|
||||
}
|
||||
|
||||
reset () {
|
||||
this.metadata = null
|
||||
this.signatureForMetadata = null
|
||||
}
|
||||
|
||||
// Creates an `md5` hash of some text.
|
||||
//
|
||||
// * `text` A {String} to hash.
|
||||
//
|
||||
// Returns a hashed {String}.
|
||||
md5 (text) {
|
||||
return crypto.createHash('md5').update(text, 'utf8').digest('hex')
|
||||
}
|
||||
|
||||
// Public: Write the given text to the clipboard.
|
||||
//
|
||||
// The metadata associated with the text is available by calling
|
||||
// {::readWithMetadata}.
|
||||
//
|
||||
// * `text` The {String} to store.
|
||||
// * `metadata` (optional) The additional info to associate with the text.
|
||||
write (text, metadata) {
|
||||
this.signatureForMetadata = this.md5(text)
|
||||
this.metadata = metadata
|
||||
clipboard.writeText(text)
|
||||
}
|
||||
|
||||
// Public: Read the text from the clipboard.
|
||||
//
|
||||
// Returns a {String}.
|
||||
read () {
|
||||
return clipboard.readText()
|
||||
}
|
||||
|
||||
// Public: Read the text from the clipboard and return both the text and the
|
||||
// associated metadata.
|
||||
//
|
||||
// Returns an {Object} with the following keys:
|
||||
// * `text` The {String} clipboard text.
|
||||
// * `metadata` The metadata stored by an earlier call to {::write}.
|
||||
readWithMetadata () {
|
||||
let text = this.read()
|
||||
if (this.signatureForMetadata === this.md5(text)) {
|
||||
return {text, metadata: this.metadata}
|
||||
} else {
|
||||
return {text}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,89 +0,0 @@
|
||||
_ = require 'underscore-plus'
|
||||
ParsedColor = null
|
||||
|
||||
# Essential: A simple color class returned from {Config::get} when the value
|
||||
# at the key path is of type 'color'.
|
||||
module.exports =
|
||||
class Color
|
||||
# Essential: Parse a {String} or {Object} into a {Color}.
|
||||
#
|
||||
# * `value` A {String} such as `'white'`, `#ff00ff`, or
|
||||
# `'rgba(255, 15, 60, .75)'` or an {Object} with `red`, `green`, `blue`,
|
||||
# and `alpha` properties.
|
||||
#
|
||||
# Returns a {Color} or `null` if it cannot be parsed.
|
||||
@parse: (value) ->
|
||||
return null if _.isArray(value) or _.isFunction(value)
|
||||
return null unless _.isObject(value) or _.isString(value)
|
||||
|
||||
ParsedColor ?= require 'color'
|
||||
|
||||
try
|
||||
parsedColor = new ParsedColor(value)
|
||||
catch error
|
||||
return null
|
||||
|
||||
new Color(parsedColor.red(), parsedColor.green(), parsedColor.blue(), parsedColor.alpha())
|
||||
|
||||
constructor: (red, green, blue, alpha) ->
|
||||
Object.defineProperties this,
|
||||
red:
|
||||
set: (newRed) -> red = parseColor(newRed)
|
||||
get: -> red
|
||||
enumerable: true
|
||||
configurable: false
|
||||
green:
|
||||
set: (newGreen) -> green = parseColor(newGreen)
|
||||
get: -> green
|
||||
enumerable: true
|
||||
configurable: false
|
||||
blue:
|
||||
set: (newBlue) -> blue = parseColor(newBlue)
|
||||
get: -> blue
|
||||
enumerable: true
|
||||
configurable: false
|
||||
alpha:
|
||||
set: (newAlpha) -> alpha = parseAlpha(newAlpha)
|
||||
get: -> alpha
|
||||
enumerable: true
|
||||
configurable: false
|
||||
|
||||
@red = red
|
||||
@green = green
|
||||
@blue = blue
|
||||
@alpha = alpha
|
||||
|
||||
# Essential: Returns a {String} in the form `'#abcdef'`.
|
||||
toHexString: ->
|
||||
"##{numberToHexString(@red)}#{numberToHexString(@green)}#{numberToHexString(@blue)}"
|
||||
|
||||
# Essential: Returns a {String} in the form `'rgba(25, 50, 75, .9)'`.
|
||||
toRGBAString: ->
|
||||
"rgba(#{@red}, #{@green}, #{@blue}, #{@alpha})"
|
||||
|
||||
isEqual: (color) ->
|
||||
return true if this is color
|
||||
color = Color.parse(color) unless color instanceof Color
|
||||
return false unless color?
|
||||
color.red is @red and color.blue is @blue and color.green is @green and color.alpha is @alpha
|
||||
|
||||
clone: -> new Color(@red, @green, @blue, @alpha)
|
||||
|
||||
parseColor = (color) ->
|
||||
color = parseInt(color)
|
||||
color = 0 if isNaN(color)
|
||||
color = Math.max(color, 0)
|
||||
color = Math.min(color, 255)
|
||||
color
|
||||
|
||||
parseAlpha = (alpha) ->
|
||||
alpha = parseFloat(alpha)
|
||||
alpha = 1 if isNaN(alpha)
|
||||
alpha = Math.max(alpha, 0)
|
||||
alpha = Math.min(alpha, 1)
|
||||
alpha
|
||||
|
||||
numberToHexString = (number) ->
|
||||
hex = number.toString(16)
|
||||
hex = "0#{hex}" if number < 16
|
||||
hex
|
||||
134
src/color.js
Normal file
134
src/color.js
Normal file
@@ -0,0 +1,134 @@
|
||||
/** @babel */
|
||||
|
||||
let ParsedColor = null
|
||||
|
||||
// Essential: A simple color class returned from {Config::get} when the value
|
||||
// at the key path is of type 'color'.
|
||||
export default class Color {
|
||||
// Essential: Parse a {String} or {Object} into a {Color}.
|
||||
//
|
||||
// * `value` A {String} such as `'white'`, `#ff00ff`, or
|
||||
// `'rgba(255, 15, 60, .75)'` or an {Object} with `red`, `green`, `blue`,
|
||||
// and `alpha` properties.
|
||||
//
|
||||
// Returns a {Color} or `null` if it cannot be parsed.
|
||||
static parse (value) {
|
||||
switch (typeof value) {
|
||||
case 'string':
|
||||
break
|
||||
case 'object':
|
||||
if (Array.isArray(value)) { return null }
|
||||
break
|
||||
default:
|
||||
return null
|
||||
}
|
||||
|
||||
if (!ParsedColor) {
|
||||
ParsedColor = require('color')
|
||||
}
|
||||
|
||||
try {
|
||||
var parsedColor = new ParsedColor(value)
|
||||
} catch (error) {
|
||||
return null
|
||||
}
|
||||
|
||||
return new Color(parsedColor.red(), parsedColor.green(), parsedColor.blue(), parsedColor.alpha())
|
||||
}
|
||||
|
||||
constructor (red, green, blue, alpha) {
|
||||
this.red = red
|
||||
this.green = green
|
||||
this.blue = blue
|
||||
this.alpha = alpha
|
||||
}
|
||||
|
||||
set red (red) {
|
||||
this._red = parseColor(red)
|
||||
}
|
||||
|
||||
set green (green) {
|
||||
this._green = parseColor(green)
|
||||
}
|
||||
|
||||
set blue (blue) {
|
||||
this._blue = parseColor(blue)
|
||||
}
|
||||
|
||||
set alpha (alpha) {
|
||||
this._alpha = parseAlpha(alpha)
|
||||
}
|
||||
|
||||
get red () {
|
||||
return this._red
|
||||
}
|
||||
|
||||
get green () {
|
||||
return this._green
|
||||
}
|
||||
|
||||
get blue () {
|
||||
return this._blue
|
||||
}
|
||||
|
||||
get alpha () {
|
||||
return this._alpha
|
||||
}
|
||||
|
||||
// Essential: Returns a {String} in the form `'#abcdef'`.
|
||||
toHexString () {
|
||||
return `#${numberToHexString(this.red)}${numberToHexString(this.green)}${numberToHexString(this.blue)}`
|
||||
}
|
||||
|
||||
// Essential: Returns a {String} in the form `'rgba(25, 50, 75, .9)'`.
|
||||
toRGBAString () {
|
||||
return `rgba(${this.red}, ${this.green}, ${this.blue}, ${this.alpha})`
|
||||
}
|
||||
|
||||
isEqual (color) {
|
||||
if (this === color) {
|
||||
return true
|
||||
}
|
||||
|
||||
if (!(color instanceof Color)) {
|
||||
color = Color.parse(color)
|
||||
}
|
||||
|
||||
if (color == null) {
|
||||
return false
|
||||
}
|
||||
|
||||
return color.red === this.red && color.blue === this.blue && color.green === this.green && color.alpha === this.alpha
|
||||
}
|
||||
|
||||
clone () {
|
||||
return new Color(this.red, this.green, this.blue, this.alpha)
|
||||
}
|
||||
}
|
||||
|
||||
function parseColor (colorString) {
|
||||
const color = parseInt(colorString, 10)
|
||||
if (isNaN(color)) {
|
||||
return 0
|
||||
} else {
|
||||
return Math.min(Math.max(color, 0), 255)
|
||||
}
|
||||
}
|
||||
|
||||
function parseAlpha (alphaString) {
|
||||
const alpha = parseFloat(alphaString)
|
||||
if (isNaN(alpha)) {
|
||||
return 1
|
||||
} else {
|
||||
return Math.min(Math.max(alpha, 0), 1)
|
||||
}
|
||||
}
|
||||
|
||||
function numberToHexString (number) {
|
||||
const hex = number.toString(16)
|
||||
if (number < 16) {
|
||||
return `0${hex}`
|
||||
} else {
|
||||
return hex
|
||||
}
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
{Disposable} = require 'event-kit'
|
||||
|
||||
# Extended: Manages the deserializers used for serialized state
|
||||
#
|
||||
# An instance of this class is always available as the `atom.deserializers`
|
||||
# global.
|
||||
#
|
||||
# ## Examples
|
||||
#
|
||||
# ```coffee
|
||||
# class MyPackageView extends View
|
||||
# atom.deserializers.add(this)
|
||||
#
|
||||
# @deserialize: (state) ->
|
||||
# new MyPackageView(state)
|
||||
#
|
||||
# constructor: (@state) ->
|
||||
#
|
||||
# serialize: ->
|
||||
# @state
|
||||
# ```
|
||||
module.exports =
|
||||
class DeserializerManager
|
||||
constructor: (@atomEnvironment) ->
|
||||
@deserializers = {}
|
||||
|
||||
# Public: Register the given class(es) as deserializers.
|
||||
#
|
||||
# * `deserializers` One or more deserializers to register. A deserializer can
|
||||
# be any object with a `.name` property and a `.deserialize()` method. A
|
||||
# common approach is to register a *constructor* as the deserializer for its
|
||||
# instances by adding a `.deserialize()` class method. When your method is
|
||||
# called, it will be passed serialized state as the first argument and the
|
||||
# {Atom} environment object as the second argument, which is useful if you
|
||||
# wish to avoid referencing the `atom` global.
|
||||
add: (deserializers...) ->
|
||||
@deserializers[deserializer.name] = deserializer for deserializer in deserializers
|
||||
new Disposable =>
|
||||
delete @deserializers[deserializer.name] for deserializer in deserializers
|
||||
return
|
||||
|
||||
getDeserializerCount: ->
|
||||
Object.keys(@deserializers).length
|
||||
|
||||
# Public: Deserialize the state and params.
|
||||
#
|
||||
# * `state` The state {Object} to deserialize.
|
||||
deserialize: (state) ->
|
||||
return unless state?
|
||||
|
||||
if deserializer = @get(state)
|
||||
stateVersion = state.get?('version') ? state.version
|
||||
return if deserializer.version? and deserializer.version isnt stateVersion
|
||||
deserializer.deserialize(state, @atomEnvironment)
|
||||
else
|
||||
console.warn "No deserializer found for", state
|
||||
|
||||
# Get the deserializer for the state.
|
||||
#
|
||||
# * `state` The state {Object} being deserialized.
|
||||
get: (state) ->
|
||||
return unless state?
|
||||
|
||||
name = state.get?('deserializer') ? state.deserializer
|
||||
@deserializers[name]
|
||||
|
||||
clear: ->
|
||||
@deserializers = {}
|
||||
100
src/deserializer-manager.js
Normal file
100
src/deserializer-manager.js
Normal file
@@ -0,0 +1,100 @@
|
||||
/** @babel */
|
||||
|
||||
import {Disposable} from 'event-kit'
|
||||
|
||||
// Extended: Manages the deserializers used for serialized state
|
||||
//
|
||||
// An instance of this class is always available as the `atom.deserializers`
|
||||
// global.
|
||||
//
|
||||
// ## Examples
|
||||
//
|
||||
// ```coffee
|
||||
// class MyPackageView extends View
|
||||
// atom.deserializers.add(this)
|
||||
//
|
||||
// @deserialize: (state) ->
|
||||
// new MyPackageView(state)
|
||||
//
|
||||
// constructor: (@state) ->
|
||||
//
|
||||
// serialize: ->
|
||||
// @state
|
||||
// ```
|
||||
export default class DeserializerManager {
|
||||
constructor (atomEnvironment) {
|
||||
this.atomEnvironment = atomEnvironment
|
||||
this.deserializers = {}
|
||||
}
|
||||
|
||||
// Public: Register the given class(es) as deserializers.
|
||||
//
|
||||
// * `deserializers` One or more deserializers to register. A deserializer can
|
||||
// be any object with a `.name` property and a `.deserialize()` method. A
|
||||
// common approach is to register a *constructor* as the deserializer for its
|
||||
// instances by adding a `.deserialize()` class method. When your method is
|
||||
// called, it will be passed serialized state as the first argument and the
|
||||
// {Atom} environment object as the second argument, which is useful if you
|
||||
// wish to avoid referencing the `atom` global.
|
||||
add (...deserializers) {
|
||||
for (let i = 0; i < deserializers.length; i++) {
|
||||
let deserializer = deserializers[i]
|
||||
this.deserializers[deserializer.name] = deserializer
|
||||
}
|
||||
|
||||
return new Disposable(() => {
|
||||
for (let j = 0; j < deserializers.length; j++) {
|
||||
let deserializer = deserializers[j]
|
||||
delete this.deserializers[deserializer.name]
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
getDeserializerCount () {
|
||||
return Object.keys(this.deserializers).length
|
||||
}
|
||||
|
||||
// Public: Deserialize the state and params.
|
||||
//
|
||||
// * `state` The state {Object} to deserialize.
|
||||
deserialize (state) {
|
||||
if (state == null) {
|
||||
return
|
||||
}
|
||||
|
||||
const deserializer = this.get(state)
|
||||
if (deserializer) {
|
||||
let stateVersion = (
|
||||
(typeof state.get === 'function') && state.get('version') ||
|
||||
state.version
|
||||
)
|
||||
|
||||
if ((deserializer.version != null) && deserializer.version !== stateVersion) {
|
||||
return
|
||||
}
|
||||
return deserializer.deserialize(state, this.atomEnvironment)
|
||||
} else {
|
||||
return console.warn('No deserializer found for', state)
|
||||
}
|
||||
}
|
||||
|
||||
// Get the deserializer for the state.
|
||||
//
|
||||
// * `state` The state {Object} being deserialized.
|
||||
get (state) {
|
||||
if (state == null) {
|
||||
return
|
||||
}
|
||||
|
||||
let stateDeserializer = (
|
||||
(typeof state.get === 'function') && state.get('deserializer') ||
|
||||
state.deserializer
|
||||
)
|
||||
|
||||
return this.deserializers[stateDeserializer]
|
||||
}
|
||||
|
||||
clear () {
|
||||
this.deserializers = {}
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,8 @@ Path = require 'path'
|
||||
|
||||
exeName = Path.basename(process.execPath)
|
||||
appPath = "\"#{process.execPath}\""
|
||||
appName = exeName.replace('atom', 'Atom').replace('beta', 'Beta').replace('.exe', '')
|
||||
isBeta = appPath.includes(' Beta')
|
||||
appName = exeName.replace('atom', (if isBeta then 'Atom Beta' else 'Atom' )).replace('.exe', '')
|
||||
|
||||
class ShellOption
|
||||
constructor: (key, parts) ->
|
||||
@@ -13,11 +14,11 @@ class ShellOption
|
||||
isRegistered: (callback) =>
|
||||
new Registry({hive: 'HKCU', key: "#{@key}\\#{@parts[0].key}"})
|
||||
.get @parts[0].name, (err, val) =>
|
||||
callback(not err? and val.value is @parts[0].value)
|
||||
callback(not err? and val? and val.value is @parts[0].value)
|
||||
|
||||
register: (callback) =>
|
||||
doneCount = @parts.length
|
||||
for part in @parts
|
||||
@parts.forEach (part) =>
|
||||
reg = new Registry({hive: 'HKCU', key: if part.key? then "#{@key}\\#{part.key}" else @key})
|
||||
reg.create( -> reg.set part.name, Registry.REG_SZ, part.value, -> callback() if --doneCount is 0)
|
||||
|
||||
@@ -31,7 +32,7 @@ class ShellOption
|
||||
update: (callback) =>
|
||||
new Registry({hive: 'HKCU', key: "#{@key}\\#{@parts[0].key}"})
|
||||
.get @parts[0].name, (err, val) =>
|
||||
if err? or not val.value.includes '\\' + exeName
|
||||
if err? or not val? or val.value.includes '\\' + exeName
|
||||
callback(err)
|
||||
else
|
||||
@register callback
|
||||
|
||||
@@ -427,7 +427,7 @@ class Package
|
||||
return @mainModule if @mainModuleRequired
|
||||
unless @isCompatible()
|
||||
console.warn """
|
||||
Failed to require the main module of '#{@name}' because it requires one or more incompatible native modules (#{_.map(@incompatibleModules, 'name').join(', ')}).
|
||||
Failed to require the main module of '#{@name}' because it requires one or more incompatible native modules (#{_.pluck(@incompatibleModules, 'name').join(', ')}).
|
||||
Run `apm rebuild` in the package directory and restart Atom to resolve.
|
||||
"""
|
||||
return
|
||||
|
||||
@@ -1,120 +0,0 @@
|
||||
{spliceWithArray} = require 'underscore-plus'
|
||||
|
||||
# Used by the display buffer to map screen rows to buffer rows and vice-versa.
|
||||
# This mapping may not be 1:1 due to folds and soft-wraps. This object maintains
|
||||
# an array of regions, which contain `bufferRows` and `screenRows` fields.
|
||||
#
|
||||
# Rectangular Regions:
|
||||
# If a region has the same number of buffer rows and screen rows, it is referred
|
||||
# to as "rectangular", and represents one or more non-soft-wrapped, non-folded
|
||||
# lines.
|
||||
#
|
||||
# Trapezoidal Regions:
|
||||
# If a region has one buffer row and more than one screen row, it represents a
|
||||
# soft-wrapped line. If a region has one screen row and more than one buffer
|
||||
# row, it represents folded lines
|
||||
module.exports =
|
||||
class RowMap
|
||||
constructor: ->
|
||||
@regions = []
|
||||
|
||||
# Public: Returns a copy of all the regions in the map
|
||||
getRegions: ->
|
||||
@regions.slice()
|
||||
|
||||
# Public: Returns an end-row-exclusive range of screen rows corresponding to
|
||||
# the given buffer row. If the buffer row is soft-wrapped, the range may span
|
||||
# multiple screen rows. Otherwise it will span a single screen row.
|
||||
screenRowRangeForBufferRow: (targetBufferRow) ->
|
||||
{region, bufferRows, screenRows} = @traverseToBufferRow(targetBufferRow)
|
||||
|
||||
if region? and region.bufferRows isnt region.screenRows
|
||||
[screenRows, screenRows + region.screenRows]
|
||||
else
|
||||
screenRows += targetBufferRow - bufferRows
|
||||
[screenRows, screenRows + 1]
|
||||
|
||||
# Public: Returns an end-row-exclusive range of buffer rows corresponding to
|
||||
# the given screen row. If the screen row is the first line of a folded range
|
||||
# of buffer rows, the range may span multiple buffer rows. Otherwise it will
|
||||
# span a single buffer row.
|
||||
bufferRowRangeForScreenRow: (targetScreenRow) ->
|
||||
{region, screenRows, bufferRows} = @traverseToScreenRow(targetScreenRow)
|
||||
if region? and region.bufferRows isnt region.screenRows
|
||||
[bufferRows, bufferRows + region.bufferRows]
|
||||
else
|
||||
bufferRows += targetScreenRow - screenRows
|
||||
[bufferRows, bufferRows + 1]
|
||||
|
||||
# Public: If the given buffer row is part of a folded row range, returns that
|
||||
# row range. Otherwise returns a range spanning only the given buffer row.
|
||||
bufferRowRangeForBufferRow: (targetBufferRow) ->
|
||||
{region, bufferRows} = @traverseToBufferRow(targetBufferRow)
|
||||
if region? and region.bufferRows isnt region.screenRows
|
||||
[bufferRows, bufferRows + region.bufferRows]
|
||||
else
|
||||
[targetBufferRow, targetBufferRow + 1]
|
||||
|
||||
# Public: Given a starting buffer row, the number of buffer rows to replace,
|
||||
# and an array of regions of shape {bufferRows: n, screenRows: m}, splices
|
||||
# the regions at the appropriate location in the map. This method is used by
|
||||
# display buffer to keep the map updated when the underlying buffer changes.
|
||||
spliceRegions: (startBufferRow, bufferRowCount, regions) ->
|
||||
endBufferRow = startBufferRow + bufferRowCount
|
||||
{index, bufferRows} = @traverseToBufferRow(startBufferRow)
|
||||
precedingRows = startBufferRow - bufferRows
|
||||
|
||||
count = 0
|
||||
while region = @regions[index + count]
|
||||
count++
|
||||
bufferRows += region.bufferRows
|
||||
if bufferRows >= endBufferRow
|
||||
followingRows = bufferRows - endBufferRow
|
||||
break
|
||||
|
||||
if precedingRows > 0
|
||||
regions.unshift({bufferRows: precedingRows, screenRows: precedingRows})
|
||||
|
||||
if followingRows > 0
|
||||
regions.push({bufferRows: followingRows, screenRows: followingRows})
|
||||
|
||||
spliceWithArray(@regions, index, count, regions)
|
||||
@mergeAdjacentRectangularRegions(index - 1, index + regions.length)
|
||||
|
||||
traverseToBufferRow: (targetBufferRow) ->
|
||||
bufferRows = 0
|
||||
screenRows = 0
|
||||
for region, index in @regions
|
||||
if (bufferRows + region.bufferRows) > targetBufferRow
|
||||
return {region, index, screenRows, bufferRows}
|
||||
bufferRows += region.bufferRows
|
||||
screenRows += region.screenRows
|
||||
{index, screenRows, bufferRows}
|
||||
|
||||
traverseToScreenRow: (targetScreenRow) ->
|
||||
bufferRows = 0
|
||||
screenRows = 0
|
||||
for region, index in @regions
|
||||
if (screenRows + region.screenRows) > targetScreenRow
|
||||
return {region, index, screenRows, bufferRows}
|
||||
bufferRows += region.bufferRows
|
||||
screenRows += region.screenRows
|
||||
{index, screenRows, bufferRows}
|
||||
|
||||
mergeAdjacentRectangularRegions: (startIndex, endIndex) ->
|
||||
for index in [endIndex..startIndex]
|
||||
if 0 < index < @regions.length
|
||||
leftRegion = @regions[index - 1]
|
||||
rightRegion = @regions[index]
|
||||
leftIsRectangular = leftRegion.bufferRows is leftRegion.screenRows
|
||||
rightIsRectangular = rightRegion.bufferRows is rightRegion.screenRows
|
||||
if leftIsRectangular and rightIsRectangular
|
||||
@regions.splice index - 1, 2,
|
||||
bufferRows: leftRegion.bufferRows + rightRegion.bufferRows
|
||||
screenRows: leftRegion.screenRows + rightRegion.screenRows
|
||||
return
|
||||
|
||||
# Public: Returns an array of strings describing the map's regions.
|
||||
inspect: ->
|
||||
for {bufferRows, screenRows} in @regions
|
||||
"#{bufferRows}:#{screenRows}"
|
||||
@@ -18,24 +18,31 @@ class TokenizedBufferIterator
|
||||
@currentLineLength = currentLine.text.length
|
||||
@containingTags = @currentLineOpenTags.map (id) => @tokenizedBuffer.grammar.scopeForId(id)
|
||||
currentColumn = 0
|
||||
|
||||
for tag, index in @currentTags
|
||||
if tag >= 0
|
||||
if currentColumn >= position.column and @isAtTagBoundary()
|
||||
if currentColumn is position.column
|
||||
@tagIndex = index
|
||||
break
|
||||
else
|
||||
currentColumn += tag
|
||||
@containingTags.pop() while @closeTags.shift()
|
||||
@containingTags.push(tag) while tag = @openTags.shift()
|
||||
else
|
||||
scopeName = @tokenizedBuffer.grammar.scopeForId(tag)
|
||||
if tag % 2 is 0
|
||||
if @openTags.length > 0
|
||||
@containingTags.push(openTag) while openTag = @openTags.shift()
|
||||
if currentColumn > position.column
|
||||
@tagIndex = index
|
||||
break
|
||||
else
|
||||
@closeTags.push(scopeName)
|
||||
else
|
||||
else
|
||||
scopeName = @tokenizedBuffer.grammar.scopeForId(tag)
|
||||
if tag % 2 is 0 # close tag
|
||||
if @openTags.length > 0
|
||||
if currentColumn is position.column
|
||||
@tagIndex = index
|
||||
break
|
||||
else
|
||||
@containingTags.pop() while @closeTags.shift()
|
||||
@containingTags.push(openTag) while openTag = @openTags.shift()
|
||||
@closeTags.push(scopeName)
|
||||
else # open tag
|
||||
@openTags.push(scopeName)
|
||||
|
||||
@tagIndex ?= @currentTags.length
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
# Public: Measure how long a function takes to run.
|
||||
#
|
||||
# description - A {String} description that will be logged to the console when
|
||||
# the function completes.
|
||||
# fn - A {Function} to measure the duration of.
|
||||
#
|
||||
# Returns the value returned by the given function.
|
||||
window.measure = (description, fn) ->
|
||||
start = Date.now()
|
||||
value = fn()
|
||||
result = Date.now() - start
|
||||
console.log description, result
|
||||
value
|
||||
|
||||
# Public: Create a dev tools profile for a function.
|
||||
#
|
||||
# description - A {String} description that will be available in the Profiles
|
||||
# tab of the dev tools.
|
||||
# fn - A {Function} to profile.
|
||||
#
|
||||
# Returns the value returned by the given function.
|
||||
window.profile = (description, fn) ->
|
||||
measure description, ->
|
||||
console.profile(description)
|
||||
value = fn()
|
||||
console.profileEnd(description)
|
||||
value
|
||||
30
src/window.js
Normal file
30
src/window.js
Normal file
@@ -0,0 +1,30 @@
|
||||
// Public: Measure how long a function takes to run.
|
||||
//
|
||||
// description - A {String} description that will be logged to the console when
|
||||
// the function completes.
|
||||
// fn - A {Function} to measure the duration of.
|
||||
//
|
||||
// Returns the value returned by the given function.
|
||||
window.measure = function (description, fn) {
|
||||
let start = Date.now()
|
||||
let value = fn()
|
||||
let result = Date.now() - start
|
||||
console.log(description, result)
|
||||
return value
|
||||
}
|
||||
|
||||
// Public: Create a dev tools profile for a function.
|
||||
//
|
||||
// description - A {String} description that will be available in the Profiles
|
||||
// tab of the dev tools.
|
||||
// fn - A {Function} to profile.
|
||||
//
|
||||
// Returns the value returned by the given function.
|
||||
window.profile = function (description, fn) {
|
||||
window.measure(description, function () {
|
||||
console.profile(description)
|
||||
let value = fn()
|
||||
console.profileEnd(description)
|
||||
return value
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user