mirror of
https://github.com/pulsar-edit/pulsar.git
synced 2024-11-10 10:17:11 +03:00
Merge pull request #3735 from atom/ns-text-editor-custom-element
Convert text editor to custom element
This commit is contained in:
commit
079ea4862a
@ -97,7 +97,7 @@
|
||||
"open-on-github": "0.30.0",
|
||||
"package-generator": "0.31.0",
|
||||
"release-notes": "0.36.0",
|
||||
"settings-view": "0.149.0",
|
||||
"settings-view": "0.150.0",
|
||||
"snippets": "0.55.0",
|
||||
"spell-check": "0.42.0",
|
||||
"status-bar": "0.46.0",
|
||||
@ -109,7 +109,7 @@
|
||||
"update-package-dependencies": "0.6.0",
|
||||
"welcome": "0.18.0",
|
||||
"whitespace": "0.25.0",
|
||||
"wrap-guide": "0.22.0",
|
||||
"wrap-guide": "0.23.0",
|
||||
"language-c": "0.28.0",
|
||||
"language-coffee-script": "0.35.0",
|
||||
"language-css": "0.20.0",
|
||||
|
@ -99,8 +99,6 @@ describe "CommandRegistry", ->
|
||||
expect(dispatchedEvent.stopImmediatePropagation).toHaveBeenCalled()
|
||||
|
||||
it "forwards .preventDefault() calls from the synthetic event to the original", ->
|
||||
calls = []
|
||||
|
||||
registry.add '.child', 'command', (event) -> event.preventDefault()
|
||||
|
||||
dispatchedEvent = new CustomEvent('command', bubbles: true)
|
||||
@ -109,8 +107,6 @@ describe "CommandRegistry", ->
|
||||
expect(dispatchedEvent.preventDefault).toHaveBeenCalled()
|
||||
|
||||
it "forwards .abortKeyBinding() calls from the synthetic event to the original", ->
|
||||
calls = []
|
||||
|
||||
registry.add '.child', 'command', (event) -> event.abortKeyBinding()
|
||||
|
||||
dispatchedEvent = new CustomEvent('command', bubbles: true)
|
||||
|
36
spec/text-editor-element-spec.coffee
Normal file
36
spec/text-editor-element-spec.coffee
Normal file
@ -0,0 +1,36 @@
|
||||
TextEditorElement = require '../src/text-editor-element'
|
||||
|
||||
# The rest of text-editor-component-spec will be moved to this file when React
|
||||
# is eliminated. This covers only concerns related to the wrapper element for now
|
||||
describe "TextEditorElement", ->
|
||||
jasmineContent = null
|
||||
|
||||
beforeEach ->
|
||||
jasmineContent = document.body.querySelector('#jasmine-content')
|
||||
|
||||
describe "instantiation", ->
|
||||
it "honors the mini attribute", ->
|
||||
jasmineContent.innerHTML = "<div is='atom-text-editor' mini>"
|
||||
element = jasmineContent.firstChild
|
||||
expect(element.getModel().isMini()).toBe true
|
||||
|
||||
it "honors the placeholder-text attribute", ->
|
||||
jasmineContent.innerHTML = "<div is='atom-text-editor' placeholder-text='testing'>"
|
||||
element = jasmineContent.firstChild
|
||||
expect(element.getModel().getPlaceholderText()).toBe 'testing'
|
||||
|
||||
describe "::focus()", ->
|
||||
it "transfers focus to the hidden text area and does not emit 'focusout' or 'blur' events", ->
|
||||
element = new TextEditorElement
|
||||
jasmineContent.appendChild(element)
|
||||
|
||||
focusoutCalled = false
|
||||
element.addEventListener 'focusout', -> focusoutCalled = true
|
||||
blurCalled = false
|
||||
element.addEventListener 'blur', -> blurCalled = true
|
||||
|
||||
element.focus()
|
||||
expect(focusoutCalled).toBe false
|
||||
expect(blurCalled).toBe false
|
||||
expect(element.hasFocus()).toBe true
|
||||
expect(element.querySelector('input')).toBe document.activeElement
|
@ -210,7 +210,7 @@ class CommandRegistry
|
||||
|
||||
loop
|
||||
listeners = @inlineListenersByCommandName[originalEvent.type]?.get(currentTarget) ? []
|
||||
unless currentTarget is window or currentTarget is document
|
||||
if currentTarget.webkitMatchesSelector?
|
||||
selectorBasedListeners =
|
||||
(@selectorBasedListenersByCommandName[originalEvent.type] ? [])
|
||||
.filter (listener) -> currentTarget.webkitMatchesSelector(listener.selector)
|
||||
|
@ -7,6 +7,8 @@ PaneElement = require './pane-element'
|
||||
PaneContainerElement = require './pane-container-element'
|
||||
PaneAxisElement = require './pane-axis-element'
|
||||
PaneAxis = require './pane-axis'
|
||||
TextEditor = require './text-editor'
|
||||
TextEditorElement = require './text-editor-element'
|
||||
ViewRegistry = require './view-registry'
|
||||
ItemRegistry = require './item-registry'
|
||||
|
||||
@ -66,6 +68,10 @@ class PaneContainer extends Model
|
||||
modelConstructor: Pane
|
||||
viewConstructor: PaneElement
|
||||
|
||||
@viewRegistry.addViewProvider
|
||||
modelConstructor: TextEditor
|
||||
viewConstructor: TextEditorElement
|
||||
|
||||
getView: (object) ->
|
||||
@viewRegistry.getView(object)
|
||||
|
||||
|
@ -176,7 +176,6 @@ TextEditorComponent = React.createClass
|
||||
|
||||
@observeEditor()
|
||||
@listenForDOMEvents()
|
||||
@listenForCommands()
|
||||
|
||||
@subscribe atom.themes.onDidAddStylesheet @onStylesheetsChanged
|
||||
@subscribe atom.themes.onDidUpdateStylesheet @onStylesheetsChanged
|
||||
@ -193,7 +192,7 @@ TextEditorComponent = React.createClass
|
||||
componentWillUnmount: ->
|
||||
{editor, parentView} = @props
|
||||
|
||||
parentView.trigger 'editor:will-be-removed', [parentView]
|
||||
parentView.__spacePenView.trigger 'editor:will-be-removed', [parentView.__spacePenView]
|
||||
@unsubscribe()
|
||||
window.removeEventListener 'resize', @requestHeightAndWidthMeasurement
|
||||
clearInterval(@domPollingIntervalId)
|
||||
@ -212,9 +211,9 @@ TextEditorComponent = React.createClass
|
||||
if @props.editor.isAlive()
|
||||
@updateParentViewFocusedClassIfNeeded(prevState)
|
||||
@updateParentViewMiniClassIfNeeded(prevState)
|
||||
@props.parentView.trigger 'cursor:moved' if cursorMoved
|
||||
@props.parentView.trigger 'selection:changed' if selectionChanged
|
||||
@props.parentView.trigger 'editor:display-updated'
|
||||
@props.parentView.__spacePenView.trigger 'cursor:moved' if cursorMoved
|
||||
@props.parentView.__spacePenView.trigger 'selection:changed' if selectionChanged
|
||||
@props.parentView.__spacePenView.trigger 'editor:display-updated'
|
||||
|
||||
becameVisible: ->
|
||||
@updatesPaused = true
|
||||
@ -255,7 +254,7 @@ TextEditorComponent = React.createClass
|
||||
@forceUpdate()
|
||||
|
||||
getTopmostDOMNode: ->
|
||||
@props.parentView.element
|
||||
@props.parentView
|
||||
|
||||
getRenderedRowRange: ->
|
||||
{editor, lineOverdrawMargin} = @props
|
||||
@ -406,123 +405,6 @@ TextEditorComponent = React.createClass
|
||||
editor.insertText(selectedText, select: true, undo: 'skip')
|
||||
event.target.value = ''
|
||||
|
||||
listenForCommands: ->
|
||||
{parentView, editor, mini} = @props
|
||||
|
||||
@addCommandListeners
|
||||
'core:move-left': -> editor.moveLeft()
|
||||
'core:move-right': -> editor.moveRight()
|
||||
'core:select-left': -> editor.selectLeft()
|
||||
'core:select-right': -> editor.selectRight()
|
||||
'core:select-all': -> editor.selectAll()
|
||||
'core:backspace': -> editor.backspace()
|
||||
'core:delete': -> editor.delete()
|
||||
'core:undo': -> editor.undo()
|
||||
'core:redo': -> editor.redo()
|
||||
'core:cut': -> editor.cutSelectedText()
|
||||
'core:copy': -> editor.copySelectedText()
|
||||
'core:paste': -> editor.pasteText()
|
||||
'editor:move-to-previous-word': -> editor.moveToPreviousWord()
|
||||
'editor:select-word': -> editor.selectWordsContainingCursors()
|
||||
'editor:consolidate-selections': @consolidateSelections
|
||||
'editor:delete-to-beginning-of-word': -> editor.deleteToBeginningOfWord()
|
||||
'editor:delete-to-beginning-of-line': -> editor.deleteToBeginningOfLine()
|
||||
'editor:delete-to-end-of-line': -> editor.deleteToEndOfLine()
|
||||
'editor:delete-to-end-of-word': -> editor.deleteToEndOfWord()
|
||||
'editor:delete-line': -> editor.deleteLine()
|
||||
'editor:cut-to-end-of-line': -> editor.cutToEndOfLine()
|
||||
'editor:move-to-beginning-of-next-paragraph': -> editor.moveToBeginningOfNextParagraph()
|
||||
'editor:move-to-beginning-of-previous-paragraph': -> editor.moveToBeginningOfPreviousParagraph()
|
||||
'editor:move-to-beginning-of-screen-line': -> editor.moveToBeginningOfScreenLine()
|
||||
'editor:move-to-beginning-of-line': -> editor.moveToBeginningOfLine()
|
||||
'editor:move-to-end-of-screen-line': -> editor.moveToEndOfScreenLine()
|
||||
'editor:move-to-end-of-line': -> editor.moveToEndOfLine()
|
||||
'editor:move-to-first-character-of-line': -> editor.moveToFirstCharacterOfLine()
|
||||
'editor:move-to-beginning-of-word': -> editor.moveToBeginningOfWord()
|
||||
'editor:move-to-end-of-word': -> editor.moveToEndOfWord()
|
||||
'editor:move-to-beginning-of-next-word': -> editor.moveToBeginningOfNextWord()
|
||||
'editor:move-to-previous-word-boundary': -> editor.moveToPreviousWordBoundary()
|
||||
'editor:move-to-next-word-boundary': -> editor.moveToNextWordBoundary()
|
||||
'editor:select-to-beginning-of-next-paragraph': -> editor.selectToBeginningOfNextParagraph()
|
||||
'editor:select-to-beginning-of-previous-paragraph': -> editor.selectToBeginningOfPreviousParagraph()
|
||||
'editor:select-to-end-of-line': -> editor.selectToEndOfLine()
|
||||
'editor:select-to-beginning-of-line': -> editor.selectToBeginningOfLine()
|
||||
'editor:select-to-end-of-word': -> editor.selectToEndOfWord()
|
||||
'editor:select-to-beginning-of-word': -> editor.selectToBeginningOfWord()
|
||||
'editor:select-to-beginning-of-next-word': -> editor.selectToBeginningOfNextWord()
|
||||
'editor:select-to-next-word-boundary': -> editor.selectToNextWordBoundary()
|
||||
'editor:select-to-previous-word-boundary': -> editor.selectToPreviousWordBoundary()
|
||||
'editor:select-to-first-character-of-line': -> editor.selectToFirstCharacterOfLine()
|
||||
'editor:select-line': -> editor.selectLinesContainingCursors()
|
||||
'editor:transpose': -> editor.transpose()
|
||||
'editor:upper-case': -> editor.upperCase()
|
||||
'editor:lower-case': -> editor.lowerCase()
|
||||
|
||||
unless mini
|
||||
@addCommandListeners
|
||||
'core:move-up': -> editor.moveUp()
|
||||
'core:move-down': -> editor.moveDown()
|
||||
'core:move-to-top': -> editor.moveToTop()
|
||||
'core:move-to-bottom': -> editor.moveToBottom()
|
||||
'core:page-up': -> editor.pageUp()
|
||||
'core:page-down': -> editor.pageDown()
|
||||
'core:select-up': -> editor.selectUp()
|
||||
'core:select-down': -> editor.selectDown()
|
||||
'core:select-to-top': -> editor.selectToTop()
|
||||
'core:select-to-bottom': -> editor.selectToBottom()
|
||||
'core:select-page-up': -> editor.selectPageUp()
|
||||
'core:select-page-down': -> editor.selectPageDown()
|
||||
'editor:indent': -> editor.indent()
|
||||
'editor:auto-indent': -> editor.autoIndentSelectedRows()
|
||||
'editor:indent-selected-rows': -> editor.indentSelectedRows()
|
||||
'editor:outdent-selected-rows': -> editor.outdentSelectedRows()
|
||||
'editor:newline': -> editor.insertNewline()
|
||||
'editor:newline-below': -> editor.insertNewlineBelow()
|
||||
'editor:newline-above': -> editor.insertNewlineAbove()
|
||||
'editor:add-selection-below': -> editor.addSelectionBelow()
|
||||
'editor:add-selection-above': -> editor.addSelectionAbove()
|
||||
'editor:split-selections-into-lines': -> editor.splitSelectionsIntoLines()
|
||||
'editor:toggle-soft-tabs': -> editor.toggleSoftTabs()
|
||||
'editor:toggle-soft-wrap': -> editor.toggleSoftWrapped()
|
||||
'editor:fold-all': -> editor.foldAll()
|
||||
'editor:unfold-all': -> editor.unfoldAll()
|
||||
'editor:fold-current-row': -> editor.foldCurrentRow()
|
||||
'editor:unfold-current-row': -> editor.unfoldCurrentRow()
|
||||
'editor:fold-selection': -> editor.foldSelectedLines()
|
||||
'editor:fold-at-indent-level-1': -> editor.foldAllAtIndentLevel(0)
|
||||
'editor:fold-at-indent-level-2': -> editor.foldAllAtIndentLevel(1)
|
||||
'editor:fold-at-indent-level-3': -> editor.foldAllAtIndentLevel(2)
|
||||
'editor:fold-at-indent-level-4': -> editor.foldAllAtIndentLevel(3)
|
||||
'editor:fold-at-indent-level-5': -> editor.foldAllAtIndentLevel(4)
|
||||
'editor:fold-at-indent-level-6': -> editor.foldAllAtIndentLevel(5)
|
||||
'editor:fold-at-indent-level-7': -> editor.foldAllAtIndentLevel(6)
|
||||
'editor:fold-at-indent-level-8': -> editor.foldAllAtIndentLevel(7)
|
||||
'editor:fold-at-indent-level-9': -> editor.foldAllAtIndentLevel(8)
|
||||
'editor:toggle-line-comments': -> editor.toggleLineCommentsInSelection()
|
||||
'editor:log-cursor-scope': -> editor.logCursorScope()
|
||||
'editor:checkout-head-revision': -> atom.project.getRepositories()[0]?.checkoutHeadForEditor(editor)
|
||||
'editor:copy-path': -> editor.copyPathToClipboard()
|
||||
'editor:move-line-up': -> editor.moveLineUp()
|
||||
'editor:move-line-down': -> editor.moveLineDown()
|
||||
'editor:duplicate-lines': -> editor.duplicateLines()
|
||||
'editor:join-lines': -> editor.joinLines()
|
||||
'editor:toggle-indent-guide': -> atom.config.set('editor.showIndentGuide', not atom.config.get('editor.showIndentGuide'))
|
||||
'editor:toggle-line-numbers': -> atom.config.set('editor.showLineNumbers', not atom.config.get('editor.showLineNumbers'))
|
||||
'editor:scroll-to-cursor': -> editor.scrollToCursorPosition()
|
||||
'benchmark:scroll': @runScrollBenchmark
|
||||
|
||||
addCommandListeners: (listenersByCommandName) ->
|
||||
{parentView} = @props
|
||||
|
||||
addListener = (command, listener) =>
|
||||
@subscribe parentView.command command, (event) ->
|
||||
event.stopPropagation()
|
||||
listener(event)
|
||||
|
||||
addListener(command, listener) for command, listener of listenersByCommandName
|
||||
|
||||
return
|
||||
|
||||
observeConfig: ->
|
||||
@subscribe atom.config.observe 'editor.showIndentGuide', @setShowIndentGuide
|
||||
@subscribe atom.config.observe 'editor.showLineNumbers', @setShowLineNumbers
|
||||
@ -864,10 +746,9 @@ TextEditorComponent = React.createClass
|
||||
return unless @isMounted()
|
||||
|
||||
{editor, parentView} = @props
|
||||
parentNode = parentView.element
|
||||
scrollViewNode = @refs.scrollView.getDOMNode()
|
||||
{position} = getComputedStyle(parentNode)
|
||||
{height} = parentNode.style
|
||||
{position} = getComputedStyle(parentView)
|
||||
{height} = parentView.style
|
||||
|
||||
if position is 'absolute' or height
|
||||
if @autoHeight
|
||||
@ -901,7 +782,7 @@ TextEditorComponent = React.createClass
|
||||
sampleBackgroundColors: (suppressUpdate) ->
|
||||
{parentView} = @props
|
||||
{showLineNumbers} = @state
|
||||
{backgroundColor} = getComputedStyle(parentView.element)
|
||||
{backgroundColor} = getComputedStyle(parentView)
|
||||
|
||||
if backgroundColor isnt @backgroundColor
|
||||
@backgroundColor = backgroundColor
|
||||
@ -1070,11 +951,11 @@ TextEditorComponent = React.createClass
|
||||
|
||||
updateParentViewFocusedClassIfNeeded: (prevState) ->
|
||||
if prevState.focused isnt @state.focused
|
||||
@props.parentView.toggleClass('is-focused', @props.focused)
|
||||
@props.parentView.classList.toggle('is-focused', @state.focused)
|
||||
|
||||
updateParentViewMiniClassIfNeeded: (prevProps) ->
|
||||
if prevProps.mini isnt @props.mini
|
||||
@props.parentView.toggleClass('mini', @props.mini)
|
||||
@props.parentView.classList.toggle('mini', @props.mini)
|
||||
|
||||
runScrollBenchmark: ->
|
||||
unless process.env.NODE_ENV is 'production'
|
||||
|
206
src/text-editor-element.coffee
Normal file
206
src/text-editor-element.coffee
Normal file
@ -0,0 +1,206 @@
|
||||
{View, $} = require 'space-pen'
|
||||
React = require 'react-atom-fork'
|
||||
{defaults} = require 'underscore-plus'
|
||||
TextBuffer = require 'text-buffer'
|
||||
TextEditor = require './text-editor'
|
||||
TextEditorComponent = require './text-editor-component'
|
||||
TextEditorView = null
|
||||
|
||||
class TextEditorElement extends HTMLElement
|
||||
model: null
|
||||
componentDescriptor: null
|
||||
component: null
|
||||
lineOverdrawMargin: null
|
||||
focusOnAttach: false
|
||||
|
||||
createdCallback: ->
|
||||
@subscriptions =
|
||||
@initializeContent()
|
||||
@createSpacePenShim()
|
||||
@addEventListener 'focus', @focused.bind(this)
|
||||
@addEventListener 'focusout', @focusedOut.bind(this)
|
||||
@addEventListener 'blur', @blurred.bind(this)
|
||||
|
||||
initializeContent: (attributes) ->
|
||||
@classList.add('editor', 'react', 'editor-colors')
|
||||
@setAttribute('tabindex', -1)
|
||||
|
||||
createSpacePenShim: ->
|
||||
TextEditorView ?= require './text-editor-view'
|
||||
@__spacePenView = new TextEditorView(this)
|
||||
|
||||
attachedCallback: ->
|
||||
@buildModel() unless @getModel()?
|
||||
@mountComponent() unless @component?.isMounted()
|
||||
@component.checkForVisibilityChange()
|
||||
@focus() if @focusOnAttach
|
||||
|
||||
setModel: (model) ->
|
||||
throw new Error("Model already assigned on TextEditorElement") if @model?
|
||||
@model = model
|
||||
@mountComponent()
|
||||
@addGrammarScopeAttribute()
|
||||
@model.onDidChangeGrammar => @addGrammarScopeAttribute()
|
||||
@__spacePenView.setModel(@model)
|
||||
@model
|
||||
|
||||
getModel: ->
|
||||
@model ? @buildModel()
|
||||
|
||||
buildModel: ->
|
||||
@setModel(new TextEditor(
|
||||
buffer: new TextBuffer
|
||||
softWrapped: false
|
||||
tabLength: 2
|
||||
softTabs: true
|
||||
mini: @hasAttribute('mini')
|
||||
placeholderText: @getAttribute('placeholder-text')
|
||||
))
|
||||
|
||||
mountComponent: ->
|
||||
@componentDescriptor ?= TextEditorComponent(
|
||||
parentView: this
|
||||
editor: @model
|
||||
mini: @model.mini
|
||||
lineOverdrawMargin: @lineOverdrawMargin
|
||||
)
|
||||
@component = React.renderComponent(@componentDescriptor, this)
|
||||
|
||||
unmountComponent: ->
|
||||
return unless @component?.isMounted()
|
||||
React.unmountComponentAtNode(this)
|
||||
@component = null
|
||||
|
||||
focused: ->
|
||||
if @component?
|
||||
@component.onFocus()
|
||||
else
|
||||
@focusOnAttach = true
|
||||
|
||||
focusedOut: (event) ->
|
||||
event.stopImmediatePropagation() if @contains(event.relatedTarget)
|
||||
|
||||
blurred: (event) ->
|
||||
event.stopImmediatePropagation() if @contains(event.relatedTarget)
|
||||
|
||||
addGrammarScopeAttribute: ->
|
||||
grammarScope = @model.getGrammar()?.scopeName?.replace(/\./g, ' ')
|
||||
@setAttribute('data-grammar', grammarScope)
|
||||
|
||||
hasFocus: ->
|
||||
this is document.activeElement or @contains(document.activeElement)
|
||||
|
||||
stopCommandEventPropagation = (commandListeners) ->
|
||||
newCommandListeners = {}
|
||||
for commandName, commandListener of commandListeners
|
||||
do (commandListener) ->
|
||||
newCommandListeners[commandName] = (event) ->
|
||||
event.stopPropagation()
|
||||
commandListener.call(this, event)
|
||||
newCommandListeners
|
||||
|
||||
atom.commands.add '[is=atom-text-editor]', stopCommandEventPropagation(
|
||||
'core:move-left': -> @getModel().moveLeft()
|
||||
'core:move-right': -> @getModel().moveRight()
|
||||
'core:select-left': -> @getModel().selectLeft()
|
||||
'core:select-right': -> @getModel().selectRight()
|
||||
'core:select-all': -> @getModel().selectAll()
|
||||
'core:backspace': -> @getModel().backspace()
|
||||
'core:delete': -> @getModel().delete()
|
||||
'core:undo': -> @getModel().undo()
|
||||
'core:redo': -> @getModel().redo()
|
||||
'core:cut': -> @getModel().cutSelectedText()
|
||||
'core:copy': -> @getModel().copySelectedText()
|
||||
'core:paste': -> @getModel().pasteText()
|
||||
'editor:move-to-previous-word': -> @getModel().moveToPreviousWord()
|
||||
'editor:select-word': -> @getModel().selectWordsContainingCursors()
|
||||
'editor:consolidate-selections': (event) -> event.abortKeyBinding() unless @getModel().consolidateSelections()
|
||||
'editor:delete-to-beginning-of-word': -> @getModel().deleteToBeginningOfWord()
|
||||
'editor:delete-to-beginning-of-line': -> @getModel().deleteToBeginningOfLine()
|
||||
'editor:delete-to-end-of-line': -> @getModel().deleteToEndOfLine()
|
||||
'editor:delete-to-end-of-word': -> @getModel().deleteToEndOfWord()
|
||||
'editor:delete-line': -> @getModel().deleteLine()
|
||||
'editor:cut-to-end-of-line': -> @getModel().cutToEndOfLine()
|
||||
'editor:move-to-beginning-of-next-paragraph': -> @getModel().moveToBeginningOfNextParagraph()
|
||||
'editor:move-to-beginning-of-previous-paragraph': -> @getModel().moveToBeginningOfPreviousParagraph()
|
||||
'editor:move-to-beginning-of-screen-line': -> @getModel().moveToBeginningOfScreenLine()
|
||||
'editor:move-to-beginning-of-line': -> @getModel().moveToBeginningOfLine()
|
||||
'editor:move-to-end-of-screen-line': -> @getModel().moveToEndOfScreenLine()
|
||||
'editor:move-to-end-of-line': -> @getModel().moveToEndOfLine()
|
||||
'editor:move-to-first-character-of-line': -> @getModel().moveToFirstCharacterOfLine()
|
||||
'editor:move-to-beginning-of-word': -> @getModel().moveToBeginningOfWord()
|
||||
'editor:move-to-end-of-word': -> @getModel().moveToEndOfWord()
|
||||
'editor:move-to-beginning-of-next-word': -> @getModel().moveToBeginningOfNextWord()
|
||||
'editor:move-to-previous-word-boundary': -> @getModel().moveToPreviousWordBoundary()
|
||||
'editor:move-to-next-word-boundary': -> @getModel().moveToNextWordBoundary()
|
||||
'editor:select-to-beginning-of-next-paragraph': -> @getModel().selectToBeginningOfNextParagraph()
|
||||
'editor:select-to-beginning-of-previous-paragraph': -> @getModel().selectToBeginningOfPreviousParagraph()
|
||||
'editor:select-to-end-of-line': -> @getModel().selectToEndOfLine()
|
||||
'editor:select-to-beginning-of-line': -> @getModel().selectToBeginningOfLine()
|
||||
'editor:select-to-end-of-word': -> @getModel().selectToEndOfWord()
|
||||
'editor:select-to-beginning-of-word': -> @getModel().selectToBeginningOfWord()
|
||||
'editor:select-to-beginning-of-next-word': -> @getModel().selectToBeginningOfNextWord()
|
||||
'editor:select-to-next-word-boundary': -> @getModel().selectToNextWordBoundary()
|
||||
'editor:select-to-previous-word-boundary': -> @getModel().selectToPreviousWordBoundary()
|
||||
'editor:select-to-first-character-of-line': -> @getModel().selectToFirstCharacterOfLine()
|
||||
'editor:select-line': -> @getModel().selectLinesContainingCursors()
|
||||
'editor:transpose': -> @getModel().transpose()
|
||||
'editor:upper-case': -> @getModel().upperCase()
|
||||
'editor:lower-case': -> @getModel().lowerCase()
|
||||
)
|
||||
|
||||
atom.commands.add '[is=atom-text-editor]:not(.mini)', stopCommandEventPropagation(
|
||||
'core:move-up': -> @getModel().moveUp()
|
||||
'core:move-down': -> @getModel().moveDown()
|
||||
'core:move-to-top': -> @getModel().moveToTop()
|
||||
'core:move-to-bottom': -> @getModel().moveToBottom()
|
||||
'core:page-up': -> @getModel().pageUp()
|
||||
'core:page-down': -> @getModel().pageDown()
|
||||
'core:select-up': -> @getModel().selectUp()
|
||||
'core:select-down': -> @getModel().selectDown()
|
||||
'core:select-to-top': -> @getModel().selectToTop()
|
||||
'core:select-to-bottom': -> @getModel().selectToBottom()
|
||||
'core:select-page-up': -> @getModel().selectPageUp()
|
||||
'core:select-page-down': -> @getModel().selectPageDown()
|
||||
'editor:indent': -> @getModel().indent()
|
||||
'editor:auto-indent': -> @getModel().autoIndentSelectedRows()
|
||||
'editor:indent-selected-rows': -> @getModel().indentSelectedRows()
|
||||
'editor:outdent-selected-rows': -> @getModel().outdentSelectedRows()
|
||||
'editor:newline': -> @getModel().insertNewline()
|
||||
'editor:newline-below': -> @getModel().insertNewlineBelow()
|
||||
'editor:newline-above': -> @getModel().insertNewlineAbove()
|
||||
'editor:add-selection-below': -> @getModel().addSelectionBelow()
|
||||
'editor:add-selection-above': -> @getModel().addSelectionAbove()
|
||||
'editor:split-selections-into-lines': -> @getModel().splitSelectionsIntoLines()
|
||||
'editor:toggle-soft-tabs': -> @getModel().toggleSoftTabs()
|
||||
'editor:toggle-soft-wrap': -> @getModel().toggleSoftWrapped()
|
||||
'editor:fold-all': -> @getModel().foldAll()
|
||||
'editor:unfold-all': -> @getModel().unfoldAll()
|
||||
'editor:fold-current-row': -> @getModel().foldCurrentRow()
|
||||
'editor:unfold-current-row': -> @getModel().unfoldCurrentRow()
|
||||
'editor:fold-selection': -> @getModel().foldSelectedLines()
|
||||
'editor:fold-at-indent-level-1': -> @getModel().foldAllAtIndentLevel(0)
|
||||
'editor:fold-at-indent-level-2': -> @getModel().foldAllAtIndentLevel(1)
|
||||
'editor:fold-at-indent-level-3': -> @getModel().foldAllAtIndentLevel(2)
|
||||
'editor:fold-at-indent-level-4': -> @getModel().foldAllAtIndentLevel(3)
|
||||
'editor:fold-at-indent-level-5': -> @getModel().foldAllAtIndentLevel(4)
|
||||
'editor:fold-at-indent-level-6': -> @getModel().foldAllAtIndentLevel(5)
|
||||
'editor:fold-at-indent-level-7': -> @getModel().foldAllAtIndentLevel(6)
|
||||
'editor:fold-at-indent-level-8': -> @getModel().foldAllAtIndentLevel(7)
|
||||
'editor:fold-at-indent-level-9': -> @getModel().foldAllAtIndentLevel(8)
|
||||
'editor:toggle-line-comments': -> @getModel().toggleLineCommentsInSelection()
|
||||
'editor:log-cursor-scope': -> @getModel().logCursorScope()
|
||||
'editor:checkout-head-revision': -> atom.project.getRepositories()[0]?.checkoutHeadForEditor(@getModel())
|
||||
'editor:copy-path': -> @getModel().copyPathToClipboard()
|
||||
'editor:move-line-up': -> @getModel().moveLineUp()
|
||||
'editor:move-line-down': -> @getModel().moveLineDown()
|
||||
'editor:duplicate-lines': -> @getModel().duplicateLines()
|
||||
'editor:join-lines': -> @getModel().joinLines()
|
||||
'editor:toggle-indent-guide': -> atom.config.set('editor.showIndentGuide', not atom.config.get('editor.showIndentGuide'))
|
||||
'editor:toggle-line-numbers': -> atom.config.set('editor.showLineNumbers', not atom.config.get('editor.showLineNumbers'))
|
||||
'editor:scroll-to-cursor': -> @getModel().scrollToCursorPosition()
|
||||
)
|
||||
|
||||
module.exports = TextEditorElement = document.registerElement 'atom-text-editor',
|
||||
prototype: TextEditorElement.prototype
|
||||
extends: 'div'
|
@ -3,6 +3,7 @@ React = require 'react-atom-fork'
|
||||
{defaults} = require 'underscore-plus'
|
||||
TextBuffer = require 'text-buffer'
|
||||
TextEditor = require './text-editor'
|
||||
TextEditorElement = require './text-editor-element'
|
||||
TextEditorComponent = require './text-editor-component'
|
||||
{deprecate} = require 'grim'
|
||||
|
||||
@ -37,51 +38,48 @@ TextEditorComponent = require './text-editor-component'
|
||||
# ```
|
||||
module.exports =
|
||||
class TextEditorView extends View
|
||||
@content: (params) ->
|
||||
attributes = params.attributes ? {}
|
||||
attributes.class = 'editor react editor-colors'
|
||||
attributes.tabIndex = -1
|
||||
@div attributes
|
||||
|
||||
focusOnAttach: false
|
||||
|
||||
# The constructor for setting up an `TextEditorView` instance.
|
||||
#
|
||||
# * `editorOrParams` Either an {TextEditor}, or an object with one property, `mini`.
|
||||
# * `modelOrParams` Either an {TextEditor}, or an object with one property, `mini`.
|
||||
# If `mini` is `true`, a "miniature" `TextEditor` is constructed.
|
||||
# Typically, this is ideal for scenarios where you need an Atom editor,
|
||||
# but without all the chrome, like scrollbars, gutter, _e.t.c._.
|
||||
#
|
||||
constructor: (editorOrParams, props) ->
|
||||
constructor: (modelOrParams, props) ->
|
||||
# Handle direct construction with an editor or params
|
||||
unless modelOrParams instanceof HTMLElement
|
||||
if modelOrParams instanceof TextEditor
|
||||
model = modelOrParams
|
||||
else
|
||||
{editor, mini, placeholderText, attributes} = modelOrParams
|
||||
model = editor ? new TextEditor
|
||||
buffer: new TextBuffer
|
||||
softWrapped: false
|
||||
tabLength: 2
|
||||
softTabs: true
|
||||
mini: mini
|
||||
placeholderText: placeholderText
|
||||
|
||||
element = new TextEditorElement
|
||||
element.lineOverdrawMargin = props?.lineOverdrawMargin
|
||||
element.setAttribute(name, value) for name, value of attributes if attributes?
|
||||
element.setModel(model)
|
||||
return element.__spacePenView
|
||||
|
||||
# Handle construction with an element
|
||||
@element = modelOrParams
|
||||
super
|
||||
|
||||
if editorOrParams instanceof TextEditor
|
||||
@editor = editorOrParams
|
||||
else
|
||||
{@editor, mini, placeholderText} = editorOrParams
|
||||
props ?= {}
|
||||
props.mini = mini
|
||||
@editor ?= new TextEditor
|
||||
buffer: new TextBuffer
|
||||
softWrapped: false
|
||||
tabLength: 2
|
||||
softTabs: true
|
||||
mini: mini
|
||||
placeholderText: placeholderText
|
||||
setModel: (@model) ->
|
||||
@editor = @model
|
||||
|
||||
props = defaults({@editor, parentView: this}, props)
|
||||
@componentDescriptor = TextEditorComponent(props)
|
||||
@component = React.renderComponent(@componentDescriptor, @element)
|
||||
|
||||
node = @component.getDOMNode()
|
||||
|
||||
@scrollView = $(node).find('.scroll-view')
|
||||
@underlayer = $(node).find('.highlights').addClass('underlayer')
|
||||
@overlayer = $(node).find('.lines').addClass('overlayer')
|
||||
@hiddenInput = $(node).find('.hidden-input')
|
||||
@scrollView = @find('.scroll-view')
|
||||
@underlayer = @find('.highlights').addClass('underlayer')
|
||||
@overlayer = @find('.lines').addClass('overlayer')
|
||||
@hiddenInput = @.find('.hidden-input')
|
||||
|
||||
@subscribe atom.config.observe 'editor.showLineNumbers', =>
|
||||
@gutter = $(node).find('.gutter')
|
||||
@gutter = @find('.gutter')
|
||||
|
||||
@gutter.removeClassFromAllLines = (klass) =>
|
||||
deprecate('Use decorations instead: http://blog.atom.io/2014/07/24/decorations.html')
|
||||
@ -97,99 +95,77 @@ class TextEditorView extends View
|
||||
lines.addClass(klass)
|
||||
lines.length > 0
|
||||
|
||||
@on 'focus', =>
|
||||
if @component?
|
||||
@component.onFocus()
|
||||
else
|
||||
@focusOnAttach = true
|
||||
|
||||
# Public: Get the underlying editor model for this view.
|
||||
#
|
||||
# Returns an {TextEditor}
|
||||
getModel: -> @editor
|
||||
getModel: -> @model
|
||||
|
||||
getEditor: -> @editor
|
||||
getEditor: -> @model
|
||||
|
||||
Object.defineProperty @::, 'lineHeight', get: -> @editor.getLineHeightInPixels()
|
||||
Object.defineProperty @::, 'charWidth', get: -> @editor.getDefaultCharWidth()
|
||||
Object.defineProperty @::, 'lineHeight', get: -> @model.getLineHeightInPixels()
|
||||
Object.defineProperty @::, 'charWidth', get: -> @model.getDefaultCharWidth()
|
||||
Object.defineProperty @::, 'firstRenderedScreenRow', get: -> @component.getRenderedRowRange()[0]
|
||||
Object.defineProperty @::, 'lastRenderedScreenRow', get: -> @component.getRenderedRowRange()[1]
|
||||
Object.defineProperty @::, 'active', get: -> @is(@getPaneView()?.activeView)
|
||||
Object.defineProperty @::, 'isFocused', get: -> @component?.state.focused
|
||||
Object.defineProperty @::, 'mini', get: -> @component?.props.mini
|
||||
Object.defineProperty @::, 'component', get: -> @element?.component
|
||||
|
||||
afterAttach: (onDom) ->
|
||||
return unless onDom
|
||||
return if @attached
|
||||
@attached = true
|
||||
@component = React.renderComponent(@componentDescriptor, @element) unless @component.isMounted()
|
||||
@component.checkForVisibilityChange()
|
||||
|
||||
@focus() if @focusOnAttach
|
||||
|
||||
@addGrammarScopeAttribute()
|
||||
@subscribe @editor.onDidChangeGrammar => @addGrammarScopeAttribute()
|
||||
|
||||
@trigger 'editor:attached', [this]
|
||||
|
||||
addGrammarScopeAttribute: ->
|
||||
grammarScope = @editor.getGrammar()?.scopeName?.replace(/\./g, ' ')
|
||||
@attr('data-grammar', grammarScope)
|
||||
beforeRemove: ->
|
||||
@trigger 'editor:detached', [this]
|
||||
@attached = false
|
||||
|
||||
remove: (selector, keepData) ->
|
||||
@model.destroy() unless keepData
|
||||
super
|
||||
|
||||
scrollTop: (scrollTop) ->
|
||||
if scrollTop?
|
||||
@editor.setScrollTop(scrollTop)
|
||||
@model.setScrollTop(scrollTop)
|
||||
else
|
||||
@editor.getScrollTop()
|
||||
@model.getScrollTop()
|
||||
|
||||
scrollLeft: (scrollLeft) ->
|
||||
if scrollLeft?
|
||||
@editor.setScrollLeft(scrollLeft)
|
||||
@model.setScrollLeft(scrollLeft)
|
||||
else
|
||||
@editor.getScrollLeft()
|
||||
@model.getScrollLeft()
|
||||
|
||||
scrollToBottom: ->
|
||||
deprecate 'Use TextEditor::scrollToBottom instead. You can get the editor via editorView.getModel()'
|
||||
@editor.setScrollBottom(Infinity)
|
||||
@model.setScrollBottom(Infinity)
|
||||
|
||||
scrollToScreenPosition: (screenPosition, options) ->
|
||||
deprecate 'Use TextEditor::scrollToScreenPosition instead. You can get the editor via editorView.getModel()'
|
||||
@editor.scrollToScreenPosition(screenPosition, options)
|
||||
@model.scrollToScreenPosition(screenPosition, options)
|
||||
|
||||
scrollToBufferPosition: (bufferPosition, options) ->
|
||||
deprecate 'Use TextEditor::scrollToBufferPosition instead. You can get the editor via editorView.getModel()'
|
||||
@editor.scrollToBufferPosition(bufferPosition, options)
|
||||
@model.scrollToBufferPosition(bufferPosition, options)
|
||||
|
||||
scrollToCursorPosition: ->
|
||||
deprecate 'Use TextEditor::scrollToCursorPosition instead. You can get the editor via editorView.getModel()'
|
||||
@editor.scrollToCursorPosition()
|
||||
@model.scrollToCursorPosition()
|
||||
|
||||
pixelPositionForBufferPosition: (bufferPosition) ->
|
||||
deprecate 'Use TextEditor::pixelPositionForBufferPosition instead. You can get the editor via editorView.getModel()'
|
||||
@editor.pixelPositionForBufferPosition(bufferPosition)
|
||||
@model.pixelPositionForBufferPosition(bufferPosition)
|
||||
|
||||
pixelPositionForScreenPosition: (screenPosition) ->
|
||||
deprecate 'Use TextEditor::pixelPositionForScreenPosition instead. You can get the editor via editorView.getModel()'
|
||||
@editor.pixelPositionForScreenPosition(screenPosition)
|
||||
@model.pixelPositionForScreenPosition(screenPosition)
|
||||
|
||||
appendToLinesView: (view) ->
|
||||
view.css('position', 'absolute')
|
||||
view.css('z-index', 1)
|
||||
@find('.lines').prepend(view)
|
||||
|
||||
detach: ->
|
||||
return unless @attached
|
||||
super
|
||||
@attached = false
|
||||
@unmountComponent()
|
||||
|
||||
beforeRemove: ->
|
||||
return unless @attached
|
||||
@attached = false
|
||||
@unmountComponent()
|
||||
@editor.destroy()
|
||||
@trigger 'editor:detached', this
|
||||
|
||||
unmountComponent: ->
|
||||
React.unmountComponentAtNode(@element) if @component.isMounted()
|
||||
|
||||
@ -248,19 +224,19 @@ class TextEditorView extends View
|
||||
|
||||
pageDown: ->
|
||||
deprecate('Use editorView.getModel().pageDown()')
|
||||
@editor.pageDown()
|
||||
@model.pageDown()
|
||||
|
||||
pageUp: ->
|
||||
deprecate('Use editorView.getModel().pageUp()')
|
||||
@editor.pageUp()
|
||||
@model.pageUp()
|
||||
|
||||
getFirstVisibleScreenRow: ->
|
||||
deprecate 'Use TextEditor::getFirstVisibleScreenRow instead. You can get the editor via editorView.getModel()'
|
||||
@editor.getFirstVisibleScreenRow()
|
||||
@model.getFirstVisibleScreenRow()
|
||||
|
||||
getLastVisibleScreenRow: ->
|
||||
deprecate 'Use TextEditor::getLastVisibleScreenRow instead. You can get the editor via editorView.getModel()'
|
||||
@editor.getLastVisibleScreenRow()
|
||||
@model.getLastVisibleScreenRow()
|
||||
|
||||
getFontFamily: ->
|
||||
deprecate 'This is going away. Use atom.config.get("editor.fontFamily") instead'
|
||||
@ -283,7 +259,7 @@ class TextEditorView extends View
|
||||
@component.setLineHeight(lineHeight)
|
||||
|
||||
setWidthInChars: (widthInChars) ->
|
||||
@component.getDOMNode().style.width = (@editor.getDefaultCharWidth() * widthInChars) + 'px'
|
||||
@component.getDOMNode().style.width = (@model.getDefaultCharWidth() * widthInChars) + 'px'
|
||||
|
||||
setShowIndentGuide: (showIndentGuide) ->
|
||||
deprecate 'This is going away. Use atom.config.set("editor.showIndentGuide", true|false) instead'
|
||||
@ -291,20 +267,20 @@ class TextEditorView extends View
|
||||
|
||||
setSoftWrap: (softWrapped) ->
|
||||
deprecate 'Use TextEditor::setSoftWrapped instead. You can get the editor via editorView.getModel()'
|
||||
@editor.setSoftWrapped(softWrapped)
|
||||
@model.setSoftWrapped(softWrapped)
|
||||
|
||||
setShowInvisibles: (showInvisibles) ->
|
||||
deprecate 'This is going away. Use atom.config.set("editor.showInvisibles", true|false) instead'
|
||||
@component.setShowInvisibles(showInvisibles)
|
||||
|
||||
getText: ->
|
||||
@editor.getText()
|
||||
@model.getText()
|
||||
|
||||
setText: (text) ->
|
||||
@editor.setText(text)
|
||||
@model.setText(text)
|
||||
|
||||
insertText: (text) ->
|
||||
@editor.insertText(text)
|
||||
@model.insertText(text)
|
||||
|
||||
isInputEnabled: ->
|
||||
@component.isInputEnabled()
|
||||
@ -326,7 +302,7 @@ class TextEditorView extends View
|
||||
|
||||
setPlaceholderText: (placeholderText) ->
|
||||
deprecate('Use TextEditor::setPlaceholderText instead. eg. editorView.getModel().setPlaceholderText(text)')
|
||||
@getModel().setPlaceholderText(placeholderText)
|
||||
@model.setPlaceholderText(placeholderText)
|
||||
|
||||
lineElementForScreenRow: (screenRow) ->
|
||||
$(@component.lineNodeForScreenRow(screenRow))
|
||||
|
@ -162,9 +162,6 @@ class TextEditor extends Model
|
||||
@subscribe @displayBuffer.onDidAddDecoration (decoration) => @emit 'decoration-added', decoration
|
||||
@subscribe @displayBuffer.onDidRemoveDecoration (decoration) => @emit 'decoration-removed', decoration
|
||||
|
||||
getViewClass: ->
|
||||
require './text-editor-view'
|
||||
|
||||
destroyed: ->
|
||||
@unsubscribe()
|
||||
selection.destroy() for selection in @getSelections()
|
||||
@ -504,6 +501,8 @@ class TextEditor extends Model
|
||||
@mini = mini
|
||||
@updateInvisibles()
|
||||
|
||||
isMini: -> @mini
|
||||
|
||||
# Set the number of characters that can be displayed horizontally in the
|
||||
# editor.
|
||||
#
|
||||
|
Loading…
Reference in New Issue
Block a user