Merge pull request #3943 from atom/ns-text-editor-shadow-dom

Render text editor contents inside shadow DOM
Esse commit está contido em:
Nathan Sobo
2014-11-05 09:42:07 -07:00
31 arquivos alterados com 667 adições e 474 exclusões
+3
Ver Arquivo
@@ -15,6 +15,9 @@
'shift-tab': 'editor:outdent-selected-rows' 'shift-tab': 'editor:outdent-selected-rows'
'ctrl-K': 'editor:delete-line' 'ctrl-K': 'editor:delete-line'
'.select-list atom-text-editor.mini':
'enter': 'core:confirm'
'.tool-panel.panel-left, .tool-panel.panel-right': '.tool-panel.panel-left, .tool-panel.panel-right':
'escape': 'tool-panel:unfocus' 'escape': 'tool-panel:unfocus'
+2 -2
Ver Arquivo
@@ -73,7 +73,7 @@
"solarized-dark-syntax": "0.22.0", "solarized-dark-syntax": "0.22.0",
"solarized-light-syntax": "0.12.0", "solarized-light-syntax": "0.12.0",
"archive-view": "0.37.0", "archive-view": "0.37.0",
"autocomplete": "0.32.0", "autocomplete": "0.33.0",
"autoflow": "0.18.0", "autoflow": "0.18.0",
"autosave": "0.18.0", "autosave": "0.18.0",
"background-tips": "0.17.0", "background-tips": "0.17.0",
@@ -96,7 +96,7 @@
"markdown-preview": "0.107.0", "markdown-preview": "0.107.0",
"metrics": "0.36.0", "metrics": "0.36.0",
"open-on-github": "0.30.0", "open-on-github": "0.30.0",
"package-generator": "0.31.0", "package-generator": "0.32.0",
"release-notes": "0.36.0", "release-notes": "0.36.0",
"settings-view": "0.154.0", "settings-view": "0.154.0",
"snippets": "0.56.0", "snippets": "0.56.0",
@@ -0,0 +1 @@
a { color: red }
+59 -35
Ver Arquivo
@@ -48,9 +48,10 @@ describe "PackageManager", ->
describe "when called multiple times", -> describe "when called multiple times", ->
it "it only calls activate on the package once", -> it "it only calls activate on the package once", ->
spyOn(Package.prototype, 'activateNow').andCallThrough() spyOn(Package.prototype, 'activateNow').andCallThrough()
atom.packages.activatePackage('package-with-index') waitsForPromise ->
atom.packages.activatePackage('package-with-index') atom.packages.activatePackage('package-with-index')
waitsForPromise ->
atom.packages.activatePackage('package-with-index')
waitsForPromise -> waitsForPromise ->
atom.packages.activatePackage('package-with-index') atom.packages.activatePackage('package-with-index')
@@ -182,8 +183,10 @@ describe "PackageManager", ->
pack.mainModule.someNumber = 77 pack.mainModule.someNumber = 77
atom.packages.deactivatePackage("package-with-serialization") atom.packages.deactivatePackage("package-with-serialization")
spyOn(pack.mainModule, 'activate').andCallThrough() spyOn(pack.mainModule, 'activate').andCallThrough()
atom.packages.activatePackage("package-with-serialization") waitsForPromise ->
expect(pack.mainModule.activate).toHaveBeenCalledWith({someNumber: 77}) atom.packages.activatePackage("package-with-serialization")
runs ->
expect(pack.mainModule.activate).toHaveBeenCalledWith({someNumber: 77})
it "logs warning instead of throwing an exception if the package fails to load", -> it "logs warning instead of throwing an exception if the package fails to load", ->
atom.config.set("core.disabledPackages", []) atom.config.set("core.disabledPackages", [])
@@ -202,11 +205,13 @@ describe "PackageManager", ->
expect(atom.keymaps.findKeyBindings(keystrokes:'ctrl-z', target:element2[0])).toHaveLength 0 expect(atom.keymaps.findKeyBindings(keystrokes:'ctrl-z', target:element2[0])).toHaveLength 0
expect(atom.keymaps.findKeyBindings(keystrokes:'ctrl-z', target:element3[0])).toHaveLength 0 expect(atom.keymaps.findKeyBindings(keystrokes:'ctrl-z', target:element3[0])).toHaveLength 0
atom.packages.activatePackage("package-with-keymaps") waitsForPromise ->
atom.packages.activatePackage("package-with-keymaps")
expect(atom.keymaps.findKeyBindings(keystrokes:'ctrl-z', target:element1[0])[0].command).toBe "test-1" runs ->
expect(atom.keymaps.findKeyBindings(keystrokes:'ctrl-z', target:element2[0])[0].command).toBe "test-2" expect(atom.keymaps.findKeyBindings(keystrokes:'ctrl-z', target:element1[0])[0].command).toBe "test-1"
expect(atom.keymaps.findKeyBindings(keystrokes:'ctrl-z', target:element3[0])).toHaveLength 0 expect(atom.keymaps.findKeyBindings(keystrokes:'ctrl-z', target:element2[0])[0].command).toBe "test-2"
expect(atom.keymaps.findKeyBindings(keystrokes:'ctrl-z', target:element3[0])).toHaveLength 0
describe "when the metadata contains a 'keymaps' manifest", -> describe "when the metadata contains a 'keymaps' manifest", ->
it "loads only the keymaps specified by the manifest, in the specified order", -> it "loads only the keymaps specified by the manifest, in the specified order", ->
@@ -215,11 +220,13 @@ describe "PackageManager", ->
expect(atom.keymaps.findKeyBindings(keystrokes:'ctrl-z', target:element1[0])).toHaveLength 0 expect(atom.keymaps.findKeyBindings(keystrokes:'ctrl-z', target:element1[0])).toHaveLength 0
atom.packages.activatePackage("package-with-keymaps-manifest") waitsForPromise ->
atom.packages.activatePackage("package-with-keymaps-manifest")
expect(atom.keymaps.findKeyBindings(keystrokes:'ctrl-z', target:element1[0])[0].command).toBe 'keymap-1' runs ->
expect(atom.keymaps.findKeyBindings(keystrokes:'ctrl-n', target:element1[0])[0].command).toBe 'keymap-2' expect(atom.keymaps.findKeyBindings(keystrokes:'ctrl-z', target:element1[0])[0].command).toBe 'keymap-1'
expect(atom.keymaps.findKeyBindings(keystrokes:'ctrl-y', target:element3[0])).toHaveLength 0 expect(atom.keymaps.findKeyBindings(keystrokes:'ctrl-n', target:element1[0])[0].command).toBe 'keymap-2'
expect(atom.keymaps.findKeyBindings(keystrokes:'ctrl-y', target:element3[0])).toHaveLength 0
describe "menu loading", -> describe "menu loading", ->
beforeEach -> beforeEach ->
@@ -232,14 +239,16 @@ describe "PackageManager", ->
expect(atom.contextMenu.templateForElement(element)).toEqual [] expect(atom.contextMenu.templateForElement(element)).toEqual []
atom.packages.activatePackage("package-with-menus") waitsForPromise ->
atom.packages.activatePackage("package-with-menus")
expect(atom.menu.template.length).toBe 2 runs ->
expect(atom.menu.template[0].label).toBe "Second to Last" expect(atom.menu.template.length).toBe 2
expect(atom.menu.template[1].label).toBe "Last" expect(atom.menu.template[0].label).toBe "Second to Last"
expect(atom.contextMenu.templateForElement(element)[0].label).toBe "Menu item 1" expect(atom.menu.template[1].label).toBe "Last"
expect(atom.contextMenu.templateForElement(element)[1].label).toBe "Menu item 2" expect(atom.contextMenu.templateForElement(element)[0].label).toBe "Menu item 1"
expect(atom.contextMenu.templateForElement(element)[2].label).toBe "Menu item 3" expect(atom.contextMenu.templateForElement(element)[1].label).toBe "Menu item 2"
expect(atom.contextMenu.templateForElement(element)[2].label).toBe "Menu item 3"
describe "when the metadata contains a 'menus' manifest", -> describe "when the metadata contains a 'menus' manifest", ->
it "loads only the menus specified by the manifest, in the specified order", -> it "loads only the menus specified by the manifest, in the specified order", ->
@@ -247,13 +256,15 @@ describe "PackageManager", ->
expect(atom.contextMenu.templateForElement(element)).toEqual [] expect(atom.contextMenu.templateForElement(element)).toEqual []
atom.packages.activatePackage("package-with-menus-manifest") waitsForPromise ->
atom.packages.activatePackage("package-with-menus-manifest")
expect(atom.menu.template[0].label).toBe "Second to Last" runs ->
expect(atom.menu.template[1].label).toBe "Last" expect(atom.menu.template[0].label).toBe "Second to Last"
expect(atom.contextMenu.templateForElement(element)[0].label).toBe "Menu item 2" expect(atom.menu.template[1].label).toBe "Last"
expect(atom.contextMenu.templateForElement(element)[1].label).toBe "Menu item 1" expect(atom.contextMenu.templateForElement(element)[0].label).toBe "Menu item 2"
expect(atom.contextMenu.templateForElement(element)[2]).toBeUndefined() expect(atom.contextMenu.templateForElement(element)[1].label).toBe "Menu item 1"
expect(atom.contextMenu.templateForElement(element)[2]).toBeUndefined()
describe "stylesheet loading", -> describe "stylesheet loading", ->
describe "when the metadata contains a 'stylesheets' manifest", -> describe "when the metadata contains a 'stylesheets' manifest", ->
@@ -270,12 +281,14 @@ describe "PackageManager", ->
expect(atom.themes.stylesheetElementForId(two)).toBeNull() expect(atom.themes.stylesheetElementForId(two)).toBeNull()
expect(atom.themes.stylesheetElementForId(three)).toBeNull() expect(atom.themes.stylesheetElementForId(three)).toBeNull()
atom.packages.activatePackage("package-with-stylesheets-manifest") waitsForPromise ->
atom.packages.activatePackage("package-with-stylesheets-manifest")
expect(atom.themes.stylesheetElementForId(one)).not.toBeNull() runs ->
expect(atom.themes.stylesheetElementForId(two)).not.toBeNull() expect(atom.themes.stylesheetElementForId(one)).not.toBeNull()
expect(atom.themes.stylesheetElementForId(three)).toBeNull() expect(atom.themes.stylesheetElementForId(two)).not.toBeNull()
expect($('#jasmine-content').css('font-size')).toBe '1px' expect(atom.themes.stylesheetElementForId(three)).toBeNull()
expect($('#jasmine-content').css('font-size')).toBe '1px'
describe "when the metadata does not contain a 'stylesheets' manifest", -> describe "when the metadata does not contain a 'stylesheets' manifest", ->
it "loads all stylesheets from the stylesheets directory", -> it "loads all stylesheets from the stylesheets directory", ->
@@ -292,11 +305,22 @@ describe "PackageManager", ->
expect(atom.themes.stylesheetElementForId(two)).toBeNull() expect(atom.themes.stylesheetElementForId(two)).toBeNull()
expect(atom.themes.stylesheetElementForId(three)).toBeNull() expect(atom.themes.stylesheetElementForId(three)).toBeNull()
waitsForPromise ->
atom.packages.activatePackage("package-with-stylesheets")
runs ->
expect(atom.themes.stylesheetElementForId(one)).not.toBeNull()
expect(atom.themes.stylesheetElementForId(two)).not.toBeNull()
expect(atom.themes.stylesheetElementForId(three)).not.toBeNull()
expect($('#jasmine-content').css('font-size')).toBe '3px'
it "assigns the stylesheet's context based on the filename", ->
waitsForPromise ->
atom.packages.activatePackage("package-with-stylesheets") atom.packages.activatePackage("package-with-stylesheets")
expect(atom.themes.stylesheetElementForId(one)).not.toBeNull()
expect(atom.themes.stylesheetElementForId(two)).not.toBeNull() runs ->
expect(atom.themes.stylesheetElementForId(three)).not.toBeNull() element = atom.styles.getStyleElements().find (element) -> element.context is 'test-context'
expect($('#jasmine-content').css('font-size')).toBe '3px' expect(element).toBeDefined()
describe "grammar loading", -> describe "grammar loading", ->
it "loads the package's grammars", -> it "loads the package's grammars", ->
+1 -1
Ver Arquivo
@@ -92,7 +92,7 @@ describe "Package", ->
it "reloads without readding to the stylesheets list", -> it "reloads without readding to the stylesheets list", ->
expect(theme.getStylesheetPaths().length).toBe 3 expect(theme.getStylesheetPaths().length).toBe 3
theme.reloadStylesheet(theme.getStylesheetPaths()[0]) theme.reloadStylesheets()
expect(theme.getStylesheetPaths().length).toBe 3 expect(theme.getStylesheetPaths().length).toBe 3
describe "events", -> describe "events", ->
+1 -1
Ver Arquivo
@@ -184,7 +184,7 @@ describe "SelectListView", ->
describe "when the mini editor loses focus", -> describe "when the mini editor loses focus", ->
it "triggers the cancelled hook and detaches the select list", -> it "triggers the cancelled hook and detaches the select list", ->
spyOn(selectList, 'detach') spyOn(selectList, 'detach')
filterEditorView.hiddenInput.trigger 'focusout' filterEditorView.trigger 'blur'
expect(selectList.cancelled).toHaveBeenCalled() expect(selectList.cancelled).toHaveBeenCalled()
expect(selectList.detach).toHaveBeenCalled() expect(selectList.detach).toHaveBeenCalled()
+4 -7
Ver Arquivo
@@ -106,6 +106,7 @@ beforeEach ->
config.set "editor.autoIndent", false config.set "editor.autoIndent", false
config.set "core.disabledPackages", ["package-that-throws-an-exception", config.set "core.disabledPackages", ["package-that-throws-an-exception",
"package-with-broken-package-json", "package-with-broken-keymap"] "package-with-broken-package-json", "package-with-broken-keymap"]
config.set "editor.useShadowDOM", true
config.load.reset() config.load.reset()
config.save.reset() config.save.reset()
@@ -219,7 +220,7 @@ addCustomMatchers = (spec) ->
@message = -> return "Expected element '" + @actual + "' or its descendants" + notText + " to have focus." @message = -> return "Expected element '" + @actual + "' or its descendants" + notText + " to have focus."
element = @actual element = @actual
element = element.get(0) if element.jquery element = element.get(0) if element.jquery
element.webkitMatchesSelector(":focus") or element.querySelector(":focus") element is document.activeElement or element.contains(document.activeElement)
toShow: -> toShow: ->
notText = if @isNot then " not" else "" notText = if @isNot then " not" else ""
@@ -338,12 +339,8 @@ window.setEditorWidthInChars = (editorView, widthInChars, charWidth=editorView.c
$(window).trigger 'resize' # update width of editor view's on-screen lines $(window).trigger 'resize' # update width of editor view's on-screen lines
window.setEditorHeightInLines = (editorView, heightInLines, lineHeight=editorView.lineHeight) -> window.setEditorHeightInLines = (editorView, heightInLines, lineHeight=editorView.lineHeight) ->
if editorView.hasClass('react') editorView.height(editorView.getEditor().getLineHeightInPixels() * heightInLines)
editorView.height(editorView.getEditor().getLineHeightInPixels() * heightInLines) editorView.component?.measureHeightAndWidth()
editorView.component?.measureHeightAndWidth()
else
editorView.height(lineHeight * heightInLines + editorView.renderedLines.position().top)
$(window).trigger 'resize' # update editor view's on-screen lines
$.fn.resultOfTrigger = (type) -> $.fn.resultOfTrigger = (type) ->
event = $.Event(type) event = $.Event(type)
+21
Ver Arquivo
@@ -52,3 +52,24 @@ describe "StylesElement", ->
expect(element.children.length).toBe initialChildCount + 1 expect(element.children.length).toBe initialChildCount + 1
expect(element.children[initialChildCount].textContent).toBe "a {color: blue;}" expect(element.children[initialChildCount].textContent).toBe "a {color: blue;}"
expect(updatedStyleElements).toEqual [element.children[initialChildCount]] expect(updatedStyleElements).toEqual [element.children[initialChildCount]]
it "only includes style elements matching the 'context' attribute", ->
initialChildCount = element.children.length
atom.styles.addStyleSheet("a {color: red;}", context: 'test-context')
atom.styles.addStyleSheet("a {color: green;}")
expect(element.children.length).toBe initialChildCount + 1
expect(element.children[initialChildCount].textContent).toBe "a {color: green;}"
element.setAttribute('context', 'test-context')
expect(element.children.length).toBe 1
expect(element.children[0].textContent).toBe "a {color: red;}"
atom.styles.addStyleSheet("a {color: blue;}", context: 'test-context')
atom.styles.addStyleSheet("a {color: yellow;}")
expect(element.children.length).toBe 2
expect(element.children[0].textContent).toBe "a {color: red;}"
expect(element.children[1].textContent).toBe "a {color: blue;}"
+8 -6
Ver Arquivo
@@ -721,11 +721,11 @@ describe "TextEditorComponent", ->
editor.setCursorScreenPosition([0, 16]) editor.setCursorScreenPosition([0, 16])
nextAnimationFrame() nextAnimationFrame()
atom.themes.applyStylesheet 'test', """ atom.styles.addStyleSheet """
.function.js { .function.js {
font-weight: bold; font-weight: bold;
} }
""" """, context: 'atom-text-editor'
nextAnimationFrame() # update based on new measurements nextAnimationFrame() # update based on new measurements
cursor = componentNode.querySelector('.cursor') cursor = componentNode.querySelector('.cursor')
@@ -1537,8 +1537,9 @@ describe "TextEditorComponent", ->
it "transfers focus to the hidden input", -> it "transfers focus to the hidden input", ->
expect(document.activeElement).toBe document.body expect(document.activeElement).toBe document.body
componentNode.focus() wrapperNode.focus()
expect(document.activeElement).toBe inputNode expect(document.activeElement).toBe wrapperNode
expect(wrapperNode.shadowRoot.activeElement).toBe inputNode
it "adds the 'is-focused' class to the editor when the hidden input is focused", -> it "adds the 'is-focused' class to the editor when the hidden input is focused", ->
expect(document.activeElement).toBe document.body expect(document.activeElement).toBe document.body
@@ -1667,12 +1668,13 @@ describe "TextEditorComponent", ->
component.measureHeightAndWidth() component.measureHeightAndWidth()
nextAnimationFrame() nextAnimationFrame()
atom.themes.applyStylesheet "test", """ atom.styles.addStyleSheet """
::-webkit-scrollbar { ::-webkit-scrollbar {
width: 8px; width: 8px;
height: 8px; height: 8px;
} }
""" """, context: 'atom-text-editor'
nextAnimationFrame() nextAnimationFrame()
scrollbarCornerNode = componentNode.querySelector('.scrollbar-corner') scrollbarCornerNode = componentNode.querySelector('.scrollbar-corner')
+38 -13
Ver Arquivo
@@ -19,18 +19,43 @@ describe "TextEditorElement", ->
element = jasmineContent.firstChild element = jasmineContent.firstChild
expect(element.getModel().getPlaceholderText()).toBe 'testing' expect(element.getModel().getPlaceholderText()).toBe 'testing'
describe "::focus()", -> describe "focus and blur handling", ->
it "transfers focus to the hidden text area and does not emit 'focusout' or 'blur' events", -> describe "when the editor.useShadowDOM config option is true", ->
element = new TextEditorElement it "proxies focus/blur events to/from the hidden input inside the shadow root", ->
jasmineContent.appendChild(element) atom.config.set('editor.useShadowDOM', true)
focusoutCalled = false element = new TextEditorElement
element.addEventListener 'focusout', -> focusoutCalled = true jasmineContent.appendChild(element)
blurCalled = false
element.addEventListener 'blur', -> blurCalled = true
element.focus() blurCalled = false
expect(focusoutCalled).toBe false element.addEventListener 'blur', -> blurCalled = true
expect(blurCalled).toBe false
expect(element.hasFocus()).toBe true element.focus()
expect(element.querySelector('input')).toBe document.activeElement expect(blurCalled).toBe false
expect(element.hasFocus()).toBe true
expect(document.activeElement).toBe element
expect(element.shadowRoot.activeElement).toBe element.shadowRoot.querySelector('input')
document.body.focus()
expect(blurCalled).toBe true
describe "when the editor.useShadowDOM config option is false", ->
afterEach ->
document.head.querySelector('atom-styles[context="atom-text-editor"]').remove()
it "proxies focus/blur events to/from the hidden input", ->
atom.config.set('editor.useShadowDOM', false)
element = new TextEditorElement
jasmineContent.appendChild(element)
blurCalled = false
element.addEventListener 'blur', -> blurCalled = true
element.focus()
expect(blurCalled).toBe false
expect(element.hasFocus()).toBe true
expect(document.activeElement).toBe element.querySelector('input')
document.body.focus()
expect(blurCalled).toBe true
+13 -13
Ver Arquivo
@@ -85,7 +85,16 @@ describe "ThemeManager", ->
runs -> runs ->
reloadHandler.reset() reloadHandler.reset()
expect($('style.theme')).toHaveLength 0 expect($('style.theme')).toHaveLength 0
atom.config.set('core.themes', ['atom-dark-syntax']) atom.config.set('core.themes', ['atom-dark-ui'])
waitsFor ->
reloadHandler.callCount == 1
runs ->
reloadHandler.reset()
expect($('style[group=theme]')).toHaveLength 1
expect($('style[group=theme]:eq(0)').attr('source-path')).toMatch /atom-dark-ui/
atom.config.set('core.themes', ['atom-light-ui', 'atom-dark-ui'])
waitsFor -> waitsFor ->
reloadHandler.callCount == 1 reloadHandler.callCount == 1
@@ -93,17 +102,8 @@ describe "ThemeManager", ->
runs -> runs ->
reloadHandler.reset() reloadHandler.reset()
expect($('style[group=theme]')).toHaveLength 2 expect($('style[group=theme]')).toHaveLength 2
expect($('style[group=theme]:eq(1)').attr('source-path')).toMatch /atom-dark-syntax/ expect($('style[group=theme]:eq(0)').attr('source-path')).toMatch /atom-dark-ui/
atom.config.set('core.themes', ['atom-light-syntax', 'atom-dark-syntax']) expect($('style[group=theme]:eq(1)').attr('source-path')).toMatch /atom-light-ui/
waitsFor ->
reloadHandler.callCount == 1
runs ->
reloadHandler.reset()
expect($('style[group=theme]')).toHaveLength 2
expect($('style[group=theme]:eq(0)').attr('source-path')).toMatch /atom-dark-syntax/
expect($('style[group=theme]:eq(1)').attr('source-path')).toMatch /atom-light-syntax/
atom.config.set('core.themes', []) atom.config.set('core.themes', [])
waitsFor -> waitsFor ->
@@ -111,7 +111,7 @@ describe "ThemeManager", ->
runs -> runs ->
reloadHandler.reset() reloadHandler.reset()
expect($('style[group=theme]')).toHaveLength 2 expect($('style[group=theme]')).toHaveLength 1
# atom-dark-ui has an directory path, the syntax one doesn't # atom-dark-ui has an directory path, the syntax one doesn't
atom.config.set('core.themes', ['theme-with-index-less', 'atom-dark-ui']) atom.config.set('core.themes', ['theme-with-index-less', 'atom-dark-ui'])
+5
Ver Arquivo
@@ -109,6 +109,11 @@ module.exports =
type: 'boolean' type: 'boolean'
default: true default: true
description: 'Disabling will improve editor font rendering but reduce scrolling performance.' description: 'Disabling will improve editor font rendering but reduce scrolling performance.'
useShadowDOM:
type: 'boolean'
default: false
title: 'Use Shadow DOM'
description: 'Enable to test out themes and packages with the new shadow DOM before it ships by default.'
confirmCheckoutHeadRevision: confirmCheckoutHeadRevision:
type: 'boolean' type: 'boolean'
default: true default: true
+6 -2
Ver Arquivo
@@ -16,12 +16,12 @@ GutterComponent = React.createClass
measuredWidth: null measuredWidth: null
render: -> render: ->
{scrollHeight, scrollViewHeight, onMouseDown, backgroundColor, gutterBackgroundColor} = @props {scrollHeight, scrollViewHeight, backgroundColor, gutterBackgroundColor} = @props
if gutterBackgroundColor isnt 'rbga(0, 0, 0, 0)' if gutterBackgroundColor isnt 'rbga(0, 0, 0, 0)'
backgroundColor = gutterBackgroundColor backgroundColor = gutterBackgroundColor
div className: 'gutter', onClick: @onClick, onMouseDown: @onMouseDown, div className: 'gutter',
div className: 'line-numbers', ref: 'lineNumbers', style: div className: 'line-numbers', ref: 'lineNumbers', style:
height: Math.max(scrollHeight, scrollViewHeight) height: Math.max(scrollHeight, scrollViewHeight)
WebkitTransform: @getTransform() WebkitTransform: @getTransform()
@@ -45,6 +45,10 @@ GutterComponent = React.createClass
@appendDummyLineNumber() @appendDummyLineNumber()
@updateLineNumbers() if @props.performedInitialMeasurement @updateLineNumbers() if @props.performedInitialMeasurement
node = @getDOMNode()
node.addEventListener 'click', @onClick
node.addEventListener 'mousedown', @onMouseDown
# Only update the gutter if the visible row range has changed or if a # Only update the gutter if the visible row range has changed or if a
# non-zero-delta change to the screen lines has occurred within the current # non-zero-delta change to the screen lines has occurred within the current
# visible row range. # visible row range.
+6
Ver Arquivo
@@ -21,5 +21,11 @@ HighlightsComponent = React.createClass
highlightComponents highlightComponents
componentDidMount: ->
if atom.config.get('editor.useShadowDOM')
insertionPoint = document.createElement('content')
insertionPoint.setAttribute('select', '.underlayer')
@getDOMNode().appendChild(insertionPoint)
shouldComponentUpdate: (newProps) -> shouldComponentUpdate: (newProps) ->
not isEqualForProperties(newProps, @props, 'highlightDecorations', 'lineHeightInPixels', 'defaultCharWidth', 'scopedCharacterWidthsChangeCount') not isEqualForProperties(newProps, @props, 'highlightDecorations', 'lineHeightInPixels', 'defaultCharWidth', 'scopedCharacterWidthsChangeCount')
+5 -10
Ver Arquivo
@@ -7,16 +7,17 @@ InputComponent = React.createClass
displayName: 'InputComponent' displayName: 'InputComponent'
render: -> render: ->
{className, style, onFocus, onBlur} = @props {className, style} = @props
input {className, style, onFocus, onBlur, 'data-react-skip-selection-restoration': true} input {className, style, 'data-react-skip-selection-restoration': true}
getInitialState: -> getInitialState: ->
{lastChar: ''} {lastChar: ''}
componentDidMount: -> componentDidMount: ->
@getDOMNode().addEventListener 'paste', @onPaste node = @getDOMNode()
@getDOMNode().addEventListener 'compositionupdate', @onCompositionUpdate node.addEventListener 'paste', @onPaste
node.addEventListener 'compositionupdate', @onCompositionUpdate
# Don't let text accumulate in the input forever, but avoid excessive reflows # Don't let text accumulate in the input forever, but avoid excessive reflows
componentDidUpdate: -> componentDidUpdate: ->
@@ -34,11 +35,5 @@ InputComponent = React.createClass
onPaste: (e) -> onPaste: (e) ->
e.preventDefault() e.preventDefault()
onFocus: ->
@props.onFocus?()
onBlur: ->
@props.onBlur?()
focus: -> focus: ->
@getDOMNode().focus() @getDOMNode().focus()
+6
Ver Arquivo
@@ -57,6 +57,12 @@ LinesComponent = React.createClass
@lineIdsByScreenRow = {} @lineIdsByScreenRow = {}
@renderedDecorationsByLineId = {} @renderedDecorationsByLineId = {}
componentDidMount: ->
if atom.config.get('editor.useShadowDOM')
insertionPoint = document.createElement('content')
insertionPoint.setAttribute('select', '.overlayer')
@getDOMNode().appendChild(insertionPoint)
shouldComponentUpdate: (newProps) -> shouldComponentUpdate: (newProps) ->
return true unless isEqualForProperties(newProps, @props, return true unless isEqualForProperties(newProps, @props,
'renderedRowRange', 'lineDecorations', 'highlightDecorations', 'lineHeightInPixels', 'defaultCharWidth', 'renderedRowRange', 'lineDecorations', 'highlightDecorations', 'lineHeightInPixels', 'defaultCharWidth',
+16 -9
Ver Arquivo
@@ -50,6 +50,7 @@ class Package
keymaps: null keymaps: null
menus: null menus: null
stylesheets: null stylesheets: null
stylesheetDisposables: null
grammars: null grammars: null
scopedProperties: null scopedProperties: null
mainModulePath: null mainModulePath: null
@@ -175,9 +176,16 @@ class Package
activateStylesheets: -> activateStylesheets: ->
return if @stylesheetsActivated return if @stylesheetsActivated
type = @getStylesheetType()
for [stylesheetPath, content] in @stylesheets group = @getStylesheetType()
atom.themes.applyStylesheet(stylesheetPath, content, type) @stylesheetDisposables = new CompositeDisposable
for [sourcePath, source] in @stylesheets
if match = path.basename(sourcePath).match(/[^.]*\.([^.]*)\./)
context = match[1]
else if @metadata.theme is 'syntax'
context = 'atom-text-editor'
@stylesheetDisposables.add(atom.styles.addStyleSheet(source, {sourcePath, group, context}))
@stylesheetsActivated = true @stylesheetsActivated = true
activateResources: -> activateResources: ->
@@ -320,7 +328,7 @@ class Package
deactivateResources: -> deactivateResources: ->
grammar.deactivate() for grammar in @grammars grammar.deactivate() for grammar in @grammars
scopedProperties.deactivate() for scopedProperties in @scopedProperties scopedProperties.deactivate() for scopedProperties in @scopedProperties
atom.themes.removeStylesheet(stylesheetPath) for [stylesheetPath] in @stylesheets @stylesheetDisposables?.dispose()
@activationDisposables?.dispose() @activationDisposables?.dispose()
@stylesheetsActivated = false @stylesheetsActivated = false
@grammarsActivated = false @grammarsActivated = false
@@ -329,11 +337,10 @@ class Package
reloadStylesheets: -> reloadStylesheets: ->
oldSheets = _.clone(@stylesheets) oldSheets = _.clone(@stylesheets)
@loadStylesheets() @loadStylesheets()
atom.themes.removeStylesheet(stylesheetPath) for [stylesheetPath] in oldSheets @stylesheetDisposables.dispose()
@reloadStylesheet(stylesheetPath, content) for [stylesheetPath, content] in @stylesheets @stylesheetDisposables = new CompositeDisposable
@stylesheetsActivated = false
reloadStylesheet: (stylesheetPath, content) -> @activateStylesheets()
atom.themes.applyStylesheet(stylesheetPath, content, @getStylesheetType())
requireMainModule: -> requireMainModule: ->
return @mainModule if @mainModule? return @mainModule if @mainModule?
+11 -3
Ver Arquivo
@@ -28,9 +28,17 @@ class PaneElement extends HTMLElement
@itemViews.setAttribute 'class', 'item-views' @itemViews.setAttribute 'class', 'item-views'
subscribeToDOMEvents: -> subscribeToDOMEvents: ->
@addEventListener 'focusin', => @model.focus() handleFocus = (event) =>
@addEventListener 'focusout', => @model.blur() @model.focus()
@addEventListener 'focus', => @getActiveView()?.focus() if event.target is this and view = @getActiveView()
view.focus()
event.stopPropagation()
handleBlur = (event) =>
@model.blur() unless @contains(event.relatedTarget)
@addEventListener 'focus', handleFocus, true
@addEventListener 'blur', handleBlur, true
createSpacePenShim: -> createSpacePenShim: ->
@__spacePenView = new PaneView(this) @__spacePenView = new PaneView(this)
+6 -1
Ver Arquivo
@@ -23,7 +23,7 @@ ScrollbarComponent = React.createClass
style.right = verticalScrollbarWidth if scrollableInOppositeDirection style.right = verticalScrollbarWidth if scrollableInOppositeDirection
style.height = horizontalScrollbarHeight style.height = horizontalScrollbarHeight
div {className, style, @onScroll}, div {className, style},
switch orientation switch orientation
when 'vertical' when 'vertical'
div className: 'scrollbar-content', style: {height: scrollHeight} div className: 'scrollbar-content', style: {height: scrollHeight}
@@ -36,6 +36,11 @@ ScrollbarComponent = React.createClass
unless orientation is 'vertical' or orientation is 'horizontal' unless orientation is 'vertical' or orientation is 'horizontal'
throw new Error("Must specify an orientation property of 'vertical' or 'horizontal'") throw new Error("Must specify an orientation property of 'vertical' or 'horizontal'")
@getDOMNode().addEventListener 'scroll', @onScroll
componentWillUnmount: ->
@getDOMNode().removeEventListener 'scroll', @onScroll
shouldComponentUpdate: (newProps) -> shouldComponentUpdate: (newProps) ->
return true if newProps.visible isnt @props.visible return true if newProps.visible isnt @props.visible
+2 -2
Ver Arquivo
@@ -57,7 +57,7 @@ class SelectListView extends View
initialize: -> initialize: ->
@filterEditorView.getEditor().getBuffer().onDidChange => @filterEditorView.getEditor().getBuffer().onDidChange =>
@schedulePopulateList() @schedulePopulateList()
@filterEditorView.hiddenInput.on 'focusout', => @filterEditorView.on 'blur', =>
@cancel() unless @cancelling @cancel() unless @cancelling
# This prevents the focusout event from firing on the filter editor view # This prevents the focusout event from firing on the filter editor view
@@ -254,7 +254,7 @@ class SelectListView extends View
# Extended: Store the currently focused element. This element will be given # Extended: Store the currently focused element. This element will be given
# back focus when {::cancel} is called. # back focus when {::cancel} is called.
storeFocusedElement: -> storeFocusedElement: ->
@previouslyFocusedElement = $(':focus') @previouslyFocusedElement = $(document.activeElement)
### ###
Section: Private Section: Private
+12
Ver Arquivo
@@ -81,6 +81,18 @@ jQuery.event.remove = (elem, types, originalHandler, selector, mappedTypes) ->
handler = HandlersByOriginalHandler.get(originalHandler) ? originalHandler handler = HandlersByOriginalHandler.get(originalHandler) ? originalHandler
JQueryEventRemove(elem, types, handler, selector, mappedTypes, RemoveEventListener if atom?.commands?) JQueryEventRemove(elem, types, handler, selector, mappedTypes, RemoveEventListener if atom?.commands?)
JQueryContains = jQuery.contains
jQuery.contains = (a, b) ->
shadowRoot = null
currentNode = b
while currentNode
if currentNode instanceof ShadowRoot and a.contains(currentNode.host)
return true
currentNode = currentNode.parentNode
JQueryContains.call(this, a, b)
tooltipDefaults = tooltipDefaults =
delay: delay:
show: 1000 show: 1000
+1
Ver Arquivo
@@ -25,6 +25,7 @@ class StyleManager
addStyleSheet: (source, params) -> addStyleSheet: (source, params) ->
sourcePath = params?.sourcePath sourcePath = params?.sourcePath
context = params?.context
group = params?.group group = params?.group
if sourcePath? and styleElement = @styleElementsBySourcePath[sourcePath] if sourcePath? and styleElement = @styleElementsBySourcePath[sourcePath]
+36 -8
Ver Arquivo
@@ -1,8 +1,8 @@
{Emitter, CompositeDisposable} = require 'event-kit' {Emitter, CompositeDisposable} = require 'event-kit'
class StylesElement extends HTMLElement class StylesElement extends HTMLElement
createdCallback: -> subscriptions: null
@emitter = new Emitter context: null
onDidAddStyleElement: (callback) -> onDidAddStyleElement: (callback) ->
@emitter.on 'did-add-style-element', callback @emitter.on 'did-add-style-element', callback
@@ -13,15 +13,42 @@ class StylesElement extends HTMLElement
onDidUpdateStyleElement: (callback) -> onDidUpdateStyleElement: (callback) ->
@emitter.on 'did-update-style-element', callback @emitter.on 'did-update-style-element', callback
attachedCallback: -> createdCallback: ->
@subscriptions = new CompositeDisposable @emitter = new Emitter
@styleElementClonesByOriginalElement = new WeakMap @styleElementClonesByOriginalElement = new WeakMap
attachedCallback: ->
@initialize()
detachedCallback: ->
@subscriptions.dispose()
@subscriptions = null
attributeChangedCallback: (attrName, oldVal, newVal) ->
@contextChanged() if attrName is 'context'
initialize: ->
return if @subscriptions?
@subscriptions = new CompositeDisposable
@context = @getAttribute('context') ? undefined
@subscriptions.add atom.styles.observeStyleElements(@styleElementAdded.bind(this)) @subscriptions.add atom.styles.observeStyleElements(@styleElementAdded.bind(this))
@subscriptions.add atom.styles.onDidRemoveStyleElement(@styleElementRemoved.bind(this)) @subscriptions.add atom.styles.onDidRemoveStyleElement(@styleElementRemoved.bind(this))
@subscriptions.add atom.styles.onDidUpdateStyleElement(@styleElementUpdated.bind(this)) @subscriptions.add atom.styles.onDidUpdateStyleElement(@styleElementUpdated.bind(this))
contextChanged: ->
return unless @subscriptions?
@styleElementRemoved(child) for child in Array::slice.call(@children)
@context = @getAttribute('context')
@styleElementAdded(styleElement) for styleElement in atom.styles.getStyleElements()
styleElementAdded: (styleElement) -> styleElementAdded: (styleElement) ->
return unless styleElement.context is @context
styleElementClone = styleElement.cloneNode(true) styleElementClone = styleElement.cloneNode(true)
styleElementClone.context = styleElement.context
@styleElementClonesByOriginalElement.set(styleElement, styleElementClone) @styleElementClonesByOriginalElement.set(styleElement, styleElementClone)
group = styleElement.getAttribute('group') group = styleElement.getAttribute('group')
@@ -35,16 +62,17 @@ class StylesElement extends HTMLElement
@emitter.emit 'did-add-style-element', styleElementClone @emitter.emit 'did-add-style-element', styleElementClone
styleElementRemoved: (styleElement) -> styleElementRemoved: (styleElement) ->
styleElementClone = @styleElementClonesByOriginalElement.get(styleElement) return unless styleElement.context is @context
styleElementClone = @styleElementClonesByOriginalElement.get(styleElement) ? styleElement
styleElementClone.remove() styleElementClone.remove()
@emitter.emit 'did-remove-style-element', styleElementClone @emitter.emit 'did-remove-style-element', styleElementClone
styleElementUpdated: (styleElement) -> styleElementUpdated: (styleElement) ->
return unless styleElement.context is @context
styleElementClone = @styleElementClonesByOriginalElement.get(styleElement) styleElementClone = @styleElementClonesByOriginalElement.get(styleElement)
styleElementClone.textContent = styleElement.textContent styleElementClone.textContent = styleElement.textContent
@emitter.emit 'did-update-style-element', styleElementClone @emitter.emit 'did-update-style-element', styleElementClone
detachedCallback: ->
@subscriptions.dispose()
module.exports = StylesElement = document.registerElement 'atom-styles', prototype: StylesElement.prototype module.exports = StylesElement = document.registerElement 'atom-styles', prototype: StylesElement.prototype
+37 -34
Ver Arquivo
@@ -93,7 +93,7 @@ TextEditorComponent = React.createClass
className += ' is-focused' if focused className += ' is-focused' if focused
className += ' has-selection' if hasSelection className += ' has-selection' if hasSelection
div {className, style, tabIndex: -1}, div {className, style},
if @shouldRenderGutter() if @shouldRenderGutter()
GutterComponent { GutterComponent {
ref: 'gutter', onMouseDown: @onGutterMouseDown, lineDecorations, ref: 'gutter', onMouseDown: @onGutterMouseDown, lineDecorations,
@@ -102,13 +102,11 @@ TextEditorComponent = React.createClass
@useHardwareAcceleration, @performedInitialMeasurement, @backgroundColor, @gutterBackgroundColor @useHardwareAcceleration, @performedInitialMeasurement, @backgroundColor, @gutterBackgroundColor
} }
div ref: 'scrollView', className: 'scroll-view', onMouseDown: @onMouseDown, div ref: 'scrollView', className: 'scroll-view',
InputComponent InputComponent
ref: 'input' ref: 'input'
className: 'hidden-input' className: 'hidden-input'
style: hiddenInputStyle style: hiddenInputStyle
onFocus: @onInputFocused
onBlur: @onInputBlurred
LinesComponent { LinesComponent {
ref: 'lines', ref: 'lines',
@@ -175,14 +173,14 @@ TextEditorComponent = React.createClass
@setScrollSensitivity(atom.config.get('editor.scrollSensitivity')) @setScrollSensitivity(atom.config.get('editor.scrollSensitivity'))
componentDidMount: -> componentDidMount: ->
{editor} = @props {editor, stylesElement} = @props
@observeEditor() @observeEditor()
@listenForDOMEvents() @listenForDOMEvents()
@subscribe atom.themes.onDidAddStylesheet @onStylesheetsChanged @subscribe stylesElement.onDidAddStyleElement @onStylesheetsChanged
@subscribe atom.themes.onDidUpdateStylesheet @onStylesheetsChanged @subscribe stylesElement.onDidUpdateStyleElement @onStylesheetsChanged
@subscribe atom.themes.onDidRemoveStylesheet @onStylesheetsChanged @subscribe stylesElement.onDidRemoveStyleElement @onStylesheetsChanged
unless atom.themes.isInitialLoadComplete() unless atom.themes.isInitialLoadComplete()
@subscribe atom.themes.onDidReloadAll @onStylesheetsChanged @subscribe atom.themes.onDidReloadAll @onStylesheetsChanged
@subscribe scrollbarStyle.changes, @refreshScrollbars @subscribe scrollbarStyle.changes, @refreshScrollbars
@@ -193,9 +191,9 @@ TextEditorComponent = React.createClass
@checkForVisibilityChange() @checkForVisibilityChange()
componentWillUnmount: -> componentWillUnmount: ->
{editor, parentView} = @props {editor, hostElement} = @props
parentView.__spacePenView.trigger 'editor:will-be-removed', [parentView.__spacePenView] hostElement.__spacePenView.trigger 'editor:will-be-removed', [hostElement.__spacePenView]
@unsubscribe() @unsubscribe()
@scopedConfigSubscriptions.dispose() @scopedConfigSubscriptions.dispose()
window.removeEventListener 'resize', @requestHeightAndWidthMeasurement window.removeEventListener 'resize', @requestHeightAndWidthMeasurement
@@ -215,9 +213,9 @@ TextEditorComponent = React.createClass
if @props.editor.isAlive() if @props.editor.isAlive()
@updateParentViewFocusedClassIfNeeded(prevState) @updateParentViewFocusedClassIfNeeded(prevState)
@updateParentViewMiniClassIfNeeded(prevState) @updateParentViewMiniClassIfNeeded(prevState)
@props.parentView.__spacePenView.trigger 'cursor:moved' if cursorMoved @props.hostElement.__spacePenView.trigger 'cursor:moved' if cursorMoved
@props.parentView.__spacePenView.trigger 'selection:changed' if selectionChanged @props.hostElement.__spacePenView.trigger 'selection:changed' if selectionChanged
@props.parentView.__spacePenView.trigger 'editor:display-updated' @props.hostElement.__spacePenView.trigger 'editor:display-updated'
becameVisible: -> becameVisible: ->
@updatesPaused = true @updatesPaused = true
@@ -261,7 +259,7 @@ TextEditorComponent = React.createClass
@forceUpdate() @forceUpdate()
getTopmostDOMNode: -> getTopmostDOMNode: ->
@props.parentView @props.hostElement
getRenderedRowRange: -> getRenderedRowRange: ->
{editor, lineOverdrawMargin} = @props {editor, lineOverdrawMargin} = @props
@@ -378,8 +376,8 @@ TextEditorComponent = React.createClass
listenForDOMEvents: -> listenForDOMEvents: ->
node = @getDOMNode() node = @getDOMNode()
node.addEventListener 'mousewheel', @onMouseWheel node.addEventListener 'mousewheel', @onMouseWheel
node.addEventListener 'focus', @onFocus # For some reason, React's built in focus events seem to bubble
node.addEventListener 'textInput', @onTextInput node.addEventListener 'textInput', @onTextInput
@refs.scrollView.getDOMNode().addEventListener 'mousedown', @onMouseDown
scrollViewNode = @refs.scrollView.getDOMNode() scrollViewNode = @refs.scrollView.getDOMNode()
scrollViewNode.addEventListener 'scroll', @onScrollViewScroll scrollViewNode.addEventListener 'scroll', @onScrollViewScroll
@@ -415,6 +413,9 @@ TextEditorComponent = React.createClass
observeConfig: -> observeConfig: ->
@subscribe atom.config.observe 'editor.useHardwareAcceleration', @setUseHardwareAcceleration @subscribe atom.config.observe 'editor.useHardwareAcceleration', @setUseHardwareAcceleration
@subscribe atom.config.onDidChange 'editor.fontSize', @sampleFontStyling
@subscribe atom.config.onDidChange 'editor.fontFamily', @sampleFontStyling
@subscribe atom.config.onDidChange 'editor.lineHeight', @sampleFontStyling
onGrammarChanged: -> onGrammarChanged: ->
{editor} = @props {editor} = @props
@@ -428,8 +429,14 @@ TextEditorComponent = React.createClass
subscriptions.add atom.config.observe scopeDescriptor, 'editor.showLineNumbers', @setShowLineNumbers subscriptions.add atom.config.observe scopeDescriptor, 'editor.showLineNumbers', @setShowLineNumbers
subscriptions.add atom.config.observe scopeDescriptor, 'editor.scrollSensitivity', @setScrollSensitivity subscriptions.add atom.config.observe scopeDescriptor, 'editor.scrollSensitivity', @setScrollSensitivity
onFocus: -> focused: ->
@refs.input.focus() if @isMounted() if @isMounted()
@setState(focused: true)
@refs.input.focus()
blurred: ->
if @isMounted()
@setState(focused: false)
onTextInput: (event) -> onTextInput: (event) ->
event.stopPropagation() event.stopPropagation()
@@ -452,12 +459,6 @@ TextEditorComponent = React.createClass
inputNode.value = event.data if editor.insertText(event.data) inputNode.value = event.data if editor.insertText(event.data)
onInputFocused: ->
@setState(focused: true)
onInputBlurred: ->
@setState(focused: false)
onVerticalScroll: (scrollTop) -> onVerticalScroll: (scrollTop) ->
{editor} = @props {editor} = @props
@@ -621,11 +622,11 @@ TextEditorComponent = React.createClass
else else
editor.setSelectedScreenRange([tailPosition, [dragRow + 1, 0]], preserveFolds: true) editor.setSelectedScreenRange([tailPosition, [dragRow + 1, 0]], preserveFolds: true)
onStylesheetsChanged: (stylesheet) -> onStylesheetsChanged: (styleElement) ->
return unless @performedInitialMeasurement return unless @performedInitialMeasurement
return unless atom.themes.isInitialLoadComplete() return unless atom.themes.isInitialLoadComplete()
@refreshScrollbars() if not stylesheet? or @containsScrollbarSelector(stylesheet) @refreshScrollbars() if not styleElement.sheet? or @containsScrollbarSelector(styleElement.sheet)
@sampleFontStyling() @sampleFontStyling()
@sampleBackgroundColors() @sampleBackgroundColors()
@remeasureCharacterWidths() @remeasureCharacterWidths()
@@ -771,10 +772,10 @@ TextEditorComponent = React.createClass
measureHeightAndWidth: -> measureHeightAndWidth: ->
return unless @isMounted() return unless @isMounted()
{editor, parentView} = @props {editor, hostElement} = @props
scrollViewNode = @refs.scrollView.getDOMNode() scrollViewNode = @refs.scrollView.getDOMNode()
{position} = getComputedStyle(parentView) {position} = getComputedStyle(hostElement)
{height} = parentView.style {height} = hostElement.style
if position is 'absolute' or height if position is 'absolute' or height
if @autoHeight if @autoHeight
@@ -806,9 +807,9 @@ TextEditorComponent = React.createClass
@remeasureCharacterWidths() @remeasureCharacterWidths()
sampleBackgroundColors: (suppressUpdate) -> sampleBackgroundColors: (suppressUpdate) ->
{parentView} = @props {hostElement} = @props
{showLineNumbers} = @state {showLineNumbers} = @state
{backgroundColor} = getComputedStyle(parentView) {backgroundColor} = getComputedStyle(hostElement)
if backgroundColor isnt @backgroundColor if backgroundColor isnt @backgroundColor
@backgroundColor = backgroundColor @backgroundColor = backgroundColor
@@ -907,10 +908,10 @@ TextEditorComponent = React.createClass
lineNumberNodeForScreenRow: (screenRow) -> @refs.gutter.lineNumberNodeForScreenRow(screenRow) lineNumberNodeForScreenRow: (screenRow) -> @refs.gutter.lineNumberNodeForScreenRow(screenRow)
screenRowForNode: (node) -> screenRowForNode: (node) ->
while node isnt document while node?
if screenRow = node.dataset.screenRow if screenRow = node.dataset.screenRow
return parseInt(screenRow) return parseInt(screenRow)
node = node.parentNode node = node.parentElement
null null
getFontSize: -> getFontSize: ->
@@ -977,11 +978,13 @@ TextEditorComponent = React.createClass
updateParentViewFocusedClassIfNeeded: (prevState) -> updateParentViewFocusedClassIfNeeded: (prevState) ->
if prevState.focused isnt @state.focused if prevState.focused isnt @state.focused
@props.parentView.classList.toggle('is-focused', @state.focused) @props.hostElement.classList.toggle('is-focused', @state.focused)
@props.rootElement.classList.toggle('is-focused', @state.focused)
updateParentViewMiniClassIfNeeded: (prevProps) -> updateParentViewMiniClassIfNeeded: (prevProps) ->
if prevProps.mini isnt @props.mini if prevProps.mini isnt @props.mini
@props.parentView.classList.toggle('mini', @props.mini) @props.hostElement.classList.toggle('mini', @props.mini)
@props.rootElement.classList.toggle('mini', @props.mini)
runScrollBenchmark: -> runScrollBenchmark: ->
unless process.env.NODE_ENV is 'production' unless process.env.NODE_ENV is 'production'
+41 -9
Ver Arquivo
@@ -6,6 +6,8 @@ TextEditor = require './text-editor'
TextEditorComponent = require './text-editor-component' TextEditorComponent = require './text-editor-component'
TextEditorView = null TextEditorView = null
GlobalStylesElement = null
class TextEditorElement extends HTMLElement class TextEditorElement extends HTMLElement
model: null model: null
componentDescriptor: null componentDescriptor: null
@@ -18,13 +20,34 @@ class TextEditorElement extends HTMLElement
@initializeContent() @initializeContent()
@createSpacePenShim() @createSpacePenShim()
@addEventListener 'focus', @focused.bind(this) @addEventListener 'focus', @focused.bind(this)
@addEventListener 'focusout', @focusedOut.bind(this)
@addEventListener 'blur', @blurred.bind(this) @addEventListener 'blur', @blurred.bind(this)
initializeContent: (attributes) -> initializeContent: (attributes) ->
@classList.add('editor', 'react', 'editor-colors') @classList.add('editor')
@setAttribute('tabindex', -1) @setAttribute('tabindex', -1)
if atom.config.get('editor.useShadowDOM')
@createShadowRoot()
@stylesElement = document.createElement('atom-styles')
@stylesElement.setAttribute('context', 'atom-text-editor')
@stylesElement.initialize()
@rootElement = document.createElement('div')
@rootElement.classList.add('editor', 'editor-colors')
@shadowRoot.appendChild(@stylesElement)
@shadowRoot.appendChild(@rootElement)
else
unless GlobalStylesElement?
GlobalStylesElement = document.createElement('atom-styles')
GlobalStylesElement.setAttribute('context', 'atom-text-editor')
GlobalStylesElement.initialize()
document.head.appendChild(GlobalStylesElement)
@stylesElement = GlobalStylesElement
@rootElement = this
createSpacePenShim: -> createSpacePenShim: ->
TextEditorView ?= require './text-editor-view' TextEditorView ?= require './text-editor-view'
@__spacePenView = new TextEditorView(this) @__spacePenView = new TextEditorView(this)
@@ -64,12 +87,19 @@ class TextEditorElement extends HTMLElement
mountComponent: -> mountComponent: ->
@componentDescriptor ?= TextEditorComponent( @componentDescriptor ?= TextEditorComponent(
parentView: this hostElement: this
rootElement: @rootElement
stylesElement: @stylesElement
editor: @model editor: @model
mini: @model.mini mini: @model.mini
lineOverdrawMargin: @lineOverdrawMargin lineOverdrawMargin: @lineOverdrawMargin
) )
@component = React.renderComponent(@componentDescriptor, this) @component = React.renderComponent(@componentDescriptor, @rootElement)
unless atom.config.get('editor.useShadowDOM')
inputNode = @component.refs.input.getDOMNode()
inputNode.addEventListener 'focus', @focused.bind(this)
inputNode.addEventListener 'blur', => @dispatchEvent(new FocusEvent('blur', bubbles: false))
unmountComponent: -> unmountComponent: ->
return unless @component?.isMounted() return unless @component?.isMounted()
@@ -79,15 +109,17 @@ class TextEditorElement extends HTMLElement
focused: -> focused: ->
if @component? if @component?
@component.onFocus() @component.focused()
else else
@focusOnAttach = true @focusOnAttach = true
focusedOut: (event) ->
event.stopImmediatePropagation() if @contains(event.relatedTarget)
blurred: (event) -> blurred: (event) ->
event.stopImmediatePropagation() if @contains(event.relatedTarget) unless atom.config.get('editor.useShadowDOM')
if event.relatedTarget is @component?.refs.input?.getDOMNode()
event.stopImmediatePropagation()
return
@component?.blurred()
addGrammarScopeAttribute: -> addGrammarScopeAttribute: ->
grammarScope = @model.getGrammar()?.scopeName?.replace(/\./g, ' ') grammarScope = @model.getGrammar()?.scopeName?.replace(/\./g, ' ')
+27 -7
Ver Arquivo
@@ -73,13 +73,26 @@ class TextEditorView extends View
setModel: (@model) -> setModel: (@model) ->
@editor = @model @editor = @model
@scrollView = @find('.scroll-view') @root = $(@element.rootElement)
@underlayer = @find('.highlights').addClass('underlayer')
@overlayer = @find('.lines').addClass('overlayer') @scrollView = @root.find('.scroll-view')
@hiddenInput = @.find('.hidden-input')
if atom.config.get('editor.useShadowDOM')
@underlayer = $("<div class='underlayer'></div>").appendTo(this)
@overlayer = $("<div class='overlayer'></div>").appendTo(this)
else
@underlayer = @find('.highlights').addClass('underlayer')
@overlayer = @find('.lines').addClass('overlayer')
@hiddenInput = @root.find('.hidden-input')
@hiddenInput.on = (args...) =>
args[0] = 'blur' if args[0] is 'focusout'
$::on.apply(this, args)
@subscribe atom.config.observe 'editor.showLineNumbers', => @subscribe atom.config.observe 'editor.showLineNumbers', =>
@gutter = @find('.gutter') @gutter = @root.find('.gutter')
@gutter.removeClassFromAllLines = (klass) => @gutter.removeClassFromAllLines = (klass) =>
deprecate('Use decorations instead: http://blog.atom.io/2014/07/24/decorations.html') deprecate('Use decorations instead: http://blog.atom.io/2014/07/24/decorations.html')
@@ -95,6 +108,13 @@ class TextEditorView extends View
lines.addClass(klass) lines.addClass(klass)
lines.length > 0 lines.length > 0
find: ->
shadowResult = @root.find.apply(@root, arguments)
if shadowResult.length > 0
shadowResult
else
super
# Public: Get the underlying editor model for this view. # Public: Get the underlying editor model for this view.
# #
# Returns an {TextEditor} # Returns an {TextEditor}
@@ -107,7 +127,7 @@ class TextEditorView extends View
Object.defineProperty @::, 'firstRenderedScreenRow', get: -> @component.getRenderedRowRange()[0] Object.defineProperty @::, 'firstRenderedScreenRow', get: -> @component.getRenderedRowRange()[0]
Object.defineProperty @::, 'lastRenderedScreenRow', get: -> @component.getRenderedRowRange()[1] Object.defineProperty @::, 'lastRenderedScreenRow', get: -> @component.getRenderedRowRange()[1]
Object.defineProperty @::, 'active', get: -> @is(@getPaneView()?.activeView) Object.defineProperty @::, 'active', get: -> @is(@getPaneView()?.activeView)
Object.defineProperty @::, 'isFocused', get: -> @component?.state.focused Object.defineProperty @::, 'isFocused', get: -> document.activeElement is @element or document.activeElement is @element.component?.refs.input.getDOMNode()
Object.defineProperty @::, 'mini', get: -> @component?.props.mini Object.defineProperty @::, 'mini', get: -> @component?.props.mini
Object.defineProperty @::, 'component', get: -> @element?.component Object.defineProperty @::, 'component', get: -> @element?.component
@@ -164,7 +184,7 @@ class TextEditorView extends View
appendToLinesView: (view) -> appendToLinesView: (view) ->
view.css('position', 'absolute') view.css('position', 'absolute')
view.css('z-index', 1) view.css('z-index', 1)
@find('.lines').prepend(view) @overlayer.append(view)
unmountComponent: -> unmountComponent: ->
React.unmountComponentAtNode(@element) if @component.isMounted() React.unmountComponentAtNode(@element) if @component.isMounted()
+3
Ver Arquivo
@@ -249,6 +249,9 @@ class ThemeManager
if nativeStylesheetPath = fs.resolveOnLoadPath(process.platform, ['css', 'less']) if nativeStylesheetPath = fs.resolveOnLoadPath(process.platform, ['css', 'less'])
@requireStylesheet(nativeStylesheetPath) @requireStylesheet(nativeStylesheetPath)
textEditorStylesPath = path.join(@resourcePath, 'static', 'text-editor-shadow.less')
atom.styles.addStyleSheet(@loadLessStylesheet(textEditorStylesPath), sourcePath: textEditorStylesPath, context: 'atom-text-editor')
stylesheetElementForId: (id) -> stylesheetElementForId: (id) ->
document.head.querySelector("atom-styles style[source-path=\"#{id}\"]") document.head.querySelector("atom-styles style[source-path=\"#{id}\"]")
+1 -1
Ver Arquivo
@@ -21,7 +21,7 @@
@import "popover-list"; @import "popover-list";
@import "messages"; @import "messages";
@import "markdown"; @import "markdown";
@import "editor"; @import "text-editor-light";
@import "select-list"; @import "select-list";
@import "syntax"; @import "syntax";
@import "utilities"; @import "utilities";
-310
Ver Arquivo
@@ -1,310 +0,0 @@
@import "ui-variables";
@import "octicon-utf-codes";
@import "octicon-mixins";
atom-text-editor.react {
.editor-contents {
width: 100%;
}
.underlayer {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
z-index: -2;
}
.lines {
min-width: 100%;
}
.cursor {
z-index: 4;
pointer-events: none;
}
.editor-contents.is-focused .cursor {
visibility: visible;
}
.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;
.scrollbar-content {
height: 15px;
}
}
.vertical-scrollbar {
overflow-x: hidden;
}
.scrollbar-corner {
position: absolute;
overflow: auto;
bottom: 0;
right: 0;
}
.scroll-view {
overflow: hidden;
z-index: 0;
}
.scroll-view-content {
position: relative;
width: 100%;
}
.gutter {
.line-number {
white-space: nowrap;
padding-left: .5em;
.icon-right {
padding: 0 .4em;
&:before {
text-align: center;
}
}
}
}
}
atom-text-editor.mini {
font-size: @input-font-size;
line-height: @component-line-height;
max-height: @component-line-height + 2; // +2 for borders
.placeholder-text {
position: absolute;
color: @text-color-subtle;
}
}
atom-text-editor {
z-index: 0;
font-family: Inconsolata, Monaco, Consolas, 'Courier New', Courier;
line-height: 1.3;
}
atom-text-editor, .editor-contents {
overflow: hidden;
cursor: text;
display: -webkit-flex;
-webkit-user-select: none;
position: relative;
}
atom-text-editor .gutter .line-number.cursor-line {
opacity: 1;
}
atom-text-editor .gutter {
overflow: hidden;
text-align: right;
cursor: default;
min-width: 1em;
box-sizing: border-box;
}
atom-text-editor .gutter .line-number {
padding-left: .5em;
opacity: 0.6;
}
atom-text-editor .gutter .line-numbers {
position: relative;
}
atom-text-editor .gutter .line-number.folded.cursor-line {
opacity: 1;
}
atom-text-editor .gutter .line-number .icon-right {
.octicon(chevron-down, 0.8em);
display: inline-block;
visibility: hidden;
padding-left: .1em;
padding-right: .5em;
opacity: .6;
}
atom-text-editor .gutter:hover .line-number.foldable .icon-right {
visibility: visible;
&:before {
content: @chevron-down;
}
&:hover {
opacity: 1;
}
}
atom-text-editor .gutter, atom-text-editor .gutter:hover {
.line-number.folded .icon-right {
.octicon(chevron-right, 0.8em);
visibility: visible;
&:before { // chevron-right renders too far right compared to chevron-down
position: relative;
left: -.1em;
content: @chevron-right;
}
}
}
atom-text-editor .fold-marker {
cursor: default;
}
atom-text-editor .fold-marker:after {
.icon(0.8em, inline);
content: @ellipsis;
padding-left: 0.2em;
}
atom-text-editor .line.cursor-line .fold-marker:after {
opacity: 1;
}
atom-text-editor.is-blurred .line.cursor-line {
background: rgba(0, 0, 0, 0);
}
atom-text-editor .invisible-character {
font-weight: normal !important;
font-style: normal !important;
}
atom-text-editor .indent-guide {
display: inline-block;
box-shadow: inset 1px 0;
}
atom-text-editor .vertical-scrollbar,
atom-text-editor .horizontal-scrollbar {
cursor: default;
}
atom-text-editor .vertical-scrollbar {
position: absolute;
top: 0;
right: 0;
bottom: 0;
width: 15px;
overflow-y: auto;
z-index: 3;
}
atom-text-editor .scroll-view {
overflow-x: auto;
overflow-y: hidden;
-webkit-flex: 1;
min-width: 0;
position: relative;
}
atom-text-editor.soft-wrap .scroll-view {
overflow-x: hidden;
}
atom-text-editor .underlayer {
z-index: 0;
position: absolute;
min-height: 100%;
}
atom-text-editor .lines {
position: relative;
z-index: 1;
}
atom-text-editor .overlayer {
z-index: 2;
position: absolute;
}
atom-text-editor .line {
white-space: pre;
}
atom-text-editor .line span {
vertical-align: top;
}
atom-text-editor .cursor {
position: absolute;
border-left: 1px solid;
}
atom-text-editor .cursor,
atom-text-editor.is-focused .cursor.blink-off {
visibility: hidden;
}
atom-text-editor.is-focused .cursor {
visibility: visible;
}
.cursor.hidden-cursor {
display: none;
}
atom-text-editor .hidden-input {
padding: 0;
border: 0;
position: absolute;
z-index: -1;
top: 0;
left: 0;
opacity: 0;
width: 1px;
}
atom-text-editor .highlight {
background: none;
padding: 0;
}
atom-text-editor .highlight .region,
atom-text-editor .selection .region {
position: absolute;
pointer-events: none;
z-index: -1;
}
atom-text-editor.mini:not(.react) {
height: auto;
line-height: 25px;
.cursor {
width: 2px;
line-height: 20px;
margin-top: 2px;
}
.gutter {
display: none;
}
.scroll-view {
overflow: hidden;
}
}
+13
Ver Arquivo
@@ -0,0 +1,13 @@
@import "ui-variables";
atom-text-editor {
display: block;
font-family: Inconsolata, Monaco, Consolas, 'Courier New', Courier;
line-height: 1.3;
}
atom-text-editor.mini {
font-size: @input-font-size;
line-height: @component-line-height;
max-height: @component-line-height + 2; // +2 for borders
}
+282
Ver Arquivo
@@ -0,0 +1,282 @@
@import "ui-variables";
@import "octicon-utf-codes";
@import "octicon-mixins";
.editor, .editor-contents {
height: 100%;
width: 100%;
}
.underlayer {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
z-index: -2;
}
.lines {
min-width: 100%;
}
.cursor {
z-index: 4;
pointer-events: none;
box-sizing: border-box;
}
.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;
.scrollbar-content {
height: 15px;
}
}
.vertical-scrollbar {
overflow-x: hidden;
}
.scrollbar-corner {
position: absolute;
overflow: auto;
bottom: 0;
right: 0;
}
.scroll-view {
overflow: hidden;
z-index: 0;
}
.scroll-view-content {
position: relative;
width: 100%;
}
.gutter {
.line-number {
white-space: nowrap;
padding-left: .5em;
.icon-right {
padding: 0 .4em;
&:before {
text-align: center;
}
}
}
}
.placeholder-text {
position: absolute;
color: @text-color-subtle;
}
.editor {
z-index: 0;
}
.editor, .editor-contents {
overflow: hidden;
cursor: text;
display: -webkit-flex;
-webkit-user-select: none;
position: relative;
}
.gutter .line-number.cursor-line {
opacity: 1;
}
.gutter {
overflow: hidden;
text-align: right;
cursor: default;
min-width: 1em;
box-sizing: border-box;
}
.gutter .line-number {
padding-left: .5em;
opacity: 0.6;
}
.gutter .line-numbers {
position: relative;
}
.gutter .line-number.folded.cursor-line {
opacity: 1;
}
.gutter .line-number .icon-right {
.octicon(chevron-down, 0.8em);
display: inline-block;
visibility: hidden;
padding-left: .1em;
padding-right: .5em;
opacity: .6;
}
.gutter:hover .line-number.foldable .icon-right {
visibility: visible;
&:before {
content: @chevron-down;
}
&:hover {
opacity: 1;
}
}
.gutter, .gutter:hover {
.line-number.folded .icon-right {
.octicon(chevron-right, 0.8em);
visibility: visible;
&:before { // chevron-right renders too far right compared to chevron-down
position: relative;
left: -.1em;
content: @chevron-right;
}
}
}
.fold-marker {
cursor: default;
}
.fold-marker:after {
.icon(0.8em, inline);
content: @ellipsis;
padding-left: 0.2em;
}
.line.cursor-line .fold-marker:after {
opacity: 1;
}
atom-text-editor::shadow.is-blurred .line.cursor-line {
background: rgba(0, 0, 0, 0);
}
.invisible-character {
font-weight: normal !important;
font-style: normal !important;
}
.indent-guide {
display: inline-block;
box-shadow: inset 1px 0;
}
.vertical-scrollbar,
.horizontal-scrollbar {
cursor: default;
}
.vertical-scrollbar {
position: absolute;
top: 0;
right: 0;
bottom: 0;
width: 15px;
overflow-y: auto;
z-index: 3;
}
.scroll-view {
overflow-x: auto;
overflow-y: hidden;
-webkit-flex: 1;
min-width: 0;
position: relative;
}
atom-text-editor::shadow.soft-wrap .scroll-view {
overflow-x: hidden;
}
.underlayer {
z-index: 0;
position: absolute;
min-height: 100%;
}
.lines {
position: relative;
z-index: 1;
}
.overlayer {
z-index: 2;
position: absolute;
}
.line {
white-space: pre;
}
.line span {
vertical-align: top;
}
.cursor {
position: absolute;
border-left: 1px solid;
}
.cursor {
visibility: hidden;
}
.is-focused .cursor {
visibility: visible;
}
.is-focused .cursor.blink-off {
visibility: hidden;
}
.cursor.hidden-cursor {
display: none;
}
.hidden-input {
padding: 0;
border: 0;
position: absolute;
z-index: -1;
top: 0;
left: 0;
opacity: 0;
width: 1px;
}
.highlight {
background: none;
padding: 0;
}
.highlight .region,
.selection .region {
position: absolute;
pointer-events: none;
z-index: -1;
}