diff --git a/package.json b/package.json
index a35f008e5..63b00ee8f 100644
--- a/package.json
+++ b/package.json
@@ -45,6 +45,7 @@
"nslog": "^3",
"oniguruma": "6.1.0",
"pathwatcher": "~6.5",
+ "postcss-selector-parser": "^2.2.1",
"property-accessors": "^1.1.3",
"random-words": "0.0.1",
"resolve": "^1.1.6",
diff --git a/spec/styles-element-spec.coffee b/spec/styles-element-spec.coffee
index b1a57938c..b29979524 100644
--- a/spec/styles-element-spec.coffee
+++ b/spec/styles-element-spec.coffee
@@ -80,34 +80,66 @@ describe "StylesElement", ->
describe "atom-text-editor shadow DOM selector upgrades", ->
beforeEach ->
- element.setAttribute('context', 'atom-text-editor')
spyOn(console, 'warn')
- it "upgrades selectors containing .editor-colors", ->
- atom.styles.addStyleSheet(".editor-colors {background: black;}", context: 'atom-text-editor')
- expect(element.firstChild.sheet.cssRules[0].selectorText).toBe ':host'
+ it "removes the ::shadow pseudo-element from atom-text-editor selectors", ->
+ atom.styles.addStyleSheet("""
+ atom-text-editor::shadow .class-1, atom-text-editor::shadow .class-2 { color: red; }
+ atom-text-editor::shadow > .class-3 { color: yellow; }
+ atom-text-editor .class-4 { color: blue; }
+ another-element::shadow .class-5 { color: white; }
+ """)
+ expect(Array.from(element.lastChild.sheet.cssRules).map((r) -> r.selectorText)).toEqual([
+ 'atom-text-editor .class-1, atom-text-editor .class-2',
+ 'atom-text-editor > .class-3',
+ 'atom-text-editor .class-4',
+ 'another-element::shadow .class-5'
+ ])
+ expect(console.warn).toHaveBeenCalled()
- it "upgrades selectors containing .editor", ->
- atom.styles.addStyleSheet """
- .editor {background: black;}
- .editor.mini {background: black;}
- .editor:focus {background: black;}
- """, context: 'atom-text-editor'
+ describe "when the context of a style sheet is 'atom-text-editor'", ->
+ it "prepends `--syntax` to selectors not contained in atom-text-editor or matching a spatial decoration", ->
+ atom.styles.addStyleSheet("""
+ .class-1 { color: red; }
+ .class-2 > .class-3, .class-4.class-5 { color: green; }
+ .class-6 atom-text-editor .class-7 { color: yellow; }
+ atom-text-editor .class-8, .class-9 { color: blue; }
+ atom-text-editor .indent-guide, atom-text-editor .leading-whitespace { background: white; }
+ .syntax--class-10 { color: gray; }
+ :host .class-11 { color: purple; }
+ #id-1 { color: gray; }
+ """, {context: 'atom-text-editor'})
+ expect(Array.from(element.lastChild.sheet.cssRules).map((r) -> r.selectorText)).toEqual([
+ '.syntax--class-1',
+ '.syntax--class-2 > .syntax--class-3, .syntax--class-4.syntax--class-5',
+ '.class-6 atom-text-editor .class-7',
+ 'atom-text-editor .class-8, .syntax--class-9',
+ 'atom-text-editor .syntax--indent-guide, atom-text-editor .syntax--leading-whitespace',
+ '.syntax--class-10',
+ 'atom-text-editor .class-11',
+ '#id-1'
+ ])
+ expect(console.warn).toHaveBeenCalled()
- expect(element.firstChild.sheet.cssRules[0].selectorText).toBe ':host'
- expect(element.firstChild.sheet.cssRules[1].selectorText).toBe ':host(.mini)'
- expect(element.firstChild.sheet.cssRules[2].selectorText).toBe ':host(:focus)'
-
- it "defers selector upgrade until the element is attached", ->
- element = new StylesElement
- element.initialize(atom.styles)
- element.setAttribute('context', 'atom-text-editor')
-
- atom.styles.addStyleSheet ".editor {background: black;}", context: 'atom-text-editor'
- expect(element.firstChild.sheet).toBeNull()
-
- document.querySelector('#jasmine-content').appendChild(element)
- expect(element.firstChild.sheet.cssRules[0].selectorText).toBe ':host'
+ describe "when the context of a style sheet is not 'atom-text-editor'", ->
+ it "never prepends class names with `--syntax`", ->
+ atom.styles.addStyleSheet("""
+ .class-1 { color: red; }
+ .class-2 > .class-3, .class-4.class-5 { color: green; }
+ .class-6 atom-text-editor .class-7 { color: yellow; }
+ atom-text-editor .class-8, .class-9 { color: blue; }
+ atom-text-editor .indent-guide, atom-text-editor .leading-whitespace { background: white; }
+ #id-1 { color: gray; }
+ """)
+ expect(Array.from(element.lastChild.sheet.cssRules).map((r) -> r.selectorText)).toEqual([
+ '.class-1'
+ '.class-2 > .class-3, .class-4.class-5'
+ '.class-6 atom-text-editor .class-7'
+ 'atom-text-editor .class-8, .class-9'
+ 'atom-text-editor .indent-guide, atom-text-editor .leading-whitespace'
+ '#id-1'
+ ])
+ expect(console.warn).not.toHaveBeenCalled()
it "does not throw exceptions on rules with no selectors", ->
atom.styles.addStyleSheet """
diff --git a/spec/text-editor-component-spec.js b/spec/text-editor-component-spec.js
index cc7b2bc0a..5feb5761c 100644
--- a/spec/text-editor-component-spec.js
+++ b/spec/text-editor-component-spec.js
@@ -405,49 +405,49 @@ describe('TextEditorComponent', function () {
}
})
- it('applies .leading-whitespace for lines with leading spaces and/or tabs', function () {
+ it('applies .syntax--leading-whitespace for lines with leading spaces and/or tabs', function () {
editor.setText(' a')
runAnimationFrames()
let leafNodes = getLeafNodes(component.lineNodeForScreenRow(0))
- expect(leafNodes[0].classList.contains('leading-whitespace')).toBe(true)
- expect(leafNodes[0].classList.contains('trailing-whitespace')).toBe(false)
+ expect(leafNodes[0].classList.contains('syntax--leading-whitespace')).toBe(true)
+ expect(leafNodes[0].classList.contains('syntax--trailing-whitespace')).toBe(false)
editor.setText('\ta')
runAnimationFrames()
leafNodes = getLeafNodes(component.lineNodeForScreenRow(0))
- expect(leafNodes[0].classList.contains('leading-whitespace')).toBe(true)
- expect(leafNodes[0].classList.contains('trailing-whitespace')).toBe(false)
+ expect(leafNodes[0].classList.contains('syntax--leading-whitespace')).toBe(true)
+ expect(leafNodes[0].classList.contains('syntax--trailing-whitespace')).toBe(false)
})
- it('applies .trailing-whitespace for lines with trailing spaces and/or tabs', function () {
+ it('applies .syntax--trailing-whitespace for lines with trailing spaces and/or tabs', function () {
editor.setText(' ')
runAnimationFrames()
let leafNodes = getLeafNodes(component.lineNodeForScreenRow(0))
- expect(leafNodes[0].classList.contains('trailing-whitespace')).toBe(true)
- expect(leafNodes[0].classList.contains('leading-whitespace')).toBe(false)
+ expect(leafNodes[0].classList.contains('syntax--trailing-whitespace')).toBe(true)
+ expect(leafNodes[0].classList.contains('syntax--leading-whitespace')).toBe(false)
editor.setText('\t')
runAnimationFrames()
leafNodes = getLeafNodes(component.lineNodeForScreenRow(0))
- expect(leafNodes[0].classList.contains('trailing-whitespace')).toBe(true)
- expect(leafNodes[0].classList.contains('leading-whitespace')).toBe(false)
+ expect(leafNodes[0].classList.contains('syntax--trailing-whitespace')).toBe(true)
+ expect(leafNodes[0].classList.contains('syntax--leading-whitespace')).toBe(false)
editor.setText('a ')
runAnimationFrames()
leafNodes = getLeafNodes(component.lineNodeForScreenRow(0))
- expect(leafNodes[0].classList.contains('trailing-whitespace')).toBe(true)
- expect(leafNodes[0].classList.contains('leading-whitespace')).toBe(false)
+ expect(leafNodes[0].classList.contains('syntax--trailing-whitespace')).toBe(true)
+ expect(leafNodes[0].classList.contains('syntax--leading-whitespace')).toBe(false)
editor.setText('a\t')
runAnimationFrames()
leafNodes = getLeafNodes(component.lineNodeForScreenRow(0))
- expect(leafNodes[0].classList.contains('trailing-whitespace')).toBe(true)
- expect(leafNodes[0].classList.contains('leading-whitespace')).toBe(false)
+ expect(leafNodes[0].classList.contains('syntax--trailing-whitespace')).toBe(true)
+ expect(leafNodes[0].classList.contains('syntax--leading-whitespace')).toBe(false)
})
it('keeps rebuilding lines when continuous reflow is on', function () {
@@ -501,14 +501,14 @@ describe('TextEditorComponent', function () {
expect(component.lineNodeForScreenRow(0).textContent).toBe('' + invisibles.space + 'a line with tabs' + invisibles.tab + 'and spaces' + invisibles.space + invisibles.eol)
let leafNodes = getLeafNodes(component.lineNodeForScreenRow(0))
- expect(leafNodes[0].classList.contains('invisible-character')).toBe(true)
- expect(leafNodes[leafNodes.length - 1].classList.contains('invisible-character')).toBe(true)
+ expect(leafNodes[0].classList.contains('syntax--invisible-character')).toBe(true)
+ expect(leafNodes[leafNodes.length - 1].classList.contains('syntax--invisible-character')).toBe(true)
})
it('displays newlines as their own token outside of the other tokens\' scopeDescriptor', function () {
editor.setText('let\n')
runAnimationFrames()
- expect(component.lineNodeForScreenRow(0).innerHTML).toBe('let' + invisibles.eol + '')
+ expect(component.lineNodeForScreenRow(0).innerHTML).toBe('let' + invisibles.eol + '')
})
it('displays trailing carriage returns using a visible, non-empty value', function () {
@@ -543,20 +543,20 @@ describe('TextEditorComponent', function () {
normalizeLineEndings: false
})
runAnimationFrames()
- expect(component.lineNodeForScreenRow(10).innerHTML).toBe('CE')
+ expect(component.lineNodeForScreenRow(10).innerHTML).toBe('CE')
editor.setTabLength(3)
runAnimationFrames()
- expect(component.lineNodeForScreenRow(10).innerHTML).toBe('CE')
+ expect(component.lineNodeForScreenRow(10).innerHTML).toBe('CE')
editor.setTabLength(1)
runAnimationFrames()
- expect(component.lineNodeForScreenRow(10).innerHTML).toBe('CE')
+ expect(component.lineNodeForScreenRow(10).innerHTML).toBe('CE')
editor.setTextInBufferRange([[9, 0], [9, Infinity]], ' ')
editor.setTextInBufferRange([[11, 0], [11, Infinity]], ' ')
runAnimationFrames()
- expect(component.lineNodeForScreenRow(10).innerHTML).toBe('CE')
+ expect(component.lineNodeForScreenRow(10).innerHTML).toBe('CE')
})
describe('when soft wrapping is enabled', function () {
@@ -583,30 +583,30 @@ describe('TextEditorComponent', function () {
runAnimationFrames()
})
- it('adds an "indent-guide" class to spans comprising the leading whitespace', function () {
+ it('adds an "syntax--indent-guide" class to spans comprising the leading whitespace', function () {
let line1LeafNodes = getLeafNodes(component.lineNodeForScreenRow(1))
expect(line1LeafNodes[0].textContent).toBe(' ')
- expect(line1LeafNodes[0].classList.contains('indent-guide')).toBe(true)
- expect(line1LeafNodes[1].classList.contains('indent-guide')).toBe(false)
+ expect(line1LeafNodes[0].classList.contains('syntax--indent-guide')).toBe(true)
+ expect(line1LeafNodes[1].classList.contains('syntax--indent-guide')).toBe(false)
let line2LeafNodes = getLeafNodes(component.lineNodeForScreenRow(2))
expect(line2LeafNodes[0].textContent).toBe(' ')
- expect(line2LeafNodes[0].classList.contains('indent-guide')).toBe(true)
+ expect(line2LeafNodes[0].classList.contains('syntax--indent-guide')).toBe(true)
expect(line2LeafNodes[1].textContent).toBe(' ')
- expect(line2LeafNodes[1].classList.contains('indent-guide')).toBe(true)
- expect(line2LeafNodes[2].classList.contains('indent-guide')).toBe(false)
+ expect(line2LeafNodes[1].classList.contains('syntax--indent-guide')).toBe(true)
+ expect(line2LeafNodes[2].classList.contains('syntax--indent-guide')).toBe(false)
})
- it('renders leading whitespace spans with the "indent-guide" class for empty lines', function () {
+ it('renders leading whitespace spans with the "syntax--indent-guide" class for empty lines', function () {
editor.getBuffer().insert([1, Infinity], '\n')
runAnimationFrames()
let line2LeafNodes = getLeafNodes(component.lineNodeForScreenRow(2))
expect(line2LeafNodes.length).toBe(2)
expect(line2LeafNodes[0].textContent).toBe(' ')
- expect(line2LeafNodes[0].classList.contains('indent-guide')).toBe(true)
+ expect(line2LeafNodes[0].classList.contains('syntax--indent-guide')).toBe(true)
expect(line2LeafNodes[1].textContent).toBe(' ')
- expect(line2LeafNodes[1].classList.contains('indent-guide')).toBe(true)
+ expect(line2LeafNodes[1].classList.contains('syntax--indent-guide')).toBe(true)
})
it('renders indent guides correctly on lines containing only whitespace', function () {
@@ -616,11 +616,11 @@ describe('TextEditorComponent', function () {
let line2LeafNodes = getLeafNodes(component.lineNodeForScreenRow(2))
expect(line2LeafNodes.length).toBe(3)
expect(line2LeafNodes[0].textContent).toBe(' ')
- expect(line2LeafNodes[0].classList.contains('indent-guide')).toBe(true)
+ expect(line2LeafNodes[0].classList.contains('syntax--indent-guide')).toBe(true)
expect(line2LeafNodes[1].textContent).toBe(' ')
- expect(line2LeafNodes[1].classList.contains('indent-guide')).toBe(true)
+ expect(line2LeafNodes[1].classList.contains('syntax--indent-guide')).toBe(true)
expect(line2LeafNodes[2].textContent).toBe(' ')
- expect(line2LeafNodes[2].classList.contains('indent-guide')).toBe(true)
+ expect(line2LeafNodes[2].classList.contains('syntax--indent-guide')).toBe(true)
})
it('renders indent guides correctly on lines containing only whitespace when invisibles are enabled', function () {
@@ -638,11 +638,11 @@ describe('TextEditorComponent', function () {
let line2LeafNodes = getLeafNodes(component.lineNodeForScreenRow(2))
expect(line2LeafNodes.length).toBe(4)
expect(line2LeafNodes[0].textContent).toBe('--')
- expect(line2LeafNodes[0].classList.contains('indent-guide')).toBe(true)
+ expect(line2LeafNodes[0].classList.contains('syntax--indent-guide')).toBe(true)
expect(line2LeafNodes[1].textContent).toBe('--')
- expect(line2LeafNodes[1].classList.contains('indent-guide')).toBe(true)
+ expect(line2LeafNodes[1].classList.contains('syntax--indent-guide')).toBe(true)
expect(line2LeafNodes[2].textContent).toBe('--')
- expect(line2LeafNodes[2].classList.contains('indent-guide')).toBe(true)
+ expect(line2LeafNodes[2].classList.contains('syntax--indent-guide')).toBe(true)
expect(line2LeafNodes[3].textContent).toBe('x')
})
@@ -653,9 +653,9 @@ describe('TextEditorComponent', function () {
let line0LeafNodes = getLeafNodes(component.lineNodeForScreenRow(0))
expect(line0LeafNodes[0].textContent).toBe(' ')
- expect(line0LeafNodes[0].classList.contains('indent-guide')).toBe(true)
+ expect(line0LeafNodes[0].classList.contains('syntax--indent-guide')).toBe(true)
expect(line0LeafNodes[1].textContent).toBe(' ')
- expect(line0LeafNodes[1].classList.contains('indent-guide')).toBe(false)
+ expect(line0LeafNodes[1].classList.contains('syntax--indent-guide')).toBe(false)
})
it('updates the indent guides on empty lines preceding an indentation change', function () {
@@ -667,9 +667,9 @@ describe('TextEditorComponent', function () {
let line12LeafNodes = getLeafNodes(component.lineNodeForScreenRow(12))
expect(line12LeafNodes[0].textContent).toBe(' ')
- expect(line12LeafNodes[0].classList.contains('indent-guide')).toBe(true)
+ expect(line12LeafNodes[0].classList.contains('syntax--indent-guide')).toBe(true)
expect(line12LeafNodes[1].textContent).toBe(' ')
- expect(line12LeafNodes[1].classList.contains('indent-guide')).toBe(true)
+ expect(line12LeafNodes[1].classList.contains('syntax--indent-guide')).toBe(true)
})
it('updates the indent guides on empty lines following an indentation change', function () {
@@ -682,9 +682,9 @@ describe('TextEditorComponent', function () {
let line13LeafNodes = getLeafNodes(component.lineNodeForScreenRow(13))
expect(line13LeafNodes[0].textContent).toBe(' ')
- expect(line13LeafNodes[0].classList.contains('indent-guide')).toBe(true)
+ expect(line13LeafNodes[0].classList.contains('syntax--indent-guide')).toBe(true)
expect(line13LeafNodes[1].textContent).toBe(' ')
- expect(line13LeafNodes[1].classList.contains('indent-guide')).toBe(true)
+ expect(line13LeafNodes[1].classList.contains('syntax--indent-guide')).toBe(true)
})
})
@@ -701,11 +701,11 @@ describe('TextEditorComponent', function () {
let line2LeafNodes = getLeafNodes(component.lineNodeForScreenRow(2))
expect(line2LeafNodes.length).toBe(3)
expect(line2LeafNodes[0].textContent).toBe(' ')
- expect(line2LeafNodes[0].classList.contains('indent-guide')).toBe(false)
+ expect(line2LeafNodes[0].classList.contains('syntax--indent-guide')).toBe(false)
expect(line2LeafNodes[1].textContent).toBe(' ')
- expect(line2LeafNodes[1].classList.contains('indent-guide')).toBe(false)
+ expect(line2LeafNodes[1].classList.contains('syntax--indent-guide')).toBe(false)
expect(line2LeafNodes[2].textContent).toBe(' ')
- expect(line2LeafNodes[2].classList.contains('indent-guide')).toBe(false)
+ expect(line2LeafNodes[2].classList.contains('syntax--indent-guide')).toBe(false)
})
})
@@ -720,19 +720,19 @@ describe('TextEditorComponent', function () {
describe('when there is a fold', function () {
it('renders a fold marker on the folded line', function () {
let foldedLineNode = component.lineNodeForScreenRow(4)
- expect(foldedLineNode.querySelector('.fold-marker')).toBeFalsy()
+ expect(foldedLineNode.querySelector('.syntax--fold-marker')).toBeFalsy()
editor.foldBufferRow(4)
runAnimationFrames()
foldedLineNode = component.lineNodeForScreenRow(4)
- expect(foldedLineNode.querySelector('.fold-marker')).toBeTruthy()
+ expect(foldedLineNode.querySelector('.syntax--fold-marker')).toBeTruthy()
editor.unfoldBufferRow(4)
runAnimationFrames()
foldedLineNode = component.lineNodeForScreenRow(4)
- expect(foldedLineNode.querySelector('.fold-marker')).toBeFalsy()
+ expect(foldedLineNode.querySelector('.syntax--fold-marker')).toBeFalsy()
})
})
})
@@ -1251,7 +1251,7 @@ describe('TextEditorComponent', function () {
let cursor = componentNode.querySelector('.cursor')
let cursorRect = cursor.getBoundingClientRect()
- let cursorLocationTextNode = component.lineNodeForScreenRow(0).querySelector('.storage.type.function.js').firstChild
+ let cursorLocationTextNode = component.lineNodeForScreenRow(0).querySelector('.syntax--storage.syntax--type.syntax--function.syntax--js').firstChild
let range = document.createRange()
range.setStart(cursorLocationTextNode, 0)
range.setEnd(cursorLocationTextNode, 1)
@@ -1268,7 +1268,7 @@ describe('TextEditorComponent', function () {
let cursor = componentNode.querySelector('.cursor')
let cursorRect = cursor.getBoundingClientRect()
- let cursorLocationTextNode = component.lineNodeForScreenRow(0).querySelector('.source.js').childNodes[2]
+ let cursorLocationTextNode = component.lineNodeForScreenRow(0).querySelector('.syntax--source.syntax--js').childNodes[2]
let range = document.createRange(cursorLocationTextNode)
range.setStart(cursorLocationTextNode, 0)
range.setEnd(cursorLocationTextNode, 1)
@@ -1284,7 +1284,7 @@ describe('TextEditorComponent', function () {
runAnimationFrames()
let cursorRect = componentNode.querySelector('.cursor').getBoundingClientRect()
- let foldMarkerRect = componentNode.querySelector('.fold-marker').getBoundingClientRect()
+ let foldMarkerRect = componentNode.querySelector('.syntax--fold-marker').getBoundingClientRect()
expect(cursorRect.left).toBeCloseTo(foldMarkerRect.right, 0)
})
@@ -1293,14 +1293,14 @@ describe('TextEditorComponent', function () {
editor.setCursorScreenPosition([0, 16])
runAnimationFrames(true)
- atom.styles.addStyleSheet('.function.js {\n font-weight: bold;\n}', {
+ atom.styles.addStyleSheet('.syntax--function.syntax--js {\n font-weight: bold;\n}', {
context: 'atom-text-editor'
})
runAnimationFrames(true)
let cursor = componentNode.querySelector('.cursor')
let cursorRect = cursor.getBoundingClientRect()
- let cursorLocationTextNode = component.lineNodeForScreenRow(0).querySelector('.storage.type.function.js').firstChild
+ let cursorLocationTextNode = component.lineNodeForScreenRow(0).querySelector('.syntax--storage.syntax--type.syntax--function.syntax--js').firstChild
let range = document.createRange()
range.setStart(cursorLocationTextNode, 0)
range.setEnd(cursorLocationTextNode, 1)
@@ -2872,19 +2872,20 @@ describe('TextEditorComponent', function () {
editor.foldBufferRange([[4, 6], [4, 10]])
editor.foldBufferRange([[4, 15], [4, 20]])
runAnimationFrames()
- let foldMarkers = component.lineNodeForScreenRow(4).querySelectorAll('.fold-marker')
+ debugger
+ let foldMarkers = component.lineNodeForScreenRow(4).querySelectorAll('.syntax--fold-marker')
expect(foldMarkers.length).toBe(2)
expect(editor.isFoldedAtBufferRow(4)).toBe(true)
clickElementAtPosition(foldMarkers[0], [4, 6])
runAnimationFrames()
- foldMarkers = component.lineNodeForScreenRow(4).querySelectorAll('.fold-marker')
+ foldMarkers = component.lineNodeForScreenRow(4).querySelectorAll('.syntax--fold-marker')
expect(foldMarkers.length).toBe(1)
expect(editor.isFoldedAtBufferRow(4)).toBe(true)
clickElementAtPosition(foldMarkers[0], [4, 15])
runAnimationFrames()
- foldMarkers = component.lineNodeForScreenRow(4).querySelectorAll('.fold-marker')
+ foldMarkers = component.lineNodeForScreenRow(4).querySelectorAll('.syntax--fold-marker')
expect(foldMarkers.length).toBe(0)
expect(editor.isFoldedAtBufferRow(4)).toBe(false)
})
@@ -2894,25 +2895,25 @@ describe('TextEditorComponent', function () {
editor.foldBufferRange([[4, 4], [4, 5]])
editor.foldBufferRange([[4, 4], [4, 20]])
runAnimationFrames()
- let foldMarkers = component.lineNodeForScreenRow(4).querySelectorAll('.fold-marker')
+ let foldMarkers = component.lineNodeForScreenRow(4).querySelectorAll('.syntax--fold-marker')
expect(foldMarkers.length).toBe(1)
expect(editor.isFoldedAtBufferRow(4)).toBe(true)
clickElementAtPosition(foldMarkers[0], [4, 4])
runAnimationFrames()
- foldMarkers = component.lineNodeForScreenRow(4).querySelectorAll('.fold-marker')
+ foldMarkers = component.lineNodeForScreenRow(4).querySelectorAll('.syntax--fold-marker')
expect(foldMarkers.length).toBe(1)
expect(editor.isFoldedAtBufferRow(4)).toBe(true)
clickElementAtPosition(foldMarkers[0], [4, 4])
runAnimationFrames()
- foldMarkers = component.lineNodeForScreenRow(4).querySelectorAll('.fold-marker')
+ foldMarkers = component.lineNodeForScreenRow(4).querySelectorAll('.syntax--fold-marker')
expect(foldMarkers.length).toBe(1)
expect(editor.isFoldedAtBufferRow(4)).toBe(true)
clickElementAtPosition(foldMarkers[0], [4, 10])
runAnimationFrames()
- foldMarkers = component.lineNodeForScreenRow(4).querySelectorAll('.fold-marker')
+ foldMarkers = component.lineNodeForScreenRow(4).querySelectorAll('.syntax--fold-marker')
expect(foldMarkers.length).toBe(0)
expect(editor.isFoldedAtBufferRow(4)).toBe(false)
})
@@ -4292,7 +4293,7 @@ describe('TextEditorComponent', function () {
atom.config.set('editor.fontFamily', 'sans-serif')
wrapperNode.style.display = 'none'
component.checkForVisibilityChange()
- atom.themes.applyStylesheet('test', '.function.js {\n font-weight: bold;\n}')
+ atom.themes.applyStylesheet('test', '.syntax--function.syntax--js {\n font-weight: bold;\n}')
wrapperNode.style.display = ''
component.checkForVisibilityChange()
editor.setCursorBufferPosition([0, Infinity])
diff --git a/src/lines-tile-component.coffee b/src/lines-tile-component.coffee
index c1cb2ba64..c7e3bf367 100644
--- a/src/lines-tile-component.coffee
+++ b/src/lines-tile-component.coffee
@@ -202,8 +202,8 @@ class LinesTileComponent
if @presenter.isCloseTagCode(tagCode)
openScopeNode = openScopeNode.parentElement
else if @presenter.isOpenTagCode(tagCode)
- scope = @presenter.tagForCode(tagCode)
- newScopeNode = @domElementPool.buildElement("span", scope.replace(/\.+/g, ' '))
+ scopes = @presenter.tagForCode(tagCode).replace(/\s+/g, '.').split('.').map((scope) -> "syntax--#{scope}")
+ newScopeNode = @domElementPool.buildElement("span", scopes.join(' '))
openScopeNode.appendChild(newScopeNode)
openScopeNode = newScopeNode
else
@@ -219,7 +219,7 @@ class LinesTileComponent
if lineText.endsWith(@presenter.displayLayer.foldCharacter)
# Insert a zero-width non-breaking whitespace, so that
- # LinesYardstick can take the fold-marker::after pseudo-element
+ # LinesYardstick can take the syntax--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)
diff --git a/src/styles-element.coffee b/src/styles-element.coffee
index d1e6bf3d9..e82775c92 100644
--- a/src/styles-element.coffee
+++ b/src/styles-element.coffee
@@ -1,4 +1,9 @@
{Emitter, CompositeDisposable} = require 'event-kit'
+selectorProcessor = require 'postcss-selector-parser'
+SPATIAL_DECORATIONS = new Set([
+ 'invisible-character', 'hard-tab', 'leading-whitespace',
+ 'trailing-whitespace', 'eol', 'indent-guide', 'fold-marker'
+])
class StylesElement extends HTMLElement
subscriptions: null
@@ -19,9 +24,8 @@ class StylesElement extends HTMLElement
@styleElementClonesByOriginalElement = new WeakMap
attachedCallback: ->
- if @context is 'atom-text-editor'
- for styleElement in @children
- @upgradeDeprecatedSelectors(styleElement)
+ for styleElement in @children
+ @upgradeDeprecatedSelectors(styleElement)
@context = @getAttribute('context') ? undefined
@@ -64,10 +68,7 @@ class StylesElement extends HTMLElement
break
@insertBefore(styleElementClone, insertBefore)
-
- if @context is 'atom-text-editor'
- @upgradeDeprecatedSelectors(styleElementClone)
-
+ @upgradeDeprecatedSelectors(styleElementClone)
@emitter.emit 'did-add-style-element', styleElementClone
styleElementRemoved: (styleElement) ->
@@ -90,28 +91,49 @@ class StylesElement extends HTMLElement
upgradeDeprecatedSelectors: (styleElement) ->
return unless styleElement.sheet?
+ transformDeprecatedShadowSelectors = (selectors) ->
+ selectors.each (selector) ->
+ isSyntaxSelector = not selector.some((node) ->
+ (node.type is 'tag' and node.value is 'atom-text-editor') or
+ (node.type is 'class' and node.value is 'region') or
+ (node.type is 'class' and node.value is 'wrap-guide') or
+ (node.type is 'class' and /spell-check/.test(node.value))
+ )
+ previousNode = null
+ selector.each (node) ->
+ isShadowPseudoClass = node.type is 'pseudo' and node.value is '::shadow'
+ isHostPseudoClass = node.type is 'pseudo' and node.value is ':host'
+ if isHostPseudoClass and not previousNode?
+ newNode = selectorProcessor.tag({value: 'atom-text-editor'})
+ node.replaceWith(newNode)
+ previousNode = newNode
+ else if isShadowPseudoClass and previousNode?.type is 'tag' and previousNode?.value is 'atom-text-editor'
+ selector.removeChild(node)
+ else
+ if styleElement.context is 'atom-text-editor' and node.type is 'class'
+ if (isSyntaxSelector and not node.value.startsWith('syntax--')) or SPATIAL_DECORATIONS.has(node.value)
+ node.value = 'syntax--' + node.value
+ previousNode = node
+
upgradedSelectors = []
-
- for rule in styleElement.sheet.cssRules
- continue unless rule.selectorText?
- continue if /\:host/.test(rule.selectorText)
-
+ for rule in styleElement.sheet.cssRules when 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
+ outputSelector = selectorProcessor(transformDeprecatedShadowSelectors).process(outputSelector).result
+ if inputSelector isnt 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"
+ upgradedSelectorsText = upgradedSelectors.map(({inputSelector, outputSelector}) -> "`#{inputSelector}` => `#{outputSelector}`").join('\n')
+ console.warn("""
+ Shadow DOM for `atom-text-editor` elements has been removed. This means
+ should stop using :host and ::shadow pseudo-selectors, and prepend all
+ your syntax selectors with `syntax--`. To prevent breakage with existing
+ stylesheets, we have automatically upgraded the following selectors in
+ `#{styleElement.sourcePath}`:
- warning += "\nSee the upgrade guide for information on removing this warning."
- console.warn(warning)
+ #{upgradedSelectorsText}
+ """)
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 596adb7c5..e01096ed6 100644
--- a/src/text-editor-component.coffee
+++ b/src/text-editor-component.coffee
@@ -538,7 +538,7 @@ class TextEditorComponent
screenPosition = @screenPositionForMouseEvent(event)
- if event.target?.classList.contains('fold-marker')
+ if event.target?.classList.contains('syntax--fold-marker')
bufferPosition = @editor.bufferPositionForScreenPosition(screenPosition)
@editor.destroyFoldsIntersectingBufferRange([bufferPosition, bufferPosition])
return
diff --git a/static/text-editor-light.less b/static/text-editor-light.less
index acc7b4472..1f16ec71e 100644
--- a/static/text-editor-light.less
+++ b/static/text-editor-light.less
@@ -20,7 +20,6 @@ atom-overlay {
z-index: 4;
}
-// TODO: Remove the following styles when the editor shadow DOM can no longer be disabled
atom-text-editor {
display: flex;
@@ -123,12 +122,12 @@ atom-text-editor {
.line {
white-space: pre;
- &.cursor-line .fold-marker::after {
+ &.cursor-line .syntax--fold-marker::after {
opacity: 1;
}
}
- .fold-marker {
+ .syntax--fold-marker {
cursor: default;
&::after {
@@ -144,12 +143,12 @@ atom-text-editor {
color: @text-color-subtle;
}
- .invisible-character {
+ .syntax--invisible-character {
font-weight: normal !important;
font-style: normal !important;
}
- .indent-guide {
+ .syntax--indent-guide {
display: inline-block;
box-shadow: inset 1px 0;
}