cordless/ui/editor.go

691 lines
22 KiB
Go

package ui
import (
"unicode"
"github.com/Bios-Marcel/cordless/tview"
"github.com/atotto/clipboard"
tcell "github.com/gdamore/tcell/v2"
"github.com/Bios-Marcel/cordless/config"
"github.com/Bios-Marcel/cordless/femto"
"github.com/Bios-Marcel/cordless/shortcuts"
"github.com/Bios-Marcel/cordless/ui/tviewutil"
)
// Editor is a simple component that wraps tview.TextView in order to give
// the user minimal text edit functionality.
type Editor struct {
internalTextView *tview.TextView
buffer *femto.Buffer
tempBuffer *femto.Buffer
inputCapture func(event *tcell.EventKey) *tcell.EventKey
autocompleteValuesUpdateHandler func(values []*AutocompleteValue)
autocompleters []*Autocomplete
heightRequestHandler func(requestHeight int)
requestedHeight int
autocompleteFrom *femto.Loc
// App is the tview Application this editor is used in. The reference is
// required to query the current bracketed paste state.
App *tview.Application
}
func (editor *Editor) applyBuffer() {
editor.applyBufferWithoutAutocompletionCheck()
if config.Current.Autocomplete {
editor.checkForAutocompletion()
}
}
func (editor *Editor) applyBufferWithoutAutocompletionCheck() {
selectionStart := editor.buffer.Cursor.CurSelection[0]
selectionEnd := editor.buffer.Cursor.CurSelection[1]
//Copy relevant buffer-state over to temporary buffer
editor.tempBuffer.Replace(editor.tempBuffer.Start(), editor.tempBuffer.End(), tviewutil.Escape(editor.buffer.String()))
editor.tempBuffer.Cursor.GotoLoc(editor.buffer.Cursor.Loc)
editor.tempBuffer.Cursor.SetSelectionStart(selectionStart)
editor.tempBuffer.Cursor.SetSelectionEnd(selectionEnd)
//The \x04 is a non-printable character, this is used in order to prevent
//weird behaviour of tview in combinations that have selection and newlines
//or whitespace.
if editor.buffer.Cursor.HasSelection() {
editor.tempBuffer.Insert(selectionEnd, "[\"\"]\x04")
editor.tempBuffer.Insert(selectionStart, "[\"selection\"]\x04")
} else {
if editor.tempBuffer.Cursor.RuneUnder(editor.tempBuffer.Cursor.Loc.X) == '\n' {
editor.tempBuffer.Insert(editor.tempBuffer.Cursor.Loc, "[\"selection\"]\x04 [\"\"]\x04")
} else {
editor.tempBuffer.Insert(editor.tempBuffer.Cursor.Loc.Move(1, editor.tempBuffer), "[\"\"]\x04")
editor.tempBuffer.Insert(editor.tempBuffer.Cursor.Loc, "[\"selection\"]\x04")
}
}
editor.internalTextView.SetText(editor.tempBuffer.String())
}
func (editor *Editor) checkForAutocompletion() {
if editor.autocompleteValuesUpdateHandler != nil {
cursorLoc := editor.buffer.Cursor.Loc
var spaceFound bool
for {
cursorLoc.X = cursorLoc.X - 1
if cursorLoc.X < 0 {
break
}
runeAtCursor := editor.buffer.RuneAt(cursorLoc)
if runeAtCursor == ' ' {
spaceFound = true
}
for _, value := range editor.autocompleters {
if value.firstRune == runeAtCursor {
if spaceFound && !value.allowSpaces {
break
}
cursorLocCopy := cursorLoc
cursorLocCopy.X--
if cursorLocCopy.X >= 0 && !unicode.IsSpace(editor.buffer.RuneAt(cursorLocCopy)) {
break
}
editor.autocompleteFrom = &cursorLoc
//We don't want the autocomplete character to be part of the search value
cursorLocCopy = cursorLoc
cursorLocCopy.X++
editor.autocompleteValuesUpdateHandler(value.valueSupplier(
editor.buffer.Substr(cursorLocCopy, editor.buffer.Cursor.Loc)))
return
}
}
}
editor.autocompleteFrom = nil
editor.autocompleteValuesUpdateHandler(nil)
}
}
// MoveCursorLeft moves the cursor left by one cell.
func (editor *Editor) MoveCursorLeft() {
if editor.buffer.Cursor.HasSelection() {
editor.buffer.Cursor.GotoLoc(editor.buffer.Cursor.CurSelection[0])
editor.buffer.Cursor.ResetSelection()
} else {
editor.buffer.Cursor.Left()
}
editor.applyBuffer()
}
// MoveCursorRight moves the cursor right by one cell.
func (editor *Editor) MoveCursorRight() {
if editor.buffer.Cursor.HasSelection() {
editor.buffer.Cursor.GotoLoc(editor.buffer.Cursor.CurSelection[1])
editor.buffer.Cursor.ResetSelection()
} else {
editor.buffer.Cursor.Right()
}
editor.applyBuffer()
}
// SelectionToLeft extends the selection one cell to the left.
func (editor *Editor) SelectionToLeft() {
editor.selectLeft(false)
}
// SelectWordLeft extends the selection one word to the left. A word may
// span multiple words. A word however can be one cell and mustn't be a word
// in terms of human language definition.
func (editor *Editor) SelectWordLeft() {
editor.selectLeft(true)
}
func (editor *Editor) selectLeft(word bool) {
oldCursor := editor.buffer.Cursor.Loc
selectionStart := editor.buffer.Cursor.CurSelection[0]
selectionEnd := editor.buffer.Cursor.CurSelection[1]
if word {
editor.buffer.Cursor.WordLeft()
} else {
editor.buffer.Cursor.Left()
}
newCursor := editor.buffer.Cursor.Loc
if !editor.buffer.Cursor.HasSelection() {
editor.buffer.Cursor.SetSelectionStart(newCursor)
editor.buffer.Cursor.SetSelectionEnd(oldCursor)
} else if oldCursor.GreaterEqual(selectionStart) {
if newCursor.GreaterEqual(selectionStart) {
editor.buffer.Cursor.SetSelectionStart(selectionStart)
editor.buffer.Cursor.SetSelectionEnd(newCursor)
} else {
editor.buffer.Cursor.SetSelectionStart(newCursor)
editor.buffer.Cursor.SetSelectionEnd(selectionEnd)
}
} else {
editor.buffer.Cursor.SetSelectionStart(newCursor)
editor.buffer.Cursor.SetSelectionEnd(selectionEnd)
}
editor.applyBuffer()
}
// SelectionToRight extends the selection one cell to the right.
func (editor *Editor) SelectionToRight() {
editor.selectRight(false)
}
// SelectWordRight extends the selection one word to the right. A word may
// span multiple words. A word however can be one cell and mustn't be a word
// in terms of human language definition.
func (editor *Editor) SelectWordRight() {
editor.selectRight(true)
}
func (editor *Editor) selectRight(word bool) {
oldCursor := editor.buffer.Cursor.Loc
selectionStart := editor.buffer.Cursor.CurSelection[0]
selectionEnd := editor.buffer.Cursor.CurSelection[1]
if word {
editor.buffer.Cursor.WordRight()
} else {
editor.buffer.Cursor.Right()
}
newCursor := editor.buffer.Cursor.Loc
if !editor.buffer.Cursor.HasSelection() {
editor.buffer.Cursor.SetSelectionStart(oldCursor)
editor.buffer.Cursor.SetSelectionEnd(newCursor)
} else if newCursor.LessThan(selectionEnd) {
editor.buffer.Cursor.SetSelectionStart(newCursor)
editor.buffer.Cursor.SetSelectionEnd(selectionEnd)
} else {
editor.buffer.Cursor.SetSelectionStart(selectionStart)
editor.buffer.Cursor.SetSelectionEnd(newCursor)
}
editor.applyBuffer()
}
// SelectAll selects all text (cells) currently filled. If no text is
// available, nothing will change.
func (editor *Editor) SelectAll() {
start := editor.buffer.Start()
editor.buffer.Cursor.SetSelectionStart(start)
end := editor.buffer.End()
editor.buffer.Cursor.SetSelectionEnd(end)
editor.buffer.Cursor.GotoLoc(end)
editor.applyBuffer()
}
// SelectToStartOfLine will select all text to the left til the next newline
// is found. Lines doesn't mean "editor line" in this context, as the editor
// doesn't currently support vertical navigation.
func (editor *Editor) SelectToStartOfLine() {
oldCursor := editor.buffer.Cursor.Loc
editor.buffer.Cursor.StartOfText()
newCursor := editor.buffer.Cursor.Loc
if !oldCursor.GreaterThan(newCursor) {
editor.buffer.Cursor.Start()
newCursor = editor.buffer.Cursor.Loc
}
editor.buffer.Cursor.SetSelectionStart(newCursor)
editor.buffer.Cursor.SetSelectionEnd(oldCursor)
editor.applyBuffer()
}
// SelectToEndOfLine will select all text to the right til the next newline
// is found. Lines doesn't mean "editor line" in this context, as the editor
// doesn't currently support vertical navigation.
func (editor *Editor) SelectToEndOfLine() {
oldCursor := editor.buffer.Cursor.Loc
editor.buffer.Cursor.End()
editor.buffer.Cursor.SetSelectionStart(oldCursor)
editor.buffer.Cursor.SetSelectionEnd(editor.buffer.Cursor.Loc)
editor.applyBuffer()
}
// SelectToStartOfText will select all text to the start of the editor.
// Meaning the top-left most cell.
func (editor *Editor) SelectToStartOfText() {
oldCursor := editor.buffer.Cursor.Loc
textStart := editor.buffer.Start()
editor.buffer.Cursor.GotoLoc(textStart)
editor.buffer.Cursor.SetSelectionStart(textStart)
editor.buffer.Cursor.SetSelectionEnd(oldCursor)
editor.applyBuffer()
}
// SelectToEndOfText will select all text to the end of the editor.
// Meaning the bottom-right most cell.
func (editor *Editor) SelectToEndOfText() {
oldCursor := editor.buffer.Cursor.Loc
textEnd := editor.buffer.End()
editor.buffer.Cursor.GotoLoc(textEnd)
editor.buffer.Cursor.SetSelectionStart(oldCursor)
editor.buffer.Cursor.SetSelectionEnd(textEnd)
editor.applyBuffer()
}
func (editor *Editor) MoveCursorWordLeft() {
if editor.buffer.Cursor.HasSelection() {
editor.buffer.Cursor.GotoLoc(editor.buffer.Cursor.CurSelection[0])
}
editor.buffer.Cursor.WordLeft()
editor.buffer.Cursor.ResetSelection()
editor.applyBuffer()
}
func (editor *Editor) MoveCursorWordRight() {
if editor.buffer.Cursor.HasSelection() {
editor.buffer.Cursor.GotoLoc(editor.buffer.Cursor.CurSelection[1])
}
editor.buffer.Cursor.WordRight()
editor.buffer.Cursor.ResetSelection()
editor.applyBuffer()
}
func (editor *Editor) MoveCursorStartOfLine() {
oldCursor := editor.buffer.Cursor.Loc
editor.buffer.Cursor.StartOfText()
if !oldCursor.GreaterThan(editor.buffer.Cursor.Loc) {
editor.buffer.Cursor.Start()
}
editor.buffer.Cursor.ResetSelection()
editor.applyBuffer()
}
func (editor *Editor) MoveCursorEndOfLine() {
editor.buffer.Cursor.End()
editor.buffer.Cursor.ResetSelection()
editor.applyBuffer()
}
func (editor *Editor) MoveCursorStartOfText() {
editor.buffer.Cursor.GotoLoc(editor.buffer.Start())
editor.buffer.Cursor.ResetSelection()
editor.applyBuffer()
}
func (editor *Editor) MoveCursorEndOfText() {
editor.buffer.Cursor.GotoLoc(editor.buffer.End())
editor.buffer.Cursor.ResetSelection()
editor.applyBuffer()
}
func (editor *Editor) Backspace() {
if editor.buffer.Cursor.HasSelection() {
editor.buffer.Remove(editor.buffer.Cursor.CurSelection[0], editor.buffer.Cursor.CurSelection[1])
editor.applyBuffer()
} else if editor.buffer.Cursor.Loc.X > 0 || editor.buffer.Cursor.Loc.Y > 0 {
editor.buffer.Remove(editor.buffer.Cursor.Loc.Move(-1, editor.buffer), editor.buffer.Cursor.Loc)
editor.applyBuffer()
}
}
func (editor *Editor) DeleteWordLeft() {
if editor.buffer.Cursor.HasSelection() {
editor.Backspace()
} else {
oldLocation := editor.buffer.Cursor.Loc
editor.buffer.Cursor.WordLeft()
newLocation := editor.buffer.Cursor.Loc
if oldLocation.X != newLocation.X || oldLocation.Y != newLocation.Y {
editor.buffer.Cursor.SetSelectionStart(newLocation)
editor.buffer.Cursor.SetSelectionEnd(oldLocation)
editor.Backspace()
}
}
}
func (editor *Editor) DeleteRight() {
if editor.buffer.Cursor.HasSelection() {
editor.buffer.Remove(editor.buffer.Cursor.CurSelection[0], editor.buffer.Cursor.CurSelection[1])
} else {
editor.buffer.Remove(editor.buffer.Cursor.Loc, editor.buffer.Cursor.Loc.Move(1, editor.buffer))
}
editor.applyBuffer()
}
func (editor *Editor) Paste(event *tcell.EventKey) {
if editor.inputCapture != nil {
result := editor.inputCapture(event)
if result == nil {
//Early exit, as even has been handled.
return
}
}
clipBoardContent, clipError := clipboard.ReadAll()
if clipError == nil {
if editor.buffer.Cursor.HasSelection() {
editor.buffer.Replace(editor.buffer.Cursor.CurSelection[0], editor.buffer.Cursor.CurSelection[1], clipBoardContent)
} else {
editor.buffer.Insert(editor.buffer.Cursor.Loc, clipBoardContent)
}
editor.applyBuffer()
}
}
func (editor *Editor) insertCharacterWithoutApply(character rune) {
selectionEnd := editor.buffer.Cursor.CurSelection[1]
selectionStart := editor.buffer.Cursor.CurSelection[0]
if editor.buffer.Cursor.HasSelection() {
editor.buffer.Replace(selectionStart, selectionEnd, string(character))
} else {
editor.buffer.Insert(editor.buffer.Cursor.Loc, string(character))
}
editor.buffer.Cursor.ResetSelection()
}
func (editor *Editor) InsertCharacter(character rune) {
selectionEnd := editor.buffer.Cursor.CurSelection[1]
selectionStart := editor.buffer.Cursor.CurSelection[0]
if editor.buffer.Cursor.HasSelection() {
editor.buffer.Replace(selectionStart, selectionEnd, string(character))
} else {
editor.buffer.Insert(editor.buffer.Cursor.Loc, string(character))
}
editor.buffer.Cursor.ResetSelection()
editor.applyBuffer()
}
// NewEditor instantiates a ready to use text editor.
func NewEditor(app *tview.Application) *Editor {
editor := Editor{
internalTextView: tview.NewTextView(),
requestedHeight: 3,
buffer: femto.NewBufferFromString("", ""),
tempBuffer: femto.NewBufferFromString("", ""),
App: app,
}
editor.internalTextView.SetWrap(true)
editor.internalTextView.SetWordWrap(true)
editor.internalTextView.SetBorder(true)
editor.internalTextView.SetRegions(true)
editor.internalTextView.SetScrollable(true)
editor.internalTextView.Highlight("selection")
editor.applyBuffer()
editor.buffer.Cursor.SetSelectionStart(editor.buffer.Start())
editor.buffer.Cursor.SetSelectionEnd(editor.buffer.End())
if editor.App != nil {
editor.internalTextView.SetOnPaste(func(pastedRunes []rune) {
var wasLastCharacterSlashR bool
for _, r := range pastedRunes {
//Workaround! Sometimes no \n is received, but only \r. Why? Unsure.
if r == '\r' {
wasLastCharacterSlashR = true
editor.insertCharacterWithoutApply('\n')
} else {
if wasLastCharacterSlashR {
wasLastCharacterSlashR = false
}
if r != '\n' {
editor.insertCharacterWithoutApply(r)
}
}
}
editor.applyBuffer()
editor.TriggerHeightRequestIfNecessary()
editor.internalTextView.ScrollToHighlight()
})
}
editor.internalTextView.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
inputCapture := editor.inputCapture
if inputCapture != nil {
event = inputCapture(event)
if event == nil {
return nil
}
}
if shortcuts.MoveCursorLeft.Equals(event) {
editor.MoveCursorLeft()
} else if shortcuts.ExpandSelectionToLeft.Equals(event) {
editor.SelectionToLeft()
} else if shortcuts.MoveCursorRight.Equals(event) {
editor.MoveCursorRight()
} else if shortcuts.ExpandSelectionToRight.Equals(event) {
editor.SelectionToRight()
} else if shortcuts.SelectWordLeft.Equals(event) {
editor.SelectWordLeft()
} else if shortcuts.SelectWordRight.Equals(event) {
editor.SelectWordRight()
} else if shortcuts.SelectToStartOfLine.Equals(event) {
editor.SelectToStartOfLine()
} else if shortcuts.SelectToEndOfLine.Equals(event) {
editor.SelectToEndOfLine()
} else if shortcuts.SelectToStartOfText.Equals(event) {
editor.SelectToStartOfText()
} else if shortcuts.SelectToEndOfText.Equals(event) {
editor.SelectToEndOfText()
} else if shortcuts.SelectAll.Equals(event) {
editor.SelectAll()
} else if shortcuts.MoveCursorWordLeft.Equals(event) {
editor.MoveCursorWordLeft()
} else if shortcuts.MoveCursorWordRight.Equals(event) {
editor.MoveCursorWordRight()
} else if shortcuts.MoveCursorStartOfLine.Equals(event) {
editor.MoveCursorStartOfLine()
} else if shortcuts.MoveCursorEndOfLine.Equals(event) {
editor.MoveCursorEndOfLine()
} else if shortcuts.MoveCursorStartOfText.Equals(event) {
editor.MoveCursorStartOfText()
} else if shortcuts.MoveCursorEndOfText.Equals(event) {
editor.MoveCursorEndOfText()
} else if shortcuts.DeleteRight.Equals(event) {
editor.DeleteRight()
} else if shortcuts.DeleteLeft.Equals(event) {
editor.Backspace()
} else if shortcuts.DeleteWordLeft.Equals(event) {
editor.DeleteWordLeft()
} else if shortcuts.CopySelection.Equals(event) {
clipboard.WriteAll(editor.buffer.Cursor.GetSelection())
//Returning nil, as copying won't do anything than filling the
//clipboard buffer.
return nil
} else if shortcuts.PasteAtSelection.Equals(event) {
editor.Paste(event)
editor.TriggerHeightRequestIfNecessary()
return nil
} else if shortcuts.InputNewLine.Equals(event) {
editor.InsertCharacter('\n')
} else {
mappedRune := mapInputToRune(event)
if mappedRune != 0 {
editor.InsertCharacter(mappedRune)
}
}
editor.TriggerHeightRequestIfNecessary()
editor.internalTextView.ScrollToHighlight()
return nil
})
return &editor
}
// mapInputToRune makes sure no invalid characters get inserted into the
// editor. All exceptions that aren't specifically declared as
// Key type 'tcell.KeyRune' have to be defined here. A prominent example
// is the newline character. This was initially added to avoid inserting
// the characters produces by Ctrl+Backspace, Alt+Backspace, Ctrl+W and so
// on and so forth. These are all control characters which we don't always
// react to, since the shortcuts are configurably from within the app.
// A return value of 0 is to be treated as 'no rune'.
func mapInputToRune(event *tcell.EventKey) rune {
//If something is specifically defined as a rune, we won't question whether
//it's valid input, as it's generally not deemed a control character.
if event.Key() == tcell.KeyRune {
return event.Rune()
}
//While '\n' is treated as newline, we'll ignore '\r', as it's useless.
if event.Rune() == '\n' {
return '\n'
}
//Handles Tab + Ctrl+H
if event.Rune() == '\t' {
return '\t'
}
return 0
}
func (editor *Editor) GetTextLeftOfSelection() string {
var to femto.Loc
if editor.buffer.Cursor.HasSelection() {
to = editor.buffer.Cursor.CurSelection[1]
} else {
to = editor.buffer.End()
}
return editor.buffer.Substr(editor.buffer.Start(), to)
}
func (editor *Editor) FindAtSymbolIndexInCurrentWord() int {
newLeft := editor.GetTextLeftOfSelection()
for i := len(newLeft) - 1; i >= 0; i-- {
if newLeft[i] == '@' && (i == 0 || newLeft[i-1] == ' ' || newLeft[i-1] == '\n') {
return i
}
}
return -1
}
func (editor *Editor) countRows(text string) int {
_, _, width, _ := editor.internalTextView.GetInnerRect()
return tviewutil.CalculateNecessaryHeight(width, text)
}
// TriggerHeightRequestIfNecessary informs the parent that more or less height
// is requierd for rendering than currently in use, unless the height is already
// optimal.
func (editor *Editor) TriggerHeightRequestIfNecessary() {
if editor.heightRequestHandler == nil {
return
}
newRequestedHeight := editor.countRows(editor.GetText())
if editor.internalTextView.IsBorderTop() {
newRequestedHeight++
}
if editor.internalTextView.IsBorderBottom() {
newRequestedHeight++
}
if newRequestedHeight != editor.requestedHeight {
editor.requestedHeight = newRequestedHeight
editor.heightRequestHandler(newRequestedHeight)
}
}
type AutocompleteValue struct {
RenderValue string
InsertValue string
}
type Autocomplete struct {
firstRune rune
allowSpaces bool
valueSupplier func(string) []*AutocompleteValue
}
func (editor *Editor) RegisterAutocomplete(firstRune rune, allowSpaces bool, valueSupplier func(string) []*AutocompleteValue) {
editor.autocompleters = append(editor.autocompleters, &Autocomplete{
firstRune: firstRune,
allowSpaces: allowSpaces,
valueSupplier: valueSupplier,
})
}
func (editor *Editor) Autocomplete(value string) {
if editor.autocompleteFrom != nil {
editor.buffer.Replace(*editor.autocompleteFrom, editor.buffer.Cursor.Loc, value+" ")
editor.autocompleteFrom = nil
//Not necessary, since you probably don't want to autocomplete any
//further after you've chosen a value.
editor.applyBufferWithoutAutocompletionCheck()
}
}
// GetRequestedHeight returns the currently requested size.
func (editor *Editor) GetRequestedHeight() int {
return editor.requestedHeight
}
// SetOnHeightChangeRequest handles the cases where the component thinks it needs
// more space or would be fine with less.
func (editor *Editor) SetOnHeightChangeRequest(handler func(requestHeight int)) {
editor.heightRequestHandler = handler
}
// SetBackgroundColor sets the background color of the internal TextView
func (editor *Editor) SetBackgroundColor(color tcell.Color) {
editor.internalTextView.SetBackgroundColor(color)
}
// SetText sets the texts of the internal TextView, but also sets the selection
// and necessary groups for the navigation behaviour.
func (editor *Editor) SetText(text string) {
if text == "" {
editor.buffer.Remove(editor.buffer.Start(), editor.buffer.End())
} else {
editor.buffer.Replace(editor.buffer.Start(), editor.buffer.End(), text)
}
editor.buffer.Cursor.ResetSelection()
editor.buffer.Cursor.GotoLoc(editor.buffer.End())
editor.applyBuffer()
editor.TriggerHeightRequestIfNecessary()
}
// SetBorderFocusColor delegates to the underlying components
// SetBorderFocusColor method.
func (editor *Editor) SetBorderFocusColor(color tcell.Color) {
editor.internalTextView.SetBorderFocusColor(color)
}
// SetBorderColor delegates to the underlying components SetBorderColor
// method.
func (editor *Editor) SetBorderColor(color tcell.Color) {
editor.internalTextView.SetBorderColor(color)
}
// SetBorderBlinking sets the blinking attribute of the border in tview.
func (editor *Editor) SetBorderBlinking(blinking bool) {
editor.internalTextView.SetBorderBlinking(blinking)
}
// SetInputCapture sets the alternative input capture that will be used if the
// components default controls aren't being triggered.
func (editor *Editor) SetInputCapture(captureFunc func(event *tcell.EventKey) *tcell.EventKey) {
editor.inputCapture = captureFunc
}
func (editor *Editor) SetAutocompleteValuesUpdateHandler(handlerFunc func(values []*AutocompleteValue)) {
editor.autocompleteValuesUpdateHandler = handlerFunc
}
// GetText returns the text without color tags, region tags and so on.
func (editor *Editor) GetText() string {
return editor.buffer.String()
}
// GetPrimitive returns the internal component that can be added to a layout
func (editor *Editor) GetPrimitive() tview.Primitive {
return editor.internalTextView
}