mirror of
https://github.com/atom/atom.git
synced 2026-01-23 13:58:08 -05:00
347 lines
9.4 KiB
JavaScript
347 lines
9.4 KiB
JavaScript
const {Emitter, Range} = require('atom')
|
|
const Grim = require('grim')
|
|
const TextEditorComponent = require('./text-editor-component')
|
|
const dedent = require('dedent')
|
|
|
|
class TextEditorElement extends HTMLElement {
|
|
initialize (component) {
|
|
this.component = component
|
|
return this
|
|
}
|
|
|
|
get shadowRoot () {
|
|
Grim.deprecate(dedent`
|
|
The contents of \`atom-text-editor\` elements are no longer encapsulated
|
|
within a shadow DOM boundary. Please, stop using \`shadowRoot\` and access
|
|
the editor contents directly instead.
|
|
`)
|
|
|
|
return this
|
|
}
|
|
|
|
get rootElement () {
|
|
Grim.deprecate(dedent`
|
|
The contents of \`atom-text-editor\` elements are no longer encapsulated
|
|
within a shadow DOM boundary. Please, stop using \`rootElement\` and access
|
|
the editor contents directly instead.
|
|
`)
|
|
|
|
return this
|
|
}
|
|
|
|
createdCallback () {
|
|
this.emitter = new Emitter()
|
|
this.initialText = this.textContent
|
|
this.tabIndex = -1
|
|
this.addEventListener('focus', (event) => this.getComponent().didFocus(event))
|
|
this.addEventListener('blur', (event) => this.getComponent().didBlur(event))
|
|
}
|
|
|
|
attachedCallback () {
|
|
this.getComponent().didAttach()
|
|
this.emitter.emit('did-attach')
|
|
}
|
|
|
|
detachedCallback () {
|
|
this.emitter.emit('did-detach')
|
|
this.getComponent().didDetach()
|
|
}
|
|
|
|
attributeChangedCallback (name, oldValue, newValue) {
|
|
if (this.component) {
|
|
switch (name) {
|
|
case 'mini':
|
|
this.getModel().update({mini: newValue != null})
|
|
break
|
|
case 'placeholder-text':
|
|
this.getModel().update({placeholderText: newValue})
|
|
break
|
|
case 'gutter-hidden':
|
|
this.getModel().update({lineNumberGutterVisible: newValue == null})
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
// Extended: Get a promise that resolves the next time the element's DOM
|
|
// is updated in any way.
|
|
//
|
|
// This can be useful when you've made a change to the model and need to
|
|
// be sure this change has been flushed to the DOM.
|
|
//
|
|
// Returns a {Promise}.
|
|
getNextUpdatePromise () {
|
|
return this.getComponent().getNextUpdatePromise()
|
|
}
|
|
|
|
getModel () {
|
|
return this.getComponent().props.model
|
|
}
|
|
|
|
setModel (model) {
|
|
this.getComponent().update({model})
|
|
this.updateModelFromAttributes()
|
|
}
|
|
|
|
updateModelFromAttributes () {
|
|
const props = {mini: this.hasAttribute('mini')}
|
|
if (this.hasAttribute('placeholder-text')) props.placeholderText = this.getAttribute('placeholder-text')
|
|
if (this.hasAttribute('gutter-hidden')) props.lineNumberGutterVisible = false
|
|
|
|
this.getModel().update(props)
|
|
if (this.initialText) this.getModel().setText(this.initialText)
|
|
}
|
|
|
|
onDidAttach (callback) {
|
|
return this.emitter.on('did-attach', callback)
|
|
}
|
|
|
|
onDidDetach (callback) {
|
|
return this.emitter.on('did-detach', callback)
|
|
}
|
|
|
|
measureDimensions () {
|
|
this.getComponent().measureDimensions()
|
|
}
|
|
|
|
setWidth (width) {
|
|
this.style.width = this.getComponent().getGutterContainerWidth() + width + 'px'
|
|
}
|
|
|
|
getWidth () {
|
|
return this.getComponent().getScrollContainerWidth()
|
|
}
|
|
|
|
setHeight (height) {
|
|
this.style.height = height + 'px'
|
|
}
|
|
|
|
getHeight () {
|
|
return this.getComponent().getScrollContainerHeight()
|
|
}
|
|
|
|
onDidChangeScrollLeft (callback) {
|
|
return this.emitter.on('did-change-scroll-left', callback)
|
|
}
|
|
|
|
onDidChangeScrollTop (callback) {
|
|
return this.emitter.on('did-change-scroll-top', callback)
|
|
}
|
|
|
|
// Deprecated: get the width of an `x` character displayed in this element.
|
|
//
|
|
// Returns a {Number} of pixels.
|
|
getDefaultCharacterWidth () {
|
|
return this.getComponent().getBaseCharacterWidth()
|
|
}
|
|
|
|
// Extended: get the width of an `x` character displayed in this element.
|
|
//
|
|
// Returns a {Number} of pixels.
|
|
getBaseCharacterWidth () {
|
|
return this.getComponent().getBaseCharacterWidth()
|
|
}
|
|
|
|
getMaxScrollTop () {
|
|
return this.getComponent().getMaxScrollTop()
|
|
}
|
|
|
|
getScrollHeight () {
|
|
return this.getComponent().getScrollHeight()
|
|
}
|
|
|
|
getScrollWidth () {
|
|
return this.getComponent().getScrollWidth()
|
|
}
|
|
|
|
getVerticalScrollbarWidth () {
|
|
return this.getComponent().getVerticalScrollbarWidth()
|
|
}
|
|
|
|
getHorizontalScrollbarHeight () {
|
|
return this.getComponent().getHorizontalScrollbarHeight()
|
|
}
|
|
|
|
getScrollTop () {
|
|
return this.getComponent().getScrollTop()
|
|
}
|
|
|
|
setScrollTop (scrollTop) {
|
|
const component = this.getComponent()
|
|
component.setScrollTop(scrollTop)
|
|
component.scheduleUpdate()
|
|
}
|
|
|
|
getScrollBottom () {
|
|
return this.getComponent().getScrollBottom()
|
|
}
|
|
|
|
setScrollBottom (scrollBottom) {
|
|
return this.getComponent().setScrollBottom(scrollBottom)
|
|
}
|
|
|
|
getScrollLeft () {
|
|
return this.getComponent().getScrollLeft()
|
|
}
|
|
|
|
setScrollLeft (scrollLeft) {
|
|
const component = this.getComponent()
|
|
component.setScrollLeft(scrollLeft)
|
|
component.scheduleUpdate()
|
|
}
|
|
|
|
getScrollRight () {
|
|
return this.getComponent().getScrollRight()
|
|
}
|
|
|
|
setScrollRight (scrollRight) {
|
|
return this.getComponent().setScrollRight(scrollRight)
|
|
}
|
|
|
|
// Essential: Scrolls the editor to the top.
|
|
scrollToTop () {
|
|
this.setScrollTop(0)
|
|
}
|
|
|
|
// Essential: Scrolls the editor to the bottom.
|
|
scrollToBottom () {
|
|
this.setScrollTop(Infinity)
|
|
}
|
|
|
|
hasFocus () {
|
|
return this.getComponent().focused
|
|
}
|
|
|
|
// Extended: Converts a buffer position to a pixel position.
|
|
//
|
|
// * `bufferPosition` A {Point}-like object that represents a buffer position.
|
|
//
|
|
// Be aware that calling this method with a column that does not translate
|
|
// to column 0 on screen could cause a synchronous DOM update in order to
|
|
// measure the requested horizontal pixel position if it isn't already
|
|
// cached.
|
|
//
|
|
// Returns an {Object} with two values: `top` and `left`, representing the
|
|
// pixel position.
|
|
pixelPositionForBufferPosition (bufferPosition) {
|
|
const screenPosition = this.getModel().screenPositionForBufferPosition(bufferPosition)
|
|
return this.getComponent().pixelPositionForScreenPosition(screenPosition)
|
|
}
|
|
|
|
// Extended: Converts a screen position to a pixel position.
|
|
//
|
|
// * `screenPosition` A {Point}-like object that represents a buffer position.
|
|
//
|
|
// Be aware that calling this method with a non-zero column value could
|
|
// cause a synchronous DOM update in order to measure the requested
|
|
// horizontal pixel position if it isn't already cached.
|
|
//
|
|
// Returns an {Object} with two values: `top` and `left`, representing the
|
|
// pixel position.
|
|
pixelPositionForScreenPosition (screenPosition) {
|
|
screenPosition = this.getModel().clipScreenPosition(screenPosition)
|
|
return this.getComponent().pixelPositionForScreenPosition(screenPosition)
|
|
}
|
|
|
|
screenPositionForPixelPosition (pixelPosition) {
|
|
return this.getComponent().screenPositionForPixelPosition(pixelPosition)
|
|
}
|
|
|
|
pixelRectForScreenRange (range) {
|
|
range = Range.fromObject(range)
|
|
|
|
const start = this.pixelPositionForScreenPosition(range.start)
|
|
const end = this.pixelPositionForScreenPosition(range.end)
|
|
const lineHeight = this.getComponent().getLineHeight()
|
|
|
|
return {
|
|
top: start.top,
|
|
left: start.left,
|
|
height: end.top + lineHeight - start.top,
|
|
width: end.left - start.left
|
|
}
|
|
}
|
|
|
|
pixelRangeForScreenRange (range) {
|
|
range = Range.fromObject(range)
|
|
return {
|
|
start: this.pixelPositionForScreenPosition(range.start),
|
|
end: this.pixelPositionForScreenPosition(range.end)
|
|
}
|
|
}
|
|
|
|
getComponent () {
|
|
if (!this.component) {
|
|
this.component = new TextEditorComponent({
|
|
element: this,
|
|
mini: this.hasAttribute('mini'),
|
|
updatedSynchronously: this.updatedSynchronously
|
|
})
|
|
this.updateModelFromAttributes()
|
|
}
|
|
|
|
return this.component
|
|
}
|
|
|
|
setUpdatedSynchronously (updatedSynchronously) {
|
|
this.updatedSynchronously = updatedSynchronously
|
|
if (this.component) this.component.updatedSynchronously = updatedSynchronously
|
|
return updatedSynchronously
|
|
}
|
|
|
|
isUpdatedSynchronously () {
|
|
return this.component ? this.component.updatedSynchronously : this.updatedSynchronously
|
|
}
|
|
|
|
// Experimental: Invalidate the passed block {Decoration}'s dimensions,
|
|
// forcing them to be recalculated and the surrounding content to be adjusted
|
|
// on the next animation frame.
|
|
//
|
|
// * {blockDecoration} A {Decoration} representing the block decoration you
|
|
// want to update the dimensions of.
|
|
invalidateBlockDecorationDimensions () {
|
|
this.getComponent().invalidateBlockDecorationDimensions(...arguments)
|
|
}
|
|
|
|
setFirstVisibleScreenRow (row) {
|
|
this.getModel().setFirstVisibleScreenRow(row)
|
|
}
|
|
|
|
getFirstVisibleScreenRow () {
|
|
return this.getModel().getFirstVisibleScreenRow()
|
|
}
|
|
|
|
getLastVisibleScreenRow () {
|
|
return this.getModel().getLastVisibleScreenRow()
|
|
}
|
|
|
|
getVisibleRowRange () {
|
|
return this.getModel().getVisibleRowRange()
|
|
}
|
|
|
|
intersectsVisibleRowRange (startRow, endRow) {
|
|
return !(
|
|
endRow <= this.getFirstVisibleScreenRow() ||
|
|
this.getLastVisibleScreenRow() <= startRow
|
|
)
|
|
}
|
|
|
|
selectionIntersectsVisibleRowRange (selection) {
|
|
const {start, end} = selection.getScreenRange()
|
|
return this.intersectsVisibleRowRange(start.row, end.row + 1)
|
|
}
|
|
|
|
setFirstVisibleScreenColumn (column) {
|
|
return this.getModel().setFirstVisibleScreenColumn(column)
|
|
}
|
|
|
|
getFirstVisibleScreenColumn () {
|
|
return this.getModel().getFirstVisibleScreenColumn()
|
|
}
|
|
}
|
|
|
|
module.exports =
|
|
document.registerElement('atom-text-editor', {
|
|
prototype: TextEditorElement.prototype
|
|
})
|