", scopes: ["text.html.ruby"]
waitsForPromise ->
@@ -377,7 +344,7 @@ describe "TokenizedBuffer", ->
runs ->
fullyTokenize(tokenizedBuffer)
- {tokens} = tokenizedBuffer.tokenizedLineForRow(0)
+ {tokens} = tokenizedBuffer.tokenizedLines[0]
expect(tokens[0]).toEqual value: '<', scopes: ["text.html.ruby", "meta.tag.block.any.html", "punctuation.definition.tag.begin.html"]
describe ".tokenForPosition(position)", ->
@@ -387,11 +354,7 @@ describe "TokenizedBuffer", ->
it "returns the correct token (regression)", ->
buffer = atom.project.bufferForPathSync('sample.js')
- tokenizedBuffer = new TokenizedBuffer({
- buffer, grammarRegistry: atom.grammars, packageManager: atom.packages,
- assert: atom.assert, tabLength: 2,
- })
- tokenizedBuffer.setGrammar(atom.grammars.grammarForScopeName('source.js'))
+ tokenizedBuffer = new TokenizedBuffer({buffer, grammar: atom.grammars.grammarForScopeName('source.js'), tabLength: 2})
fullyTokenize(tokenizedBuffer)
expect(tokenizedBuffer.tokenForPosition([1, 0]).scopes).toEqual ["source.js"]
expect(tokenizedBuffer.tokenForPosition([1, 1]).scopes).toEqual ["source.js"]
@@ -400,16 +363,12 @@ describe "TokenizedBuffer", ->
describe ".bufferRangeForScopeAtPosition(selector, position)", ->
beforeEach ->
buffer = atom.project.bufferForPathSync('sample.js')
- tokenizedBuffer = new TokenizedBuffer({
- buffer, grammarRegistry: atom.grammars, packageManager: atom.packages,
- assert: atom.assert, tabLength: 2,
- })
- tokenizedBuffer.setGrammar(atom.grammars.grammarForScopeName('source.js'))
+ tokenizedBuffer = new TokenizedBuffer({buffer, grammar: atom.grammars.grammarForScopeName('source.js'), tabLength: 2})
fullyTokenize(tokenizedBuffer)
describe "when the selector does not match the token at the position", ->
it "returns a falsy value", ->
- expect(tokenizedBuffer.bufferRangeForScopeAtPosition('.bogus', [0, 1])).toBeFalsy()
+ expect(tokenizedBuffer.bufferRangeForScopeAtPosition('.bogus', [0, 1])).toBeUndefined()
describe "when the selector matches a single token at the position", ->
it "returns the range covered by the token", ->
@@ -423,11 +382,7 @@ describe "TokenizedBuffer", ->
describe ".indentLevelForRow(row)", ->
beforeEach ->
buffer = atom.project.bufferForPathSync('sample.js')
- tokenizedBuffer = new TokenizedBuffer({
- buffer, grammarRegistry: atom.grammars, packageManager: atom.packages,
- assert: atom.assert, tabLength: 2,
- })
- tokenizedBuffer.setGrammar(atom.grammars.grammarForScopeName('source.js'))
+ tokenizedBuffer = new TokenizedBuffer({buffer, grammar: atom.grammars.grammarForScopeName('source.js'), tabLength: 2})
fullyTokenize(tokenizedBuffer)
describe "when the line is non-empty", ->
@@ -469,7 +424,7 @@ describe "TokenizedBuffer", ->
buffer.insert([12, 0], ' ')
expect(tokenizedBuffer.indentLevelForRow(13)).toBe 2
- expect(tokenizedBuffer.tokenizedLineForRow(14)).not.toBeDefined()
+ expect(tokenizedBuffer.tokenizedLines[14]).not.toBeDefined()
it "updates the indentLevel of empty lines surrounding a change that inserts lines", ->
buffer.insert([7, 0], '\n\n')
@@ -503,11 +458,7 @@ describe "TokenizedBuffer", ->
buffer = atom.project.bufferForPathSync('sample.js')
buffer.insert [10, 0], " // multi-line\n // comment\n // block\n"
buffer.insert [0, 0], "// multi-line\n// comment\n// block\n"
- tokenizedBuffer = new TokenizedBuffer({
- buffer, grammarRegistry: atom.grammars, packageManager: atom.packages,
- assert: atom.assert, tabLength: 2,
- })
- tokenizedBuffer.setGrammar(atom.grammars.grammarForScopeName('source.js'))
+ tokenizedBuffer = new TokenizedBuffer({buffer, grammar: atom.grammars.grammarForScopeName('source.js'), tabLength: 2})
fullyTokenize(tokenizedBuffer)
it "includes the first line of multi-line comments", ->
@@ -570,61 +521,90 @@ describe "TokenizedBuffer", ->
expect(tokenizedBuffer.isFoldableAtRow(7)).toBe false
expect(tokenizedBuffer.isFoldableAtRow(8)).toBe false
+ describe "::tokenizedLineForRow(row)", ->
+ it "returns the tokenized line for a row, or a placeholder line if it hasn't been tokenized yet", ->
+ buffer = atom.project.bufferForPathSync('sample.js')
+ grammar = atom.grammars.grammarForScopeName('source.js')
+ tokenizedBuffer = new TokenizedBuffer({buffer, grammar, tabLength: 2})
+ line0 = buffer.lineForRow(0)
+
+ jsScopeStartId = grammar.startIdForScope(grammar.scopeName)
+ jsScopeEndId = grammar.endIdForScope(grammar.scopeName)
+ startTokenizing(tokenizedBuffer)
+ expect(tokenizedBuffer.tokenizedLines[0]).toBeUndefined()
+ expect(tokenizedBuffer.tokenizedLineForRow(0).text).toBe(line0)
+ expect(tokenizedBuffer.tokenizedLineForRow(0).tags).toEqual([jsScopeStartId, line0.length, jsScopeEndId])
+ advanceClock(1)
+ expect(tokenizedBuffer.tokenizedLines[0]).not.toBeUndefined()
+ expect(tokenizedBuffer.tokenizedLineForRow(0).text).toBe(line0)
+ expect(tokenizedBuffer.tokenizedLineForRow(0).tags).not.toEqual([jsScopeStartId, line0.length, jsScopeEndId])
+
+ nullScopeStartId = NullGrammar.startIdForScope(NullGrammar.scopeName)
+ nullScopeEndId = NullGrammar.endIdForScope(NullGrammar.scopeName)
+ tokenizedBuffer.setGrammar(NullGrammar)
+ startTokenizing(tokenizedBuffer)
+ expect(tokenizedBuffer.tokenizedLines[0]).toBeUndefined()
+ expect(tokenizedBuffer.tokenizedLineForRow(0).text).toBe(line0)
+ expect(tokenizedBuffer.tokenizedLineForRow(0).tags).toEqual([nullScopeStartId, line0.length, nullScopeEndId])
+ advanceClock(1)
+ expect(tokenizedBuffer.tokenizedLineForRow(0).text).toBe(line0)
+ expect(tokenizedBuffer.tokenizedLineForRow(0).tags).toEqual([nullScopeStartId, line0.length, nullScopeEndId])
+
+ it "returns undefined if the requested row is outside the buffer range", ->
+ buffer = atom.project.bufferForPathSync('sample.js')
+ grammar = atom.grammars.grammarForScopeName('source.js')
+ tokenizedBuffer = new TokenizedBuffer({buffer, grammar, tabLength: 2})
+ fullyTokenize(tokenizedBuffer)
+ expect(tokenizedBuffer.tokenizedLineForRow(999)).toBeUndefined()
+
describe "when the buffer is configured with the null grammar", ->
- it "uses the placeholder tokens and does not actually tokenize using the grammar", ->
- spyOn(atom.grammars.nullGrammar, 'tokenizeLine').andCallThrough()
+ it "does not actually tokenize using the grammar", ->
+ spyOn(NullGrammar, 'tokenizeLine').andCallThrough()
buffer = atom.project.bufferForPathSync('sample.will-use-the-null-grammar')
buffer.setText('a\nb\nc')
-
- tokenizedBuffer = new TokenizedBuffer({
- buffer, grammarRegistry: atom.grammars, packageManager: atom.packages,
- assert: atom.assert, tabLength: 2,
- })
+ tokenizedBuffer = new TokenizedBuffer({buffer, tabLength: 2})
tokenizeCallback = jasmine.createSpy('onDidTokenize')
tokenizedBuffer.onDidTokenize(tokenizeCallback)
+ expect(tokenizedBuffer.tokenizedLines[0]).toBeUndefined()
+ expect(tokenizedBuffer.tokenizedLines[1]).toBeUndefined()
+ expect(tokenizedBuffer.tokenizedLines[2]).toBeUndefined()
+ expect(tokenizeCallback.callCount).toBe(0)
+ expect(NullGrammar.tokenizeLine).not.toHaveBeenCalled()
+
fullyTokenize(tokenizedBuffer)
-
- expect(tokenizeCallback.callCount).toBe 1
- expect(atom.grammars.nullGrammar.tokenizeLine.callCount).toBe 0
-
- expect(tokenizedBuffer.tokenizedLineForRow(0).tokens.length).toBe 1
- expect(tokenizedBuffer.tokenizedLineForRow(0).tokens[0].value).toBe 'a'
- expect(tokenizedBuffer.tokenizedLineForRow(1).tokens.length).toBe 1
- expect(tokenizedBuffer.tokenizedLineForRow(1).tokens[0].value).toBe 'b'
- expect(tokenizedBuffer.tokenizedLineForRow(2).tokens.length).toBe 1
- expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[0].value).toBe 'c'
+ expect(tokenizedBuffer.tokenizedLines[0]).toBeUndefined()
+ expect(tokenizedBuffer.tokenizedLines[1]).toBeUndefined()
+ expect(tokenizedBuffer.tokenizedLines[2]).toBeUndefined()
+ expect(tokenizeCallback.callCount).toBe(0)
+ expect(NullGrammar.tokenizeLine).not.toHaveBeenCalled()
describe "text decoration layer API", ->
describe "iterator", ->
it "iterates over the syntactic scope boundaries", ->
buffer = new TextBuffer(text: "var foo = 1 /*\nhello*/var bar = 2\n")
- tokenizedBuffer = new TokenizedBuffer({
- buffer, grammarRegistry: atom.grammars, packageManager: atom.packages,
- assert: atom.assert, tabLength: 2,
- })
- tokenizedBuffer.setGrammar(atom.grammars.selectGrammar(".js"))
+ tokenizedBuffer = new TokenizedBuffer({buffer, grammar: atom.grammars.grammarForScopeName("source.js"), tabLength: 2})
fullyTokenize(tokenizedBuffer)
iterator = tokenizedBuffer.buildIterator()
iterator.seek(Point(0, 0))
expectedBoundaries = [
- {position: Point(0, 0), closeTags: [], openTags: ["source.js", "storage.type.var.js"]}
- {position: Point(0, 3), closeTags: ["storage.type.var.js"], openTags: []}
- {position: Point(0, 8), closeTags: [], openTags: ["keyword.operator.assignment.js"]}
- {position: Point(0, 9), closeTags: ["keyword.operator.assignment.js"], openTags: []}
- {position: Point(0, 10), closeTags: [], openTags: ["constant.numeric.decimal.js"]}
- {position: Point(0, 11), closeTags: ["constant.numeric.decimal.js"], openTags: []}
- {position: Point(0, 12), closeTags: [], openTags: ["comment.block.js", "punctuation.definition.comment.js"]}
- {position: Point(0, 14), closeTags: ["punctuation.definition.comment.js"], openTags: []}
- {position: Point(1, 5), closeTags: [], openTags: ["punctuation.definition.comment.js"]}
- {position: Point(1, 7), closeTags: ["punctuation.definition.comment.js", "comment.block.js"], openTags: ["storage.type.var.js"]}
- {position: Point(1, 10), closeTags: ["storage.type.var.js"], openTags: []}
- {position: Point(1, 15), closeTags: [], openTags: ["keyword.operator.assignment.js"]}
- {position: Point(1, 16), closeTags: ["keyword.operator.assignment.js"], openTags: []}
- {position: Point(1, 17), closeTags: [], openTags: ["constant.numeric.decimal.js"]}
- {position: Point(1, 18), closeTags: ["constant.numeric.decimal.js"], openTags: []}
+ {position: Point(0, 0), closeTags: [], openTags: ["syntax--source.syntax--js", "syntax--storage.syntax--type.syntax--var.syntax--js"]}
+ {position: Point(0, 3), closeTags: ["syntax--storage.syntax--type.syntax--var.syntax--js"], openTags: []}
+ {position: Point(0, 8), closeTags: [], openTags: ["syntax--keyword.syntax--operator.syntax--assignment.syntax--js"]}
+ {position: Point(0, 9), closeTags: ["syntax--keyword.syntax--operator.syntax--assignment.syntax--js"], openTags: []}
+ {position: Point(0, 10), closeTags: [], openTags: ["syntax--constant.syntax--numeric.syntax--decimal.syntax--js"]}
+ {position: Point(0, 11), closeTags: ["syntax--constant.syntax--numeric.syntax--decimal.syntax--js"], openTags: []}
+ {position: Point(0, 12), closeTags: [], openTags: ["syntax--comment.syntax--block.syntax--js", "syntax--punctuation.syntax--definition.syntax--comment.syntax--js"]}
+ {position: Point(0, 14), closeTags: ["syntax--punctuation.syntax--definition.syntax--comment.syntax--js"], openTags: []}
+ {position: Point(1, 5), closeTags: [], openTags: ["syntax--punctuation.syntax--definition.syntax--comment.syntax--js"]}
+ {position: Point(1, 7), closeTags: ["syntax--punctuation.syntax--definition.syntax--comment.syntax--js", "syntax--comment.syntax--block.syntax--js"], openTags: ["syntax--storage.syntax--type.syntax--var.syntax--js"]}
+ {position: Point(1, 10), closeTags: ["syntax--storage.syntax--type.syntax--var.syntax--js"], openTags: []}
+ {position: Point(1, 15), closeTags: [], openTags: ["syntax--keyword.syntax--operator.syntax--assignment.syntax--js"]}
+ {position: Point(1, 16), closeTags: ["syntax--keyword.syntax--operator.syntax--assignment.syntax--js"], openTags: []}
+ {position: Point(1, 17), closeTags: [], openTags: ["syntax--constant.syntax--numeric.syntax--decimal.syntax--js"]}
+ {position: Point(1, 18), closeTags: ["syntax--constant.syntax--numeric.syntax--decimal.syntax--js"], openTags: []}
]
loop
@@ -637,16 +617,16 @@ describe "TokenizedBuffer", ->
expect(boundary).toEqual(expectedBoundaries.shift())
break unless iterator.moveToSuccessor()
- expect(iterator.seek(Point(0, 1))).toEqual(["source.js", "storage.type.var.js"])
+ expect(iterator.seek(Point(0, 1))).toEqual(["syntax--source.syntax--js", "syntax--storage.syntax--type.syntax--var.syntax--js"])
expect(iterator.getPosition()).toEqual(Point(0, 3))
- expect(iterator.seek(Point(0, 8))).toEqual(["source.js"])
+ expect(iterator.seek(Point(0, 8))).toEqual(["syntax--source.syntax--js"])
expect(iterator.getPosition()).toEqual(Point(0, 8))
- expect(iterator.seek(Point(1, 0))).toEqual(["source.js", "comment.block.js"])
+ expect(iterator.seek(Point(1, 0))).toEqual(["syntax--source.syntax--js", "syntax--comment.syntax--block.syntax--js"])
expect(iterator.getPosition()).toEqual(Point(1, 0))
- expect(iterator.seek(Point(1, 18))).toEqual(["source.js", "constant.numeric.decimal.js"])
+ expect(iterator.seek(Point(1, 18))).toEqual(["syntax--source.syntax--js", "syntax--constant.syntax--numeric.syntax--decimal.syntax--js"])
expect(iterator.getPosition()).toEqual(Point(1, 18))
- expect(iterator.seek(Point(2, 0))).toEqual(["source.js"])
+ expect(iterator.seek(Point(2, 0))).toEqual(["syntax--source.syntax--js"])
iterator.moveToSuccessor() # ensure we don't infinitely loop (regression test)
it "does not report columns beyond the length of the line", ->
@@ -655,11 +635,7 @@ describe "TokenizedBuffer", ->
runs ->
buffer = new TextBuffer(text: "# hello\n# world")
- tokenizedBuffer = new TokenizedBuffer({
- buffer, grammarRegistry: atom.grammars, packageManager: atom.packages,
- assert: atom.assert, tabLength: 2,
- })
- tokenizedBuffer.setGrammar(atom.grammars.selectGrammar(".coffee"))
+ tokenizedBuffer = new TokenizedBuffer({buffer, grammar: atom.grammars.grammarForScopeName("source.coffee"), tabLength: 2})
fullyTokenize(tokenizedBuffer)
iterator = tokenizedBuffer.buildIterator()
@@ -688,16 +664,12 @@ describe "TokenizedBuffer", ->
})
buffer = new TextBuffer(text: 'start x\nend x\nx')
- tokenizedBuffer = new TokenizedBuffer({
- buffer, grammarRegistry: atom.grammars, packageManager: atom.packages,
- assert: atom.assert, tabLength: 2,
- })
- tokenizedBuffer.setGrammar(grammar)
+ tokenizedBuffer = new TokenizedBuffer({buffer, grammar, tabLength: 2})
fullyTokenize(tokenizedBuffer)
iterator = tokenizedBuffer.buildIterator()
iterator.seek(Point(1, 0))
expect(iterator.getPosition()).toEqual([1, 0])
- expect(iterator.getCloseTags()).toEqual ['blue.broken']
- expect(iterator.getOpenTags()).toEqual ['yellow.broken']
+ expect(iterator.getCloseTags()).toEqual ['syntax--blue.syntax--broken']
+ expect(iterator.getOpenTags()).toEqual ['syntax--yellow.syntax--broken']
diff --git a/src/block-decorations-component.coffee b/src/block-decorations-component.coffee
deleted file mode 100644
index 48bbf77f3..000000000
--- a/src/block-decorations-component.coffee
+++ /dev/null
@@ -1,86 +0,0 @@
-cloneObject = (object) ->
- clone = {}
- clone[key] = value for key, value of object
- clone
-
-module.exports =
-class BlockDecorationsComponent
- constructor: (@container, @views, @presenter, @domElementPool) ->
- @newState = null
- @oldState = null
- @blockDecorationNodesById = {}
- @domNode = @domElementPool.buildElement("content")
- @domNode.setAttribute("select", ".atom--invisible-block-decoration")
- @domNode.style.visibility = "hidden"
-
- getDomNode: ->
- @domNode
-
- updateSync: (state) ->
- @newState = state.content
- @oldState ?= {blockDecorations: {}, width: 0}
-
- if @newState.width isnt @oldState.width
- @domNode.style.width = @newState.width + "px"
- @oldState.width = @newState.width
-
- for id of @oldState.blockDecorations
- unless @newState.blockDecorations.hasOwnProperty(id)
- blockDecorationNode = @blockDecorationNodesById[id]
- blockDecorationNode.previousSibling.remove()
- blockDecorationNode.nextSibling.remove()
- blockDecorationNode.remove()
- delete @blockDecorationNodesById[id]
- delete @oldState.blockDecorations[id]
-
- for id of @newState.blockDecorations
- if @oldState.blockDecorations.hasOwnProperty(id)
- @updateBlockDecorationNode(id)
- else
- @oldState.blockDecorations[id] = {}
- @createAndAppendBlockDecorationNode(id)
-
- measureBlockDecorations: ->
- for decorationId, blockDecorationNode of @blockDecorationNodesById
- decoration = @newState.blockDecorations[decorationId].decoration
- topRuler = blockDecorationNode.previousSibling
- bottomRuler = blockDecorationNode.nextSibling
-
- width = blockDecorationNode.offsetWidth
- height = bottomRuler.offsetTop - topRuler.offsetTop
- @presenter.setBlockDecorationDimensions(decoration, width, height)
-
- createAndAppendBlockDecorationNode: (id) ->
- blockDecorationState = @newState.blockDecorations[id]
- blockDecorationClass = "atom--block-decoration-#{id}"
- topRuler = document.createElement("div")
- blockDecorationNode = @views.getView(blockDecorationState.decoration.getProperties().item)
- bottomRuler = document.createElement("div")
- topRuler.classList.add(blockDecorationClass)
- blockDecorationNode.classList.add(blockDecorationClass)
- bottomRuler.classList.add(blockDecorationClass)
-
- @container.appendChild(topRuler)
- @container.appendChild(blockDecorationNode)
- @container.appendChild(bottomRuler)
-
- @blockDecorationNodesById[id] = blockDecorationNode
- @updateBlockDecorationNode(id)
-
- updateBlockDecorationNode: (id) ->
- newBlockDecorationState = @newState.blockDecorations[id]
- oldBlockDecorationState = @oldState.blockDecorations[id]
- blockDecorationNode = @blockDecorationNodesById[id]
-
- if newBlockDecorationState.isVisible
- blockDecorationNode.previousSibling.classList.remove("atom--invisible-block-decoration")
- blockDecorationNode.classList.remove("atom--invisible-block-decoration")
- blockDecorationNode.nextSibling.classList.remove("atom--invisible-block-decoration")
- else
- blockDecorationNode.previousSibling.classList.add("atom--invisible-block-decoration")
- blockDecorationNode.classList.add("atom--invisible-block-decoration")
- blockDecorationNode.nextSibling.classList.add("atom--invisible-block-decoration")
-
- if oldBlockDecorationState.screenRow isnt newBlockDecorationState.screenRow
- blockDecorationNode.dataset.screenRow = newBlockDecorationState.screenRow
- oldBlockDecorationState.screenRow = newBlockDecorationState.screenRow
diff --git a/src/config-schema.coffee b/src/config-schema.js
similarity index 54%
rename from src/config-schema.coffee
rename to src/config-schema.js
index ec0b20ca3..694e132db 100644
--- a/src/config-schema.coffee
+++ b/src/config-schema.js
@@ -1,68 +1,88 @@
-path = require 'path'
-fs = require 'fs-plus'
+/** @babel */
-# This is loaded by atom.coffee. See https://atom.io/docs/api/latest/Config for
-# more information about config schemas.
-module.exports =
- core:
- type: 'object'
- properties:
- ignoredNames:
- type: 'array'
- default: [".git", ".hg", ".svn", ".DS_Store", "._*", "Thumbs.db"]
- items:
+import path from 'path'
+import fs from 'fs-plus'
+
+// This is loaded by atom-environment.coffee. See
+// https://atom.io/docs/api/latest/Config for more information about config
+// schemas.
+const configSchema = {
+ core: {
+ type: 'object',
+ properties: {
+ ignoredNames: {
+ type: 'array',
+ default: ['.git', '.hg', '.svn', '.DS_Store', '._*', 'Thumbs.db'],
+ items: {
type: 'string'
+ },
description: 'List of [glob patterns](https://en.wikipedia.org/wiki/Glob_%28programming%29). Files and directories matching these patterns will be ignored by some packages, such as the fuzzy finder and tree view. Individual packages might have additional config settings for ignoring names.'
- excludeVcsIgnoredPaths:
- type: 'boolean'
- default: true
- title: 'Exclude VCS Ignored Paths'
+ },
+ excludeVcsIgnoredPaths: {
+ type: 'boolean',
+ default: true,
+ title: 'Exclude VCS Ignored Paths',
description: 'Files and directories ignored by the current project\'s VCS system will be ignored by some packages, such as the fuzzy finder and find and replace. For example, projects using Git have these paths defined in the .gitignore file. Individual packages might have additional config settings for ignoring VCS ignored files and folders.'
- followSymlinks:
- type: 'boolean'
- default: true
+ },
+ followSymlinks: {
+ type: 'boolean',
+ default: true,
description: 'Follow symbolic links when searching files and when opening files with the fuzzy finder.'
- disabledPackages:
- type: 'array'
- default: []
- items:
+ },
+ disabledPackages: {
+ type: 'array',
+ default: [],
+
+ items: {
type: 'string'
+ },
+
description: 'List of names of installed packages which are not loaded at startup.'
- customFileTypes:
- type: 'object'
- default: {}
- description: 'Associates scope names (e.g. `"source.js"`) with arrays of file extensions and file names (e.g. `["Somefile", ".js2"]`)'
- additionalProperties:
- type: 'array'
- items:
+ },
+ customFileTypes: {
+ type: 'object',
+ default: {},
+ description: 'Associates scope names (e.g. `"source.js"`) with arrays of file extensions and file names (e.g. `["Somefile", ".js2"]`)',
+ additionalProperties: {
+ type: 'array',
+ items: {
type: 'string'
- themes:
- type: 'array'
- default: ['one-dark-ui', 'one-dark-syntax']
- items:
+ }
+ }
+ },
+ themes: {
+ type: 'array',
+ default: ['one-dark-ui', 'one-dark-syntax'],
+ items: {
type: 'string'
+ },
description: 'Names of UI and syntax themes which will be used when Atom starts.'
- projectHome:
- type: 'string'
- default: path.join(fs.getHomeDirectory(), 'github')
+ },
+ projectHome: {
+ type: 'string',
+ default: path.join(fs.getHomeDirectory(), 'github'),
description: 'The directory where projects are assumed to be located. Packages created using the Package Generator will be stored here by default.'
- audioBeep:
- type: 'boolean'
- default: true
+ },
+ audioBeep: {
+ type: 'boolean',
+ default: true,
description: 'Trigger the system\'s beep sound when certain actions cannot be executed or there are no results.'
- destroyEmptyPanes:
- type: 'boolean'
- default: true
- title: 'Remove Empty Panes'
+ },
+ destroyEmptyPanes: {
+ type: 'boolean',
+ default: true,
+ title: 'Remove Empty Panes',
description: 'When the last tab of a pane is closed, remove that pane as well.'
- closeEmptyWindows:
- type: 'boolean'
- default: true
+ },
+ closeEmptyWindows: {
+ type: 'boolean',
+ default: true,
description: 'When a window with no open tabs or panes is given the \'Close Tab\' command, close that window.'
- fileEncoding:
- description: 'Default character set encoding to use when reading and writing files.'
- type: 'string'
- default: 'utf8'
+ },
+ fileEncoding: {
+ description: 'Default character set encoding to use when reading and writing files.',
+ type: 'string',
+ default: 'utf8',
enum: [
'cp437',
'eucjp',
@@ -100,179 +120,237 @@ module.exports =
'windows1258',
'windows866'
]
- openEmptyEditorOnStart:
- description: 'Automatically open an empty editor on startup.'
- type: 'boolean'
+ },
+ openEmptyEditorOnStart: {
+ description: 'Automatically open an empty editor on startup.',
+ type: 'boolean',
default: true
- automaticallyUpdate:
- description: 'Automatically update Atom when a new release is available.'
- type: 'boolean'
+ },
+ automaticallyUpdate: {
+ description: 'Automatically update Atom when a new release is available.',
+ type: 'boolean',
default: true
- allowPendingPaneItems:
- description: 'Allow items to be previewed without adding them to a pane permanently, such as when single clicking files in the tree view.'
- type: 'boolean'
+ },
+ allowPendingPaneItems: {
+ description: 'Allow items to be previewed without adding them to a pane permanently, such as when single clicking files in the tree view.',
+ type: 'boolean',
default: true
- telemetryConsent:
- description: 'Allow usage statistics and exception reports to be sent to the Atom team to help improve the product.'
- title: 'Send Telemetry to the Atom Team'
- type: 'string'
- default: 'undecided'
+ },
+ telemetryConsent: {
+ description: 'Allow usage statistics and exception reports to be sent to the Atom team to help improve the product.',
+ title: 'Send Telemetry to the Atom Team',
+ type: 'string',
+ default: 'undecided',
enum: [
- {value: 'limited', description: 'Allow limited anonymous usage stats, exception and crash reporting'}
- {value: 'no', description: 'Do not send any telemetry data'}
- {value: 'undecided', description: 'Undecided (Atom will ask again next time it is launched)'}
+ {
+ value: 'limited',
+ description: 'Allow limited anonymous usage stats, exception and crash reporting'
+ },
+ {
+ value: 'no',
+ description: 'Do not send any telemetry data'
+ },
+ {
+ value: 'undecided',
+ description: 'Undecided (Atom will ask again next time it is launched)'
+ }
]
- warnOnLargeFileLimit:
- description: 'Warn before opening files larger than this number of megabytes.'
- type: 'number'
+ },
+ warnOnLargeFileLimit: {
+ description: 'Warn before opening files larger than this number of megabytes.',
+ type: 'number',
default: 20
- editor:
- type: 'object'
- properties:
- # These settings are used in scoped fashion only. No defaults.
- commentStart:
+ }
+ }
+ },
+ editor: {
+ type: 'object',
+ // These settings are used in scoped fashion only. No defaults.
+ properties: {
+ commentStart: {
type: ['string', 'null']
- commentEnd:
+ },
+ commentEnd: {
type: ['string', 'null']
- increaseIndentPattern:
+ },
+ increaseIndentPattern: {
type: ['string', 'null']
- decreaseIndentPattern:
+ },
+ decreaseIndentPattern: {
type: ['string', 'null']
- foldEndPattern:
+ },
+ foldEndPattern: {
type: ['string', 'null']
-
- # These can be used as globals or scoped, thus defaults.
- fontFamily:
- type: 'string'
- default: ''
+ },
+ // These can be used as globals or scoped, thus defaults.
+ fontFamily: {
+ type: 'string',
+ default: '',
description: 'The name of the font family used for editor text.'
- fontSize:
- type: 'integer'
- default: 14
- minimum: 1
- maximum: 100
+ },
+ fontSize: {
+ type: 'integer',
+ default: 14,
+ minimum: 1,
+ maximum: 100,
description: 'Height in pixels of editor text.'
- lineHeight:
- type: ['string', 'number']
- default: 1.5
+ },
+ lineHeight: {
+ type: ['string', 'number'],
+ default: 1.5,
description: 'Height of editor lines, as a multiplier of font size.'
- showInvisibles:
- type: 'boolean'
- default: false
+ },
+ showInvisibles: {
+ type: 'boolean',
+ default: false,
description: 'Render placeholders for invisible characters, such as tabs, spaces and newlines.'
- showIndentGuide:
- type: 'boolean'
- default: false
+ },
+ showIndentGuide: {
+ type: 'boolean',
+ default: false,
description: 'Show indentation indicators in the editor.'
- showLineNumbers:
- type: 'boolean'
- default: true
+ },
+ showLineNumbers: {
+ type: 'boolean',
+ default: true,
description: 'Show line numbers in the editor\'s gutter.'
- atomicSoftTabs:
- type: 'boolean'
- default: true
+ },
+ atomicSoftTabs: {
+ type: 'boolean',
+ default: true,
description: 'Skip over tab-length runs of leading whitespace when moving the cursor.'
- autoIndent:
- type: 'boolean'
- default: true
+ },
+ autoIndent: {
+ type: 'boolean',
+ default: true,
description: 'Automatically indent the cursor when inserting a newline.'
- autoIndentOnPaste:
- type: 'boolean'
- default: true
+ },
+ autoIndentOnPaste: {
+ type: 'boolean',
+ default: true,
description: 'Automatically indent pasted text based on the indentation of the previous line.'
- nonWordCharacters:
- type: 'string'
- default: "/\\()\"':,.;<>~!@#$%^&*|+=[]{}`?-ā¦"
+ },
+ nonWordCharacters: {
+ type: 'string',
+ default: "/\\()\"':,.;<>~!@#$%^&*|+=[]{}`?-ā¦",
description: 'A string of non-word characters to define word boundaries.'
- preferredLineLength:
- type: 'integer'
- default: 80
- minimum: 1
+ },
+ preferredLineLength: {
+ type: 'integer',
+ default: 80,
+ minimum: 1,
description: 'Identifies the length of a line which is used when wrapping text with the `Soft Wrap At Preferred Line Length` setting enabled, in number of characters.'
- tabLength:
- type: 'integer'
- default: 2
- minimum: 1
+ },
+ tabLength: {
+ type: 'integer',
+ default: 2,
+ minimum: 1,
description: 'Number of spaces used to represent a tab.'
- softWrap:
- type: 'boolean'
- default: false
+ },
+ softWrap: {
+ type: 'boolean',
+ default: false,
description: 'Wraps lines that exceed the width of the window. When `Soft Wrap At Preferred Line Length` is set, it will wrap to the number of characters defined by the `Preferred Line Length` setting.'
- softTabs:
- type: 'boolean'
- default: true
+ },
+ softTabs: {
+ type: 'boolean',
+ default: true,
description: 'If the `Tab Type` config setting is set to "auto" and autodetection of tab type from buffer content fails, then this config setting determines whether a soft tab or a hard tab will be inserted when the Tab key is pressed.'
- tabType:
- type: 'string'
- default: 'auto'
- enum: ['auto', 'soft', 'hard']
+ },
+ tabType: {
+ type: 'string',
+ default: 'auto',
+ enum: ['auto', 'soft', 'hard'],
description: 'Determine character inserted when Tab key is pressed. Possible values: "auto", "soft" and "hard". When set to "soft" or "hard", soft tabs (spaces) or hard tabs (tab characters) are used. When set to "auto", the editor auto-detects the tab type based on the contents of the buffer (it uses the first leading whitespace on a non-comment line), or uses the value of the Soft Tabs config setting if auto-detection fails.'
- softWrapAtPreferredLineLength:
- type: 'boolean'
- default: false
+ },
+ softWrapAtPreferredLineLength: {
+ type: 'boolean',
+ default: false,
description: 'Instead of wrapping lines to the window\'s width, wrap lines to the number of characters defined by the `Preferred Line Length` setting. This will only take effect when the soft wrap config setting is enabled globally or for the current language. **Note:** If you want to hide the wrap guide (the vertical line) you can disable the `wrap-guide` package.'
- softWrapHangingIndent:
- type: 'integer'
- default: 0
- minimum: 0
+ },
+ softWrapHangingIndent: {
+ type: 'integer',
+ default: 0,
+ minimum: 0,
description: 'When soft wrap is enabled, defines length of additional indentation applied to wrapped lines, in number of characters.'
- scrollSensitivity:
- type: 'integer'
- default: 40
- minimum: 10
- maximum: 200
+ },
+ scrollSensitivity: {
+ type: 'integer',
+ default: 40,
+ minimum: 10,
+ maximum: 200,
description: 'Determines how fast the editor scrolls when using a mouse or trackpad.'
- scrollPastEnd:
- type: 'boolean'
- default: false
+ },
+ scrollPastEnd: {
+ type: 'boolean',
+ default: false,
description: 'Allow the editor to be scrolled past the end of the last line.'
- undoGroupingInterval:
- type: 'integer'
- default: 300
- minimum: 0
+ },
+ undoGroupingInterval: {
+ type: 'integer',
+ default: 300,
+ minimum: 0,
description: 'Time interval in milliseconds within which text editing operations will be grouped together in the undo history.'
- confirmCheckoutHeadRevision:
- type: 'boolean'
- default: true
- title: 'Confirm Checkout HEAD Revision'
+ },
+ confirmCheckoutHeadRevision: {
+ type: 'boolean',
+ default: true,
+ title: 'Confirm Checkout HEAD Revision',
description: 'Show confirmation dialog when checking out the HEAD revision and discarding changes to current file since last commit.'
- invisibles:
- type: 'object'
- description: 'A hash of characters Atom will use to render whitespace characters. Keys are whitespace character types, values are rendered characters (use value false to turn off individual whitespace character types).'
- properties:
- eol:
- type: ['boolean', 'string']
- default: '\u00ac'
- maximumLength: 1
+ },
+ invisibles: {
+ type: 'object',
+ description: 'A hash of characters Atom will use to render whitespace characters. Keys are whitespace character types, values are rendered characters (use value false to turn off individual whitespace character types).',
+ properties: {
+ eol: {
+ type: ['boolean', 'string'],
+ default: '¬',
+ maximumLength: 1,
description: 'Character used to render newline characters (\\n) when the `Show Invisibles` setting is enabled. '
- space:
- type: ['boolean', 'string']
- default: '\u00b7'
- maximumLength: 1
+ },
+ space: {
+ type: ['boolean', 'string'],
+ default: 'Ā·',
+ maximumLength: 1,
description: 'Character used to render leading and trailing space characters when the `Show Invisibles` setting is enabled.'
- tab:
- type: ['boolean', 'string']
- default: '\u00bb'
- maximumLength: 1
+ },
+ tab: {
+ type: ['boolean', 'string'],
+ default: 'Ā»',
+ maximumLength: 1,
description: 'Character used to render hard tab characters (\\t) when the `Show Invisibles` setting is enabled.'
- cr:
- type: ['boolean', 'string']
- default: '\u00a4'
- maximumLength: 1
+ },
+ cr: {
+ type: ['boolean', 'string'],
+ default: '¤',
+ maximumLength: 1,
description: 'Character used to render carriage return characters (for Microsoft-style line endings) when the `Show Invisibles` setting is enabled.'
- zoomFontWhenCtrlScrolling:
- type: 'boolean'
- default: process.platform isnt 'darwin'
+ }
+ }
+ },
+ zoomFontWhenCtrlScrolling: {
+ type: 'boolean',
+ default: process.platform !== 'darwin',
description: 'Change the editor font size when pressing the Ctrl key and scrolling the mouse up/down.'
+ }
+ }
+ }
+}
-if process.platform in ['win32', 'linux']
- module.exports.core.properties.autoHideMenuBar =
- type: 'boolean'
- default: false
+if (['win32', 'linux'].includes(process.platform)) {
+ configSchema.core.properties.autoHideMenuBar = {
+ type: 'boolean',
+ default: false,
description: 'Automatically hide the menu bar and toggle it by pressing Alt. This is only supported on Windows & Linux.'
+ }
+}
-if process.platform is 'darwin'
- module.exports.core.properties.useCustomTitleBar =
- type: 'boolean'
- default: false
+if (process.platform === 'darwin') {
+ configSchema.core.properties.useCustomTitleBar = {
+ type: 'boolean',
+ default: false,
description: 'Use custom, theme-aware title bar.
Note: This currently does not include a proxy icon.
This setting will require a relaunch of Atom to take effect.'
+ }
+}
+
+export default configSchema
diff --git a/src/deprecated-syntax-selectors.js b/src/deprecated-syntax-selectors.js
new file mode 100644
index 000000000..4f8b9cefc
--- /dev/null
+++ b/src/deprecated-syntax-selectors.js
@@ -0,0 +1,964 @@
+module.exports = new Set([
+ 'AFDKO', 'AFKDO', 'ASS', 'AVX', 'AVX2', 'AVX512', 'AVX512BW', 'AVX512DQ',
+ 'Alignment', 'Alpha', 'AlphaLevel', 'Angle', 'Animation', 'AnimationGroup',
+ 'ArchaeologyDigSiteFrame', 'Arrow__', 'AtLilyPond', 'AttrBaseType',
+ 'AttrSetVal__', 'BackColour', 'Banner', 'Bold', 'Bonlang', 'BorderStyle',
+ 'Browser', 'Button', 'C99', 'CALCULATE', 'CharacterSet', 'ChatScript',
+ 'Chatscript', 'CheckButton', 'ClipboardFormat', 'ClipboardType',
+ 'Clipboard__', 'CodePage', 'Codepages__', 'Collisions', 'ColorSelect',
+ 'ColourActual', 'ColourLogical', 'ColourReal', 'ColourScheme', 'ColourSize',
+ 'Column', 'Comment', 'ConfCachePolicy', 'ControlPoint', 'Cooldown', 'DBE',
+ 'DDL', 'DML', 'DSC', 'Database__', 'DdcMode', 'Dialogue',
+ 'DiscussionFilterType', 'DiscussionStatus', 'DisplaySchemes',
+ 'Document-Structuring-Comment', 'DressUpModel', 'Edit', 'EditBox', 'Effect',
+ 'Encoding', 'End', 'ExternalLinkBehaviour', 'ExternalLinkDirection', 'F16c',
+ 'FMA', 'FilterType', 'Font', 'FontInstance', 'FontString', 'Fontname',
+ 'Fonts__', 'Fontsize', 'Format', 'Frame', 'GameTooltip', 'GroupList', 'HLE',
+ 'HeaderEvent', 'HistoryType', 'HttpVerb', 'II', 'IO', 'Icon', 'IconID',
+ 'InPlaceBox__', 'InPlaceEditEvent', 'Info', 'Italic', 'JSXEndTagStart',
+ 'JSXStartTagEnd', 'KNC', 'KeyModifier', 'Kotlin', 'LUW', 'Language', 'Layer',
+ 'LayeredRegion', 'LdapItemList', 'LineSpacing', 'LinkFilter', 'LinkLimit',
+ 'ListView', 'Locales__', 'Lock', 'LoginPolicy', 'MA_End__', 'MA_StdCombo__',
+ 'MA_StdItem__', 'MA_StdMenu__', 'MISSING', 'Mapping', 'MarginL', 'MarginR',
+ 'MarginV', 'Marked', 'MessageFrame', 'Minimap', 'MovieFrame', 'Name',
+ 'Outline', 'OutlineColour', 'ParentedObject', 'Path', 'Permission', 'PlayRes',
+ 'PlayerModel', 'PrimaryColour', 'Proof', 'QuestPOIFrame', 'RTM',
+ 'RecentModule__', 'Regexp', 'Region', 'Rotation', 'SCADABasic', 'SSA',
+ 'Scale', 'ScaleX', 'ScaleY', 'ScaledBorderAndShadow', 'ScenarioPOIFrame',
+ 'ScriptObject', 'Script__', 'Scroll', 'ScrollEvent', 'ScrollFrame',
+ 'ScrollSide', 'ScrollingMessageFrame', 'SecondaryColour', 'Sensitivity',
+ 'Shadow', 'SimpleHTML', 'Slider', 'Spacing', 'Start', 'StatusBar', 'Stream',
+ 'StrikeOut', 'Style', 'TIS', 'TODO', 'TabardModel', 'Text', 'Texture',
+ 'Timer', 'ToolType', 'Translation', 'TreeView', 'TriggerStatus', 'UIObject',
+ 'Underline', 'UserClass', 'UserList', 'UserNotifyList', 'VisibleRegion',
+ 'Vplus', 'WrapStyle', 'XHPEndTagStart', 'XHPStartTagEnd', 'ZipType',
+ '__package-name__', '_c', '_function', 'a', 'a10networks', 'aaa', 'abaqus',
+ 'abbrev', 'abbreviated', 'abbreviation', 'abcnotation', 'abl', 'abnf', 'abp',
+ 'absolute', 'abstract', 'academic', 'access', 'access-control',
+ 'access-qualifiers', 'accessed', 'accessor', 'account', 'accumulator', 'ace',
+ 'ace3', 'acl', 'acos', 'act', 'action', 'action-map', 'actionhandler',
+ 'actionpack', 'actions', 'actionscript', 'activerecord', 'activesupport',
+ 'actual', 'acute-accent', 'ada', 'add', 'adddon', 'added', 'addition',
+ 'additional-character', 'additive', 'addon', 'address', 'address-of',
+ 'address-space', 'addrfam', 'adjustment', 'admonition', 'adr', 'adverb',
+ 'adx', 'ael', 'aem', 'aerospace', 'aes', 'aes_functions', 'aesni',
+ 'aexLightGreen', 'af', 'afii', 'aflex', 'after', 'after-expression', 'agc',
+ 'agda', 'agentspeak', 'aggregate', 'aggregation', 'ahk', 'ai-connection',
+ 'ai-player', 'ai-wheeled-vehicle', 'aif', 'alabel', 'alarms', 'alda', 'alert',
+ 'algebraic-type', 'alias', 'aliases', 'align', 'align-attribute', 'alignment',
+ 'alignment-cue-setting', 'alignment-mode', 'all', 'all-once', 'all-solutions',
+ 'allocate', 'alloy', 'alloyglobals', 'alloyxml', 'alog', 'alpha',
+ 'alphabeticalllt', 'alphabeticallyge', 'alphabeticallygt', 'alphabeticallyle',
+ 'alt', 'alter', 'alternate-wysiwyg-string', 'alternates', 'alternation',
+ 'alternatives', 'am', 'ambient-audio-manager', 'ambient-reflectivity', 'amd',
+ 'amd3DNow', 'amdnops', 'ameter', 'amount', 'amp', 'ampersand', 'ampl',
+ 'ampscript', 'an', 'analysis', 'analytics', 'anb', 'anchor', 'and', 'andop',
+ 'angelscript', 'angle', 'angle-brackets', 'angular', 'animation', 'annot',
+ 'annotated', 'annotation', 'annotation-arguments', 'anon', 'anonymous',
+ 'another', 'ansi', 'ansi-c', 'ansi-colored', 'ansi-escape-code',
+ 'ansi-formatted', 'ansi2', 'ansible', 'answer', 'antialiasing', 'antl',
+ 'antlr', 'antlr4', 'anubis', 'any', 'any-method', 'anyclass', 'aolserver',
+ 'apa', 'apache', 'apache-config', 'apc', 'apdl', 'apex', 'api',
+ 'api-notation', 'apiary', 'apib', 'apl', 'apostrophe', 'appcache',
+ 'applescript', 'application', 'application-name', 'application-process',
+ 'approx-equal', 'aql', 'aqua', 'ar', 'arbitrary-radix',
+ 'arbitrary-repetition', 'arbitrary-repitition', 'arch', 'arch_specification',
+ 'architecture', 'archive', 'archives', 'arduino', 'area-code', 'arendelle',
+ 'argcount', 'args', 'argument', 'argument-label', 'argument-separator',
+ 'argument-seperator', 'argument-type', 'arguments', 'arith', 'arithmetic',
+ 'arithmetical', 'arithmeticcql', 'ark', 'arm', 'arma', 'armaConfig',
+ 'arnoldc', 'arp', 'arpop', 'arr', 'array', 'array-expression',
+ 'array-literal', 'arrays', 'arrow', 'articulation', 'artihmetic', 'arvo',
+ 'aryop', 'as', 'as4', 'ascii', 'asciidoc', 'asdoc', 'ash', 'ashx', 'asl',
+ 'asm', 'asm-instruction', 'asm-type-prefix', 'asn', 'asp', 'asp-core-2',
+ 'aspx', 'ass', 'assembly', 'assert', 'assertion', 'assigment', 'assign',
+ 'assign-class', 'assigned', 'assigned-class', 'assigned-value', 'assignee',
+ 'assignement', 'assignment', 'assignmentforge-config', 'associate',
+ 'association', 'associativity', 'assocs', 'asterisk', 'async', 'at-marker',
+ 'at-root', 'at-rule', 'at-sign', 'atmark', 'atml3', 'atoemp', 'atom',
+ 'atom-term-processing', 'atomic', 'atomscript', 'att', 'attachment', 'attr',
+ 'attribute', 'attribute-entry', 'attribute-expression', 'attribute-key-value',
+ 'attribute-list', 'attribute-lookup', 'attribute-name', 'attribute-reference',
+ 'attribute-selector', 'attribute-value', 'attribute-values',
+ 'attribute-with-value', 'attribute_list', 'attribute_value',
+ 'attribute_value2', 'attributelist', 'attributes', 'attrset',
+ 'attrset-or-function', 'audio', 'audio-file', 'auditor', 'augmented', 'auth',
+ 'auth_basic', 'author', 'author-names', 'authorization', 'auto', 'auto-event',
+ 'autoconf', 'autoindex', 'autoit', 'automake', 'automatic', 'autotools',
+ 'autovar', 'aux', 'auxiliary', 'avdl', 'avra', 'avrasm', 'avrdisasm', 'avs',
+ 'avx', 'avx2', 'avx512', 'awk', 'axes_group', 'axis', 'axl', 'b',
+ 'b-spline-patch', 'babel', 'back', 'back-from', 'back-reference',
+ 'back-slash', 'backend', 'background', 'backreference', 'backslash',
+ 'backslash-bar', 'backslash-g', 'backspace', 'backtick', 'bad-ampersand',
+ 'bad-angle-bracket', 'bad-assignment', 'bad-comments-or-CDATA', 'bad-escape',
+ 'bad-octal', 'bad-var', 'bang', 'banner', 'bar', 'bareword', 'barline',
+ 'base', 'base-11', 'base-12', 'base-13', 'base-14', 'base-15', 'base-16',
+ 'base-17', 'base-18', 'base-19', 'base-20', 'base-21', 'base-22', 'base-23',
+ 'base-24', 'base-25', 'base-26', 'base-27', 'base-28', 'base-29', 'base-3',
+ 'base-30', 'base-31', 'base-32', 'base-33', 'base-34', 'base-35', 'base-36',
+ 'base-4', 'base-5', 'base-6', 'base-7', 'base-9', 'base-call', 'base-integer',
+ 'base64', 'base85', 'base_pound_number_pound', 'basetype', 'basic',
+ 'basic-arithmetic', 'basic-type', 'basic_functions', 'basicblock',
+ 'basis-matrix', 'bat', 'batch', 'batchfile', 'battlesim', 'bb', 'bbcode',
+ 'bcmath', 'be', 'beam', 'beamer', 'beancount', 'before', 'begin',
+ 'begin-document', 'begin-emphasis', 'begin-end', 'begin-end-group',
+ 'begin-literal', 'begin-symbolic', 'begintimeblock', 'behaviour', 'bem',
+ 'between-tag-pair', 'bevel', 'bezier-patch', 'bfeac', 'bff', 'bg', 'bg-black',
+ 'bg-blue', 'bg-cyan', 'bg-green', 'bg-normal', 'bg-purple', 'bg-red',
+ 'bg-white', 'bg-yellow', 'bhtml', 'bhv', 'bibitem', 'bibliography-anchor',
+ 'biblioref', 'bibpaper', 'bibtex', 'bif', 'big-arrow', 'big-arrow-left',
+ 'bigdecimal', 'bigint', 'biicode', 'biiconf', 'bin', 'binOp', 'binary',
+ 'binary-arithmetic', 'bind', 'binder', 'binding', 'binding-prefix',
+ 'bindings', 'binop', 'bioinformatics', 'biosphere', 'bird-track', 'bis',
+ 'bison', 'bit', 'bit-and-byte', 'bit-range', 'bit-wise', 'bitarray', 'bitop',
+ 'bits-mov', 'bitvector', 'bitwise', 'black', 'blade', 'blanks', 'blaze',
+ 'blenc', 'blend', 'blending', 'blendtype', 'blendu', 'blendv', 'blip',
+ 'block', 'block-attribute', 'block-dartdoc', 'block-data', 'block-level',
+ 'blockid', 'blockname', 'blockquote', 'blocktitle', 'blue', 'blueprint',
+ 'bluespec', 'blur', 'bm', 'bmi', 'bmi1', 'bmi2', 'bnd', 'bnf', 'body',
+ 'body-statement', 'bold', 'bold-italic-text', 'bold-text', 'bolt', 'bond',
+ 'bonlang', 'boo', 'boogie', 'bool', 'boolean', 'boolean-test', 'boost',
+ 'boot', 'bord', 'border', 'botml', 'bottom', 'boundary', 'bounded', 'bounds',
+ 'bow', 'box', 'bpl', 'bpr', 'bqparam', 'brace', 'braced', 'braces', 'bracket',
+ 'bracketed', 'brackets', 'brainfuck', 'branch', 'branch-point', 'break',
+ 'breakpoint', 'breakpoints', 'breaks', 'bridle', 'brightscript', 'bro',
+ 'broken', 'browser', 'browsers', 'bs', 'bsl', 'btw', 'buffered', 'buffers',
+ 'bugzilla-number', 'build', 'buildin', 'buildout', 'built-in',
+ 'built-in-variable', 'built-ins', 'builtin', 'builtin-comparison', 'builtins',
+ 'bullet', 'bullet-point', 'bump', 'bump-multiplier', 'bundle', 'but',
+ 'button', 'buttons', 'by', 'by-name', 'by-number', 'byref', 'byte',
+ 'bytearray', 'bz2', 'bzl', 'c', 'c-style', 'c0', 'c1', 'c2hs', 'ca', 'cabal',
+ 'cabal-keyword', 'cache', 'cache-management', 'cacheability-control', 'cake',
+ 'calc', 'calca', 'calendar', 'call', 'callable', 'callback', 'caller',
+ 'calling', 'callmethod', 'callout', 'callparent', 'camera', 'camlp4',
+ 'camlp4-stream', 'canonicalized-program-name', 'canopen', 'capability',
+ 'capnp', 'cappuccino', 'caps', 'caption', 'capture', 'capturename',
+ 'cardinal-curve', 'cardinal-patch', 'cascade', 'case', 'case-block',
+ 'case-body', 'case-class', 'case-clause', 'case-clause-body',
+ 'case-expression', 'case-modifier', 'case-pattern', 'case-statement',
+ 'case-terminator', 'case-value', 'cassius', 'cast', 'catch',
+ 'catch-exception', 'catcode', 'categories', 'categort', 'category', 'cba',
+ 'cbmbasic', 'cbot', 'cbs', 'cc', 'cc65', 'ccml', 'cdata', 'cdef', 'cdtor',
+ 'ceiling', 'cell', 'cellcontents', 'cellwall', 'ceq', 'ces', 'cet', 'cexpr',
+ 'cextern', 'ceylon', 'ceylondoc', 'cf', 'cfdg', 'cfengine', 'cfg', 'cfml',
+ 'cfscript', 'cfunction', 'cg', 'cgi', 'cgx', 'chain', 'chained', 'chaining',
+ 'chainname', 'changed', 'changelogs', 'changes', 'channel', 'chapel',
+ 'chapter', 'char', 'characater', 'character', 'character-class',
+ 'character-data-not-allowed-here', 'character-literal',
+ 'character-literal-too-long', 'character-not-allowed-here', 'character-range',
+ 'character-reference', 'character-token', 'character_not_allowed',
+ 'character_not_allowed_here', 'characters', 'chars', 'chars-and-bytes-io',
+ 'charset', 'check', 'check-identifier', 'checkboxes', 'checker', 'chef',
+ 'chem', 'chemical', 'children', 'choice', 'choicescript', 'chord', 'chorus',
+ 'chuck', 'chunk', 'ciexyz', 'circle', 'circle-jot', 'cirru', 'cisco',
+ 'cisco-ios-config', 'citation', 'cite', 'citrine', 'cjam', 'cjson', 'clamp',
+ 'clamping', 'class', 'class-constraint', 'class-constraints',
+ 'class-declaration', 'class-definition', 'class-fns', 'class-instance',
+ 'class-list', 'class-struct-block', 'class-type', 'class-type-definition',
+ 'classcode', 'classes', 'classic', 'classicalb', 'classmethods', 'classobj',
+ 'classtree', 'clause', 'clause-head-body', 'clauses', 'clear',
+ 'clear-argument', 'cleared', 'clflushopt', 'click', 'client', 'client-server',
+ 'clip', 'clipboard', 'clips', 'clmul', 'clock', 'clojure', 'cloned', 'close',
+ 'closed', 'closing', 'closing-text', 'closure', 'clothes-body', 'cm', 'cmake',
+ 'cmb', 'cmd', 'cnet', 'cns', 'cobject', 'cocoa', 'cocor', 'cod4mp', 'code',
+ 'code-example', 'codeblock', 'codepoint', 'codimension', 'codstr', 'coffee',
+ 'coffeescript', 'coffeescript-preview', 'coil', 'collection', 'collision',
+ 'colon', 'colons', 'color', 'color-adjustment', 'coloring', 'colour',
+ 'colour-correction', 'colour-interpolation', 'colour-name', 'colour-scheme',
+ 'colspan', 'column', 'column-divider', 'column-specials', 'com',
+ 'combinators', 'comboboxes', 'comma', 'comma-bar', 'comma-parenthesis',
+ 'command', 'command-name', 'command-synopsis', 'commandline', 'commands',
+ 'comment', 'comment-ish', 'comment-italic', 'commented-out', 'commit-command',
+ 'commit-message', 'commodity', 'common', 'commonform', 'communications',
+ 'community', 'commute', 'comnd', 'compare', 'compareOp', 'comparison',
+ 'compile', 'compile-only', 'compiled', 'compiled-papyrus', 'compiler',
+ 'compiler-directive', 'compiletime', 'compiling-and-loading', 'complement',
+ 'complete', 'completed', 'complex', 'component', 'component-separator',
+ 'component_instantiation', 'compositor', 'compound', 'compound-assignment',
+ 'compress', 'computer', 'computercraft', 'concat', 'concatenated-arguments',
+ 'concatenation', 'concatenator', 'concatination', 'concealed', 'concise',
+ 'concrete', 'condition', 'conditional', 'conditional-directive',
+ 'conditional-short', 'conditionals', 'conditions', 'conf', 'config',
+ 'configuration', 'configure', 'confluence', 'conftype', 'conjunction',
+ 'conky', 'connect', 'connection-state', 'connectivity', 'connstate', 'cons',
+ 'consecutive-tags', 'considering', 'console', 'const', 'const-data',
+ 'constant', 'constants', 'constrained', 'constraint', 'constraints',
+ 'construct', 'constructor', 'constructor-list', 'constructs', 'consult',
+ 'contacts', 'container', 'containers-raycast', 'contains', 'content',
+ 'content-detective', 'contentSupplying', 'contentitem', 'context',
+ 'context-free', 'context-signature', 'continuation', 'continuations',
+ 'continue', 'continued', 'continuum', 'contol', 'contract', 'contracts',
+ 'contrl', 'control', 'control-char', 'control-handlers', 'control-management',
+ 'control-systems', 'control-transfer', 'controller', 'controlline',
+ 'controls', 'contstant', 'conventional', 'conversion', 'convert-type',
+ 'cookie', 'cool', 'coord1', 'coord2', 'coord3', 'coordinates', 'copy',
+ 'copying', 'coq', 'core', 'core-parse', 'coreutils', 'correct', 'cos',
+ 'counter', 'counters', 'cover', 'cplkg', 'cplusplus', 'cpm', 'cpp',
+ 'cpp-include', 'cpp-type', 'cpp_type', 'cpu12', 'cql', 'cram', 'crc32',
+ 'create', 'creation', 'critic', 'crl', 'crontab', 'crypto', 'crystal', 'cs',
+ 'csharp', 'cshtml', 'csi', 'csjs', 'csound', 'csound-document',
+ 'csound-score', 'cspm', 'css', 'csv', 'csx', 'ct', 'ctkey', 'ctor', 'ctxvar',
+ 'ctxvarbracket', 'ctype', 'cubic-bezier', 'cucumber', 'cuda',
+ 'cue-identifier', 'cue-timings', 'cuesheet', 'cup', 'cupsym', 'curl',
+ 'curley', 'curly', 'currency', 'current', 'current-escape-char',
+ 'curve', 'curve-2d', 'curve-fitting', 'curve-reference', 'curve-technique',
+ 'custom', 'customevent', 'cut', 'cve-number', 'cvs', 'cw', 'cxx', 'cy-GB',
+ 'cyan', 'cyc', 'cycle', 'cypher', 'cyrix', 'cython', 'd', 'da', 'daml',
+ 'dana', 'danger', 'danmakufu', 'dark_aqua', 'dark_blue', 'dark_gray',
+ 'dark_green', 'dark_purple', 'dark_red', 'dart', 'dartdoc', 'dash', 'dasm',
+ 'data', 'data-acquisition', 'data-extension', 'data-integrity', 'data-item',
+ 'data-step', 'data-transfer', 'database', 'database-name', 'datablock',
+ 'datablocks', 'datafeed', 'datatype', 'datatypes', 'date', 'date-time',
+ 'datetime', 'dav', 'day', 'dayofmonth', 'dayofweek', 'db', 'dba', 'dbx', 'dc',
+ 'dcon', 'dd', 'ddp', 'de', 'dealii', 'deallocate', 'deb-control', 'debian',
+ 'debris', 'debug', 'debug-specification', 'debugger', 'debugging',
+ 'debugging-comment', 'dec', 'decal', 'decimal', 'decimal-arithmetic',
+ 'decision', 'decl', 'declaration', 'declaration-expr', 'declaration-prod',
+ 'declarations', 'declarator', 'declaratyion', 'declare', 'decode',
+ 'decoration', 'decorator', 'decreasing', 'decrement', 'def', 'default',
+ 'define', 'define-colour', 'defined', 'definedness', 'definingobj',
+ 'definition', 'definitions', 'defintions', 'deflate', 'delay', 'delegated',
+ 'delete', 'deleted', 'deletion', 'delimeter', 'delimited', 'delimiter',
+ 'delimiter-too-long', 'delimiters', 'dense', 'deprecated', 'depricated',
+ 'dereference', 'derived-type', 'deriving', 'desc', 'describe', 'description',
+ 'descriptors', 'design', 'desktop', 'destination', 'destructor',
+ 'destructured', 'determ', 'developer', 'device', 'device-io', 'dformat', 'dg',
+ 'dhcp', 'diagnostic', 'dialogue', 'diamond', 'dict', 'dictionary',
+ 'dictionaryname', 'diff', 'difference', 'different', 'diffuse-reflectivity',
+ 'digdag', 'digit-width', 'dim', 'dimension', 'dip', 'dir', 'dir-target',
+ 'dircolors', 'direct', 'direction', 'directive', 'directive-option',
+ 'directives', 'directory', 'dirjs', 'dirtyblue', 'dirtygreen', 'disable',
+ 'disable-markdown', 'disable-todo', 'discarded', 'discusson', 'disjunction',
+ 'disk', 'disk-folder-file', 'dism', 'displacement', 'display', 'dissolve',
+ 'dissolve-interpolation', 'distribution', 'diverging-function', 'divert',
+ 'divide', 'divider', 'django', 'dl', 'dlv', 'dm', 'dmf', 'dml', 'do',
+ 'dobody', 'doc', 'doc-comment', 'docRoot', 'dockerfile', 'dockerignore',
+ 'doconce', 'docstring', 'doctest', 'doctree-option', 'doctype', 'document',
+ 'documentation', 'documentroot', 'does', 'dogescript', 'doki', 'dollar',
+ 'dollar-quote', 'dollar_variable', 'dom', 'domain', 'dontcollect', 'doors',
+ 'dop', 'dot', 'dot-access', 'dotenv', 'dotfiles', 'dothandout', 'dotnet',
+ 'dotnote', 'dots', 'dotted', 'dotted-circle', 'dotted-del', 'dotted-greater',
+ 'dotted-tack-up', 'double', 'double-arrow', 'double-colon', 'double-dash',
+ 'double-dash-not-allowed', 'double-dot', 'double-number-sign',
+ 'double-percentage', 'double-qoute', 'double-quote', 'double-quoted',
+ 'double-quoted-string', 'double-semicolon', 'double-slash', 'doublequote',
+ 'doubleslash', 'dougle', 'down', 'download', 'downwards', 'doxyfile',
+ 'doxygen', 'dragdrop', 'drawing', 'drive', 'droiuby', 'drop', 'drop-shadow',
+ 'droplevel', 'drummode', 'drupal', 'dsl', 'dsv', 'dt', 'dtl', 'due', 'dummy',
+ 'dummy-variable', 'dump', 'duration', 'dust', 'dust_Conditional',
+ 'dust_end_section_tag', 'dust_filter', 'dust_partial',
+ 'dust_partial_not_self_closing', 'dust_ref', 'dust_ref_name',
+ 'dust_section_context', 'dust_section_name', 'dust_section_params',
+ 'dust_self_closing_section_tag', 'dust_special', 'dust_start_section_tag',
+ 'dustjs', 'dut', 'dwscript', 'dxl', 'dylan', 'dynamic', 'dyndoc', 'dyon', 'e',
+ 'e3globals', 'each', 'eachin', 'earl-grey', 'ebnf', 'ebuild', 'echo',
+ 'eclass', 'ecmascript', 'eco', 'ecr', 'ect', 'ect2', 'ect3', 'ect4', 'edasm',
+ 'edge', 'edit-manager', 'editfields', 'editors', 'ee', 'eex', 'effect',
+ 'effectgroup', 'effective_routine_body', 'effects', 'eiffel', 'eight', 'eio',
+ 'eiz', 'ejectors', 'el', 'elasticsearch', 'elasticsearch2', 'element',
+ 'elements', 'elemnt', 'elif', 'elipse', 'elision', 'elixir', 'ellipsis',
+ 'elm', 'elmx', 'else', 'else-condition', 'else-if', 'elseif',
+ 'elseif-condition', 'elsewhere', 'eltype', 'elvis', 'em', 'email', 'embed',
+ 'embed-diversion', 'embedded', 'embedded-c', 'embedded-ruby', 'embedded2',
+ 'embeded', 'ember', 'emberscript', 'emblem', 'embperl', 'emissive-colour',
+ 'eml', 'emlist', 'emoji', 'emojicode', 'emp', 'emph', 'emphasis', 'empty',
+ 'empty-dictionary', 'empty-list', 'empty-parenthesis', 'empty-start',
+ 'empty-string', 'empty-tag', 'empty-tuple', 'empty-typing-pair', 'empty_gif',
+ 'emptyelement', 'en', 'en-Scouse', 'en-au', 'en-lol', 'en-old', 'en-pirate',
+ 'enable', 'enc', 'enchant', 'enclose', 'encode', 'encoding', 'encryption',
+ 'end', 'end-block-data', 'end-definition', 'end-document', 'end-enum',
+ 'end-footnote', 'end-of-line', 'end-statement', 'end-value', 'endassociate',
+ 'endcode', 'enddo', 'endfile', 'endforall', 'endfunction', 'endian',
+ 'endianness', 'endif', 'endinfo', 'ending', 'ending-space', 'endinterface',
+ 'endlocaltable', 'endmodule', 'endobject', 'endobjecttable', 'endparamtable',
+ 'endprogram', 'endproperty', 'endpropertygroup', 'endpropertygrouptable',
+ 'endpropertytable', 'endselect', 'endstate', 'endstatetable', 'endstruct',
+ 'endstructtable', 'endsubmodule', 'endsubroutine', 'endtimeblock', 'endtype',
+ 'enduserflagsref', 'endvariable', 'endvariabletable', 'endwhere', 'engine',
+ 'enterprise', 'entity', 'entity-creation-and-abolishing',
+ 'entity_instantiation', 'entry', 'entry-definition', 'entry-key',
+ 'entry-type', 'entrypoint', 'enum', 'enum-block', 'enum-declaration',
+ 'enumeration', 'enumerator', 'enumerator-specification', 'env', 'environment',
+ 'environment-variable', 'eo', 'eof', 'epatch', 'eq', 'eqn', 'eqnarray',
+ 'equal', 'equal-or-greater', 'equal-or-less', 'equalexpr', 'equality',
+ 'equals', 'equals-sign', 'equation', 'equation-label', 'erb', 'ereg',
+ 'erlang', 'error', 'error-control', 'errorfunc', 'errorstop', 'es', 'es6',
+ 'es6import', 'esc', 'escape', 'escape-char', 'escape-code', 'escape-sequence',
+ 'escape-unicode', 'escaped', 'escapes', 'escript', 'eso-lua', 'eso-txt',
+ 'essence', 'et', 'eth', 'ethaddr', 'etml', 'etpl', 'eudoc', 'euler',
+ 'euphoria', 'european', 'evaled', 'evaluable', 'evaluation', 'even-tab',
+ 'event', 'event-call', 'event-handler', 'event-handling', 'event-schedulling',
+ 'eventType', 'eventb', 'eventend', 'events', 'evnd', 'exactly', 'example',
+ 'exampleText', 'examples', 'exceeding-sections', 'excel-link', 'exception',
+ 'exceptions', 'exclaimation-point', 'exclamation', 'exec', 'exec-command',
+ 'execution-context', 'exif', 'existential', 'exit', 'exp', 'expand-register',
+ 'expanded', 'expansion', 'expected-array-separator',
+ 'expected-dictionary-separator', 'expected-extends', 'expected-implements',
+ 'expected-range-separator', 'experimental', 'expires', 'expl3', 'explosion',
+ 'exponent', 'exponential', 'export', 'exports', 'expr', 'expression',
+ 'expression-separator', 'expression-seperator', 'expressions',
+ 'expressions-and-types', 'exprwrap', 'ext', 'extempore', 'extend', 'extended',
+ 'extends', 'extension', 'extension-specification', 'extensions', 'extern',
+ 'extern-block', 'external', 'external-call', 'external-signature', 'extersk',
+ 'extglob', 'extra', 'extra-characters', 'extra-equals-sign', 'extracted',
+ 'extras', 'extrassk', 'exxample', 'eztpl', 'f', 'f5networks', 'fa', 'face',
+ 'fact', 'factor', 'factorial', 'fadeawayheight', 'fadeawaywidth', 'fail',
+ 'fakeroot', 'fallback', 'fallout4', 'false', 'fandoc', 'fann', 'fantom',
+ 'fastcgi', 'fbaccidental', 'fbfigure', 'fbgroupclose', 'fbgroupopen', 'fbp',
+ 'fctn', 'fe', 'feature', 'features', 'feedrate', 'fenced', 'fftwfn', 'fhem',
+ 'fi', 'field', 'field-assignment', 'field-completions', 'field-id',
+ 'field-level-comment', 'field-name', 'field-tag', 'fields', 'figbassmode',
+ 'figure', 'figuregroup', 'filder-design-hdl-coder', 'file', 'file-i-o',
+ 'file-io', 'file-name', 'file-object', 'file-path', 'fileinfo', 'filename',
+ 'filepath', 'filetest', 'filter', 'filter-pipe', 'filteredtranscludeblock',
+ 'filters', 'final', 'final-procedure', 'finally', 'financial',
+ 'financial-derivatives', 'find', 'find-in-files', 'find-m', 'finder',
+ 'finish', 'finn', 'fire', 'firebug', 'first', 'first-class', 'first-line',
+ 'fish', 'fitnesse', 'five', 'fix_this_later', 'fixed', 'fixed-income',
+ 'fixed-point', 'fixme', 'fl', 'flag', 'flag-control', 'flags', 'flash',
+ 'flatbuffers', 'flex-config', 'fload', 'float', 'float-exponent', 'float_exp',
+ 'floating-point', 'floating_point', 'floor', 'flow', 'flow-control',
+ 'flowcontrol', 'flows', 'flowtype', 'flush', 'fma', 'fma4', 'fmod', 'fn',
+ 'fold', 'folder', 'folder-actions', 'following', 'font',
+ 'font-cache', 'font-face', 'font-name', 'font-size', 'fontface', 'fontforge',
+ 'foobar', 'footer', 'footnote', 'for', 'for-in-loop', 'for-loop',
+ 'for-quantity', 'forall', 'force', 'foreach', 'foreign', 'forever',
+ 'forge-config', 'forin', 'form', 'form-feed', 'formal', 'format',
+ 'format-register', 'format-verb', 'formatted', 'formatter', 'formatting',
+ 'forth', 'fortran', 'forward', 'foundation', 'fountain', 'four',
+ 'fourd-command', 'fourd-constant', 'fourd-constant-hex',
+ 'fourd-constant-number', 'fourd-constant-string', 'fourd-control-begin',
+ 'fourd-control-end', 'fourd-declaration', 'fourd-declaration-array',
+ 'fourd-local-variable', 'fourd-parameter', 'fourd-table', 'fourd-tag',
+ 'fourd-variable', 'fpm', 'fpu', 'fpu_x87', 'fr', 'fragment', 'frame',
+ 'frames', 'frametitle', 'framexml', 'free', 'free-form', 'freebasic',
+ 'freefem', 'freespace2', 'from', 'from-file', 'front-matter', 'fs', 'fs2',
+ 'fsc', 'fsgsbase', 'fsharp', 'fsi', 'fsl', 'fsm', 'fsp', 'fsx', 'fth', 'ftl',
+ 'ftl20n', 'full-line', 'full-stop', 'fun', 'funarg', 'func-tag', 'func_call',
+ 'funchand', 'function', 'function-arity', 'function-attribute',
+ 'function-call', 'function-definition', 'function-literal',
+ 'function-parameter', 'function-recursive', 'function-return',
+ 'function-type', 'functionDeclaration', 'functionDefinition',
+ 'function_definition', 'function_prototype', 'functional_test', 'functionend',
+ 'functions', 'functionstart', 'fundimental', 'funk', 'funtion-definition',
+ 'fus', 'future', 'futures', 'fuzzy-logic', 'fx', 'fx-foliage-replicator',
+ 'fx-light', 'fx-shape-replicator', 'fx-sun-light', 'g', 'g-code', 'ga',
+ 'gain', 'galaxy', 'gallery', 'game-base', 'game-connection', 'game-server',
+ 'gamebusk', 'gamescript', 'gams', 'gams-lst', 'gap', 'garch', 'gather',
+ 'gcode', 'gdb', 'gdscript', 'gdx', 'ge', 'geant4-macro', 'geck',
+ 'geck-keyword', 'general', 'general-purpose', 'generate', 'generator',
+ 'generic', 'generic-config', 'generic-spec', 'generic-type', 'generic_list',
+ 'genericcall', 'generics', 'genetic-algorithms', 'geo', 'geometric',
+ 'geometry', 'geometry-adjustment', 'get', 'getproperty', 'getsec', 'getset',
+ 'getter', 'gettext', 'getword', 'gfm', 'gfm-todotxt', 'gfx', 'gh-number',
+ 'gherkin', 'gisdk', 'git', 'git-attributes', 'git-commit', 'git-config',
+ 'git-rebase', 'gitignore', 'given', 'gj', 'gl', 'glob', 'global',
+ 'global-functions', 'globals', 'globalsection', 'glsl', 'glue',
+ 'glyph_class_name', 'glyphname-value', 'gml', 'gmp', 'gmsh', 'gmx', 'gn',
+ 'gnu', 'gnuplot', 'go', 'goal', 'goatee', 'godmode', 'gohtml', 'gold', 'golo',
+ 'google', 'gosub', 'gotemplate', 'goto', 'goto-label', 'gpd', 'gpd_note',
+ 'gpp', 'grace', 'grade-down', 'grade-up', 'gradient', 'gradle', 'grails',
+ 'grammar', 'grammar-rule', 'grammar_production', 'grap', 'grapahql', 'graph',
+ 'graphics', 'graphql', 'grave-accent', 'gray', 'greater', 'greater-equal',
+ 'greater-or-equal', 'greek', 'greek-letter', 'green', 'gremlin', 'grey',
+ 'grg', 'grid-table', 'gridlists', 'grog', 'groovy', 'groovy-properties',
+ 'group', 'group-level-comment', 'group-name', 'group-number',
+ 'group-reference', 'group-title', 'group1', 'group10', 'group11', 'group2',
+ 'group3', 'group4', 'group5', 'group6', 'group7', 'group8', 'group9',
+ 'groupend', 'groupflag', 'grouping-statement', 'groupname', 'groupstart',
+ 'growl', 'grr', 'gs', 'gsc', 'gsp', 'gt', 'guard', 'guards', 'gui',
+ 'gui-bitmap-ctrl', 'gui-button-base-ctrl', 'gui-canvas', 'gui-control',
+ 'gui-filter-ctrl', 'gui-frameset-ctrl', 'gui-menu-bar',
+ 'gui-message-vector-ctrl', 'gui-ml-text-ctrl', 'gui-popup-menu-ctrl',
+ 'gui-scroll-ctrl', 'gui-slider-ctrl', 'gui-text-ctrl', 'gui-text-edit-ctrl',
+ 'gui-text-list-ctrl', 'guid', 'guillemot', 'guis', 'gzip', 'gzip_static', 'h',
+ 'h1', 'hack', 'hackfragment', 'haddock', 'hairpin', 'ham', 'haml', 'hamlbars',
+ 'hamlc', 'hamlet', 'hamlpy', 'handlebar', 'handlebars', 'handler',
+ 'hanging-paragraph', 'haproxy-config', 'harbou', 'harbour', 'hard-break',
+ 'hardlinebreaks', 'hash', 'hash-tick', 'hashbang', 'hashicorp', 'hashkey',
+ 'haskell', 'haxe', 'hbs', 'hcl', 'hdl', 'hdr', 'he', 'header',
+ 'header-continuation', 'header-value', 'headername', 'headers', 'heading',
+ 'heading-0', 'heading-1', 'heading-2', 'heading-3', 'heading-4', 'heading-5',
+ 'heading-6', 'height', 'helen', 'help', 'helper', 'helpers', 'heredoc',
+ 'heredoc-token', 'herestring', 'heritage', 'hex', 'hex-ascii', 'hex-byte',
+ 'hex-literal', 'hex-old', 'hex-string', 'hex-value', 'hex8', 'hexadecimal',
+ 'hexidecimal', 'hexprefix', 'hg-commit', 'hgignore', 'hi', 'hidden', 'hide',
+ 'high-minus', 'highlight-end', 'highlight-group',
+ 'highlight-start', 'hint', 'history', 'hive', 'hive-name', 'hjson', 'hl7',
+ 'hlsl', 'hn', 'hoa', 'hoc', 'hocharacter', 'hocomment', 'hocon', 'hoconstant',
+ 'hocontinuation', 'hocontrol', 'hombrew-formula', 'homebrew', 'homematic',
+ 'hook', 'hoon', 'horizontal-blending', 'horizontal-packed-arithmetic',
+ 'horizontal-rule', 'hostname', 'hosts', 'hour', 'hours', 'hps', 'hql', 'hr',
+ 'hrm', 'hs', 'hsc2hs', 'ht', 'htaccess', 'htl', 'html', 'html_entity',
+ 'htmlbars', 'http', 'hu', 'hungary', 'hxml', 'hy', 'hydrant', 'hydrogen',
+ 'hyperbolic', 'hyperlink', 'hyphen', 'hyphenation', 'hyphenation-char', 'i',
+ 'i-beam', 'i18n', 'iRev', 'ice', 'icinga2', 'icmc', 'icmptype', 'icmpv6type',
+ 'icmpxtype', 'iconv', 'id', 'id-type', 'id-with-protocol', 'idd', 'ideal',
+ 'identical', 'identifer', 'identified', 'identifier', 'identifier-type',
+ 'identifiers-and-DTDs', 'identity', 'idf', 'idl', 'idris', 'ieee', 'if',
+ 'if-block', 'if-branch', 'if-condition', 'if-else', 'if-then', 'ifacespec',
+ 'ifdef', 'ifname', 'ifndef', 'ignore', 'ignore-eol', 'ignore-errors',
+ 'ignorebii', 'ignored', 'ignored-binding', 'ignoring', 'iisfunc', 'ijk',
+ 'ilasm', 'illagal', 'illeagal', 'illegal', 'illumination-model', 'image',
+ 'image-acquisition', 'image-alignment', 'image-option', 'image-processing',
+ 'images', 'imap', 'imba', 'imfchan', 'img', 'immediate',
+ 'immediately-evaluated', 'immutable', 'impex', 'implementation',
+ 'implementation-defined-hooks', 'implemented', 'implements', 'implicit',
+ 'import', 'import-all', 'importall', 'important', 'in', 'in-block',
+ 'in-module', 'in-out', 'inappropriate', 'include', 'include-statement',
+ 'includefile', 'incomplete', 'incomplete-variable-assignment', 'inconsistent',
+ 'increment', 'increment-decrement', 'indent', 'indented',
+ 'indented-paragraph', 'indepimage', 'index', 'index-seperator', 'indexed',
+ 'indexer', 'indexes', 'indicator', 'indices', 'indirect', 'indirection',
+ 'individual-enum-definition', 'individual-rpc-call', 'inet', 'inetprototype',
+ 'inferred', 'infes', 'infinity', 'infix', 'info', 'inform', 'inform6',
+ 'inform7', 'infotype', 'ingore-eol', 'inherit', 'inheritDoc', 'inheritance',
+ 'inherited', 'inherited-class', 'inherited-struct', 'inherits', 'ini', 'init',
+ 'initial-lowercase', 'initial-uppercase', 'initial-value', 'initialization',
+ 'initialize', 'initializer-list', 'ink', 'inline', 'inline-data',
+ 'inlineConditionalBranchSeparator', 'inlineConditionalClause',
+ 'inlineConditionalEnd', 'inlineConditionalStart', 'inlineLogicEnd',
+ 'inlineLogicStart', 'inlineSequenceEnd', 'inlineSequenceSeparator',
+ 'inlineSequenceStart', 'inlineSequenceTypeChar', 'inlineblock', 'inlinecode',
+ 'inlinecomment', 'inlinetag', 'inner', 'inner-class', 'inno', 'ino', 'inout',
+ 'input', 'inquire', 'inserted', 'insertion', 'insertion-and-extraction',
+ 'inside', 'install', 'instance', 'instancemethods', 'instanceof', 'instances',
+ 'instantiation', 'instruction', 'instruction-pointer', 'instructions',
+ 'instrument', 'instrument-block', 'instrument-control',
+ 'instrument-declaration', 'int', 'int32', 'int64', 'integer', 'integer-float',
+ 'intel', 'intel-hex', 'intent', 'intepreted', 'interaction', 'interbase',
+ 'interface', 'interface-block', 'interface-or-protocol', 'interfaces',
+ 'interior-instance', 'interiors', 'interlink', 'internal', 'internet',
+ 'interpolate-argument', 'interpolate-string', 'interpolate-variable',
+ 'interpolated', 'interpolation', 'interrupt', 'intersection', 'interval',
+ 'intervalOrList', 'intl', 'intrinsic', 'intuicio4', 'invalid',
+ 'invalid-character', 'invalid-character-escape', 'invalid-inequality',
+ 'invalid-quote', 'invalid-variable-name', 'invariant', 'invocation', 'invoke',
+ 'invokee', 'io', 'ior', 'iota', 'ip', 'ip-port', 'ip6', 'ipkg', 'ipsec',
+ 'ipv4', 'ipv6', 'ipynb', 'irct', 'irule', 'is', 'isa', 'isc', 'iscexport',
+ 'isclass', 'isml', 'issue', 'it', 'italic', 'italic-text', 'item',
+ 'item-access', 'itemlevel', 'items', 'iteration', 'itunes', 'ivar', 'ja',
+ 'jack', 'jade', 'jakefile', 'jasmin', 'java', 'java-properties', 'java-props',
+ 'javadoc', 'javascript', 'jbeam', 'jekyll', 'jflex', 'jibo-rule', 'jinja',
+ 'jison', 'jisonlex', 'jmp', 'joint', 'joker', 'jolie', 'jot', 'journaling',
+ 'jpl', 'jq', 'jquery', 'js', 'js-label', 'jsdoc', 'jsduck', 'jsim', 'json',
+ 'json5', 'jsoniq', 'jsonnet', 'jsont', 'jsp', 'jsx', 'julia', 'julius',
+ 'jump', 'juniper', 'juniper-junos-config', 'junit-test-report', 'junos',
+ 'juttle', 'jv', 'jxa', 'k', 'kag', 'kagex', 'kb', 'kbd', 'kconfig',
+ 'kerboscript', 'kernel', 'kevs', 'kevscript', 'kewyword', 'key',
+ 'key-assignment', 'key-letter', 'key-pair', 'key-path', 'key-value',
+ 'keyboard', 'keyframe', 'keyframes', 'keygroup', 'keyname', 'keyspace',
+ 'keyspace-name', 'keyvalue', 'keyword', 'keyword-parameter', 'keyword1',
+ 'keyword2', 'keyword3', 'keyword4', 'keyword5', 'keyword6', 'keyword7',
+ 'keyword8', 'keyword_arrays', 'keyword_objects', 'keyword_roots',
+ 'keyword_string', 'keywords', 'keywork', 'kickstart', 'kind', 'kmd', 'kn',
+ 'knitr', 'knockout', 'knot', 'ko', 'ko-virtual', 'kos', 'kotlin', 'krl',
+ 'ksp-cfg', 'kspcfg', 'kurumin', 'kv', 'kxi', 'kxigauge', 'l', 'l20n',
+ 'l4proto', 'label', 'label-expression', 'labeled', 'labeled-parameter',
+ 'labelled-thing', 'lagda', 'lambda', 'lambda-function', 'lammps', 'langref',
+ 'language', 'language-range', 'languagebabel', 'langversion', 'largesk',
+ 'lasso', 'last', 'last-paren-match', 'latex', 'latex2', 'latino', 'latte',
+ 'launch', 'layout', 'layoutbii', 'lbsearch', 'lc', 'lc-3', 'lcb', 'ldap',
+ 'ldif', 'le', 'leader-char', 'leading', 'leading-space', 'leading-tabs',
+ 'leaf', 'lean', 'ledger', 'left', 'left-margin', 'leftshift', 'lefttoright',
+ 'legacy', 'legacy-setting', 'lemon', 'len', 'length', 'leopard', 'less',
+ 'less-equal', 'less-or-equal', 'let', 'letter', 'level', 'level-of-detail',
+ 'level1', 'level2', 'level3', 'level4', 'level5', 'level6', 'levels', 'lex',
+ 'lexc', 'lexical', 'lf-in-string', 'lhs', 'li', 'lib', 'libfile', 'library',
+ 'libs', 'libxml', 'lid', 'lifetime', 'ligature', 'light', 'light_purple',
+ 'lighting', 'lightning', 'lilypond', 'lilypond-drummode',
+ 'lilypond-figbassmode', 'lilypond-figuregroup', 'lilypond-internals',
+ 'lilypond-lyricsmode', 'lilypond-markupmode', 'lilypond-notedrum',
+ 'lilypond-notemode', 'lilypond-notemode-explicit', 'lilypond-notenames',
+ 'lilypond-schememode', 'limit_zone', 'line-block', 'line-break',
+ 'line-continuation', 'line-cue-setting', 'line-statement',
+ 'line-too-long', 'linebreak', 'linenumber', 'link', 'link-label',
+ 'link-text', 'link-url', 'linkage', 'linkage-type', 'linkedin',
+ 'linkedsockets', 'linkplain', 'linkplain-label', 'linq', 'linuxcncgcode',
+ 'liquid', 'liquidhaskell', 'liquidsoap', 'lisp', 'lisp-repl', 'list',
+ 'list-done', 'list-separator', 'list-style-type', 'list-today', 'list_item',
+ 'listing', 'listnum', 'listvalues', 'litaco', 'litcoffee', 'literal',
+ 'literal-string', 'literate', 'litword', 'livecodescript', 'livescript',
+ 'livescriptscript', 'll', 'llvm', 'load-constants', 'load-hint', 'loader',
+ 'local', 'local-variables', 'localhost', 'localizable', 'localized',
+ 'localname', 'locals', 'localtable', 'location', 'lock', 'log', 'log-debug',
+ 'log-error', 'log-failed', 'log-info', 'log-patch', 'log-success',
+ 'log-verbose', 'log-warning', 'logarithm', 'logging', 'logic', 'logicBegin',
+ 'logical', 'logical-expression', 'logicblox', 'logicode', 'logo', 'logstash',
+ 'logtalk', 'lol', 'long', 'look-ahead', 'look-behind', 'lookahead',
+ 'lookaround', 'lookbehind', 'loop', 'loop-control', 'low-high', 'lowercase',
+ 'lowercase_character_not_allowed_here', 'lozenge', 'lparen', 'lsg', 'lsl',
+ 'lst', 'lst-cpu12', 'lstdo', 'lt', 'lt-gt', 'lterat', 'lu', 'lua', 'lucee',
+ 'lucius', 'lury', 'lv', 'lyricsmode', 'm', 'm4', 'm4sh', 'm65816', 'm68k',
+ 'mac-classic', 'mac-fsaa', 'machine', 'machineclause', 'macro', 'macro-usage',
+ 'macro11', 'macrocallblock', 'macrocallinline', 'madoko', 'magenta', 'magic',
+ 'magik', 'mail', 'mailer', 'mailto', 'main', 'makefile', 'makefile2', 'mako',
+ 'mamba', 'man', 'mantissa', 'manualmelisma', 'map', 'map-library', 'map-name',
+ 'mapfile', 'mapkey', 'mapping', 'mapping-type', 'maprange', 'marasm',
+ 'margin', 'marginpar', 'mark', 'mark-input', 'markdown', 'marker', 'marko',
+ 'marko-attribute', 'marko-tag', 'markup', 'markupmode', 'mas2j', 'mask',
+ 'mason', 'mat', 'mata', 'match', 'match-bind', 'match-branch',
+ 'match-condition', 'match-definition', 'match-exception', 'match-option',
+ 'match-pattern', 'material', 'material-library', 'material-name', 'math',
+ 'math-symbol', 'math_complex', 'math_real', 'mathematic', 'mathematica',
+ 'mathematical', 'mathematical-symbols', 'mathematics', 'mathjax', 'mathml',
+ 'matlab', 'matrix', 'maude', 'maven', 'max', 'max-angle', 'max-distance',
+ 'max-length', 'maxscript', 'maybe', 'mb', 'mbstring', 'mc', 'mcc', 'mccolor',
+ 'mch', 'mcn', 'mcode', 'mcq', 'mcr', 'mcrypt', 'mcs', 'md', 'mdash', 'mdoc',
+ 'mdx', 'me', 'measure', 'media', 'media-feature', 'media-property',
+ 'media-type', 'mediawiki', 'mei', 'mel', 'memaddress', 'member',
+ 'member-function-attribute', 'member-of', 'membership', 'memcache',
+ 'memcached', 'memoir', 'memoir-alltt', 'memoir-fbox', 'memoir-verbatim',
+ 'memory', 'memory-management', 'memory-protection', 'memos', 'menhir',
+ 'mention', 'menu', 'mercury', 'merge-group', 'merge-key', 'merlin',
+ 'mesgTrigger', 'mesgType', 'message', 'message-declaration',
+ 'message-forwarding-handler', 'message-sending', 'message-vector', 'messages',
+ 'meta', 'meta-conditional', 'meta-data', 'meta-file', 'meta-info',
+ 'metaclass', 'metacommand', 'metadata', 'metakey', 'metamodel', 'metapost',
+ 'metascript', 'meteor', 'method', 'method-call', 'method-definition',
+ 'method-modification', 'method-mofification', 'method-parameter',
+ 'method-parameters', 'method-restriction', 'methodcalls', 'methods',
+ 'metrics', 'mhash', 'microsites', 'microsoft-dynamics', 'middle',
+ 'midi_processing', 'migration', 'mime', 'min', 'minelua', 'minetweaker',
+ 'minitemplate', 'minitest', 'minus', 'minute', 'mips', 'mirah', 'misc',
+ 'miscellaneous', 'mismatched', 'missing', 'missing-asterisk',
+ 'missing-inheritance', 'missing-parameters', 'missing-section-begin',
+ 'missingend', 'mission-area', 'mixin', 'mixin-name', 'mjml', 'ml', 'mlab',
+ 'mls', 'mm', 'mml', 'mmx', 'mmx_instructions', 'mn', 'mnemonic',
+ 'mobile-messaging', 'mochi', 'mod', 'mod-r', 'mod_perl', 'mod_perl_1',
+ 'modblock', 'modbus', 'mode', 'model', 'model-based-calibration',
+ 'model-predictive-control', 'modelica', 'modelicascript', 'modeline',
+ 'models', 'modern', 'modified', 'modifier', 'modifiers', 'modify',
+ 'modify-range', 'modifytime', 'modl', 'modr', 'modula-2', 'module',
+ 'module-alias', 'module-binding', 'module-definition', 'module-expression',
+ 'module-function', 'module-reference', 'module-rename', 'module-sum',
+ 'module-type', 'module-type-definition', 'modules', 'modulo', 'modx',
+ 'mojolicious', 'mojom', 'moment', 'mond', 'money', 'mongo', 'mongodb',
+ 'monicelli', 'monitor', 'monkberry', 'monkey', 'monospace', 'monospaced',
+ 'monte', 'month', 'moon', 'moonscript', 'moos', 'moose', 'moosecpp', 'motion',
+ 'mouse', 'mov', 'movement', 'movie', 'movie-file', 'mozu', 'mpw', 'mpx',
+ 'mqsc', 'ms', 'mscgen', 'mscript', 'msg', 'msgctxt', 'msgenny', 'msgid',
+ 'msgstr', 'mson', 'mson-block', 'mss', 'mta', 'mtl', 'mucow', 'mult', 'multi',
+ 'multi-line', 'multi-symbol', 'multi-threading', 'multiclet', 'multids-file',
+ 'multiline', 'multiline-cell', 'multiline-text-reference',
+ 'multiline-tiddler-title', 'multimethod', 'multipart', 'multiplication',
+ 'multiplicative', 'multiply', 'multiverse', 'mumps', 'mundosk', 'music',
+ 'must_be', 'mustache', 'mut', 'mutable', 'mutator', 'mx', 'mxml', 'mydsl1',
+ 'mylanguage', 'mysql', 'mysqli', 'mysqlnd-memcache', 'mysqlnd-ms',
+ 'mysqlnd-qc', 'mysqlnd-uh', 'mzn', 'nabla', 'nagios', 'name', 'name-list',
+ 'name-of-parameter', 'named', 'named-char', 'named-key', 'named-tuple',
+ 'nameless-typed', 'namelist', 'names', 'namespace', 'namespace-block',
+ 'namespace-definition', 'namespace-language', 'namespace-prefix',
+ 'namespace-reference', 'namespace-statement', 'namespaces', 'nan', 'nand',
+ 'nant', 'nant-build', 'narration', 'nas', 'nasal', 'nasl', 'nasm', 'nastran',
+ 'nat', 'native', 'nativeint', 'natural', 'navigation', 'nbtkey', 'ncf', 'ncl',
+ 'ndash', 'ne', 'nearley', 'neg-ratio', 'negatable', 'negate', 'negated',
+ 'negation', 'negative', 'negative-look-ahead', 'negative-look-behind',
+ 'negativity', 'nesc', 'nessuskb', 'nested', 'nested_braces',
+ 'nested_brackets', 'nested_ltgt', 'nested_parens', 'nesty', 'net',
+ 'net-object', 'netbios', 'network', 'network-value', 'networking',
+ 'neural-network', 'new', 'new-line', 'new-object', 'newline',
+ 'newline-spacing', 'newlinetext', 'newlisp', 'newobject', 'nez', 'nft',
+ 'ngdoc', 'nginx', 'nickname', 'nil', 'nim', 'nine', 'ninja', 'ninjaforce',
+ 'nit', 'nitro', 'nix', 'nl', 'nlf', 'nm', 'nm7', 'no', 'no-capture',
+ 'no-completions', 'no-content', 'no-default', 'no-indent',
+ 'no-leading-digits', 'no-trailing-digits', 'no-validate-params', 'node',
+ 'nogc', 'noindent', 'nokia-sros-config', 'non', 'non-capturing',
+ 'non-immediate', 'non-null-typehinted', 'non-standard', 'non-terminal',
+ 'nondir-target', 'none', 'none-parameter', 'nonlocal', 'nonterminal', 'noon',
+ 'noop', 'nop', 'noparams', 'nor', 'normal', 'normal_numeric',
+ 'normal_objects', 'normal_text', 'normalised', 'not', 'not-a-number',
+ 'not-equal', 'not-identical', 'notation', 'note', 'notechord', 'notemode',
+ 'notequal', 'notequalexpr', 'notes', 'notidentical', 'notification', 'nowdoc',
+ 'noweb', 'nrtdrv', 'nsapi', 'nscript', 'nse', 'nsis', 'nsl', 'ntriples',
+ 'nul', 'null', 'nullify', 'nullological', 'nulltype', 'num', 'number',
+ 'number-sign', 'number-sign-equals', 'numbered', 'numberic', 'numbers',
+ 'numbersign', 'numeric', 'numeric_std', 'numerical', 'nunjucks', 'nut',
+ 'nvatom', 'nxc', 'o', 'obj', 'objaggregation', 'objc', 'objcpp', 'objdump',
+ 'object', 'object-comments', 'object-definition', 'object-level-comment',
+ 'object-name', 'objects', 'objectset', 'objecttable', 'objectvalues', 'objj',
+ 'obsolete', 'ocaml', 'ocamllex', 'occam', 'oci8', 'ocmal', 'oct', 'octal',
+ 'octave', 'octave-change', 'octave-shift', 'octet', 'octo', 'octobercms',
+ 'octothorpe', 'odd-tab', 'odedsl', 'ods', 'of', 'off', 'offset', 'ofx',
+ 'ogre', 'ok', 'ol', 'old', 'old-style', 'omap', 'omitted', 'on-background',
+ 'on-error', 'once', 'one', 'one-sixth-em', 'one-twelfth-em', 'oniguruma',
+ 'oniguruma-comment', 'only', 'only-in', 'onoff', 'ooc', 'oot', 'op-domain',
+ 'op-range', 'opa', 'opaque', 'opc', 'opcache', 'opcode',
+ 'opcode-argument-types', 'opcode-declaration', 'opcode-definition',
+ 'opcode-details', 'open', 'open-gl', 'openal', 'openbinding', 'opencl',
+ 'opendss', 'opening', 'opening-text', 'openmp', 'openssl', 'opentype',
+ 'operand', 'operands', 'operation', 'operator', 'operator2', 'operator3',
+ 'operators', 'opmask', 'opmaskregs', 'optical-density', 'optimization',
+ 'option', 'option-description', 'option-toggle', 'optional',
+ 'optional-parameter', 'optional-parameter-assignment', 'optionals',
+ 'optionname', 'options', 'optiontype', 'or', 'oracle', 'orbbasic', 'orcam',
+ 'orchestra', 'order', 'ordered', 'ordered-block', 'ordinal', 'organized',
+ 'orgtype', 'origin', 'osiris', 'other', 'other-inherited-class',
+ 'other_buildins', 'other_keywords', 'others', 'otherwise',
+ 'otherwise-expression', 'out', 'outer', 'output', 'overload', 'override',
+ 'owner', 'ownership', 'oz', 'p', 'p4', 'p5', 'p8', 'pa', 'package',
+ 'package-definition', 'package_body', 'packages', 'packed',
+ 'packed-arithmetic', 'packed-blending', 'packed-comparison',
+ 'packed-conversion', 'packed-floating-point', 'packed-integer', 'packed-math',
+ 'packed-mov', 'packed-other', 'packed-shift', 'packed-shuffle', 'packed-test',
+ 'padlock', 'page', 'page-props', 'pagebreak', 'pair', 'pair-programming',
+ 'paket', 'pandoc', 'papyrus', 'papyrus-assembly', 'paragraph', 'parallel',
+ 'param', 'param-list', 'paramater', 'paramerised-type', 'parameter',
+ 'parameter-entity', 'parameter-space', 'parameterless', 'parameters',
+ 'paramless', 'params', 'paramtable', 'paramter', 'paren', 'paren-group',
+ 'parens', 'parent', 'parent-reference', 'parent-selector',
+ 'parent-selector-suffix', 'parenthases', 'parentheses', 'parenthesis',
+ 'parenthetical', 'parenthetical_list', 'parenthetical_pair', 'parfor',
+ 'parfor-quantity', 'parse', 'parsed', 'parser', 'parser-function',
+ 'parser-token', 'parser3', 'part', 'partial', 'particle', 'pascal', 'pass',
+ 'pass-through', 'passive', 'passthrough', 'password', 'password-hash',
+ 'patch', 'path', 'path-camera', 'path-pattern', 'pathoperation', 'paths',
+ 'pathspec', 'patientId', 'pattern', 'pattern-argument', 'pattern-binding',
+ 'pattern-definition', 'pattern-match', 'pattern-offset', 'patterns', 'pause',
+ 'payee', 'payload', 'pbo', 'pbtxt', 'pcdata', 'pcntl', 'pdd', 'pddl', 'ped',
+ 'pegcoffee', 'pegjs', 'pending', 'percentage', 'percentage-sign',
+ 'percussionnote', 'period', 'perl', 'perl-section', 'perl6', 'perl6fe',
+ 'perlfe', 'perlt6e', 'perm', 'permutations', 'personalization', 'pervasive',
+ 'pf', 'pflotran', 'pfm', 'pfx', 'pgn', 'pgsql', 'phone', 'phone-number',
+ 'phonix', 'php', 'php-code-in-comment', 'php_apache', 'php_dom', 'php_ftp',
+ 'php_imap', 'php_mssql', 'php_odbc', 'php_pcre', 'php_spl', 'php_zip',
+ 'phpdoc', 'phrasemodifiers', 'phraslur', 'physical-zone', 'physics', 'pi',
+ 'pic', 'pick', 'pickup', 'picture', 'pig', 'pillar', 'pipe', 'pipe-sign',
+ 'pipeline', 'piratesk', 'pitch', 'pixie', 'pkgbuild', 'pl', 'placeholder',
+ 'placeholder-parts', 'plain', 'plainsimple-emphasize', 'plainsimple-heading',
+ 'plainsimple-number', 'plantuml', 'player', 'playerversion', 'pld_modeling',
+ 'please-build', 'please-build-defs', 'plist', 'plsql', 'plugin', 'plus',
+ 'plztarget', 'pmc', 'pml', 'pmlPhysics-arrangecharacter',
+ 'pmlPhysics-emphasisequote', 'pmlPhysics-graphic', 'pmlPhysics-header',
+ 'pmlPhysics-htmlencoded', 'pmlPhysics-links', 'pmlPhysics-listtable',
+ 'pmlPhysics-physicalquantity', 'pmlPhysics-relationships',
+ 'pmlPhysics-slides', 'pmlPhysics-slidestacks', 'pmlPhysics-speech',
+ 'pmlPhysics-structure', 'pnt', 'po', 'pod', 'poe', 'pogoscript', 'point',
+ 'point-size', 'pointer', 'pointer-arith', 'pointer-following', 'points',
+ 'polarcoord', 'policiesbii', 'policy', 'polydelim', 'polygonal', 'polymer',
+ 'polymorphic', 'polymorphic-variant', 'polynomial-degree', 'polysep', 'pony',
+ 'port', 'port_list', 'pos-ratio', 'position-cue-setting', 'positional',
+ 'positive', 'posix', 'posix-reserved', 'post-match', 'postblit', 'postcss',
+ 'postfix', 'postpone', 'postscript', 'potigol', 'potion', 'pound',
+ 'pound-sign', 'povray', 'power', 'power_set', 'powershell', 'pp', 'ppc',
+ 'ppcasm', 'ppd', 'praat', 'pragma', 'pragma-all-once', 'pragma-mark',
+ 'pragma-message', 'pragma-newline-spacing', 'pragma-newline-spacing-value',
+ 'pragma-once', 'pragma-stg', 'pragma-stg-value', 'pre', 'pre-defined',
+ 'pre-match', 'preamble', 'prec', 'precedence', 'precipitation', 'precision',
+ 'precision-point', 'pred', 'predefined', 'predicate', 'prefetch',
+ 'prefetchwt', 'prefix', 'prefixed-uri', 'prefixes', 'preinst', 'prelude',
+ 'prepare', 'prepocessor', 'preposition', 'prepositional', 'preprocessor',
+ 'prerequisites', 'preset', 'preview', 'previous', 'prg', 'primary',
+ 'primitive', 'primitive-datatypes', 'primitive-field', 'print',
+ 'print-argument', 'priority', 'prism', 'private', 'privileged', 'pro',
+ 'probe', 'proc', 'procedure', 'procedure_definition', 'procedure_prototype',
+ 'process', 'process-id', 'process-substitution', 'processes', 'processing',
+ 'proctitle', 'production', 'profile', 'profiling', 'program', 'program-block',
+ 'program-name', 'progressbars', 'proguard', 'project', 'projectile', 'prolog',
+ 'prolog-flags', 'prologue', 'promoted', 'prompt', 'prompt-prefix', 'prop',
+ 'properties', 'properties_literal', 'property', 'property-flag',
+ 'property-list', 'property-name', 'property-value',
+ 'property-with-attributes', 'propertydef', 'propertyend', 'propertygroup',
+ 'propertygrouptable', 'propertyset', 'propertytable', 'proposition',
+ 'protection', 'protections', 'proto', 'protobuf', 'protobufs', 'protocol',
+ 'protocol-specification', 'prototype', 'provision', 'proxy', 'psci', 'pseudo',
+ 'pseudo-class', 'pseudo-element', 'pseudo-method', 'pseudo-mnemonic',
+ 'pseudo-variable', 'pshdl', 'pspell', 'psql', 'pt', 'ptc-config',
+ 'ptc-config-modelcheck', 'pthread', 'ptr', 'ptx', 'public', 'pug',
+ 'punchcard', 'punctual', 'punctuation', 'punctutation', 'puncuation',
+ 'puncutation', 'puntuation', 'puppet', 'purebasic', 'purescript', 'pweave',
+ 'pwisa', 'pwn', 'py2pml', 'pyj', 'pyjade', 'pymol', 'pyresttest', 'python',
+ 'python-function', 'q', 'q-brace', 'q-bracket', 'q-ltgt', 'q-paren', 'qa',
+ 'qm', 'qml', 'qos', 'qoute', 'qq', 'qq-brace', 'qq-bracket', 'qq-ltgt',
+ 'qq-paren', 'qry', 'qtpro', 'quad', 'quad-arrow-down', 'quad-arrow-left',
+ 'quad-arrow-right', 'quad-arrow-up', 'quad-backslash', 'quad-caret-down',
+ 'quad-caret-up', 'quad-circle', 'quad-colon', 'quad-del-down', 'quad-del-up',
+ 'quad-diamond', 'quad-divide', 'quad-equal', 'quad-jot', 'quad-less',
+ 'quad-not-equal', 'quad-question', 'quad-quote', 'quad-slash', 'quadrigraph',
+ 'qual', 'qualified', 'qualifier', 'quality', 'quant', 'quantifier',
+ 'quantifiers', 'quartz', 'quasi', 'quasiquote', 'quasiquotes', 'query',
+ 'query-dsl', 'question', 'questionmark', 'quicel', 'quicktemplate',
+ 'quicktime-file', 'quotation', 'quote', 'quoted', 'quoted-identifier',
+ 'quoted-object', 'quoted-or-unquoted', 'quotes', 'qx', 'qx-brace',
+ 'qx-bracket', 'qx-ltgt', 'qx-paren', 'r', 'r3', 'rabl', 'racket', 'radar',
+ 'radar-area', 'radiobuttons', 'radix', 'rails', 'rainmeter', 'raml', 'random',
+ 'random_number', 'randomsk', 'range', 'range-2', 'rank', 'rant', 'rapid',
+ 'rarity', 'ratio', 'rational-form', 'raw', 'raw-regex', 'raxe', 'rb', 'rd',
+ 'rdfs-type', 'rdrand', 'rdseed', 'react', 'read', 'readline', 'readonly',
+ 'readwrite', 'real', 'realip', 'rebeca', 'rebol', 'rec', 'receive',
+ 'receive-channel', 'recipe', 'recipient-subscriber-list', 'recode', 'record',
+ 'record-field', 'record-usage', 'recordfield', 'recutils', 'red',
+ 'redbook-audio', 'redirect', 'redirection', 'redprl', 'redundancy', 'ref',
+ 'refer', 'reference', 'referer', 'refinement', 'reflection', 'reg', 'regex',
+ 'regexname', 'regexp', 'regexp-option', 'region-anchor-setting',
+ 'region-cue-setting', 'region-identifier-setting', 'region-lines-setting',
+ 'region-scroll-setting', 'region-viewport-anchor-setting',
+ 'region-width-setting', 'register', 'register-64', 'registers', 'regular',
+ 'reiny', 'reject', 'rejecttype', 'rel', 'related', 'relation', 'relational',
+ 'relations', 'relationship', 'relationship-name', 'relationship-pattern',
+ 'relationship-pattern-end', 'relationship-pattern-start', 'relationship-type',
+ 'relationship-type-or', 'relationship-type-ored', 'relationship-type-start',
+ 'relative', 'rem', 'reminder', 'remote', 'removed', 'rename', 'renamed-from',
+ 'renamed-to', 'renaming', 'render', 'renpy', 'reocrd', 'reparator', 'repeat',
+ 'repl-prompt', 'replace', 'replaceXXX', 'replaced', 'replacement', 'reply',
+ 'repo', 'reporter', 'reporting', 'repository', 'request', 'request-type',
+ 'require', 'required', 'requiredness', 'requirement', 'requirements',
+ 'rescue', 'reserved', 'reset', 'resolution', 'resource', 'resource-manager',
+ 'response', 'response-type', 'rest', 'rest-args', 'rester', 'restriced',
+ 'restructuredtext', 'result', 'result-separator', 'results', 'retro',
+ 'return', 'return-type', 'return-value', 'returns', 'rev', 'reverse',
+ 'reversed', 'review', 'rewrite', 'rewrite-condition', 'rewrite-operator',
+ 'rewrite-pattern', 'rewrite-substitution', 'rewrite-test', 'rewritecond',
+ 'rewriterule', 'rf', 'rfc', 'rgb', 'rgb-percentage', 'rgb-value', 'rhap',
+ 'rho', 'rhs', 'rhtml', 'richtext', 'rid', 'right', 'ring', 'riot',
+ 'rivescript', 'rjs', 'rl', 'rmarkdown', 'rnc', 'rng', 'ro', 'roboconf',
+ 'robot', 'robotc', 'robust-control', 'rockerfile', 'roff', 'role',
+ 'rollout-control', 'root', 'rotate', 'rotate-first', 'rotate-last', 'round',
+ 'round-brackets', 'router', 'routeros', 'routes', 'routine', 'row', 'row2',
+ 'rowspan', 'roxygen', 'rparent', 'rpc', 'rpc-definition', 'rpe', 'rpm-spec',
+ 'rpmspec', 'rpt', 'rq', 'rrd', 'rsl', 'rspec', 'rtemplate', 'ru', 'ruby',
+ 'rubymotion', 'rule', 'rule-identifier', 'rule-name', 'rule-pattern',
+ 'rule-tag', 'ruleDefinition', 'rules', 'run', 'rune', 'runoff', 'runtime',
+ 'rust', 'rviz', 'rx', 's', 'safe-call', 'safe-navigation', 'safe-trap',
+ 'safer', 'safety', 'sage', 'salesforce', 'salt', 'sampler',
+ 'sampler-comparison', 'samplerarg', 'sampling', 'sas', 'sass',
+ 'sass-script-maps', 'satcom', 'satisfies', 'sblock', 'scad', 'scala',
+ 'scaladoc', 'scalar', 'scale', 'scam', 'scan', 'scenario', 'scenario_outline',
+ 'scene', 'scene-object', 'scheduled', 'schelp', 'schem', 'schema', 'scheme',
+ 'schememode', 'scientific', 'scilab', 'sck', 'scl', 'scope', 'scope-name',
+ 'scope-resolution', 'scoping', 'score', 'screen', 'scribble', 'script',
+ 'script-flag', 'script-metadata', 'script-object', 'script-tag', 'scripting',
+ 'scriptlet', 'scriptlocal', 'scriptname', 'scriptname-declaration', 'scripts',
+ 'scroll', 'scrollbars', 'scrollpanes', 'scss', 'scumm', 'sdbl', 'sdl', 'sdo',
+ 'sealed', 'search', 'seawolf', 'second', 'secondary', 'section',
+ 'section-attribute', 'sectionname', 'sections', 'see', 'segment',
+ 'segment-registers', 'segment-resolution', 'select', 'select-block',
+ 'selector', 'self', 'self-binding', 'self-close', 'sem', 'semantic',
+ 'semanticmodel', 'semi-colon', 'semicolon', 'semicoron', 'semireserved',
+ 'send-channel', 'sender', 'senum', 'sep', 'separator', 'separatory',
+ 'sepatator', 'seperator', 'sequence', 'sequences', 'serial', 'serpent',
+ 'server', 'service', 'service-declaration', 'service-rpc', 'services',
+ 'session', 'set', 'set-colour', 'set-size', 'set-variable', 'setbagmix',
+ 'setname', 'setproperty', 'sets', 'setter', 'setting', 'settings', 'settype',
+ 'setword', 'seven', 'severity', 'sexpr', 'sfd', 'sfst', 'sgml', 'sgx1',
+ 'sgx2', 'sha', 'sha256', 'sha512', 'sha_functions', 'shad', 'shade',
+ 'shaderlab', 'shadow-object', 'shape', 'shape-base', 'shape-base-data',
+ 'shared', 'shared-static', 'sharp', 'sharpequal', 'sharpge', 'sharpgt',
+ 'sharple', 'sharplt', 'sharpness', 'shebang', 'shell', 'shell-function',
+ 'shell-session', 'shift', 'shift-and-rotate', 'shift-left', 'shift-right',
+ 'shine', 'shinescript', 'shipflow', 'shmop', 'short', 'shortcut', 'shortcuts',
+ 'shorthand', 'shorthandpropertyname', 'show', 'show-argument',
+ 'shuffle-and-unpack', 'shutdown', 'shy', 'sidebar', 'sifu', 'sigdec', 'sigil',
+ 'sign-line', 'signal', 'signal-processing', 'signature', 'signed',
+ 'signed-int', 'signedness', 'signifier', 'silent', 'sim-group', 'sim-object',
+ 'sim-set', 'simd', 'simd-horizontal', 'simd-integer', 'simple',
+ 'simple-delimiter', 'simple-divider', 'simple-element', 'simple_delimiter',
+ 'simplexml', 'simplez', 'simulate', 'since', 'singe', 'single', 'single-line',
+ 'single-quote', 'single-quoted', 'single_quote', 'singlequote', 'singleton',
+ 'singleword', 'sites', 'six', 'size', 'size-cue-setting', 'sized_integer',
+ 'sizeof', 'sjs', 'sjson', 'sk', 'skaction', 'skdragon', 'skeeland',
+ 'skellett', 'sketchplugin', 'skevolved', 'skew', 'skill', 'skipped',
+ 'skmorkaz', 'skquery', 'skrambled', 'skrayfall', 'skript', 'skrpg', 'sksharp',
+ 'skstuff', 'skutilities', 'skvoice', 'sky', 'skyrim', 'sl', 'slash',
+ 'slash-bar', 'slash-option', 'slash-sign', 'slashes', 'sleet', 'slice',
+ 'slim', 'slm', 'sln', 'slot', 'slugignore', 'sma', 'smali', 'smalltalk',
+ 'smarty', 'smb', 'smbinternal', 'smilebasic', 'sml', 'smoothing-group',
+ 'smpte', 'smtlib', 'smx', 'snakeskin', 'snapshot', 'snlog', 'snmp', 'so',
+ 'soap', 'social', 'socketgroup', 'sockets', 'soft', 'solidity', 'solve',
+ 'soma', 'somearg', 'something', 'soql', 'sort', 'sorting', 'souce', 'sound',
+ 'sound_processing', 'sound_synthesys', 'source', 'source-constant', 'soy',
+ 'sp', 'space', 'space-after-command', 'spacebars', 'spaces', 'sparql',
+ 'spath', 'spec', 'special', 'special-attributes', 'special-character',
+ 'special-curve', 'special-functions', 'special-hook', 'special-keyword',
+ 'special-method', 'special-point', 'special-token-sequence', 'special-tokens',
+ 'special-type', 'specification', 'specifier', 'spectral-curve',
+ 'specular-exponent', 'specular-reflectivity', 'sphinx', 'sphinx-domain',
+ 'spice', 'spider', 'spindlespeed', 'splat', 'spline', 'splunk', 'splunk-conf',
+ 'splus', 'spn', 'spread', 'spread-line', 'spreadmap', 'sprite', 'sproto',
+ 'sproutcore', 'sqf', 'sql', 'sqlbuiltin', 'sqlite', 'sqlsrv', 'sqr', 'sqsp',
+ 'squad', 'square', 'squart', 'squirrel', 'sr-Cyrl', 'sr-Latn', 'src',
+ 'srltext', 'sros', 'srt', 'srv', 'ss', 'ssa', 'sse', 'sse2', 'sse2_simd',
+ 'sse3', 'sse4', 'sse4_simd', 'sse5', 'sse_avx', 'sse_simd', 'ssh-config',
+ 'ssi', 'ssl', 'ssn', 'sstemplate', 'st', 'stable', 'stack', 'stack-effect',
+ 'stackframe', 'stage', 'stan', 'standard', 'standard-key', 'standard-links',
+ 'standard-suite', 'standardadditions', 'standoc', 'star', 'starline', 'start',
+ 'start-block', 'start-condition', 'start-symbol', 'start-value',
+ 'starting-function-params', 'starting-functions', 'starting-functions-point',
+ 'startshape', 'stata', 'statamic', 'state', 'state-flag', 'state-management',
+ 'stateend', 'stategrouparg', 'stategroupval', 'statement',
+ 'statement-separator', 'states', 'statestart', 'statetable', 'static',
+ 'static-assert', 'static-classes', 'static-if', 'static-shape',
+ 'staticimages', 'statistics', 'stats', 'std', 'stdWrap', 'std_logic',
+ 'std_logic_1164', 'stderr-write-file', 'stdint', 'stdlib', 'stdlibcall',
+ 'stdplugin', 'stem', 'step', 'step-size', 'steps', 'stg', 'stile-shoe-left',
+ 'stile-shoe-up', 'stile-tilde', 'stitch', 'stk', 'stmt', 'stochastic', 'stop',
+ 'stopping', 'storage', 'story', 'stp', 'straight-quote', 'stray',
+ 'stray-comment-end', 'stream', 'stream-selection-and-control', 'streamsfuncs',
+ 'streem', 'strict', 'strictness', 'strike', 'strikethrough', 'string',
+ 'string-constant', 'string-format', 'string-interpolation',
+ 'string-long-quote', 'string-long-single-quote', 'string-single-quote',
+ 'stringchar', 'stringize', 'strings', 'strong', 'struc', 'struct',
+ 'struct-union-block', 'structdef', 'structend', 'structs', 'structstart',
+ 'structtable', 'structure', 'stuff', 'stupid-goddamn-hack', 'style',
+ 'styleblock', 'styles', 'stylus', 'sub', 'sub-pattern', 'subchord', 'subckt',
+ 'subcmd', 'subexp', 'subexpression', 'subkey', 'subkeys', 'subl', 'submodule',
+ 'subnet', 'subnet6', 'subpattern', 'subprogram', 'subroutine', 'subscript',
+ 'subsection', 'subsections', 'subset', 'subshell', 'subsort', 'substituted',
+ 'substitution', 'substitution-definition', 'subtitle', 'subtlegradient',
+ 'subtlegray', 'subtract', 'subtraction', 'subtype', 'suffix', 'sugarml',
+ 'sugarss', 'sugly', 'sugly-comparison-operators', 'sugly-control-keywords',
+ 'sugly-declare-function', 'sugly-delcare-operator', 'sugly-delcare-variable',
+ 'sugly-else-in-invalid-position', 'sugly-encode-clause',
+ 'sugly-function-groups', 'sugly-function-recursion',
+ 'sugly-function-variables', 'sugly-general-functions',
+ 'sugly-general-operators', 'sugly-generic-classes', 'sugly-generic-types',
+ 'sugly-global-function', 'sugly-int-constants', 'sugly-invoke-function',
+ 'sugly-json-clause', 'sugly-language-constants', 'sugly-math-clause',
+ 'sugly-math-constants', 'sugly-multiple-parameter-function',
+ 'sugly-number-constants', 'sugly-operator-operands', 'sugly-print-clause',
+ 'sugly-single-parameter-function', 'sugly-subject-or-predicate',
+ 'sugly-type-function', 'sugly-uri-clause', 'summary', 'super', 'superclass',
+ 'supercollider', 'superscript', 'superset', 'supervisor', 'supervisord',
+ 'supplemental', 'supplimental', 'support', 'suppress-image-or-category',
+ 'suppressed', 'surface', 'surface-technique', 'sv', 'svg', 'svm', 'svn',
+ 'swift', 'swig', 'switch', 'switch-block', 'switch-expression',
+ 'switch-statement', 'switchEnd', 'switchStart', 'swizzle', 'sybase',
+ 'syllableseparator', 'symbol', 'symbol-definition', 'symbol-type', 'symbolic',
+ 'symbolic-math', 'symbols', 'symmetry', 'sync-match', 'sync-mode',
+ 'sync-mode-location', 'synchronization', 'synchronize', 'synchronized',
+ 'synergy', 'synopsis', 'syntax', 'syntax-case', 'syntax-cluster',
+ 'syntax-conceal', 'syntax-error', 'syntax-include', 'syntax-item',
+ 'syntax-keywords', 'syntax-match', 'syntax-option', 'syntax-region',
+ 'syntax-rule', 'syntax-spellcheck', 'syntax-sync', 'sys-types', 'sysj',
+ 'syslink', 'syslog-ng', 'system', 'system-events', 'system-identification',
+ 'system-table-pointer', 'systemreference', 'sytem-events', 't',
+ 't3datastructure', 't4', 't5', 't7', 'ta', 'tab', 'table', 'table-name',
+ 'tablename', 'tabpanels', 'tabs', 'tabular', 'tacacs', 'tack-down', 'tack-up',
+ 'taco', 'tads3', 'tag', 'tag-string', 'tag-value', 'tagbraces', 'tagdef',
+ 'tagged', 'tagger_script', 'taglib', 'tagname', 'tagnamedjango', 'tags',
+ 'taint', 'take', 'target', 'targetobj', 'targetprop', 'task', 'tasks',
+ 'tbdfile', 'tbl', 'tbody', 'tcl', 'tcoffee', 'tcp-object', 'td', 'tdl', 'tea',
+ 'team', 'telegram', 'tell', 'telnet', 'temp', 'template', 'template-call',
+ 'template-parameter', 'templatetag', 'tempo', 'temporal', 'term',
+ 'term-comparison', 'term-creation-and-decomposition', 'term-io',
+ 'term-testing', 'term-unification', 'terminal', 'terminate', 'termination',
+ 'terminator', 'terms', 'ternary', 'ternary-if', 'terra', 'terraform',
+ 'terrain-block', 'test', 'testcase', 'testing', 'tests', 'testsuite', 'testx',
+ 'tex', 'texres', 'texshop', 'text', 'text-reference', 'text-suite', 'textbf',
+ 'textcolor', 'textile', 'textio', 'textit', 'textlabels', 'textmate',
+ 'texttt', 'textual', 'texture', 'texture-map', 'texture-option', 'tfoot',
+ 'th', 'thead', 'then', 'therefore', 'thin', 'thing1', 'third', 'this',
+ 'thorn', 'thread', 'three', 'thrift', 'throughput', 'throw', 'throwables',
+ 'throws', 'tick', 'ticket-num', 'ticket-psa', 'tid-file', 'tidal',
+ 'tidalcycles', 'tiddler', 'tiddler-field', 'tiddler-fields', 'tidy', 'tier',
+ 'tieslur', 'tikz', 'tilde', 'time', 'timeblock', 'timehrap', 'timeout',
+ 'timer', 'times', 'timesig', 'timespan', 'timespec', 'timestamp', 'timing',
+ 'titanium', 'title', 'title-page', 'title-text', 'titled-paragraph', 'tjs',
+ 'tl', 'tla', 'tlh', 'tmpl', 'tmsim', 'tmux', 'tnote', 'tnsaudit', 'to',
+ 'to-file', 'to-type', 'toc', 'toc-list', 'todo', 'todo_extra', 'todotxt',
+ 'token', 'token-def', 'token-paste', 'token-type', 'tokenised', 'tokenizer',
+ 'toml', 'too-many-tildes', 'tool', 'toolbox', 'tooltip', 'top', 'top-level',
+ 'top_level', 'topas', 'topic', 'topic-decoration', 'topic-title', 'tornado',
+ 'torque', 'torquescript', 'tosca', 'total-config', 'totaljs', 'tpye', 'tr',
+ 'trace', 'trace-argument', 'trace-object', 'traceback', 'tracing',
+ 'track_processing', 'trader', 'tradersk', 'trail', 'trailing',
+ 'trailing-array-separator', 'trailing-dictionary-separator', 'trailing-match',
+ 'trait', 'traits', 'traits-keyword', 'transaction',
+ 'transcendental', 'transcludeblock', 'transcludeinline', 'transclusion',
+ 'transform', 'transformation', 'transient', 'transition',
+ 'transitionable-property-value', 'translation', 'transmission-filter',
+ 'transparency', 'transparent-line', 'transpose', 'transposed-func',
+ 'transposed-matrix', 'transposed-parens', 'transposed-variable', 'trap',
+ 'tree', 'treetop', 'trenni', 'trigEvent_', 'trigLevelMod_', 'trigLevel_',
+ 'trigger', 'trigger-words', 'triggermodifier', 'trigonometry',
+ 'trimming-loop', 'triple', 'triple-dash', 'triple-slash', 'triple-star',
+ 'true', 'truncate', 'truncation', 'truthgreen', 'try', 'try-catch',
+ 'trycatch', 'ts', 'tsql', 'tss', 'tst', 'tsv', 'tsx', 'tt', 'ttcn3',
+ 'ttlextension', 'ttpmacro', 'tts', 'tubaina', 'tubaina2', 'tul', 'tup',
+ 'tuple', 'turbulence', 'turing', 'turquoise', 'turtle', 'tutch', 'tvml',
+ 'tw5', 'twig', 'twigil', 'twiki', 'two', 'txl', 'txt', 'txt2tags', 'type',
+ 'type-annotation', 'type-cast', 'type-cheat', 'type-checking',
+ 'type-constrained', 'type-constraint', 'type-declaration', 'type-def',
+ 'type-definition', 'type-definition-group', 'type-definitions',
+ 'type-descriptor', 'type-of', 'type-or', 'type-parameter', 'type-parameters',
+ 'type-signature', 'type-spec', 'type-specialization', 'type-specifiers',
+ 'type_2', 'type_trait', 'typeabbrev', 'typeclass', 'typed', 'typed-hole',
+ 'typedblock', 'typedcoffeescript', 'typedecl', 'typedef', 'typeexp',
+ 'typehint', 'typehinted', 'typeid', 'typename', 'types', 'typesbii',
+ 'typescriptish', 'typographic-quotes', 'typoscript', 'typoscript2', 'u',
+ 'u-degree', 'u-end', 'u-offset', 'u-resolution', 'u-scale', 'u-segments',
+ 'u-size', 'u-start', 'u-value', 'uc', 'ucicfg', 'ucicmd', 'udaf', 'udf',
+ 'udl', 'udp', 'udtf', 'ui', 'ui-block', 'ui-group', 'ui-state', 'ui-subgroup',
+ 'uintptr', 'ujm', 'uk', 'ul', 'umbaska', 'unOp', 'unary', 'unbuffered',
+ 'unchecked', 'uncleared', 'unclosed', 'unclosed-string', 'unconstrained',
+ 'undef', 'undefined', 'underbar-circle', 'underbar-diamond', 'underbar-iota',
+ 'underbar-jot', 'underbar-quote', 'underbar-semicolon', 'underline',
+ 'underline-text', 'underlined', 'underscore', 'undocumented',
+ 'unescaped-quote', 'unexpected', 'unexpected-characters',
+ 'unexpected-extends', 'unexpected-extends-character', 'unfiled',
+ 'unformatted', 'unicode', 'unicode-16-bit', 'unicode-32-bit',
+ 'unicode-escape', 'unicode-raw', 'unicode-raw-regex', 'unified', 'unify',
+ 'unimplemented', 'unimportant', 'union', 'union-declaration', 'unique-id',
+ 'unit', 'unit-checking', 'unit-test', 'unit_test', 'unittest', 'unity',
+ 'unityscript', 'universal-match', 'unix', 'unknown', 'unknown-escape',
+ 'unknown-method', 'unknown-property-name', 'unknown-rune', 'unlabeled',
+ 'unless', 'unnecessary', 'unnumbered', 'uno', 'unoconfig', 'unop', 'unoproj',
+ 'unordered', 'unordered-block', 'unosln', 'unpack', 'unpacking', 'unparsed',
+ 'unqualified', 'unquoted', 'unrecognized', 'unrecognized-character',
+ 'unrecognized-character-escape', 'unrecognized-string-escape', 'unsafe',
+ 'unsigned', 'unsigned-int', 'unsized_integer', 'unsupplied', 'until',
+ 'untitled', 'untyped', 'unused', 'uopz', 'update', 'uppercase', 'upstream',
+ 'upwards', 'ur', 'uri', 'url', 'usable', 'usage', 'use', 'use-as', 'use-map',
+ 'use-material', 'usebean', 'usecase', 'usecase-block', 'user', 'user-defined',
+ 'user-defined-property', 'user-defined-type', 'user-interaction',
+ 'userflagsref', 'userid', 'username', 'users', 'using',
+ 'using-namespace-declaration', 'using_animtree', 'util', 'utilities',
+ 'utility', 'utxt', 'uv-resolution', 'uvu', 'uvw', 'ux', 'uxc', 'uxl', 'uz',
+ 'v', 'v-degree', 'v-end', 'v-offset', 'v-resolution', 'v-scale', 'v-segments',
+ 'v-size', 'v-start', 'v-value', 'val', 'vala', 'valgrind', 'valid',
+ 'valid-ampersand', 'valid-bracket', 'valign', 'value', 'value-pair',
+ 'value-signature', 'value-size', 'value-type', 'valuepair', 'vamos', 'vamp',
+ 'vane-down', 'vane-left', 'vane-right', 'vane-up', 'var',
+ 'var-single-variable', 'var1', 'var2', 'variable', 'variable-access',
+ 'variable-assignment', 'variable-declaration', 'variable-definition',
+ 'variable-modifier', 'variable-parameter', 'variable-reference',
+ 'variable-usage', 'variables', 'variabletable', 'variant',
+ 'variant-definition', 'varname', 'varnish', 'vars', 'vb', 'vbnet', 'vbs',
+ 'vc', 'vcard', 'vcd', 'vcl', 'vcs', 'vector', 'vector-load', 'vectors',
+ 'vehicle', 'velocity', 'vendor-prefix', 'verb', 'verbatim', 'verdict',
+ 'verilog', 'version', 'version-number', 'version-specification', 'vertex',
+ 'vertex-reference', 'vertical-blending', 'vertical-span',
+ 'vertical-text-cue-setting', 'vex', 'vhdl', 'vhost', 'vi', 'via',
+ 'video-texturing', 'video_processing', 'view', 'viewhelpers', 'vimAugroupKey',
+ 'vimBehaveModel', 'vimFTCmd', 'vimFTOption', 'vimFuncKey', 'vimGroupSpecial',
+ 'vimHiAttrib', 'vimHiClear', 'vimMapModKey', 'vimPattern', 'vimSynCase',
+ 'vimSynType', 'vimSyncC', 'vimSyncLinecont', 'vimSyncMatch', 'vimSyncNone',
+ 'vimSyncRegion', 'vimUserAttrbCmplt', 'vimUserAttrbKey', 'vimUserCommand',
+ 'viml', 'virtual', 'virtual-host', 'virtual-reality', 'visibility',
+ 'visualforce', 'visualization', 'vlanhdr', 'vle', 'vmap', 'vmx', 'voice',
+ 'void', 'volatile', 'volt', 'volume', 'vpath', 'vplus', 'vrf', 'vtt', 'vue',
+ 'vue-jade', 'vue-stylus', 'w-offset', 'w-scale', 'w-value',
+ 'w3c-extended-color-name', 'w3c-non-standard-color-name',
+ 'w3c-standard-color-name', 'wait', 'waitress', 'waitress-config',
+ 'waitress-rb', 'warn', 'warning', 'warnings', 'wast', 'water', 'watson-todo',
+ 'wavefront', 'wavelet', 'wddx', 'wdiff', 'weapon', 'weave', 'weaveBracket',
+ 'weaveBullet', 'webidl', 'webspeed', 'webvtt', 'weekday', 'weirdland', 'wf',
+ 'wh', 'whatever', 'wheeled-vehicle', 'when', 'where', 'while',
+ 'while-condition', 'while-loop', 'whiskey', 'white', 'whitespace', 'widget',
+ 'width', 'wiki', 'wiki-link', 'wildcard', 'wildsk', 'win', 'window',
+ 'window-classes', 'windows', 'winered', 'with', 'with-arg', 'with-args',
+ 'with-arguments', 'with-params', 'with-prefix', 'with-side-effects',
+ 'with-suffix', 'with-terminator', 'with-value', 'with_colon', 'without-args',
+ 'without-arguments', 'wla-dx', 'word', 'word-op', 'wordnet', 'wordpress',
+ 'words', 'workitem', 'world', 'wow', 'wp', 'write', 'wrong',
+ 'wrong-access-type', 'wrong-division', 'wrong-division-assignment', 'ws',
+ 'www', 'wxml', 'wysiwyg-string', 'x10', 'x86', 'x86_64', 'x86asm', 'xacro',
+ 'xbase', 'xchg', 'xhp', 'xhprof', 'xikij', 'xml', 'xml-attr', 'xmlrpc',
+ 'xmlwriter', 'xop', 'xor', 'xparse', 'xq', 'xquery', 'xref', 'xsave',
+ 'xsd-all', 'xsd_nillable', 'xsd_optional', 'xsl', 'xslt', 'xsse3_simd', 'xst',
+ 'xtend', 'xtoy', 'xtpl', 'xu', 'xvc', 'xve', 'xyzw', 'y', 'y1', 'y2', 'yabb',
+ 'yaml', 'yaml-ext', 'yang', 'yara', 'yate', 'yaws', 'year', 'yellow', 'yield',
+ 'ykk', 'yorick', 'you-forgot-semicolon', 'z', 'z80', 'zap', 'zapper', 'zep',
+ 'zepon', 'zepto', 'zero', 'zero-width-marker', 'zero-width-print', 'zeroop',
+ 'zh-CN', 'zh-TW', 'zig', 'zilde', 'zlib', 'zoomfilter', 'zzz'
+])
diff --git a/src/highlights-component.coffee b/src/highlights-component.coffee
index a6e85b7e5..e5c1db60e 100644
--- a/src/highlights-component.coffee
+++ b/src/highlights-component.coffee
@@ -97,24 +97,23 @@ class HighlightsComponent
flashHighlightNodeIfRequested: (id, newHighlightState) ->
oldHighlightState = @oldState[id]
- return unless newHighlightState.flashCount > oldHighlightState.flashCount
+ if newHighlightState.needsFlash and oldHighlightState.flashCount isnt newHighlightState.flashCount
+ highlightNode = @highlightNodesById[id]
- highlightNode = @highlightNodesById[id]
+ addFlashClass = =>
+ highlightNode.classList.add(newHighlightState.flashClass)
+ oldHighlightState.flashClass = newHighlightState.flashClass
+ @flashTimeoutId = setTimeout(removeFlashClass, newHighlightState.flashDuration)
- addFlashClass = =>
- highlightNode.classList.add(newHighlightState.flashClass)
- oldHighlightState.flashClass = newHighlightState.flashClass
- @flashTimeoutId = setTimeout(removeFlashClass, newHighlightState.flashDuration)
+ removeFlashClass = =>
+ highlightNode.classList.remove(oldHighlightState.flashClass)
+ oldHighlightState.flashClass = null
+ clearTimeout(@flashTimeoutId)
- removeFlashClass = =>
- highlightNode.classList.remove(oldHighlightState.flashClass)
- oldHighlightState.flashClass = null
- clearTimeout(@flashTimeoutId)
+ if oldHighlightState.flashClass?
+ removeFlashClass()
+ requestAnimationFrame(addFlashClass)
+ else
+ addFlashClass()
- if oldHighlightState.flashClass?
- removeFlashClass()
- requestAnimationFrame(addFlashClass)
- else
- addFlashClass()
-
- oldHighlightState.flashCount = newHighlightState.flashCount
+ oldHighlightState.flashCount = newHighlightState.flashCount
diff --git a/src/initialize-benchmark-window.js b/src/initialize-benchmark-window.js
new file mode 100644
index 000000000..e4be4420b
--- /dev/null
+++ b/src/initialize-benchmark-window.js
@@ -0,0 +1,111 @@
+/** @babel */
+
+import {remote} from 'electron'
+import path from 'path'
+import ipcHelpers from './ipc-helpers'
+import util from 'util'
+
+export default async function () {
+ const {getWindowLoadSettings} = require('./window-load-settings-helpers')
+ const {test, headless, resourcePath, benchmarkPaths} = getWindowLoadSettings()
+ try {
+ const Clipboard = require('../src/clipboard')
+ const ApplicationDelegate = require('../src/application-delegate')
+ const AtomEnvironment = require('../src/atom-environment')
+ const TextEditor = require('../src/text-editor')
+
+ const exportsPath = path.join(resourcePath, 'exports')
+ require('module').globalPaths.push(exportsPath) // Add 'exports' to module search path.
+ process.env.NODE_PATH = exportsPath // Set NODE_PATH env variable since tasks may need it.
+
+ document.title = 'Benchmarks'
+ // Allow `document.title` to be assigned in benchmarks without actually changing the window title.
+ let documentTitle = null
+ Object.defineProperty(document, 'title', {
+ get () { return documentTitle },
+ set (title) { documentTitle = title }
+ })
+
+ window.addEventListener('keydown', (event) => {
+ // Reload: cmd-r / ctrl-r
+ if ((event.metaKey || event.ctrlKey) && event.keyCode === 82) {
+ ipcHelpers.call('window-method', 'reload')
+ }
+
+ // Toggle Dev Tools: cmd-alt-i (Mac) / ctrl-shift-i (Linux/Windows)
+ if (event.keyCode === 73) {
+ const isDarwin = process.platform === 'darwin'
+ if ((isDarwin && event.metaKey && event.altKey) || (!isDarwin && event.ctrlKey && event.shiftKey)) {
+ ipcHelpers.call('window-method', 'toggleDevTools')
+ }
+ }
+
+ // Close: cmd-w / ctrl-w
+ if ((event.metaKey || event.ctrlKey) && event.keyCode === 87) {
+ ipcHelpers.call('window-method', 'close')
+ }
+
+ // Copy: cmd-c / ctrl-c
+ if ((event.metaKey || event.ctrlKey) && event.keyCode === 67) {
+ ipcHelpers.call('window-method', 'copy')
+ }
+ }, true)
+
+ const clipboard = new Clipboard()
+ TextEditor.setClipboard(clipboard)
+
+ const applicationDelegate = new ApplicationDelegate()
+ global.atom = new AtomEnvironment({
+ applicationDelegate,
+ window,
+ document,
+ clipboard,
+ configDirPath: process.env.ATOM_HOME,
+ enablePersistence: false
+ })
+
+ // Prevent benchmarks from modifying application menus
+ global.atom.menu.sendToBrowserProcess = function () { }
+
+ if (headless) {
+ Object.defineProperties(process, {
+ stdout: { value: remote.process.stdout },
+ stderr: { value: remote.process.stderr }
+ })
+
+ console.log = function (...args) {
+ const formatted = util.format(...args)
+ process.stdout.write(formatted + '\n')
+ }
+ console.warn = function (...args) {
+ const formatted = util.format(...args)
+ process.stderr.write(formatted + '\n')
+ }
+ console.error = function (...args) {
+ const formatted = util.format(...args)
+ process.stderr.write(formatted + '\n')
+ }
+ } else {
+ remote.getCurrentWindow().show()
+ }
+
+ const benchmarkRunner = require('../benchmarks/benchmark-runner')
+ const statusCode = await benchmarkRunner({test, benchmarkPaths})
+ if (headless) {
+ exitWithStatusCode(statusCode)
+ }
+ } catch (error) {
+ if (headless) {
+ console.error(error.stack || error)
+ exitWithStatusCode(1)
+ } else {
+ ipcHelpers.call('window-method', 'openDevTools')
+ throw error
+ }
+ }
+}
+
+function exitWithStatusCode (statusCode) {
+ remote.app.emit('will-quit')
+ remote.process.exit(statusCode)
+}
diff --git a/src/language-mode.coffee b/src/language-mode.coffee
index ad038d7db..bb9f339c4 100644
--- a/src/language-mode.coffee
+++ b/src/language-mode.coffee
@@ -148,19 +148,19 @@ class LanguageMode
rowRange
rowRangeForCommentAtBufferRow: (bufferRow) ->
- return unless @editor.tokenizedBuffer.tokenizedLineForRow(bufferRow).isComment()
+ return unless @editor.tokenizedBuffer.tokenizedLines[bufferRow]?.isComment()
startRow = bufferRow
endRow = bufferRow
if bufferRow > 0
for currentRow in [bufferRow-1..0] by -1
- break unless @editor.tokenizedBuffer.tokenizedLineForRow(currentRow).isComment()
+ break unless @editor.tokenizedBuffer.tokenizedLines[currentRow]?.isComment()
startRow = currentRow
if bufferRow < @buffer.getLastRow()
for currentRow in [bufferRow+1..@buffer.getLastRow()] by 1
- break unless @editor.tokenizedBuffer.tokenizedLineForRow(currentRow).isComment()
+ break unless @editor.tokenizedBuffer.tokenizedLines[currentRow]?.isComment()
endRow = currentRow
return [startRow, endRow] if startRow isnt endRow
@@ -189,7 +189,7 @@ class LanguageMode
# row is a comment.
isLineCommentedAtBufferRow: (bufferRow) ->
return false unless 0 <= bufferRow <= @editor.getLastBufferRow()
- @editor.tokenizedBuffer.tokenizedLineForRow(bufferRow).isComment()
+ @editor.tokenizedBuffer.tokenizedLines[bufferRow]?.isComment()
# Find a row range for a 'paragraph' around specified bufferRow. A paragraph
# is a block of text bounded by and empty line or a block of text that is not
@@ -246,10 +246,7 @@ class LanguageMode
@suggestedIndentForTokenizedLineAtBufferRow(bufferRow, line, tokenizedLine, options)
suggestedIndentForLineAtBufferRow: (bufferRow, line, options) ->
- if @editor.largeFileMode or @editor.tokenizedBuffer.grammar is NullGrammar
- tokenizedLine = @editor.tokenizedBuffer.buildPlaceholderTokenizedLineForRowWithText(bufferRow, line)
- else
- tokenizedLine = @editor.tokenizedBuffer.buildTokenizedLineForRowWithText(bufferRow, line)
+ tokenizedLine = @editor.tokenizedBuffer.buildTokenizedLineForRowWithText(bufferRow, line)
@suggestedIndentForTokenizedLineAtBufferRow(bufferRow, line, tokenizedLine, options)
suggestedIndentForTokenizedLineAtBufferRow: (bufferRow, line, tokenizedLine, options) ->
diff --git a/src/lines-component.coffee b/src/lines-component.coffee
index fb9b28b03..6c9271179 100644
--- a/src/lines-component.coffee
+++ b/src/lines-component.coffee
@@ -19,7 +19,7 @@ module.exports =
class LinesComponent extends TiledComponent
placeholderTextDiv: null
- constructor: ({@presenter, @domElementPool, @assert}) ->
+ constructor: ({@views, @presenter, @domElementPool, @assert}) ->
@domNode = document.createElement('div')
@domNode.classList.add('lines')
@tilesNode = document.createElement("div")
@@ -32,10 +32,6 @@ class LinesComponent extends TiledComponent
@cursorsComponent = new CursorsComponent
@domNode.appendChild(@cursorsComponent.getDomNode())
- insertionPoint = document.createElement('content')
- insertionPoint.setAttribute('select', '.overlayer')
- @domNode.appendChild(insertionPoint)
-
getDomNode: ->
@domNode
@@ -61,9 +57,17 @@ class LinesComponent extends TiledComponent
@domNode.appendChild(@placeholderTextDiv)
@oldState.placeholderText = @newState.placeholderText
+ # Removing and updating block decorations needs to be done in two different
+ # steps, so that the same decoration node can be moved from one tile to
+ # another in the same animation frame.
+ for component in @getComponents()
+ component.removeDeletedBlockDecorations()
+ for component in @getComponents()
+ component.updateBlockDecorations()
+
@cursorsComponent.updateSync(state)
- buildComponentForTile: (id) -> new LinesTileComponent({id, @presenter, @domElementPool, @assert})
+ buildComponentForTile: (id) -> new LinesTileComponent({id, @presenter, @domElementPool, @assert, @views})
buildEmptyState: ->
{tiles: {}}
@@ -87,6 +91,11 @@ class LinesComponent extends TiledComponent
@presenter.setLineHeight(lineHeightInPixels)
@presenter.setBaseCharacterWidth(defaultCharWidth, doubleWidthCharWidth, halfWidthCharWidth, koreanCharWidth)
+ measureBlockDecorations: ->
+ for component in @getComponents()
+ component.measureBlockDecorations()
+ return
+
lineIdForScreenRow: (screenRow) ->
tile = @presenter.tileForRow(screenRow)
@getComponentForTile(tile)?.lineIdForScreenRow(screenRow)
diff --git a/src/lines-tile-component.coffee b/src/lines-tile-component.coffee
deleted file mode 100644
index c1cb2ba64..000000000
--- a/src/lines-tile-component.coffee
+++ /dev/null
@@ -1,288 +0,0 @@
-HighlightsComponent = require './highlights-component'
-ZERO_WIDTH_NBSP = '\ufeff'
-
-cloneObject = (object) ->
- clone = {}
- clone[key] = value for key, value of object
- clone
-
-module.exports =
-class LinesTileComponent
- constructor: ({@presenter, @id, @domElementPool, @assert}) ->
- @measuredLines = new Set
- @lineNodesByLineId = {}
- @screenRowsByLineId = {}
- @lineIdsByScreenRow = {}
- @textNodesByLineId = {}
- @insertionPointsBeforeLineById = {}
- @insertionPointsAfterLineById = {}
- @domNode = @domElementPool.buildElement("div")
- @domNode.style.position = "absolute"
- @domNode.style.display = "block"
-
- @highlightsComponent = new HighlightsComponent(@domElementPool)
- @domNode.appendChild(@highlightsComponent.getDomNode())
-
- destroy: ->
- @domElementPool.freeElementAndDescendants(@domNode)
-
- getDomNode: ->
- @domNode
-
- updateSync: (state) ->
- @newState = state
- unless @oldState
- @oldState = {tiles: {}}
- @oldState.tiles[@id] = {lines: {}}
-
- @newTileState = @newState.tiles[@id]
- @oldTileState = @oldState.tiles[@id]
-
- if @newState.backgroundColor isnt @oldState.backgroundColor
- @domNode.style.backgroundColor = @newState.backgroundColor
- @oldState.backgroundColor = @newState.backgroundColor
-
- if @newTileState.zIndex isnt @oldTileState.zIndex
- @domNode.style.zIndex = @newTileState.zIndex
- @oldTileState.zIndex = @newTileState.zIndex
-
- if @newTileState.display isnt @oldTileState.display
- @domNode.style.display = @newTileState.display
- @oldTileState.display = @newTileState.display
-
- if @newTileState.height isnt @oldTileState.height
- @domNode.style.height = @newTileState.height + 'px'
- @oldTileState.height = @newTileState.height
-
- if @newState.width isnt @oldState.width
- @domNode.style.width = @newState.width + 'px'
- @oldTileState.width = @newTileState.width
-
- if @newTileState.top isnt @oldTileState.top or @newTileState.left isnt @oldTileState.left
- @domNode.style['-webkit-transform'] = "translate3d(#{@newTileState.left}px, #{@newTileState.top}px, 0px)"
- @oldTileState.top = @newTileState.top
- @oldTileState.left = @newTileState.left
-
- @updateLineNodes()
-
- @highlightsComponent.updateSync(@newTileState)
-
- removeLineNodes: ->
- @removeLineNode(id) for id of @oldTileState.lines
- return
-
- removeLineNode: (id) ->
- @domElementPool.freeElementAndDescendants(@lineNodesByLineId[id])
- @removeBlockDecorationInsertionPointBeforeLine(id)
- @removeBlockDecorationInsertionPointAfterLine(id)
-
- delete @lineNodesByLineId[id]
- delete @textNodesByLineId[id]
- delete @lineIdsByScreenRow[@screenRowsByLineId[id]]
- delete @screenRowsByLineId[id]
- delete @oldTileState.lines[id]
-
- updateLineNodes: ->
- for id of @oldTileState.lines
- unless @newTileState.lines.hasOwnProperty(id)
- @removeLineNode(id)
-
- newLineIds = null
- newLineNodes = null
-
- for id, lineState of @newTileState.lines
- if @oldTileState.lines.hasOwnProperty(id)
- @updateLineNode(id)
- else
- newLineIds ?= []
- newLineNodes ?= []
- newLineIds.push(id)
- newLineNodes.push(@buildLineNode(id))
- @screenRowsByLineId[id] = lineState.screenRow
- @lineIdsByScreenRow[lineState.screenRow] = id
- @oldTileState.lines[id] = cloneObject(lineState)
-
- return unless newLineIds?
-
- for id, i in newLineIds
- lineNode = newLineNodes[i]
- @lineNodesByLineId[id] = lineNode
- if nextNode = @findNodeNextTo(lineNode)
- @domNode.insertBefore(lineNode, nextNode)
- else
- @domNode.appendChild(lineNode)
-
- @insertBlockDecorationInsertionPointBeforeLine(id)
- @insertBlockDecorationInsertionPointAfterLine(id)
-
- removeBlockDecorationInsertionPointBeforeLine: (id) ->
- if insertionPoint = @insertionPointsBeforeLineById[id]
- @domElementPool.freeElementAndDescendants(insertionPoint)
- delete @insertionPointsBeforeLineById[id]
-
- insertBlockDecorationInsertionPointBeforeLine: (id) ->
- {hasPrecedingBlockDecorations, screenRow} = @newTileState.lines[id]
-
- if hasPrecedingBlockDecorations
- lineNode = @lineNodesByLineId[id]
- insertionPoint = @domElementPool.buildElement("content")
- @domNode.insertBefore(insertionPoint, lineNode)
- @insertionPointsBeforeLineById[id] = insertionPoint
- insertionPoint.dataset.screenRow = screenRow
- @updateBlockDecorationInsertionPointBeforeLine(id)
-
- updateBlockDecorationInsertionPointBeforeLine: (id) ->
- oldLineState = @oldTileState.lines[id]
- newLineState = @newTileState.lines[id]
- insertionPoint = @insertionPointsBeforeLineById[id]
- return unless insertionPoint?
-
- if newLineState.screenRow isnt oldLineState.screenRow
- insertionPoint.dataset.screenRow = newLineState.screenRow
-
- precedingBlockDecorationsSelector = newLineState.precedingBlockDecorations.map((d) -> ".atom--block-decoration-#{d.id}").join(',')
-
- if precedingBlockDecorationsSelector isnt oldLineState.precedingBlockDecorationsSelector
- insertionPoint.setAttribute("select", precedingBlockDecorationsSelector)
- oldLineState.precedingBlockDecorationsSelector = precedingBlockDecorationsSelector
-
- removeBlockDecorationInsertionPointAfterLine: (id) ->
- if insertionPoint = @insertionPointsAfterLineById[id]
- @domElementPool.freeElementAndDescendants(insertionPoint)
- delete @insertionPointsAfterLineById[id]
-
- insertBlockDecorationInsertionPointAfterLine: (id) ->
- {hasFollowingBlockDecorations, screenRow} = @newTileState.lines[id]
-
- if hasFollowingBlockDecorations
- lineNode = @lineNodesByLineId[id]
- insertionPoint = @domElementPool.buildElement("content")
- @domNode.insertBefore(insertionPoint, lineNode.nextSibling)
- @insertionPointsAfterLineById[id] = insertionPoint
- insertionPoint.dataset.screenRow = screenRow
- @updateBlockDecorationInsertionPointAfterLine(id)
-
- updateBlockDecorationInsertionPointAfterLine: (id) ->
- oldLineState = @oldTileState.lines[id]
- newLineState = @newTileState.lines[id]
- insertionPoint = @insertionPointsAfterLineById[id]
- return unless insertionPoint?
-
- if newLineState.screenRow isnt oldLineState.screenRow
- insertionPoint.dataset.screenRow = newLineState.screenRow
-
- followingBlockDecorationsSelector = newLineState.followingBlockDecorations.map((d) -> ".atom--block-decoration-#{d.id}").join(',')
-
- if followingBlockDecorationsSelector isnt oldLineState.followingBlockDecorationsSelector
- insertionPoint.setAttribute("select", followingBlockDecorationsSelector)
- oldLineState.followingBlockDecorationsSelector = followingBlockDecorationsSelector
-
- findNodeNextTo: (node) ->
- for nextNode, index in @domNode.children
- continue if index is 0 # skips highlights node
- return nextNode if @screenRowForNode(node) < @screenRowForNode(nextNode)
- return
-
- screenRowForNode: (node) -> parseInt(node.dataset.screenRow)
-
- buildLineNode: (id) ->
- {lineText, tagCodes, screenRow, decorationClasses} = @newTileState.lines[id]
-
- lineNode = @domElementPool.buildElement("div", "line")
- lineNode.dataset.screenRow = screenRow
-
- if decorationClasses?
- for decorationClass in decorationClasses
- lineNode.classList.add(decorationClass)
-
- textNodes = []
- startIndex = 0
- openScopeNode = lineNode
- for tagCode in tagCodes when tagCode isnt 0
- if @presenter.isCloseTagCode(tagCode)
- openScopeNode = openScopeNode.parentElement
- else if @presenter.isOpenTagCode(tagCode)
- scope = @presenter.tagForCode(tagCode)
- newScopeNode = @domElementPool.buildElement("span", scope.replace(/\.+/g, ' '))
- openScopeNode.appendChild(newScopeNode)
- openScopeNode = newScopeNode
- else
- textNode = @domElementPool.buildText(lineText.substr(startIndex, tagCode))
- startIndex += tagCode
- openScopeNode.appendChild(textNode)
- textNodes.push(textNode)
-
- if startIndex is 0
- textNode = @domElementPool.buildText(' ')
- lineNode.appendChild(textNode)
- textNodes.push(textNode)
-
- if lineText.endsWith(@presenter.displayLayer.foldCharacter)
- # Insert a zero-width non-breaking whitespace, so that
- # LinesYardstick can take the fold-marker::after pseudo-element
- # into account during measurements when such marker is the last
- # character on the line.
- textNode = @domElementPool.buildText(ZERO_WIDTH_NBSP)
- lineNode.appendChild(textNode)
- textNodes.push(textNode)
-
- @textNodesByLineId[id] = textNodes
- lineNode
-
- updateLineNode: (id) ->
- oldLineState = @oldTileState.lines[id]
- newLineState = @newTileState.lines[id]
-
- lineNode = @lineNodesByLineId[id]
-
- newDecorationClasses = newLineState.decorationClasses
- oldDecorationClasses = oldLineState.decorationClasses
-
- if oldDecorationClasses?
- for decorationClass in oldDecorationClasses
- unless newDecorationClasses? and decorationClass in newDecorationClasses
- lineNode.classList.remove(decorationClass)
-
- if newDecorationClasses?
- for decorationClass in newDecorationClasses
- unless oldDecorationClasses? and decorationClass in oldDecorationClasses
- lineNode.classList.add(decorationClass)
-
- oldLineState.decorationClasses = newLineState.decorationClasses
-
- if not oldLineState.hasPrecedingBlockDecorations and newLineState.hasPrecedingBlockDecorations
- @insertBlockDecorationInsertionPointBeforeLine(id)
- else if oldLineState.hasPrecedingBlockDecorations and not newLineState.hasPrecedingBlockDecorations
- @removeBlockDecorationInsertionPointBeforeLine(id)
-
- if not oldLineState.hasFollowingBlockDecorations and newLineState.hasFollowingBlockDecorations
- @insertBlockDecorationInsertionPointAfterLine(id)
- else if oldLineState.hasFollowingBlockDecorations and not newLineState.hasFollowingBlockDecorations
- @removeBlockDecorationInsertionPointAfterLine(id)
-
- if newLineState.screenRow isnt oldLineState.screenRow
- lineNode.dataset.screenRow = newLineState.screenRow
- @lineIdsByScreenRow[newLineState.screenRow] = id
- @screenRowsByLineId[id] = newLineState.screenRow
-
- @updateBlockDecorationInsertionPointBeforeLine(id)
- @updateBlockDecorationInsertionPointAfterLine(id)
-
- oldLineState.screenRow = newLineState.screenRow
- oldLineState.hasPrecedingBlockDecorations = newLineState.hasPrecedingBlockDecorations
- oldLineState.hasFollowingBlockDecorations = newLineState.hasFollowingBlockDecorations
-
- lineNodeForScreenRow: (screenRow) ->
- @lineNodesByLineId[@lineIdsByScreenRow[screenRow]]
-
- lineNodeForLineId: (lineId) ->
- @lineNodesByLineId[lineId]
-
- textNodesForLineId: (lineId) ->
- @textNodesByLineId[lineId].slice()
-
- lineIdForScreenRow: (screenRow) ->
- @lineIdsByScreenRow[screenRow]
-
- textNodesForScreenRow: (screenRow) ->
- @textNodesByLineId[@lineIdsByScreenRow[screenRow]]?.slice()
diff --git a/src/lines-tile-component.js b/src/lines-tile-component.js
new file mode 100644
index 000000000..202e16708
--- /dev/null
+++ b/src/lines-tile-component.js
@@ -0,0 +1,401 @@
+const HighlightsComponent = require('./highlights-component')
+const ZERO_WIDTH_NBSP = '\ufeff'
+
+module.exports = class LinesTileComponent {
+ constructor ({presenter, id, domElementPool, assert, views}) {
+ this.id = id
+ this.presenter = presenter
+ this.views = views
+ this.domElementPool = domElementPool
+ this.assert = assert
+ this.lineNodesByLineId = {}
+ this.screenRowsByLineId = {}
+ this.lineIdsByScreenRow = {}
+ this.textNodesByLineId = {}
+ this.blockDecorationNodesByLineIdAndDecorationId = {}
+ this.domNode = this.domElementPool.buildElement('div')
+ this.domNode.style.position = 'absolute'
+ this.domNode.style.display = 'block'
+ this.highlightsComponent = new HighlightsComponent(this.domElementPool)
+ this.domNode.appendChild(this.highlightsComponent.getDomNode())
+ }
+
+ destroy () {
+ this.removeLineNodes()
+ this.domElementPool.freeElementAndDescendants(this.domNode)
+ }
+
+ getDomNode () {
+ return this.domNode
+ }
+
+ updateSync (state) {
+ this.newState = state
+ if (this.oldState == null) {
+ this.oldState = {tiles: {}}
+ this.oldState.tiles[this.id] = {lines: {}}
+ }
+
+ this.newTileState = this.newState.tiles[this.id]
+ this.oldTileState = this.oldState.tiles[this.id]
+
+ if (this.newState.backgroundColor !== this.oldState.backgroundColor) {
+ this.domNode.style.backgroundColor = this.newState.backgroundColor
+ this.oldState.backgroundColor = this.newState.backgroundColor
+ }
+
+ if (this.newTileState.zIndex !== this.oldTileState.zIndex) {
+ this.domNode.style.zIndex = this.newTileState.zIndex
+ this.oldTileState.zIndex = this.newTileState.zIndex
+ }
+
+ if (this.newTileState.display !== this.oldTileState.display) {
+ this.domNode.style.display = this.newTileState.display
+ this.oldTileState.display = this.newTileState.display
+ }
+
+ if (this.newTileState.height !== this.oldTileState.height) {
+ this.domNode.style.height = this.newTileState.height + 'px'
+ this.oldTileState.height = this.newTileState.height
+ }
+
+ if (this.newState.width !== this.oldState.width) {
+ this.domNode.style.width = this.newState.width + 'px'
+ this.oldState.width = this.newState.width
+ }
+
+ if (this.newTileState.top !== this.oldTileState.top || this.newTileState.left !== this.oldTileState.left) {
+ this.domNode.style.transform = `translate3d(${this.newTileState.left}px, ${this.newTileState.top}px, 0px)`
+ this.oldTileState.top = this.newTileState.top
+ this.oldTileState.left = this.newTileState.left
+ }
+
+ this.updateLineNodes()
+ this.highlightsComponent.updateSync(this.newTileState)
+ }
+
+ removeLineNodes () {
+ for (const id of Object.keys(this.oldTileState.lines)) {
+ this.removeLineNode(id)
+ }
+ }
+
+ removeLineNode (lineId) {
+ this.domElementPool.freeElementAndDescendants(this.lineNodesByLineId[lineId])
+ for (const decorationId of Object.keys(this.oldTileState.lines[lineId].precedingBlockDecorations)) {
+ const {topRulerNode, blockDecorationNode, bottomRulerNode} =
+ this.blockDecorationNodesByLineIdAndDecorationId[lineId][decorationId]
+ topRulerNode.remove()
+ blockDecorationNode.remove()
+ bottomRulerNode.remove()
+ }
+ for (const decorationId of Object.keys(this.oldTileState.lines[lineId].followingBlockDecorations)) {
+ const {topRulerNode, blockDecorationNode, bottomRulerNode} =
+ this.blockDecorationNodesByLineIdAndDecorationId[lineId][decorationId]
+ topRulerNode.remove()
+ blockDecorationNode.remove()
+ bottomRulerNode.remove()
+ }
+
+ delete this.blockDecorationNodesByLineIdAndDecorationId[lineId]
+ delete this.lineNodesByLineId[lineId]
+ delete this.textNodesByLineId[lineId]
+ delete this.lineIdsByScreenRow[this.screenRowsByLineId[lineId]]
+ delete this.screenRowsByLineId[lineId]
+ delete this.oldTileState.lines[lineId]
+ }
+
+ updateLineNodes () {
+ for (const id of Object.keys(this.oldTileState.lines)) {
+ if (!this.newTileState.lines.hasOwnProperty(id)) {
+ this.removeLineNode(id)
+ }
+ }
+
+ const newLineIds = []
+ const newLineNodes = []
+ for (const id of Object.keys(this.newTileState.lines)) {
+ const lineState = this.newTileState.lines[id]
+ if (this.oldTileState.lines.hasOwnProperty(id)) {
+ this.updateLineNode(id)
+ } else {
+ newLineIds.push(id)
+ newLineNodes.push(this.buildLineNode(id))
+ this.screenRowsByLineId[id] = lineState.screenRow
+ this.lineIdsByScreenRow[lineState.screenRow] = id
+ this.oldTileState.lines[id] = Object.assign({}, lineState)
+ // Avoid assigning state for block decorations, because we need to
+ // process it later when updating the DOM.
+ this.oldTileState.lines[id].precedingBlockDecorations = {}
+ this.oldTileState.lines[id].followingBlockDecorations = {}
+ }
+ }
+
+ while (newLineIds.length > 0) {
+ const id = newLineIds.shift()
+ const lineNode = newLineNodes.shift()
+ this.lineNodesByLineId[id] = lineNode
+ const nextNode = this.findNodeNextTo(lineNode)
+ if (nextNode == null) {
+ this.domNode.appendChild(lineNode)
+ } else {
+ this.domNode.insertBefore(lineNode, nextNode)
+ }
+ }
+ }
+
+ findNodeNextTo (node) {
+ let i = 1 // skip highlights node
+ while (i < this.domNode.children.length) {
+ const nextNode = this.domNode.children[i]
+ if (this.screenRowForNode(node) < this.screenRowForNode(nextNode)) {
+ return nextNode
+ }
+ i++
+ }
+ return null
+ }
+
+ screenRowForNode (node) {
+ return parseInt(node.dataset.screenRow)
+ }
+
+ buildLineNode (id) {
+ const {lineText, tagCodes, screenRow, decorationClasses} = this.newTileState.lines[id]
+
+ const lineNode = this.domElementPool.buildElement('div', 'line')
+ lineNode.dataset.screenRow = screenRow
+ if (decorationClasses != null) {
+ for (const decorationClass of decorationClasses) {
+ lineNode.classList.add(decorationClass)
+ }
+ }
+
+ const textNodes = []
+ let startIndex = 0
+ let openScopeNode = lineNode
+ for (const tagCode of tagCodes) {
+ if (tagCode !== 0) {
+ if (this.presenter.isCloseTagCode(tagCode)) {
+ openScopeNode = openScopeNode.parentElement
+ } else if (this.presenter.isOpenTagCode(tagCode)) {
+ const scope = this.presenter.tagForCode(tagCode)
+ const newScopeNode = this.domElementPool.buildElement('span', scope.replace(/\.+/g, ' '))
+ openScopeNode.appendChild(newScopeNode)
+ openScopeNode = newScopeNode
+ } else {
+ const textNode = this.domElementPool.buildText(lineText.substr(startIndex, tagCode))
+ startIndex += tagCode
+ openScopeNode.appendChild(textNode)
+ textNodes.push(textNode)
+ }
+ }
+ }
+
+ if (startIndex === 0) {
+ const textNode = this.domElementPool.buildText(' ')
+ lineNode.appendChild(textNode)
+ textNodes.push(textNode)
+ }
+
+ if (lineText.endsWith(this.presenter.displayLayer.foldCharacter)) {
+ // Insert a zero-width non-breaking whitespace, so that LinesYardstick can
+ // take the fold-marker::after pseudo-element into account during
+ // measurements when such marker is the last character on the line.
+ const textNode = this.domElementPool.buildText(ZERO_WIDTH_NBSP)
+ lineNode.appendChild(textNode)
+ textNodes.push(textNode)
+ }
+
+ this.textNodesByLineId[id] = textNodes
+ return lineNode
+ }
+
+ updateLineNode (id) {
+ const oldLineState = this.oldTileState.lines[id]
+ const newLineState = this.newTileState.lines[id]
+ const lineNode = this.lineNodesByLineId[id]
+ const newDecorationClasses = newLineState.decorationClasses
+ const oldDecorationClasses = oldLineState.decorationClasses
+
+ if (oldDecorationClasses != null) {
+ for (const decorationClass of oldDecorationClasses) {
+ if (newDecorationClasses == null || !newDecorationClasses.includes(decorationClass)) {
+ lineNode.classList.remove(decorationClass)
+ }
+ }
+ }
+
+ if (newDecorationClasses != null) {
+ for (const decorationClass of newDecorationClasses) {
+ if (oldDecorationClasses == null || !oldDecorationClasses.includes(decorationClass)) {
+ lineNode.classList.add(decorationClass)
+ }
+ }
+ }
+
+ oldLineState.decorationClasses = newLineState.decorationClasses
+
+ if (newLineState.screenRow !== oldLineState.screenRow) {
+ lineNode.dataset.screenRow = newLineState.screenRow
+ this.lineIdsByScreenRow[newLineState.screenRow] = id
+ this.screenRowsByLineId[id] = newLineState.screenRow
+ }
+
+ oldLineState.screenRow = newLineState.screenRow
+ }
+
+ removeDeletedBlockDecorations () {
+ for (const lineId of Object.keys(this.newTileState.lines)) {
+ const oldLineState = this.oldTileState.lines[lineId]
+ const newLineState = this.newTileState.lines[lineId]
+ for (const decorationId of Object.keys(oldLineState.precedingBlockDecorations)) {
+ if (!newLineState.precedingBlockDecorations.hasOwnProperty(decorationId)) {
+ const {topRulerNode, blockDecorationNode, bottomRulerNode} =
+ this.blockDecorationNodesByLineIdAndDecorationId[lineId][decorationId]
+ topRulerNode.remove()
+ blockDecorationNode.remove()
+ bottomRulerNode.remove()
+ delete this.blockDecorationNodesByLineIdAndDecorationId[lineId][decorationId]
+ delete oldLineState.precedingBlockDecorations[decorationId]
+ }
+ }
+ for (const decorationId of Object.keys(oldLineState.followingBlockDecorations)) {
+ if (!newLineState.followingBlockDecorations.hasOwnProperty(decorationId)) {
+ const {topRulerNode, blockDecorationNode, bottomRulerNode} =
+ this.blockDecorationNodesByLineIdAndDecorationId[lineId][decorationId]
+ topRulerNode.remove()
+ blockDecorationNode.remove()
+ bottomRulerNode.remove()
+ delete this.blockDecorationNodesByLineIdAndDecorationId[lineId][decorationId]
+ delete oldLineState.followingBlockDecorations[decorationId]
+ }
+ }
+ }
+ }
+
+ updateBlockDecorations () {
+ for (const lineId of Object.keys(this.newTileState.lines)) {
+ const oldLineState = this.oldTileState.lines[lineId]
+ const newLineState = this.newTileState.lines[lineId]
+ const lineNode = this.lineNodesByLineId[lineId]
+ if (!this.blockDecorationNodesByLineIdAndDecorationId.hasOwnProperty(lineId)) {
+ this.blockDecorationNodesByLineIdAndDecorationId[lineId] = {}
+ }
+ for (const decorationId of Object.keys(newLineState.precedingBlockDecorations)) {
+ const oldBlockDecorationState = oldLineState.precedingBlockDecorations[decorationId]
+ const newBlockDecorationState = newLineState.precedingBlockDecorations[decorationId]
+ if (oldBlockDecorationState != null) {
+ const {topRulerNode, blockDecorationNode, bottomRulerNode} =
+ this.blockDecorationNodesByLineIdAndDecorationId[lineId][decorationId]
+ if (oldBlockDecorationState.screenRow !== newBlockDecorationState.screenRow) {
+ topRulerNode.remove()
+ blockDecorationNode.remove()
+ bottomRulerNode.remove()
+ topRulerNode.dataset.screenRow = newBlockDecorationState.screenRow
+ this.domNode.insertBefore(topRulerNode, lineNode)
+ blockDecorationNode.dataset.screenRow = newBlockDecorationState.screenRow
+ this.domNode.insertBefore(blockDecorationNode, lineNode)
+ bottomRulerNode.dataset.screenRow = newBlockDecorationState.screenRow
+ this.domNode.insertBefore(bottomRulerNode, lineNode)
+ }
+ } else {
+ const topRulerNode = document.createElement('div')
+ topRulerNode.dataset.screenRow = newBlockDecorationState.screenRow
+ this.domNode.insertBefore(topRulerNode, lineNode)
+ const blockDecorationNode = this.views.getView(newBlockDecorationState.decoration.getProperties().item)
+ blockDecorationNode.dataset.screenRow = newBlockDecorationState.screenRow
+ this.domNode.insertBefore(blockDecorationNode, lineNode)
+ const bottomRulerNode = document.createElement('div')
+ bottomRulerNode.dataset.screenRow = newBlockDecorationState.screenRow
+ this.domNode.insertBefore(bottomRulerNode, lineNode)
+
+ this.blockDecorationNodesByLineIdAndDecorationId[lineId][decorationId] =
+ {topRulerNode, blockDecorationNode, bottomRulerNode}
+ }
+ oldLineState.precedingBlockDecorations[decorationId] = Object.assign({}, newBlockDecorationState)
+ }
+ for (const decorationId of Object.keys(newLineState.followingBlockDecorations)) {
+ const oldBlockDecorationState = oldLineState.followingBlockDecorations[decorationId]
+ const newBlockDecorationState = newLineState.followingBlockDecorations[decorationId]
+ if (oldBlockDecorationState != null) {
+ const {topRulerNode, blockDecorationNode, bottomRulerNode} =
+ this.blockDecorationNodesByLineIdAndDecorationId[lineId][decorationId]
+ if (oldBlockDecorationState.screenRow !== newBlockDecorationState.screenRow) {
+ topRulerNode.remove()
+ blockDecorationNode.remove()
+ bottomRulerNode.remove()
+ bottomRulerNode.dataset.screenRow = newBlockDecorationState.screenRow
+ this.domNode.insertBefore(bottomRulerNode, lineNode.nextSibling)
+ blockDecorationNode.dataset.screenRow = newBlockDecorationState.screenRow
+ this.domNode.insertBefore(blockDecorationNode, lineNode.nextSibling)
+ topRulerNode.dataset.screenRow = newBlockDecorationState.screenRow
+ this.domNode.insertBefore(topRulerNode, lineNode.nextSibling)
+ }
+ } else {
+ const bottomRulerNode = document.createElement('div')
+ bottomRulerNode.dataset.screenRow = newBlockDecorationState.screenRow
+ this.domNode.insertBefore(bottomRulerNode, lineNode.nextSibling)
+ const blockDecorationNode = this.views.getView(newBlockDecorationState.decoration.getProperties().item)
+ blockDecorationNode.dataset.screenRow = newBlockDecorationState.screenRow
+ this.domNode.insertBefore(blockDecorationNode, lineNode.nextSibling)
+ const topRulerNode = document.createElement('div')
+ topRulerNode.dataset.screenRow = newBlockDecorationState.screenRow
+ this.domNode.insertBefore(topRulerNode, lineNode.nextSibling)
+
+ this.blockDecorationNodesByLineIdAndDecorationId[lineId][decorationId] =
+ {topRulerNode, blockDecorationNode, bottomRulerNode}
+ }
+ oldLineState.followingBlockDecorations[decorationId] = Object.assign({}, newBlockDecorationState)
+ }
+ }
+ }
+
+ measureBlockDecorations () {
+ for (const lineId of Object.keys(this.newTileState.lines)) {
+ const newLineState = this.newTileState.lines[lineId]
+
+ for (const decorationId of Object.keys(newLineState.precedingBlockDecorations)) {
+ const {topRulerNode, blockDecorationNode, bottomRulerNode} =
+ this.blockDecorationNodesByLineIdAndDecorationId[lineId][decorationId]
+ const width = blockDecorationNode.offsetWidth
+ const height = bottomRulerNode.offsetTop - topRulerNode.offsetTop
+ const {decoration} = newLineState.precedingBlockDecorations[decorationId]
+ this.presenter.setBlockDecorationDimensions(decoration, width, height)
+ }
+ for (const decorationId of Object.keys(newLineState.followingBlockDecorations)) {
+ const {topRulerNode, blockDecorationNode, bottomRulerNode} =
+ this.blockDecorationNodesByLineIdAndDecorationId[lineId][decorationId]
+ const width = blockDecorationNode.offsetWidth
+ const height = bottomRulerNode.offsetTop - topRulerNode.offsetTop
+ const {decoration} = newLineState.followingBlockDecorations[decorationId]
+ this.presenter.setBlockDecorationDimensions(decoration, width, height)
+ }
+ }
+ }
+
+ lineNodeForScreenRow (screenRow) {
+ return this.lineNodesByLineId[this.lineIdsByScreenRow[screenRow]]
+ }
+
+ lineNodeForLineId (lineId) {
+ return this.lineNodesByLineId[lineId]
+ }
+
+ textNodesForLineId (lineId) {
+ return this.textNodesByLineId[lineId].slice()
+ }
+
+ lineIdForScreenRow (screenRow) {
+ return this.lineIdsByScreenRow[screenRow]
+ }
+
+ textNodesForScreenRow (screenRow) {
+ const textNodes = this.textNodesByLineId[this.lineIdsByScreenRow[screenRow]]
+ if (textNodes == null) {
+ return null
+ } else {
+ return textNodes.slice()
+ }
+ }
+}
diff --git a/src/lines-yardstick.coffee b/src/lines-yardstick.coffee
index 551733a55..d4979865c 100644
--- a/src/lines-yardstick.coffee
+++ b/src/lines-yardstick.coffee
@@ -13,19 +13,21 @@ class LinesYardstick
measuredRowForPixelPosition: (pixelPosition) ->
targetTop = pixelPosition.top
row = Math.floor(targetTop / @model.getLineHeightInPixels())
- row if 0 <= row <= @model.getLastScreenRow()
+ row if 0 <= row
screenPositionForPixelPosition: (pixelPosition) ->
targetTop = pixelPosition.top
- targetLeft = pixelPosition.left
- row = @lineTopIndex.rowForPixelPosition(targetTop)
- targetLeft = 0 if targetTop < 0 or targetLeft < 0
- targetLeft = Infinity if row > @model.getLastScreenRow()
- row = Math.min(row, @model.getLastScreenRow())
- row = Math.max(0, row)
-
+ row = Math.max(0, @lineTopIndex.rowForPixelPosition(targetTop))
lineNode = @lineNodesProvider.lineNodeForScreenRow(row)
- return Point(row, 0) unless lineNode
+ unless lineNode
+ lastScreenRow = @model.getLastScreenRow()
+ if row > lastScreenRow
+ return Point(lastScreenRow, @model.lineLengthForScreenRow(lastScreenRow))
+ else
+ return Point(row, 0)
+
+ targetLeft = pixelPosition.left
+ targetLeft = 0 if targetTop < 0 or targetLeft < 0
textNodes = @lineNodesProvider.textNodesForScreenRow(row)
lineOffset = lineNode.getBoundingClientRect().left
diff --git a/src/main-process/atom-application.coffee b/src/main-process/atom-application.coffee
index 21f138eed..aaceebffe 100644
--- a/src/main-process/atom-application.coffee
+++ b/src/main-process/atom-application.coffee
@@ -42,7 +42,7 @@ class AtomApplication
# take a few seconds to trigger 'error' event, it could be a bug of node
# or atom-shell, before it's fixed we check the existence of socketPath to
# speedup startup.
- if (process.platform isnt 'win32' and not fs.existsSync options.socketPath) or options.test
+ if (process.platform isnt 'win32' and not fs.existsSync options.socketPath) or options.test or options.benchmark or options.benchmarkTest
new AtomApplication(options).initialize(options)
return
@@ -64,7 +64,7 @@ class AtomApplication
constructor: (options) ->
{@resourcePath, @devResourcePath, @version, @devMode, @safeMode, @socketPath, @logFile, @setPortable, @userDataDir} = options
- @socketPath = null if options.test
+ @socketPath = null if options.test or options.benchmark or options.benchmarkTest
@pidsToOpenWindows = {}
@windows = []
@@ -86,7 +86,9 @@ class AtomApplication
@config.onDidChange 'core.useCustomTitleBar', @promptForRestart.bind(this)
- @autoUpdateManager = new AutoUpdateManager(@version, options.test, @resourcePath, @config)
+ @autoUpdateManager = new AutoUpdateManager(
+ @version, options.test or options.benchmark or options.benchmarkTest, @resourcePath, @config
+ )
@applicationMenu = new ApplicationMenu(@version, @autoUpdateManager)
@atomProtocolHandler = new AtomProtocolHandler(@resourcePath, @safeMode)
@@ -103,23 +105,41 @@ class AtomApplication
Promise.all(windowsClosePromises).then(=> @disposable.dispose())
launch: (options) ->
- if options.pathsToOpen?.length > 0 or options.urlsToOpen?.length > 0 or options.test
+ if options.pathsToOpen?.length > 0 or options.urlsToOpen?.length > 0 or options.test or options.benchmark or options.benchmarkTest
@openWithOptions(options)
else
@loadState(options) or @openPath(options)
- openWithOptions: ({initialPaths, pathsToOpen, executedFrom, urlsToOpen, test, pidToKillWhenClosed, devMode, safeMode, newWindow, logFile, profileStartup, timeout, clearWindowState, addToLastWindow, env}) ->
+ openWithOptions: (options) ->
+ {
+ initialPaths, pathsToOpen, executedFrom, urlsToOpen, benchmark,
+ benchmarkTest, test, pidToKillWhenClosed, devMode, safeMode, newWindow,
+ logFile, profileStartup, timeout, clearWindowState, addToLastWindow, env
+ } = options
+
app.focus()
if test
- @runTests({headless: true, devMode, @resourcePath, executedFrom, pathsToOpen, logFile, timeout, env})
+ @runTests({
+ headless: true, devMode, @resourcePath, executedFrom, pathsToOpen,
+ logFile, timeout, env
+ })
+ else if benchmark or benchmarkTest
+ @runBenchmarks({headless: true, test: benchmarkTest, @resourcePath, executedFrom, pathsToOpen, timeout, env})
else if pathsToOpen.length > 0
- @openPaths({initialPaths, pathsToOpen, executedFrom, pidToKillWhenClosed, newWindow, devMode, safeMode, profileStartup, clearWindowState, addToLastWindow, env})
+ @openPaths({
+ initialPaths, pathsToOpen, executedFrom, pidToKillWhenClosed, newWindow,
+ devMode, safeMode, profileStartup, clearWindowState, addToLastWindow, env
+ })
else if urlsToOpen.length > 0
- @openUrl({urlToOpen, devMode, safeMode, env}) for urlToOpen in urlsToOpen
+ for urlToOpen in urlsToOpen
+ @openUrl({urlToOpen, devMode, safeMode, env})
else
# Always open a editor window if this is the first instance of Atom.
- @openPath({initialPaths, pidToKillWhenClosed, newWindow, devMode, safeMode, profileStartup, clearWindowState, addToLastWindow, env})
+ @openPath({
+ initialPaths, pidToKillWhenClosed, newWindow, devMode, safeMode, profileStartup,
+ clearWindowState, addToLastWindow, env
+ })
# Public: Removes the {AtomWindow} from the global window list.
removeWindow: (window) ->
@@ -280,6 +300,9 @@ class AtomApplication
@disposable.add ipcHelpers.on ipcMain, 'run-package-specs', (event, packageSpecPath) =>
@runTests({resourcePath: @devResourcePath, pathsToOpen: [packageSpecPath], headless: false})
+ @disposable.add ipcHelpers.on ipcMain, 'run-benchmarks', (event, benchmarksPath) =>
+ @runBenchmarks({resourcePath: @devResourcePath, pathsToOpen: [benchmarksPath], headless: false, test: false})
+
@disposable.add ipcHelpers.on ipcMain, 'command', (event, command) =>
@emit(command)
@@ -651,6 +674,29 @@ class AtomApplication
safeMode ?= false
new AtomWindow(this, @fileRecoveryService, {windowInitializationScript, resourcePath, headless, isSpec, devMode, testRunnerPath, legacyTestRunnerPath, testPaths, logFile, safeMode, env})
+ runBenchmarks: ({headless, test, resourcePath, executedFrom, pathsToOpen, env}) ->
+ if resourcePath isnt @resourcePath and not fs.existsSync(resourcePath)
+ resourcePath = @resourcePath
+
+ try
+ windowInitializationScript = require.resolve(path.resolve(@devResourcePath, 'src', 'initialize-benchmark-window'))
+ catch error
+ windowInitializationScript = require.resolve(path.resolve(__dirname, '..', '..', 'src', 'initialize-benchmark-window'))
+
+ benchmarkPaths = []
+ if pathsToOpen?
+ for pathToOpen in pathsToOpen
+ benchmarkPaths.push(path.resolve(executedFrom, fs.normalize(pathToOpen)))
+
+ if benchmarkPaths.length is 0
+ process.stderr.write 'Error: Specify at least one benchmark path.\n\n'
+ process.exit(1)
+
+ devMode = true
+ isSpec = true
+ safeMode = false
+ new AtomWindow(this, @fileRecoveryService, {windowInitializationScript, resourcePath, headless, test, isSpec, devMode, benchmarkPaths, safeMode, env})
+
resolveTestRunnerPath: (testPath) ->
FindParentDir ?= require 'find-parent-dir'
diff --git a/src/main-process/main.js b/src/main-process/main.js
index 28871b661..7ccd1a6c3 100644
--- a/src/main-process/main.js
+++ b/src/main-process/main.js
@@ -19,7 +19,7 @@ if (args.resourcePath) {
const stableResourcePath = path.dirname(path.dirname(__dirname))
const defaultRepositoryPath = path.join(electron.app.getPath('home'), 'github', 'atom')
- if (args.dev || args.test) {
+ if (args.dev || args.test || args.benchmark || args.benchmarkTest) {
if (process.env.ATOM_DEV_RESOURCE_PATH) {
resourcePath = process.env.ATOM_DEV_RESOURCE_PATH
} else if (fs.statSyncNoException(defaultRepositoryPath)) {
diff --git a/src/main-process/parse-command-line.js b/src/main-process/parse-command-line.js
index 07b2b49f8..68a18fa30 100644
--- a/src/main-process/parse-command-line.js
+++ b/src/main-process/parse-command-line.js
@@ -45,6 +45,8 @@ module.exports = function parseCommandLine (processArgs) {
'portable',
'Set portable mode. Copies the ~/.atom folder to be a sibling of the installed Atom location if a .atom folder is not already there.'
)
+ options.boolean('benchmark').describe('benchmark', 'Open a new window that runs the specified benchmarks.')
+ options.boolean('benchmark-test').describe('benchmark--test', 'Run a faster version of the benchmarks in headless mode.')
options.alias('t', 'test').boolean('t').describe('t', 'Run the specified specs and exit with error code on failures.')
options.alias('m', 'main-process').boolean('m').describe('m', 'Run the specified specs in the main process.')
options.string('timeout').describe(
@@ -78,6 +80,8 @@ module.exports = function parseCommandLine (processArgs) {
const addToLastWindow = args['add']
const safeMode = args['safe']
const pathsToOpen = args._
+ const benchmark = args['benchmark']
+ const benchmarkTest = args['benchmark-test']
const test = args['test']
const mainProcess = args['main-process']
const timeout = args['timeout']
@@ -132,10 +136,29 @@ module.exports = function parseCommandLine (processArgs) {
devResourcePath = normalizeDriveLetterName(devResourcePath)
return {
- resourcePath, devResourcePath, pathsToOpen, urlsToOpen, executedFrom, test,
- version, pidToKillWhenClosed, devMode, safeMode, newWindow, logFile, socketPath,
- userDataDir, profileStartup, timeout, setPortable, clearWindowState,
- addToLastWindow, mainProcess, env: process.env
+ resourcePath,
+ devResourcePath,
+ pathsToOpen,
+ urlsToOpen,
+ executedFrom,
+ test,
+ version,
+ pidToKillWhenClosed,
+ devMode,
+ safeMode,
+ newWindow,
+ logFile,
+ socketPath,
+ userDataDir,
+ profileStartup,
+ timeout,
+ setPortable,
+ clearWindowState,
+ addToLastWindow,
+ mainProcess,
+ benchmark,
+ benchmarkTest,
+ env: process.env
}
}
diff --git a/src/main-process/start.js b/src/main-process/start.js
index 125f3ed54..84ae9b8c2 100644
--- a/src/main-process/start.js
+++ b/src/main-process/start.js
@@ -57,7 +57,7 @@ module.exports = function start (resourcePath, startTime) {
if (args.userDataDir != null) {
app.setPath('userData', args.userDataDir)
- } else if (args.test) {
+ } else if (args.test || args.benchmark || args.benchmarkTest) {
app.setPath('userData', temp.mkdirSync('atom-test-data'))
}
diff --git a/src/native-compile-cache.js b/src/native-compile-cache.js
index 50fa71fc3..a9857fc0c 100644
--- a/src/native-compile-cache.js
+++ b/src/native-compile-cache.js
@@ -45,7 +45,7 @@ class NativeCompileCache {
Module.prototype._compile = function (content, filename) {
let moduleSelf = this
// remove shebang
- content = content.replace(/^\#\!.*/, '')
+ content = content.replace(/^#!.*/, '')
function require (path) {
return moduleSelf.require(path)
}
diff --git a/src/null-grammar.js b/src/null-grammar.js
index 0ca3f83f1..fe9c3889e 100644
--- a/src/null-grammar.js
+++ b/src/null-grammar.js
@@ -2,12 +2,39 @@
import {Disposable} from 'event-kit'
-export default Object.freeze({
+export default {
name: 'Null Grammar',
- scopeName: 'text.plain',
+ scopeName: 'text.plain.null-grammar',
+ scopeForId (id) {
+ if (id === -1 || id === -2) {
+ return this.scopeName
+ } else {
+ return null
+ }
+ },
+ startIdForScope (scopeName) {
+ if (scopeName === this.scopeName) {
+ return -1
+ } else {
+ return null
+ }
+ },
+ endIdForScope (scopeName) {
+ if (scopeName === this.scopeName) {
+ return -2
+ } else {
+ return null
+ }
+ },
+ tokenizeLine (text) {
+ return {
+ tags: [this.startIdForScope(this.scopeName), text.length, this.endIdForScope(this.scopeName)],
+ ruleStack: null
+ }
+ },
onDidUpdate (callback) {
return new Disposable(noop)
}
-})
+}
function noop () {}
diff --git a/src/off-screen-block-decorations-component.js b/src/off-screen-block-decorations-component.js
new file mode 100644
index 000000000..0460c854e
--- /dev/null
+++ b/src/off-screen-block-decorations-component.js
@@ -0,0 +1,62 @@
+module.exports = class OffScreenBlockDecorationsComponent {
+ constructor ({presenter, views}) {
+ this.presenter = presenter
+ this.views = views
+ this.newState = {offScreenBlockDecorations: {}, width: 0}
+ this.oldState = {offScreenBlockDecorations: {}, width: 0}
+ this.domNode = document.createElement('div')
+ this.domNode.style.visibility = 'hidden'
+ this.domNode.style.position = 'absolute'
+ this.blockDecorationNodesById = {}
+ }
+
+ getDomNode () {
+ return this.domNode
+ }
+
+ updateSync (state) {
+ this.newState = state.content
+
+ if (this.newState.width !== this.oldState.width) {
+ this.domNode.style.width = `${this.newState.width}px`
+ this.oldState.width = this.newState.width
+ }
+
+ for (const id of Object.keys(this.oldState.offScreenBlockDecorations)) {
+ if (!this.newState.offScreenBlockDecorations.hasOwnProperty(id)) {
+ const {topRuler, blockDecoration, bottomRuler} = this.blockDecorationNodesById[id]
+ topRuler.remove()
+ blockDecoration.remove()
+ bottomRuler.remove()
+ delete this.blockDecorationNodesById[id]
+ delete this.oldState.offScreenBlockDecorations[id]
+ }
+ }
+
+ for (const id of Object.keys(this.newState.offScreenBlockDecorations)) {
+ const decoration = this.newState.offScreenBlockDecorations[id]
+ if (!this.oldState.offScreenBlockDecorations.hasOwnProperty(id)) {
+ const topRuler = document.createElement('div')
+ this.domNode.appendChild(topRuler)
+ const blockDecoration = this.views.getView(decoration.getProperties().item)
+ this.domNode.appendChild(blockDecoration)
+ const bottomRuler = document.createElement('div')
+ this.domNode.appendChild(bottomRuler)
+
+ this.blockDecorationNodesById[id] = {topRuler, blockDecoration, bottomRuler}
+ }
+
+ this.oldState.offScreenBlockDecorations[id] = decoration
+ }
+ }
+
+ measureBlockDecorations () {
+ for (const id of Object.keys(this.blockDecorationNodesById)) {
+ const {topRuler, blockDecoration, bottomRuler} = this.blockDecorationNodesById[id]
+ const width = blockDecoration.offsetWidth
+ const height = bottomRuler.offsetTop - topRuler.offsetTop
+ const decoration = this.newState.offScreenBlockDecorations[id]
+ this.presenter.setBlockDecorationDimensions(decoration, width, height)
+ }
+ }
+}
diff --git a/src/register-default-commands.coffee b/src/register-default-commands.coffee
index 3ba50d497..8196d9237 100644
--- a/src/register-default-commands.coffee
+++ b/src/register-default-commands.coffee
@@ -54,6 +54,7 @@ module.exports = ({commandRegistry, commandInstaller, config, notificationManage
'application:open-your-stylesheet': -> ipcRenderer.send('command', 'application:open-your-stylesheet')
'application:open-license': -> @getModel().openLicense()
'window:run-package-specs': -> @runPackageSpecs()
+ 'window:run-benchmarks': -> @runBenchmarks()
'window:focus-next-pane': -> @getModel().activateNextPane()
'window:focus-previous-pane': -> @getModel().activatePreviousPane()
'window:focus-pane-above': -> @focusPaneViewAbove()
diff --git a/src/style-manager.coffee b/src/style-manager.coffee
deleted file mode 100644
index 8f932d229..000000000
--- a/src/style-manager.coffee
+++ /dev/null
@@ -1,177 +0,0 @@
-fs = require 'fs-plus'
-path = require 'path'
-{Emitter, Disposable} = require 'event-kit'
-StylesElement = require './styles-element'
-
-# Extended: A singleton instance of this class available via `atom.styles`,
-# which you can use to globally query and observe the set of active style
-# sheets. The `StyleManager` doesn't add any style elements to the DOM on its
-# own, but is instead subscribed to by individual `
` elements,
-# which clone and attach style elements in different contexts.
-module.exports =
-class StyleManager
- constructor: ({@configDirPath}) ->
- @emitter = new Emitter
- @styleElements = []
- @styleElementsBySourcePath = {}
-
- ###
- Section: Event Subscription
- ###
-
- # Extended: Invoke `callback` for all current and future style elements.
- #
- # * `callback` {Function} that is called with style elements.
- # * `styleElement` An `HTMLStyleElement` instance. The `.sheet` property
- # will be null because this element isn't attached to the DOM. If you want
- # to attach this element to the DOM, be sure to clone it first by calling
- # `.cloneNode(true)` on it. The style element will also have the following
- # non-standard properties:
- # * `sourcePath` A {String} containing the path from which the style
- # element was loaded.
- # * `context` A {String} indicating the target context of the style
- # element.
- #
- # Returns a {Disposable} on which `.dispose()` can be called to cancel the
- # subscription.
- observeStyleElements: (callback) ->
- callback(styleElement) for styleElement in @getStyleElements()
- @onDidAddStyleElement(callback)
-
- # Extended: Invoke `callback` when a style element is added.
- #
- # * `callback` {Function} that is called with style elements.
- # * `styleElement` An `HTMLStyleElement` instance. The `.sheet` property
- # will be null because this element isn't attached to the DOM. If you want
- # to attach this element to the DOM, be sure to clone it first by calling
- # `.cloneNode(true)` on it. The style element will also have the following
- # non-standard properties:
- # * `sourcePath` A {String} containing the path from which the style
- # element was loaded.
- # * `context` A {String} indicating the target context of the style
- # element.
- #
- # Returns a {Disposable} on which `.dispose()` can be called to cancel the
- # subscription.
- onDidAddStyleElement: (callback) ->
- @emitter.on 'did-add-style-element', callback
-
- # Extended: Invoke `callback` when a style element is removed.
- #
- # * `callback` {Function} that is called with style elements.
- # * `styleElement` An `HTMLStyleElement` instance.
- #
- # Returns a {Disposable} on which `.dispose()` can be called to cancel the
- # subscription.
- onDidRemoveStyleElement: (callback) ->
- @emitter.on 'did-remove-style-element', callback
-
- # Extended: Invoke `callback` when an existing style element is updated.
- #
- # * `callback` {Function} that is called with style elements.
- # * `styleElement` An `HTMLStyleElement` instance. The `.sheet` property
- # will be null because this element isn't attached to the DOM. The style
- # element will also have the following non-standard properties:
- # * `sourcePath` A {String} containing the path from which the style
- # element was loaded.
- # * `context` A {String} indicating the target context of the style
- # element.
- #
- # Returns a {Disposable} on which `.dispose()` can be called to cancel the
- # subscription.
- onDidUpdateStyleElement: (callback) ->
- @emitter.on 'did-update-style-element', callback
-
- ###
- Section: Reading Style Elements
- ###
-
- # Extended: Get all loaded style elements.
- getStyleElements: ->
- @styleElements.slice()
-
- addStyleSheet: (source, params) ->
- sourcePath = params?.sourcePath
- context = params?.context
- priority = params?.priority
-
- if sourcePath? and styleElement = @styleElementsBySourcePath[sourcePath]
- updated = true
- else
- styleElement = document.createElement('style')
- if sourcePath?
- styleElement.sourcePath = sourcePath
- styleElement.setAttribute('source-path', sourcePath)
-
- if context?
- styleElement.context = context
- styleElement.setAttribute('context', context)
-
- if priority?
- styleElement.priority = priority
- styleElement.setAttribute('priority', priority)
-
- styleElement.textContent = source
-
- if updated
- @emitter.emit 'did-update-style-element', styleElement
- else
- @addStyleElement(styleElement)
-
- new Disposable => @removeStyleElement(styleElement)
-
- addStyleElement: (styleElement) ->
- {sourcePath, priority} = styleElement
-
- if priority?
- for existingElement, index in @styleElements
- if existingElement.priority > priority
- insertIndex = index
- break
-
- insertIndex ?= @styleElements.length
-
- @styleElements.splice(insertIndex, 0, styleElement)
- @styleElementsBySourcePath[sourcePath] ?= styleElement if sourcePath?
- @emitter.emit 'did-add-style-element', styleElement
-
- removeStyleElement: (styleElement) ->
- index = @styleElements.indexOf(styleElement)
- unless index is -1
- @styleElements.splice(index, 1)
- delete @styleElementsBySourcePath[styleElement.sourcePath] if styleElement.sourcePath?
- @emitter.emit 'did-remove-style-element', styleElement
-
- getSnapshot: ->
- @styleElements.slice()
-
- restoreSnapshot: (styleElementsToRestore) ->
- for styleElement in @getStyleElements()
- @removeStyleElement(styleElement) unless styleElement in styleElementsToRestore
-
- existingStyleElements = @getStyleElements()
- for styleElement in styleElementsToRestore
- @addStyleElement(styleElement) unless styleElement in existingStyleElements
-
- return
-
- buildStylesElement: ->
- stylesElement = new StylesElement
- stylesElement.initialize(this)
- stylesElement
-
- ###
- Section: Paths
- ###
-
- # Extended: Get the path of the user style sheet in `~/.atom`.
- #
- # Returns a {String}.
- getUserStyleSheetPath: ->
- return "" unless @configDirPath?
-
- stylesheetPath = fs.resolve(path.join(@configDirPath, 'styles'), ['css', 'less'])
- if fs.isFileSync(stylesheetPath)
- stylesheetPath
- else
- path.join(@configDirPath, 'styles.less')
diff --git a/src/style-manager.js b/src/style-manager.js
new file mode 100644
index 000000000..b273f449b
--- /dev/null
+++ b/src/style-manager.js
@@ -0,0 +1,307 @@
+const {Emitter, Disposable} = require('event-kit')
+const crypto = require('crypto')
+const fs = require('fs-plus')
+const path = require('path')
+const postcss = require('postcss')
+const selectorParser = require('postcss-selector-parser')
+const StylesElement = require('./styles-element')
+const DEPRECATED_SYNTAX_SELECTORS = require('./deprecated-syntax-selectors')
+
+// Extended: A singleton instance of this class available via `atom.styles`,
+// which you can use to globally query and observe the set of active style
+// sheets. The `StyleManager` doesn't add any style elements to the DOM on its
+// own, but is instead subscribed to by individual `` elements,
+// which clone and attach style elements in different contexts.
+module.exports = class StyleManager {
+ constructor ({configDirPath}) {
+ this.configDirPath = configDirPath
+ if (this.configDirPath != null) {
+ this.cacheDirPath = path.join(this.configDirPath, 'compile-cache', 'style-manager')
+ }
+ this.emitter = new Emitter()
+ this.styleElements = []
+ this.styleElementsBySourcePath = {}
+ this.deprecationsBySourcePath = {}
+ }
+
+ /*
+ Section: Event Subscription
+ */
+
+ // Extended: Invoke `callback` for all current and future style elements.
+ //
+ // * `callback` {Function} that is called with style elements.
+ // * `styleElement` An `HTMLStyleElement` instance. The `.sheet` property
+ // will be null because this element isn't attached to the DOM. If you want
+ // to attach this element to the DOM, be sure to clone it first by calling
+ // `.cloneNode(true)` on it. The style element will also have the following
+ // non-standard properties:
+ // * `sourcePath` A {String} containing the path from which the style
+ // element was loaded.
+ // * `context` A {String} indicating the target context of the style
+ // element.
+ //
+ // Returns a {Disposable} on which `.dispose()` can be called to cancel the
+ // subscription.
+ observeStyleElements (callback) {
+ for (let styleElement of this.getStyleElements()) {
+ callback(styleElement)
+ }
+
+ return this.onDidAddStyleElement(callback)
+ }
+
+ // Extended: Invoke `callback` when a style element is added.
+ //
+ // * `callback` {Function} that is called with style elements.
+ // * `styleElement` An `HTMLStyleElement` instance. The `.sheet` property
+ // will be null because this element isn't attached to the DOM. If you want
+ // to attach this element to the DOM, be sure to clone it first by calling
+ // `.cloneNode(true)` on it. The style element will also have the following
+ // non-standard properties:
+ // * `sourcePath` A {String} containing the path from which the style
+ // element was loaded.
+ // * `context` A {String} indicating the target context of the style
+ // element.
+ //
+ // Returns a {Disposable} on which `.dispose()` can be called to cancel the
+ // subscription.
+ onDidAddStyleElement (callback) {
+ return this.emitter.on('did-add-style-element', callback)
+ }
+
+ // Extended: Invoke `callback` when a style element is removed.
+ //
+ // * `callback` {Function} that is called with style elements.
+ // * `styleElement` An `HTMLStyleElement` instance.
+ //
+ // Returns a {Disposable} on which `.dispose()` can be called to cancel the
+ // subscription.
+ onDidRemoveStyleElement (callback) {
+ return this.emitter.on('did-remove-style-element', callback)
+ }
+
+ // Extended: Invoke `callback` when an existing style element is updated.
+ //
+ // * `callback` {Function} that is called with style elements.
+ // * `styleElement` An `HTMLStyleElement` instance. The `.sheet` property
+ // will be null because this element isn't attached to the DOM. The style
+ // element will also have the following non-standard properties:
+ // * `sourcePath` A {String} containing the path from which the style
+ // element was loaded.
+ // * `context` A {String} indicating the target context of the style
+ // element.
+ //
+ // Returns a {Disposable} on which `.dispose()` can be called to cancel the
+ // subscription.
+ onDidUpdateStyleElement (callback) {
+ return this.emitter.on('did-update-style-element', callback)
+ }
+
+ onDidUpdateDeprecations (callback) {
+ return this.emitter.on('did-update-deprecations', callback)
+ }
+
+ /*
+ Section: Reading Style Elements
+ */
+
+ // Extended: Get all loaded style elements.
+ getStyleElements () {
+ return this.styleElements.slice()
+ }
+
+ addStyleSheet (source, params = {}) {
+ let styleElement
+ let updated
+ if (params.sourcePath != null && this.styleElementsBySourcePath[params.sourcePath] != null) {
+ updated = true
+ styleElement = this.styleElementsBySourcePath[params.sourcePath]
+ } else {
+ updated = false
+ styleElement = document.createElement('style')
+ if (params.sourcePath != null) {
+ styleElement.sourcePath = params.sourcePath
+ styleElement.setAttribute('source-path', params.sourcePath)
+ }
+ if (params.context != null) {
+ styleElement.context = params.context
+ styleElement.setAttribute('context', params.context)
+ }
+ if (params.priority != null) {
+ styleElement.priority = params.priority
+ styleElement.setAttribute('priority', params.priority)
+ }
+ }
+
+ let transformed
+ if (this.cacheDirPath != null) {
+ const hash = crypto.createHash('sha1')
+ if (params.context != null) {
+ hash.update(params.context)
+ }
+ hash.update(source)
+ const cacheFilePath = path.join(this.cacheDirPath, hash.digest('hex'))
+ try {
+ transformed = JSON.parse(fs.readFileSync(cacheFilePath))
+ } catch (e) {
+ transformed = transformDeprecatedShadowDOMSelectors(source, params.context)
+ fs.writeFileSync(cacheFilePath, JSON.stringify(transformed))
+ }
+ } else {
+ transformed = transformDeprecatedShadowDOMSelectors(source, params.context)
+ }
+
+ styleElement.textContent = transformed.source
+ if (transformed.deprecationMessage) {
+ this.deprecationsBySourcePath[params.sourcePath] = {message: transformed.deprecationMessage}
+ this.emitter.emit('did-update-deprecations')
+ }
+ if (updated) {
+ this.emitter.emit('did-update-style-element', styleElement)
+ } else {
+ this.addStyleElement(styleElement)
+ }
+ return new Disposable(() => { this.removeStyleElement(styleElement) })
+ }
+
+ addStyleElement (styleElement) {
+ let insertIndex = this.styleElements.length
+ if (styleElement.priority != null) {
+ for (let [index, existingElement] of this.styleElements.entries()) {
+ if (existingElement.priority > styleElement.priority) {
+ insertIndex = index
+ break
+ }
+ }
+ }
+
+ this.styleElements.splice(insertIndex, 0, styleElement)
+ if (styleElement.sourcePath != null && this.styleElementsBySourcePath[styleElement.sourcePath] == null) {
+ this.styleElementsBySourcePath[styleElement.sourcePath] = styleElement
+ }
+ this.emitter.emit('did-add-style-element', styleElement)
+ }
+
+ removeStyleElement (styleElement) {
+ const index = this.styleElements.indexOf(styleElement)
+ if (index !== -1) {
+ this.styleElements.splice(index, 1)
+ if (styleElement.sourcePath != null) {
+ delete this.styleElementsBySourcePath[styleElement.sourcePath]
+ }
+ this.emitter.emit('did-remove-style-element', styleElement)
+ }
+ }
+
+ getDeprecations () {
+ return this.deprecationsBySourcePath
+ }
+
+ clearDeprecations () {
+ this.deprecationsBySourcePath = {}
+ }
+
+ getSnapshot () {
+ return this.styleElements.slice()
+ }
+
+ restoreSnapshot (styleElementsToRestore) {
+ for (let styleElement of this.getStyleElements()) {
+ if (!styleElementsToRestore.includes(styleElement)) {
+ this.removeStyleElement(styleElement)
+ }
+ }
+
+ const existingStyleElements = this.getStyleElements()
+ for (let styleElement of styleElementsToRestore) {
+ if (!existingStyleElements.includes(styleElement)) {
+ this.addStyleElement(styleElement)
+ }
+ }
+ }
+
+ buildStylesElement () {
+ var stylesElement = new StylesElement()
+ stylesElement.initialize(this)
+ return stylesElement
+ }
+
+ /*
+ Section: Paths
+ */
+
+ // Extended: Get the path of the user style sheet in `~/.atom`.
+ //
+ // Returns a {String}.
+ getUserStyleSheetPath () {
+ if (this.configDirPath == null) {
+ return ''
+ } else {
+ const stylesheetPath = fs.resolve(path.join(this.configDirPath, 'styles'), ['css', 'less'])
+ if (fs.isFileSync(stylesheetPath)) {
+ return stylesheetPath
+ } else {
+ return path.join(this.configDirPath, 'styles.less')
+ }
+ }
+ }
+}
+
+function transformDeprecatedShadowDOMSelectors (css, context) {
+ const transformedSelectors = []
+ const transformedSource = postcss.parse(css)
+ transformedSource.walkRules((rule) => {
+ const transformedSelector = selectorParser((selectors) => {
+ selectors.each((selector) => {
+ const firstNode = selector.nodes[0]
+ if (context === 'atom-text-editor' && firstNode.type === 'pseudo' && firstNode.value === ':host') {
+ const atomTextEditorElementNode = selectorParser.tag({value: 'atom-text-editor'})
+ firstNode.replaceWith(atomTextEditorElementNode)
+ }
+
+ let previousNodeIsAtomTextEditor = false
+ let targetsAtomTextEditorShadow = context === 'atom-text-editor'
+ let previousNode
+ selector.each((node) => {
+ if (targetsAtomTextEditorShadow && node.type === 'class') {
+ if (DEPRECATED_SYNTAX_SELECTORS.has(node.value)) {
+ node.value = `syntax--${node.value}`
+ }
+ } else {
+ if (previousNodeIsAtomTextEditor && node.type === 'pseudo' && node.value === '::shadow') {
+ selector.removeChild(node)
+ targetsAtomTextEditorShadow = true
+ }
+ }
+
+ previousNode = node
+ if (node.type === 'combinator') {
+ previousNodeIsAtomTextEditor = false
+ } else if (previousNode.type === 'tag' && previousNode.value === 'atom-text-editor') {
+ previousNodeIsAtomTextEditor = true
+ }
+ })
+ })
+ }).process(rule.selector, {lossless: true}).result
+ if (transformedSelector !== rule.selector) {
+ transformedSelectors.push({before: rule.selector, after: transformedSelector})
+ rule.selector = transformedSelector
+ }
+ })
+ let deprecationMessage
+ if (transformedSelectors.length > 0) {
+ deprecationMessage = 'Starting from Atom v1.13.0, the contents of `atom-text-editor` elements '
+ deprecationMessage += 'are no longer encapsulated within a shadow DOM boundary. '
+ deprecationMessage += 'This means you should stop using `:host` and `::shadow` '
+ deprecationMessage += 'pseudo-selectors, and prepend all your syntax selectors with `syntax--`. '
+ deprecationMessage += 'To prevent breakage with existing style sheets, Atom will automatically '
+ deprecationMessage += 'upgrade the following selectors:\n\n'
+ deprecationMessage += transformedSelectors
+ .map((selector) => `* \`${selector.before}\` => \`${selector.after}\``)
+ .join('\n\n') + '\n\n'
+ deprecationMessage += 'Automatic translation of selectors will be removed in a few release cycles to minimize startup time. '
+ deprecationMessage += 'Please, make sure to upgrade the above selectors as soon as possible.'
+ }
+ return {source: transformedSource.toString(), deprecationMessage}
+}
diff --git a/src/styles-element.coffee b/src/styles-element.coffee
index d1e6bf3d9..2c53300c2 100644
--- a/src/styles-element.coffee
+++ b/src/styles-element.coffee
@@ -19,10 +19,6 @@ class StylesElement extends HTMLElement
@styleElementClonesByOriginalElement = new WeakMap
attachedCallback: ->
- if @context is 'atom-text-editor'
- for styleElement in @children
- @upgradeDeprecatedSelectors(styleElement)
-
@context = @getAttribute('context') ? undefined
detachedCallback: ->
@@ -64,10 +60,6 @@ class StylesElement extends HTMLElement
break
@insertBefore(styleElementClone, insertBefore)
-
- if @context is 'atom-text-editor'
- @upgradeDeprecatedSelectors(styleElementClone)
-
@emitter.emit 'did-add-style-element', styleElementClone
styleElementRemoved: (styleElement) ->
@@ -87,31 +79,4 @@ class StylesElement extends HTMLElement
styleElementMatchesContext: (styleElement) ->
not @context? or styleElement.context is @context
- upgradeDeprecatedSelectors: (styleElement) ->
- return unless styleElement.sheet?
-
- upgradedSelectors = []
-
- for rule in styleElement.sheet.cssRules
- continue unless rule.selectorText?
- continue if /\:host/.test(rule.selectorText)
-
- inputSelector = rule.selectorText
- outputSelector = rule.selectorText
- .replace(/\.editor-colors($|[ >])/g, ':host$1')
- .replace(/\.editor([:.][^ ,>]+)/g, ':host($1)')
- .replace(/\.editor($|[ ,>])/g, ':host$1')
-
- unless inputSelector is outputSelector
- rule.selectorText = outputSelector
- upgradedSelectors.push({inputSelector, outputSelector})
-
- if upgradedSelectors.length > 0
- warning = "Upgraded the following syntax theme selectors in `#{styleElement.sourcePath}` for shadow DOM compatibility:\n\n"
- for {inputSelector, outputSelector} in upgradedSelectors
- warning += "`#{inputSelector}` => `#{outputSelector}`\n"
-
- warning += "\nSee the upgrade guide for information on removing this warning."
- console.warn(warning)
-
module.exports = StylesElement = document.registerElement 'atom-styles', prototype: StylesElement.prototype
diff --git a/src/text-editor-component.coffee b/src/text-editor-component.coffee
index d2647d767..cccd2f4c8 100644
--- a/src/text-editor-component.coffee
+++ b/src/text-editor-component.coffee
@@ -8,12 +8,12 @@ TextEditorPresenter = require './text-editor-presenter'
GutterContainerComponent = require './gutter-container-component'
InputComponent = require './input-component'
LinesComponent = require './lines-component'
+OffScreenBlockDecorationsComponent = require './off-screen-block-decorations-component'
ScrollbarComponent = require './scrollbar-component'
ScrollbarCornerComponent = require './scrollbar-corner-component'
OverlayManager = require './overlay-manager'
DOMElementPool = require './dom-element-pool'
LinesYardstick = require './lines-yardstick'
-BlockDecorationsComponent = require './block-decorations-component'
LineTopIndex = require 'line-top-index'
module.exports =
@@ -42,7 +42,7 @@ class TextEditorComponent
@assert domNode?, "TextEditorComponent::domNode was set to null."
@domNodeValue = domNode
- constructor: ({@editor, @hostElement, @rootElement, @stylesElement, tileSize, @views, @themes, @assert}) ->
+ constructor: ({@editor, @hostElement, tileSize, @views, @themes, @styles, @assert}) ->
@tileSize = tileSize if tileSize?
@disposables = new CompositeDisposable
@@ -64,11 +64,7 @@ class TextEditorComponent
@domNode = document.createElement('div')
@domNode.classList.add('editor-contents--private')
- insertionPoint = document.createElement('content')
- insertionPoint.setAttribute('select', 'atom-overlay')
- @domNode.appendChild(insertionPoint)
- @overlayManager = new OverlayManager(@presenter, @hostElement, @views)
- @blockDecorationsComponent = new BlockDecorationsComponent(@hostElement, @views, @presenter, @domElementPool)
+ @overlayManager = new OverlayManager(@presenter, @domNode, @views)
@scrollViewNode = document.createElement('div')
@scrollViewNode.classList.add('scroll-view')
@@ -77,11 +73,11 @@ class TextEditorComponent
@hiddenInputComponent = new InputComponent
@scrollViewNode.appendChild(@hiddenInputComponent.getDomNode())
- @linesComponent = new LinesComponent({@presenter, @hostElement, @domElementPool, @assert, @grammars})
+ @linesComponent = new LinesComponent({@presenter, @domElementPool, @assert, @grammars, @views})
@scrollViewNode.appendChild(@linesComponent.getDomNode())
- if @blockDecorationsComponent?
- @linesComponent.getDomNode().appendChild(@blockDecorationsComponent.getDomNode())
+ @offScreenBlockDecorationsComponent = new OffScreenBlockDecorationsComponent({@presenter, @views})
+ @scrollViewNode.appendChild(@offScreenBlockDecorationsComponent.getDomNode())
@linesYardstick = new LinesYardstick(@editor, @linesComponent, lineTopIndex)
@presenter.setLinesYardstick(@linesYardstick)
@@ -98,9 +94,9 @@ class TextEditorComponent
@observeEditor()
@listenForDOMEvents()
- @disposables.add @stylesElement.onDidAddStyleElement @onStylesheetsChanged
- @disposables.add @stylesElement.onDidUpdateStyleElement @onStylesheetsChanged
- @disposables.add @stylesElement.onDidRemoveStyleElement @onStylesheetsChanged
+ @disposables.add @styles.onDidAddStyleElement @onStylesheetsChanged
+ @disposables.add @styles.onDidUpdateStyleElement @onStylesheetsChanged
+ @disposables.add @styles.onDidRemoveStyleElement @onStylesheetsChanged
unless @themes.isInitialLoadComplete()
@disposables.add @themes.onDidChangeActiveThemes @onAllThemesLoaded
@disposables.add scrollbarStyle.onDidChangePreferredScrollbarStyle @refreshScrollbars
@@ -165,8 +161,8 @@ class TextEditorComponent
@gutterContainerComponent = null
@hiddenInputComponent.updateSync(@newState)
+ @offScreenBlockDecorationsComponent.updateSync(@newState)
@linesComponent.updateSync(@newState)
- @blockDecorationsComponent?.updateSync(@newState)
@horizontalScrollbarComponent.updateSync(@newState)
@verticalScrollbarComponent.updateSync(@newState)
@scrollbarCornerComponent.updateSync(@newState)
@@ -186,7 +182,8 @@ class TextEditorComponent
readAfterUpdateSync: =>
@overlayManager?.measureOverlays()
- @blockDecorationsComponent?.measureBlockDecorations() if @isVisible()
+ @linesComponent.measureBlockDecorations()
+ @offScreenBlockDecorationsComponent.measureBlockDecorations()
mountGutterContainerComponent: ->
@gutterContainerComponent = new GutterContainerComponent({@editor, @onLineNumberGutterMouseDown, @domElementPool, @views})
@@ -969,9 +966,7 @@ class TextEditorComponent
updateParentViewFocusedClassIfNeeded: ->
if @oldState.focused isnt @newState.focused
@hostElement.classList.toggle('is-focused', @newState.focused)
- @rootElement.classList.toggle('is-focused', @newState.focused)
@oldState.focused = @newState.focused
updateParentViewMiniClass: ->
@hostElement.classList.toggle('mini', @editor.isMini())
- @rootElement.classList.toggle('mini', @editor.isMini())
diff --git a/src/text-editor-element.coffee b/src/text-editor-element.coffee
index 27a29bed6..4a7d1598d 100644
--- a/src/text-editor-element.coffee
+++ b/src/text-editor-element.coffee
@@ -1,9 +1,7 @@
+Grim = require 'grim'
{Emitter, CompositeDisposable} = require 'event-kit'
TextBuffer = require 'text-buffer'
TextEditorComponent = require './text-editor-component'
-StylesElement = require './styles-element'
-
-ShadowStyleSheet = null
class TextEditorElement extends HTMLElement
model: null
@@ -14,6 +12,7 @@ class TextEditorElement extends HTMLElement
focusOnAttach: false
hasTiledRendering: true
logicalDisplayBuffer: true
+ lightDOM: true
createdCallback: ->
# Use globals when the following instance variables aren't set.
@@ -33,22 +32,18 @@ class TextEditorElement extends HTMLElement
@setAttribute('tabindex', -1)
initializeContent: (attributes) ->
- unless ShadowStyleSheet?
- ShadowStyleSheet = document.createElement('style')
- ShadowStyleSheet.textContent = @themes.loadLessStylesheet(require.resolve('../static/text-editor-shadow.less'))
-
- @createShadowRoot()
-
- @shadowRoot.appendChild(ShadowStyleSheet.cloneNode(true))
- @stylesElement = new StylesElement
- @stylesElement.initialize(@styles)
- @stylesElement.setAttribute('context', 'atom-text-editor')
-
+ Object.defineProperty(this, 'shadowRoot', {
+ get: =>
+ Grim.deprecate("""
+ The contents of `atom-text-editor` elements are no longer encapsulated
+ within a shadow DOM boundary. Please, stop using `shadowRoot` and access
+ the editor contents directly instead.
+ """)
+ this
+ })
@rootElement = document.createElement('div')
@rootElement.classList.add('editor--private')
-
- @shadowRoot.appendChild(@stylesElement)
- @shadowRoot.appendChild(@rootElement)
+ @appendChild(@rootElement)
attachedCallback: ->
@buildModel() unless @getModel()?
@@ -56,7 +51,7 @@ class TextEditorElement extends HTMLElement
@mountComponent() unless @component?
@listenForComponentEvents()
@component.checkForVisibilityChange()
- if this is document.activeElement
+ if @hasFocus()
@focused()
@emitter.emit("did-attach")
@@ -116,18 +111,18 @@ class TextEditorElement extends HTMLElement
mountComponent: ->
@component = new TextEditorComponent(
hostElement: this
- rootElement: @rootElement
- stylesElement: @stylesElement
editor: @model
tileSize: @tileSize
views: @views
themes: @themes
+ styles: @styles
workspace: @workspace
assert: @assert
)
@rootElement.appendChild(@component.getDomNode())
-
- @shadowRoot.addEventListener('blur', @shadowRootBlurred.bind(this), true)
+ inputNode = @component.hiddenInputComponent.getDomNode()
+ inputNode.addEventListener 'focus', @focused.bind(this)
+ inputNode.addEventListener 'blur', @inputNodeBlurred.bind(this)
unmountComponent: ->
if @component?
@@ -135,21 +130,18 @@ class TextEditorElement extends HTMLElement
@component.getDomNode().remove()
@component = null
- focused: ->
+ focused: (event) ->
@component?.focused()
blurred: (event) ->
+ if event.relatedTarget is @component?.hiddenInputComponent.getDomNode()
+ event.stopImmediatePropagation()
+ return
@component?.blurred()
- # Work around what seems to be a bug in Chromium. Focus can be stolen from the
- # hidden input when clicking on the gutter and transferred to the
- # already-focused host element. The host element never gets a 'focus' event
- # however, which leaves us in a limbo state where the text editor element is
- # focused but the hidden input isn't focused. This always refocuses the hidden
- # input if a blur event occurs in the shadow DOM that is transferring focus
- # back to the host element.
- shadowRootBlurred: (event) ->
- @component.focused() if event.relatedTarget is this
+ inputNodeBlurred: (event) ->
+ if event.relatedTarget isnt this
+ @dispatchEvent(new FocusEvent('blur', bubbles: false))
addGrammarScopeAttribute: ->
@dataset.grammar = @model.getGrammar()?.scopeName?.replace(/\./g, ' ')
diff --git a/src/text-editor-presenter.coffee b/src/text-editor-presenter.coffee
index 2fba9ab80..22268af18 100644
--- a/src/text-editor-presenter.coffee
+++ b/src/text-editor-presenter.coffee
@@ -35,7 +35,10 @@ class TextEditorPresenter
@observedBlockDecorations = new Set()
@invalidatedDimensionsByBlockDecoration = new Set()
@invalidateAllBlockDecorationsDimensions = false
+ @precedingBlockDecorationsByScreenRowAndId = {}
+ @followingBlockDecorationsByScreenRowAndId = {}
@screenRowsToMeasure = []
+ @flashCountsByDecorationId = {}
@transferMeasurementsToModel()
@transferMeasurementsFromModel()
@observeModel()
@@ -138,6 +141,11 @@ class TextEditorPresenter
@shouldUpdateDecorations = true
observeModel: ->
+ @disposables.add @model.displayLayer.onDidReset =>
+ @spliceBlockDecorationsInRange(0, Infinity, Infinity)
+ @shouldUpdateDecorations = true
+ @emitDidUpdateState()
+
@disposables.add @model.displayLayer.onDidChangeSync (changes) =>
for change in changes
startRow = change.start.row
@@ -192,7 +200,7 @@ class TextEditorPresenter
highlights: {}
overlays: {}
cursors: {}
- blockDecorations: {}
+ offScreenBlockDecorations: {}
gutters: []
# Shared state that is copied into ``@state.gutters`.
@sharedGutterStyles = {}
@@ -292,24 +300,21 @@ class TextEditorPresenter
tileForRow: (row) ->
row - (row % @tileSize)
- constrainRow: (row) ->
- Math.max(0, Math.min(row, @model.getScreenLineCount()))
-
getStartTileRow: ->
- @constrainRow(@tileForRow(@startRow ? 0))
+ @tileForRow(@startRow ? 0)
getEndTileRow: ->
- @constrainRow(@tileForRow(@endRow ? 0))
+ @tileForRow(@endRow ? 0)
isValidScreenRow: (screenRow) ->
- screenRow >= 0 and screenRow < @model.getScreenLineCount()
+ screenRow >= 0 and screenRow < @model.getApproximateScreenLineCount()
getScreenRowsToRender: ->
startRow = @getStartTileRow()
- endRow = @constrainRow(@getEndTileRow() + @tileSize)
+ endRow = @getEndTileRow() + @tileSize
screenRows = [startRow...endRow]
- longestScreenRow = @model.getLongestScreenRow()
+ longestScreenRow = @model.getApproximateLongestScreenRow()
if longestScreenRow?
screenRows.push(longestScreenRow)
if @screenRowsToMeasure?
@@ -355,7 +360,7 @@ class TextEditorPresenter
zIndex = 0
for tileStartRow in [@tileForRow(endRow)..@tileForRow(startRow)] by -@tileSize
- tileEndRow = @constrainRow(tileStartRow + @tileSize)
+ tileEndRow = tileStartRow + @tileSize
rowsWithinTile = []
while screenRowIndex >= 0
@@ -390,7 +395,7 @@ class TextEditorPresenter
visibleTiles[tileStartRow] = true
zIndex++
- if @mouseWheelScreenRow? and 0 <= @mouseWheelScreenRow < @model.getScreenLineCount()
+ if @mouseWheelScreenRow? and 0 <= @mouseWheelScreenRow < @model.getApproximateScreenLineCount()
mouseWheelTile = @tileForRow(@mouseWheelScreenRow)
unless visibleTiles[mouseWheelTile]?
@@ -409,20 +414,17 @@ class TextEditorPresenter
visibleLineIds = {}
for screenRow in screenRows
line = @linesByScreenRow.get(screenRow)
- unless line?
- throw new Error("No line exists for row #{screenRow}. Last screen row: #{@model.getLastScreenRow()}")
+ continue unless line?
visibleLineIds[line.id] = true
- precedingBlockDecorations = @precedingBlockDecorationsByScreenRow[screenRow] ? []
- followingBlockDecorations = @followingBlockDecorationsByScreenRow[screenRow] ? []
+ precedingBlockDecorations = @precedingBlockDecorationsByScreenRowAndId[screenRow] ? {}
+ followingBlockDecorations = @followingBlockDecorationsByScreenRowAndId[screenRow] ? {}
if tileState.lines.hasOwnProperty(line.id)
lineState = tileState.lines[line.id]
lineState.screenRow = screenRow
lineState.decorationClasses = @lineDecorationClassesForRow(screenRow)
lineState.precedingBlockDecorations = precedingBlockDecorations
lineState.followingBlockDecorations = followingBlockDecorations
- lineState.hasPrecedingBlockDecorations = precedingBlockDecorations.length > 0
- lineState.hasFollowingBlockDecorations = followingBlockDecorations.length > 0
else
tileState.lines[line.id] =
screenRow: screenRow
@@ -431,8 +433,6 @@ class TextEditorPresenter
decorationClasses: @lineDecorationClassesForRow(screenRow)
precedingBlockDecorations: precedingBlockDecorations
followingBlockDecorations: followingBlockDecorations
- hasPrecedingBlockDecorations: precedingBlockDecorations.length > 0
- hasFollowingBlockDecorations: followingBlockDecorations.length > 0
for id, line of tileState.lines
delete tileState.lines[id] unless visibleLineIds.hasOwnProperty(id)
@@ -598,7 +598,9 @@ class TextEditorPresenter
visibleLineNumberIds = {}
for screenRow in screenRows when @isRowRendered(screenRow)
- lineId = @linesByScreenRow.get(screenRow).id
+ line = @linesByScreenRow.get(screenRow)
+ continue unless line?
+ lineId = line.id
{bufferRow, softWrappedAtStart: softWrapped} = @displayLayer.softWrapDescriptorForScreenRow(screenRow)
foldable = not softWrapped and @model.isFoldableAtBufferRow(bufferRow)
decorationClasses = @lineNumberDecorationClassesForRow(screenRow)
@@ -625,7 +627,7 @@ class TextEditorPresenter
return unless @scrollTop? and @lineHeight? and @height?
@endRow = Math.min(
- @model.getScreenLineCount(),
+ @model.getApproximateScreenLineCount(),
@lineTopIndex.rowForPixelPosition(@scrollTop + @height + @lineHeight - 1) + 1
)
@@ -659,7 +661,7 @@ class TextEditorPresenter
updateVerticalDimensions: ->
if @lineHeight?
oldContentHeight = @contentHeight
- @contentHeight = Math.round(@lineTopIndex.pixelPositionAfterBlocksForRow(@model.getScreenLineCount()))
+ @contentHeight = Math.round(@lineTopIndex.pixelPositionAfterBlocksForRow(@model.getApproximateScreenLineCount()))
if @contentHeight isnt oldContentHeight
@updateHeight()
@@ -669,7 +671,7 @@ class TextEditorPresenter
updateHorizontalDimensions: ->
if @baseCharacterWidth?
oldContentWidth = @contentWidth
- rightmostPosition = @model.getRightmostScreenPosition()
+ rightmostPosition = @model.getApproximateRightmostScreenPosition()
@contentWidth = @pixelPositionForScreenPosition(rightmostPosition).left
@contentWidth += @scrollLeft
@contentWidth += 1 unless @model.isSoftWrapped() # account for cursor width
@@ -1060,41 +1062,42 @@ class TextEditorPresenter
@decorations = @model.decorationsStateForScreenRowRange(@startRow, @endRow - 1)
updateBlockDecorations: ->
- @blockDecorationsToRenderById = {}
- @precedingBlockDecorationsByScreenRow = {}
- @followingBlockDecorationsByScreenRow = {}
- visibleDecorationsByMarkerId = @model.decorationsForScreenRowRange(@getStartTileRow(), @getEndTileRow() + @tileSize - 1)
-
if @invalidateAllBlockDecorationsDimensions
for decoration in @model.getDecorations(type: 'block')
@invalidatedDimensionsByBlockDecoration.add(decoration)
@invalidateAllBlockDecorationsDimensions = false
- for markerId, decorations of visibleDecorationsByMarkerId
+ visibleDecorationsById = {}
+ visibleDecorationsByScreenRowAndId = {}
+ for markerId, decorations of @model.decorationsForScreenRowRange(@getStartTileRow(), @getEndTileRow() + @tileSize - 1)
for decoration in decorations when decoration.isType('block')
- @updateBlockDecorationState(decoration, true)
+ screenRow = decoration.getMarker().getHeadScreenPosition().row
+ if decoration.getProperties().position is "after"
+ @followingBlockDecorationsByScreenRowAndId[screenRow] ?= {}
+ @followingBlockDecorationsByScreenRowAndId[screenRow][decoration.id] = {screenRow, decoration}
+ else
+ @precedingBlockDecorationsByScreenRowAndId[screenRow] ?= {}
+ @precedingBlockDecorationsByScreenRowAndId[screenRow][decoration.id] = {screenRow, decoration}
+ visibleDecorationsById[decoration.id] = true
+ visibleDecorationsByScreenRowAndId[screenRow] ?= {}
+ visibleDecorationsByScreenRowAndId[screenRow][decoration.id] = true
+ for screenRow, blockDecorations of @precedingBlockDecorationsByScreenRowAndId
+ if Number(screenRow) isnt @mouseWheelScreenRow
+ for id, blockDecoration of blockDecorations
+ unless visibleDecorationsByScreenRowAndId[screenRow]?[id]
+ delete @precedingBlockDecorationsByScreenRowAndId[screenRow][id]
+
+ for screenRow, blockDecorations of @followingBlockDecorationsByScreenRowAndId
+ if Number(screenRow) isnt @mouseWheelScreenRow
+ for id, blockDecoration of blockDecorations
+ unless visibleDecorationsByScreenRowAndId[screenRow]?[id]
+ delete @followingBlockDecorationsByScreenRowAndId[screenRow][id]
+
+ @state.content.offScreenBlockDecorations = {}
@invalidatedDimensionsByBlockDecoration.forEach (decoration) =>
- @updateBlockDecorationState(decoration, false)
-
- for decorationId, decorationState of @state.content.blockDecorations
- continue if @blockDecorationsToRenderById[decorationId]
- continue if decorationState.screenRow is @mouseWheelScreenRow
-
- delete @state.content.blockDecorations[decorationId]
-
- updateBlockDecorationState: (decoration, isVisible) ->
- return if @blockDecorationsToRenderById[decoration.getId()]
-
- screenRow = decoration.getMarker().getHeadScreenPosition().row
- if decoration.getProperties().position is "after"
- @followingBlockDecorationsByScreenRow[screenRow] ?= []
- @followingBlockDecorationsByScreenRow[screenRow].push(decoration)
- else
- @precedingBlockDecorationsByScreenRow[screenRow] ?= []
- @precedingBlockDecorationsByScreenRow[screenRow].push(decoration)
- @state.content.blockDecorations[decoration.getId()] = {decoration, screenRow, isVisible}
- @blockDecorationsToRenderById[decoration.getId()] = true
+ unless visibleDecorationsById[decoration.id]
+ @state.content.offScreenBlockDecorations[decoration.id] = decoration
updateLineDecorations: ->
@lineDecorationsByScreenRow = {}
@@ -1181,6 +1184,9 @@ class TextEditorPresenter
startTile = @tileForRow(screenRange.start.row)
endTile = @tileForRow(screenRange.end.row)
+ needsFlash = properties.flashCount? and @flashCountsByDecorationId[decorationId] isnt properties.flashCount
+ if needsFlash
+ @flashCountsByDecorationId[decorationId] = properties.flashCount
for tileStartRow in [startTile..endTile] by @tileSize
rangeWithinTile = @intersectRangeWithTile(screenRange, tileStartRow)
@@ -1190,6 +1196,7 @@ class TextEditorPresenter
tileState = @state.content.tiles[tileStartRow] ?= {highlights: {}}
highlightState = tileState.highlights[decorationId] ?= {}
+ highlightState.needsFlash = needsFlash
highlightState.flashCount = properties.flashCount
highlightState.flashClass = properties.flashClass
highlightState.flashDuration = properties.flashDuration
@@ -1296,7 +1303,7 @@ class TextEditorPresenter
setBlockDecorationDimensions: (decoration, width, height) ->
return unless @observedBlockDecorations.has(decoration)
- @lineTopIndex.resizeBlock(decoration.getId(), height)
+ @lineTopIndex.resizeBlock(decoration.id, height)
@invalidatedDimensionsByBlockDecoration.delete(decoration)
@shouldUpdateDecorations = true
@@ -1333,7 +1340,7 @@ class TextEditorPresenter
@didDestroyBlockDecoration(decoration)
isAfter = decoration.getProperties().position is "after"
- @lineTopIndex.insertBlock(decoration.getId(), decoration.getMarker().getHeadScreenPosition().row, 0, isAfter)
+ @lineTopIndex.insertBlock(decoration.id, decoration.getMarker().getHeadScreenPosition().row, 0, isAfter)
@observedBlockDecorations.add(decoration)
@invalidateBlockDecorationDimensions(decoration)
@@ -1347,14 +1354,14 @@ class TextEditorPresenter
# change.
return if markerEvent.textChanged
- @lineTopIndex.moveBlock(decoration.getId(), decoration.getMarker().getHeadScreenPosition().row)
+ @lineTopIndex.moveBlock(decoration.id, decoration.getMarker().getHeadScreenPosition().row)
@shouldUpdateDecorations = true
@emitDidUpdateState()
didDestroyBlockDecoration: (decoration) ->
return unless @observedBlockDecorations.has(decoration)
- @lineTopIndex.removeBlock(decoration.getId())
+ @lineTopIndex.removeBlock(decoration.id)
@observedBlockDecorations.delete(decoration)
@invalidatedDimensionsByBlockDecoration.delete(decoration)
@shouldUpdateDecorations = true
@@ -1530,7 +1537,7 @@ class TextEditorPresenter
[@startRow, @endRow]
isRowRendered: (row) ->
- @getStartTileRow() <= row < @constrainRow(@getEndTileRow() + @tileSize)
+ @getStartTileRow() <= row < @getEndTileRow() + @tileSize
isOpenTagCode: (tagCode) ->
@displayLayer.isOpenTagCode(tagCode)
diff --git a/src/text-editor.coffee b/src/text-editor.coffee
index 02cce3daf..6907db8fe 100644
--- a/src/text-editor.coffee
+++ b/src/text-editor.coffee
@@ -2,7 +2,7 @@ _ = require 'underscore-plus'
path = require 'path'
fs = require 'fs-plus'
Grim = require 'grim'
-{CompositeDisposable, Emitter} = require 'event-kit'
+{CompositeDisposable, Disposable, Emitter} = require 'event-kit'
{Point, Range} = TextBuffer = require 'text-buffer'
LanguageMode = require './language-mode'
DecorationManager = require './decoration-manager'
@@ -184,6 +184,10 @@ class TextEditor extends Model
else
@displayLayer = @buffer.addDisplayLayer(displayLayerParams)
+ @backgroundWorkHandle = requestIdleCallback(@doBackgroundWork)
+ @disposables.add new Disposable =>
+ cancelIdleCallback(@backgroundWorkHandle) if @backgroundWorkHandle?
+
@displayLayer.setTextDecorationLayer(@tokenizedBuffer)
@defaultMarkerLayer = @displayLayer.addMarkerLayer()
@selectionsMarkerLayer ?= @addMarkerLayer(maintainHistory: true, persistent: true)
@@ -210,6 +214,13 @@ class TextEditor extends Model
priority: 0
visible: lineNumberGutterVisible
+ doBackgroundWork: (deadline) =>
+ if @displayLayer.doBackgroundWork(deadline)
+ @presenter?.updateVerticalDimensions()
+ @backgroundWorkHandle = requestIdleCallback(@doBackgroundWork)
+ else
+ @backgroundWorkHandle = null
+
update: (params) ->
displayLayerParams = {}
@@ -394,6 +405,9 @@ class TextEditor extends Model
@disposables.add @displayLayer.onDidChangeSync (e) =>
@mergeIntersectingSelections()
@emitter.emit 'did-change', e
+ @disposables.add @displayLayer.onDidReset =>
+ @mergeIntersectingSelections()
+ @emitter.emit 'did-change', {}
destroyed: ->
@disposables.dispose()
@@ -910,6 +924,8 @@ class TextEditor extends Model
# editor. This accounts for folds.
getScreenLineCount: -> @displayLayer.getScreenLineCount()
+ getApproximateScreenLineCount: -> @displayLayer.getApproximateScreenLineCount()
+
# Essential: Returns a {Number} representing the last zero-indexed buffer row
# number of the editor.
getLastBufferRow: -> @buffer.getLastRow()
@@ -956,7 +972,6 @@ class TextEditor extends Model
tokens
screenLineForScreenRow: (screenRow) ->
- return if screenRow < 0 or screenRow > @getLastScreenRow()
@displayLayer.getScreenLines(screenRow, screenRow + 1)[0]
bufferRowForScreenRow: (screenRow) ->
@@ -974,10 +989,14 @@ class TextEditor extends Model
getRightmostScreenPosition: -> @displayLayer.getRightmostScreenPosition()
+ getApproximateRightmostScreenPosition: -> @displayLayer.getApproximateRightmostScreenPosition()
+
getMaxScreenLineLength: -> @getRightmostScreenPosition().column
getLongestScreenRow: -> @getRightmostScreenPosition().row
+ getApproximateLongestScreenRow: -> @getApproximateRightmostScreenPosition().row
+
lineLengthForScreenRow: (screenRow) -> @displayLayer.lineLengthForScreenRow(screenRow)
# Returns the range for the given buffer row.
@@ -2868,7 +2887,7 @@ class TextEditor extends Model
# whitespace.
usesSoftTabs: ->
for bufferRow in [0..@buffer.getLastRow()]
- continue if @tokenizedBuffer.tokenizedLineForRow(bufferRow).isComment()
+ continue if @tokenizedBuffer.tokenizedLines[bufferRow]?.isComment()
line = @buffer.lineForRow(bufferRow)
return true if line[0] is ' '
diff --git a/src/tiled-component.coffee b/src/tiled-component.coffee
index 2e8dc7149..37de27e9b 100644
--- a/src/tiled-component.coffee
+++ b/src/tiled-component.coffee
@@ -1,10 +1,3 @@
-{values} = require 'underscore-plus'
-
-cloneObject = (object) ->
- clone = {}
- clone[key] = value for key, value of object
- clone
-
module.exports =
class TiledComponent
updateSync: (state) ->
@@ -41,7 +34,7 @@ class TiledComponent
component = @componentsByTileId[tileRow] = @buildComponentForTile(tileRow)
@getTilesNode().appendChild(component.getDomNode())
- @oldState.tiles[tileRow] = cloneObject(tileState)
+ @oldState.tiles[tileRow] = Object.assign({}, tileState)
component.updateSync(@newState)
@@ -50,5 +43,9 @@ class TiledComponent
getComponentForTile: (tileRow) ->
@componentsByTileId[tileRow]
+ getComponents: ->
+ for _, component of @componentsByTileId
+ component
+
getTiles: ->
- values(@componentsByTileId).map (component) -> component.getDomNode()
+ @getComponents().map((component) -> component.getDomNode())
diff --git a/src/tokenized-buffer-iterator.coffee b/src/tokenized-buffer-iterator.coffee
deleted file mode 100644
index 23b72d5a9..000000000
--- a/src/tokenized-buffer-iterator.coffee
+++ /dev/null
@@ -1,126 +0,0 @@
-{Point} = require 'text-buffer'
-
-module.exports =
-class TokenizedBufferIterator
- constructor: (@tokenizedBuffer) ->
- @openTags = null
- @closeTags = null
- @containingTags = null
-
- seek: (position) ->
- @openTags = []
- @closeTags = []
- @tagIndex = null
-
- currentLine = @tokenizedBuffer.tokenizedLineForRow(position.row)
- @currentTags = currentLine.tags
- @currentLineOpenTags = currentLine.openScopes
- @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
- @tagIndex = index
- break
- else
- currentColumn += tag
- @containingTags.pop() while @closeTags.shift()
- @containingTags.push(openTag) while openTag = @openTags.shift()
- else
- scopeName = @tokenizedBuffer.grammar.scopeForId(tag)
- if tag % 2 is 0 # close tag
- if @openTags.length > 0
- if currentColumn >= 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
- @position = Point(position.row, Math.min(@currentLineLength, currentColumn))
- @containingTags.slice()
-
- moveToSuccessor: ->
- @containingTags.pop() for tag in @closeTags
- @containingTags.push(tag) for tag in @openTags
- @openTags = []
- @closeTags = []
-
- loop
- if @tagIndex is @currentTags.length
- if @isAtTagBoundary()
- break
- else
- if @shouldMoveToNextLine
- @moveToNextLine()
- @openTags = @currentLineOpenTags.map (id) => @tokenizedBuffer.grammar.scopeForId(id)
- @shouldMoveToNextLine = false
- else if @nextLineHasMismatchedContainingTags()
- @closeTags = @containingTags.slice().reverse()
- @containingTags = []
- @shouldMoveToNextLine = true
- else
- return false unless @moveToNextLine()
- else
- tag = @currentTags[@tagIndex]
- if tag >= 0
- if @isAtTagBoundary()
- break
- else
- @position = Point(@position.row, Math.min(@currentLineLength, @position.column + @currentTags[@tagIndex]))
- else
- scopeName = @tokenizedBuffer.grammar.scopeForId(tag)
- if tag % 2 is 0
- if @openTags.length > 0
- break
- else
- @closeTags.push(scopeName)
- else
- @openTags.push(scopeName)
- @tagIndex++
-
- true
-
- getPosition: ->
- @position
-
- getCloseTags: ->
- @closeTags.slice()
-
- getOpenTags: ->
- @openTags.slice()
-
- ###
- Section: Private Methods
- ###
-
- nextLineHasMismatchedContainingTags: ->
- if line = @tokenizedBuffer.tokenizedLineForRow(@position.row + 1)
- return true if line.openScopes.length isnt @containingTags.length
-
- for i in [0...@containingTags.length] by 1
- if @containingTags[i] isnt @tokenizedBuffer.grammar.scopeForId(line.openScopes[i])
- return true
- false
- else
- false
-
- moveToNextLine: ->
- @position = Point(@position.row + 1, 0)
- if tokenizedLine = @tokenizedBuffer.tokenizedLineForRow(@position.row)
- @currentTags = tokenizedLine.tags
- @currentLineLength = tokenizedLine.text.length
- @currentLineOpenTags = tokenizedLine.openScopes
- @tagIndex = 0
- true
- else
- false
-
- isAtTagBoundary: ->
- @closeTags.length > 0 or @openTags.length > 0
diff --git a/src/tokenized-buffer-iterator.js b/src/tokenized-buffer-iterator.js
new file mode 100644
index 000000000..29d2fdf86
--- /dev/null
+++ b/src/tokenized-buffer-iterator.js
@@ -0,0 +1,174 @@
+const {Point} = require('text-buffer')
+
+module.exports = class TokenizedBufferIterator {
+ constructor (tokenizedBuffer) {
+ this.tokenizedBuffer = tokenizedBuffer
+ this.openTags = null
+ this.closeTags = null
+ this.containingTags = null
+ }
+
+ seek (position) {
+ this.openTags = []
+ this.closeTags = []
+ this.tagIndex = null
+
+ const currentLine = this.tokenizedBuffer.tokenizedLineForRow(position.row)
+ this.currentTags = currentLine.tags
+ this.currentLineOpenTags = currentLine.openScopes
+ this.currentLineLength = currentLine.text.length
+ this.containingTags = this.currentLineOpenTags.map((id) => this.scopeForId(id))
+
+ let currentColumn = 0
+ for (let [index, tag] of this.currentTags.entries()) {
+ if (tag >= 0) {
+ if (currentColumn >= position.column) {
+ this.tagIndex = index
+ break
+ } else {
+ currentColumn += tag
+ while (this.closeTags.length > 0) {
+ this.closeTags.shift()
+ this.containingTags.pop()
+ }
+ while (this.openTags.length > 0) {
+ const openTag = this.openTags.shift()
+ this.containingTags.push(openTag)
+ }
+ }
+ } else {
+ const scopeName = this.scopeForId(tag)
+ if (tag % 2 === 0) {
+ if (this.openTags.length > 0) {
+ if (currentColumn >= position.column) {
+ this.tagIndex = index
+ break
+ } else {
+ while (this.closeTags.length > 0) {
+ this.closeTags.shift()
+ this.containingTags.pop()
+ }
+ while (this.openTags.length > 0) {
+ const openTag = this.openTags.shift()
+ this.containingTags.push(openTag)
+ }
+ }
+ }
+ this.closeTags.push(scopeName)
+ } else {
+ this.openTags.push(scopeName)
+ }
+ }
+ }
+
+ if (this.tagIndex == null) {
+ this.tagIndex = this.currentTags.length
+ }
+ this.position = Point(position.row, Math.min(this.currentLineLength, currentColumn))
+ return this.containingTags.slice()
+ }
+
+ moveToSuccessor () {
+ for (let tag of this.closeTags) { // eslint-disable-line no-unused-vars
+ this.containingTags.pop()
+ }
+ for (let tag of this.openTags) {
+ this.containingTags.push(tag)
+ }
+ this.openTags = []
+ this.closeTags = []
+ while (true) {
+ if (this.tagIndex === this.currentTags.length) {
+ if (this.isAtTagBoundary()) {
+ break
+ } else if (this.shouldMoveToNextLine) {
+ this.moveToNextLine()
+ this.openTags = this.currentLineOpenTags.map((id) => this.scopeForId(id))
+ this.shouldMoveToNextLine = false
+ } else if (this.nextLineHasMismatchedContainingTags()) {
+ this.closeTags = this.containingTags.slice().reverse()
+ this.containingTags = []
+ this.shouldMoveToNextLine = true
+ } else if (!this.moveToNextLine()) {
+ return false
+ }
+ } else {
+ const tag = this.currentTags[this.tagIndex]
+ if (tag >= 0) {
+ if (this.isAtTagBoundary()) {
+ break
+ } else {
+ this.position = Point(this.position.row, Math.min(
+ this.currentLineLength,
+ this.position.column + this.currentTags[this.tagIndex]
+ ))
+ }
+ } else {
+ const scopeName = this.scopeForId(tag)
+ if (tag % 2 === 0) {
+ if (this.openTags.length > 0) {
+ break
+ } else {
+ this.closeTags.push(scopeName)
+ }
+ } else {
+ this.openTags.push(scopeName)
+ }
+ }
+ this.tagIndex++
+ }
+ }
+ return true
+ }
+
+ getPosition () {
+ return this.position
+ }
+
+ getCloseTags () {
+ return this.closeTags.slice()
+ }
+
+ getOpenTags () {
+ return this.openTags.slice()
+ }
+
+ nextLineHasMismatchedContainingTags () {
+ const line = this.tokenizedBuffer.tokenizedLineForRow(this.position.row + 1)
+ if (line == null) {
+ return false
+ } else {
+ return (
+ this.containingTags.length !== line.openScopes.length ||
+ this.containingTags.some((tag, i) => tag !== this.scopeForId(line.openScopes[i]))
+ )
+ }
+ }
+
+ moveToNextLine () {
+ this.position = Point(this.position.row + 1, 0)
+ const tokenizedLine = this.tokenizedBuffer.tokenizedLineForRow(this.position.row)
+ if (tokenizedLine == null) {
+ return false
+ } else {
+ this.currentTags = tokenizedLine.tags
+ this.currentLineLength = tokenizedLine.text.length
+ this.currentLineOpenTags = tokenizedLine.openScopes
+ this.tagIndex = 0
+ return true
+ }
+ }
+
+ isAtTagBoundary () {
+ return this.closeTags.length > 0 || this.openTags.length > 0
+ }
+
+ scopeForId (id) {
+ const scope = this.tokenizedBuffer.grammar.scopeForId(id)
+ if (scope) {
+ return `syntax--${scope.replace(/\./g, '.syntax--')}`
+ } else {
+ return null
+ }
+ }
+}
diff --git a/src/tokenized-buffer.coffee b/src/tokenized-buffer.coffee
index 80358f23d..ce56e0388 100644
--- a/src/tokenized-buffer.coffee
+++ b/src/tokenized-buffer.coffee
@@ -36,7 +36,6 @@ class TokenizedBuffer extends Model
@tokenIterator = new TokenIterator(this)
@disposables.add @buffer.registerTextDecorationLayer(this)
- @rootScopeDescriptor = new ScopeDescriptor(scopes: ['text.plain'])
@setGrammar(grammar ? NullGrammar)
@@ -95,14 +94,17 @@ class TokenizedBuffer extends Model
false
retokenizeLines: ->
- lastRow = @buffer.getLastRow()
@fullyTokenized = false
- @tokenizedLines = new Array(lastRow + 1)
+ @tokenizedLines = new Array(@buffer.getLineCount())
@invalidRows = []
- @invalidateRow(0)
+ if @largeFileMode or @grammar.name is 'Null Grammar'
+ @markTokenizationComplete()
+ else
+ @invalidateRow(0)
setVisible: (@visible) ->
- @tokenizeInBackground() if @visible
+ if @visible and @grammar.name isnt 'Null Grammar' and not @largeFileMode
+ @tokenizeInBackground()
getTabLength: -> @tabLength
@@ -117,12 +119,6 @@ class TokenizedBuffer extends Model
@tokenizeNextChunk() if @isAlive() and @buffer.isAlive()
tokenizeNextChunk: ->
- # Short circuit null grammar which can just use the placeholder tokens
- if (@grammar.name is 'Null Grammar') and @firstInvalidRow()?
- @invalidRows = []
- @markTokenizationComplete()
- return
-
rowsRemaining = @chunkSize
while @firstInvalidRow()? and rowsRemaining > 0
@@ -167,8 +163,6 @@ class TokenizedBuffer extends Model
return
invalidateRow: (row) ->
- return if @largeFileMode
-
@invalidRows.push(row)
@invalidRows.sort (a, b) -> a - b
@tokenizeInBackground()
@@ -189,18 +183,19 @@ class TokenizedBuffer extends Model
start = oldRange.start.row
end = oldRange.end.row
delta = newRange.end.row - oldRange.end.row
+ oldLineCount = oldRange.end.row - oldRange.start.row + 1
+ newLineCount = newRange.end.row - newRange.start.row + 1
@updateInvalidRows(start, end, delta)
previousEndStack = @stackForRow(end) # used in spill detection below
- if @largeFileMode or @grammar is NullGrammar
- newTokenizedLines = @buildPlaceholderTokenizedLinesForRows(start, end + delta)
+ if @largeFileMode or @grammar.name is 'Null Grammar'
+ _.spliceWithArray(@tokenizedLines, start, oldLineCount, new Array(newLineCount))
else
newTokenizedLines = @buildTokenizedLinesForRows(start, end + delta, @stackForRow(start - 1), @openScopesForRow(start))
- _.spliceWithArray(@tokenizedLines, start, end - start + 1, newTokenizedLines)
-
- newEndStack = @stackForRow(end + delta)
- if newEndStack and not _.isEqual(newEndStack, previousEndStack)
- @invalidateRow(end + delta + 1)
+ _.spliceWithArray(@tokenizedLines, start, oldLineCount, newTokenizedLines)
+ newEndStack = @stackForRow(end + delta)
+ if newEndStack and not _.isEqual(newEndStack, previousEndStack)
+ @invalidateRow(end + delta + 1)
isFoldableAtRow: (row) ->
if @largeFileMode
@@ -211,46 +206,39 @@ class TokenizedBuffer extends Model
# Returns a {Boolean} indicating whether the given buffer row starts
# a a foldable row range due to the code's indentation patterns.
isFoldableCodeAtRow: (row) ->
- # Investigating an exception that's occurring here due to the line being
- # undefined. This should paper over the problem but we want to figure out
- # what is happening:
- tokenizedLine = @tokenizedLineForRow(row)
- @assert tokenizedLine?, "TokenizedLine is undefined", (error) =>
- error.metadata = {
- row: row
- rowCount: @tokenizedLines.length
- tokenizedBufferChangeCount: @changeCount
- bufferChangeCount: @buffer.changeCount
- }
-
- return false unless tokenizedLine?
-
- return false if @buffer.isRowBlank(row) or tokenizedLine.isComment()
- nextRow = @buffer.nextNonBlankRow(row)
- return false unless nextRow?
-
- @indentLevelForRow(nextRow) > @indentLevelForRow(row)
+ if 0 <= row <= @buffer.getLastRow()
+ nextRow = @buffer.nextNonBlankRow(row)
+ tokenizedLine = @tokenizedLines[row]
+ if @buffer.isRowBlank(row) or tokenizedLine?.isComment() or not nextRow?
+ false
+ else
+ @indentLevelForRow(nextRow) > @indentLevelForRow(row)
+ else
+ false
isFoldableCommentAtRow: (row) ->
previousRow = row - 1
nextRow = row + 1
- return false if nextRow > @buffer.getLastRow()
-
- (row is 0 or not @tokenizedLineForRow(previousRow).isComment()) and
- @tokenizedLineForRow(row).isComment() and
- @tokenizedLineForRow(nextRow).isComment()
+ if nextRow > @buffer.getLastRow()
+ false
+ else
+ Boolean(
+ not (@tokenizedLines[previousRow]?.isComment()) and
+ @tokenizedLines[row]?.isComment() and
+ @tokenizedLines[nextRow]?.isComment()
+ )
buildTokenizedLinesForRows: (startRow, endRow, startingStack, startingopenScopes) ->
ruleStack = startingStack
openScopes = startingopenScopes
stopTokenizingAt = startRow + @chunkSize
- tokenizedLines = for row in [startRow..endRow]
+ tokenizedLines = for row in [startRow..endRow] by 1
if (ruleStack or row is 0) and row < stopTokenizingAt
tokenizedLine = @buildTokenizedLineForRow(row, ruleStack, openScopes)
ruleStack = tokenizedLine.ruleStack
openScopes = @scopesFromTags(openScopes, tokenizedLine.tags)
else
- tokenizedLine = @buildPlaceholderTokenizedLineForRow(row, openScopes)
+ tokenizedLine = undefined
tokenizedLine
if endRow >= stopTokenizingAt
@@ -259,21 +247,6 @@ class TokenizedBuffer extends Model
tokenizedLines
- buildPlaceholderTokenizedLinesForRows: (startRow, endRow) ->
- @buildPlaceholderTokenizedLineForRow(row) for row in [startRow..endRow] by 1
-
- buildPlaceholderTokenizedLineForRow: (row) ->
- @buildPlaceholderTokenizedLineForRowWithText(row, @buffer.lineForRow(row))
-
- buildPlaceholderTokenizedLineForRowWithText: (row, text) ->
- if @grammar isnt NullGrammar
- openScopes = [@grammar.startIdForScope(@grammar.scopeName)]
- else
- openScopes = []
- tags = [text.length]
- lineEnding = @buffer.lineEndingForRow(row)
- new TokenizedLine({openScopes, text, tags, lineEnding, @tokenIterator})
-
buildTokenizedLineForRow: (row, ruleStack, openScopes) ->
@buildTokenizedLineForRowWithText(row, @buffer.lineForRow(row), ruleStack, openScopes)
@@ -283,8 +256,14 @@ class TokenizedBuffer extends Model
new TokenizedLine({openScopes, text, tags, ruleStack, lineEnding, @tokenIterator})
tokenizedLineForRow: (bufferRow) ->
- if 0 <= bufferRow < @tokenizedLines.length
- @tokenizedLines[bufferRow] ?= @buildPlaceholderTokenizedLineForRow(bufferRow)
+ if 0 <= bufferRow <= @buffer.getLastRow()
+ if tokenizedLine = @tokenizedLines[bufferRow]
+ tokenizedLine
+ else
+ text = @buffer.lineForRow(bufferRow)
+ lineEnding = @buffer.lineEndingForRow(bufferRow)
+ tags = [@grammar.startIdForScope(@grammar.scopeName), text.length, @grammar.endIdForScope(@grammar.scopeName)]
+ @tokenizedLines[bufferRow] = new TokenizedLine({openScopes: [], text, tags, lineEnding, @tokenIterator})
tokenizedLinesForRows: (startRow, endRow) ->
for row in [startRow..endRow] by 1
@@ -294,8 +273,7 @@ class TokenizedBuffer extends Model
@tokenizedLines[bufferRow]?.ruleStack
openScopesForRow: (bufferRow) ->
- if bufferRow > 0
- precedingLine = @tokenizedLineForRow(bufferRow - 1)
+ if precedingLine = @tokenizedLines[bufferRow - 1]
@scopesFromTags(precedingLine.openScopes, precedingLine.tags)
else
[]
@@ -448,7 +426,7 @@ class TokenizedBuffer extends Model
logLines: (start=0, end=@buffer.getLastRow()) ->
for row in [start..end]
- line = @tokenizedLineForRow(row).text
+ line = @tokenizedLines[row].text
console.log row, line, line.length
return
diff --git a/src/workspace-element.coffee b/src/workspace-element.coffee
index ab8e39532..be0af81ed 100644
--- a/src/workspace-element.coffee
+++ b/src/workspace-element.coffee
@@ -142,4 +142,13 @@ class WorkspaceElement extends HTMLElement
ipcRenderer.send('run-package-specs', specPath)
+ runBenchmarks: ->
+ if activePath = @workspace.getActivePaneItem()?.getPath?()
+ [projectPath] = @project.relativizePath(activePath)
+ else
+ [projectPath] = @project.getPaths()
+
+ if projectPath
+ ipcRenderer.send('run-benchmarks', path.join(projectPath, 'benchmarks'))
+
module.exports = WorkspaceElement = document.registerElement 'atom-workspace', prototype: WorkspaceElement.prototype
diff --git a/static/cursors.less b/static/cursors.less
index b2807217e..0b54c6ea1 100644
--- a/static/cursors.less
+++ b/static/cursors.less
@@ -13,14 +13,14 @@
// Editors
& when ( lightness(@syntax-background-color) < 50% ) {
- .platform-darwin atom-text-editor:not([mini])::shadow .editor-contents--private {
+ .platform-darwin atom-text-editor:not([mini]) .editor-contents--private {
.cursor-white();
}
}
// Mini Editors
& when ( lightness(@input-background-color) < 50% ) {
- .platform-darwin atom-text-editor[mini]::shadow .editor-contents--private {
+ .platform-darwin atom-text-editor[mini] .editor-contents--private {
.cursor-white();
}
}
diff --git a/static/text-editor-light.less b/static/text-editor-light.less
index 2082dc715..5f159ce94 100644
--- a/static/text-editor-light.less
+++ b/static/text-editor-light.less
@@ -2,31 +2,17 @@
@import "octicon-utf-codes";
@import "octicon-mixins";
-atom-text-editor {
- display: block;
- font-family: Menlo, Consolas, 'DejaVu Sans Mono', monospace;
-}
-
-atom-text-editor[mini] {
- font-size: @input-font-size;
- line-height: @component-line-height;
- max-height: @component-line-height + 2; // +2 for borders
- overflow: auto;
-}
-
-atom-overlay {
- position: fixed;
- display: block;
- z-index: 4;
-}
-
-// TODO: Remove the following styles when the editor shadow DOM can no longer be disabled
atom-text-editor {
display: flex;
+ font-family: Menlo, Consolas, 'DejaVu Sans Mono', monospace;
- .editor-contents {
+ .editor--private, .editor-contents--private {
+ height: 100%;
+ width: 100%;
+ }
+
+ .editor-contents--private {
width: 100%;
- overflow: hidden;
cursor: text;
display: flex;
-webkit-user-select: none;
@@ -35,6 +21,7 @@ atom-text-editor {
.gutter {
overflow: hidden;
+ z-index: 0;
text-align: right;
cursor: default;
min-width: 1em;
@@ -217,3 +204,16 @@ atom-text-editor {
right: 0;
}
}
+
+atom-text-editor[mini] {
+ font-size: @input-font-size;
+ line-height: @component-line-height;
+ max-height: @component-line-height + 2; // +2 for borders
+ overflow: auto;
+}
+
+atom-overlay {
+ position: fixed;
+ display: block;
+ z-index: 4;
+}
diff --git a/static/text-editor-shadow.less b/static/text-editor-shadow.less
deleted file mode 100644
index a3d44f568..000000000
--- a/static/text-editor-shadow.less
+++ /dev/null
@@ -1,201 +0,0 @@
-@import "ui-variables";
-@import "octicon-utf-codes";
-@import "octicon-mixins";
-
-.editor--private, .editor-contents--private {
- height: 100%;
- width: 100%;
-}
-
-.editor-contents--private {
- width: 100%;
- cursor: text;
- display: flex;
- -webkit-user-select: none;
- position: relative;
-}
-
-.gutter {
- overflow: hidden;
- z-index: 0;
- text-align: right;
- cursor: default;
- min-width: 1em;
- box-sizing: border-box;
-}
-
-.line-numbers {
- position: relative;
-}
-
-.line-number {
- position: relative;
- white-space: nowrap;
- padding-left: .5em;
- opacity: 0.6;
-
- &.cursor-line {
- opacity: 1;
- }
-
- .icon-right {
- .octicon(chevron-down, 0.8em);
- display: inline-block;
- visibility: hidden;
- opacity: .6;
- padding: 0 .4em;
-
- &::before {
- text-align: center;
- }
- }
-}
-
-.gutter:hover {
- .line-number.foldable .icon-right {
- visibility: visible;
-
- &:hover {
- opacity: 1;
- }
- }
-}
-
-.gutter, .gutter:hover {
- .line-number.folded .icon-right {
- .octicon(chevron-right, 0.8em);
-
- visibility: visible;
-
- &::before {
- position: relative;
- left: -.1em;
- }
- }
-}
-
-.scroll-view {
- position: relative;
- z-index: 0;
-
- overflow: hidden;
- flex: 1;
- min-width: 0;
- min-height: 0;
-}
-
-.highlight {
- background: none;
- padding: 0;
-}
-
-.highlight .region {
- position: absolute;
- pointer-events: none;
- z-index: -1;
-}
-
-.lines {
- min-width: 100%;
- position: relative;
- z-index: 1;
-}
-
-.line {
- white-space: pre;
-
- &.cursor-line .fold-marker::after {
- opacity: 1;
- }
-}
-
-.fold-marker {
- cursor: default;
-
- &::after {
- .icon(0.8em, inline);
-
- content: @ellipsis;
- padding-left: 0.2em;
- }
-}
-
-.placeholder-text {
- position: absolute;
- color: @text-color-subtle;
-}
-
-.invisible-character {
- font-weight: normal !important;
- font-style: normal !important;
-}
-
-.indent-guide {
- display: inline-block;
- box-shadow: inset 1px 0;
-}
-
-.hidden-input {
- padding: 0;
- border: 0;
- position: absolute;
- z-index: -1;
- top: 0;
- left: 0;
- opacity: 0;
- width: 1px;
-}
-
-.cursor {
- z-index: 4;
- pointer-events: none;
- box-sizing: border-box;
- position: absolute;
- border-left: 1px solid;
- opacity: 0;
-}
-
-&.is-focused .cursor {
- opacity: 1;
-}
-
-.cursors.blink-off .cursor {
- opacity: 0;
-}
-
-.horizontal-scrollbar {
- position: absolute;
- left: 0;
- right: 0;
- bottom: 0;
-
- height: 15px;
- overflow-x: auto;
- overflow-y: hidden;
- z-index: 3;
- cursor: default;
-
- .scrollbar-content {
- height: 15px;
- }
-}
-
-.vertical-scrollbar {
- position: absolute;
- top: 0;
- right: 0;
- bottom: 0;
-
- width: 15px;
- overflow-x: hidden;
- overflow-y: auto;
- z-index: 3;
- cursor: default;
-}
-
-.scrollbar-corner {
- position: absolute;
- overflow: auto;
- bottom: 0;
- right: 0;
-}