package tk
import (
)
type CodeArea interface {
Widget
CopyState() CodeAreaState
MutateState(f func(*CodeAreaState))
Submit()
}
type CodeAreaSpec struct {
Bindings Bindings
Highlighter func(code string) (ui.Text, []error)
Prompt func() ui.Text
RPrompt func() ui.Text
Abbreviations func(f func(abbr, full string))
SmallWordAbbreviations func(f func(abbr, full string))
QuotePaste func() bool
OnSubmit func()
State CodeAreaState
}
type CodeAreaState struct {
Buffer CodeBuffer
Pending PendingCode
HideRPrompt bool
}
type CodeBuffer struct {
Content string
Dot int
}
type PendingCode struct {
From int
To int
Content string
}
func ( *CodeAreaState) () {
.Buffer, _, _ = patchPending(.Buffer, .Pending)
.Pending = PendingCode{}
}
func ( *CodeBuffer) ( string) {
* = CodeBuffer{
Content: .Content[:.Dot] + + .Content[.Dot:],
Dot: .Dot + len(),
}
}
type codeArea struct {
StateMutex sync.RWMutex
CodeAreaSpec
inserts string
lastCodeBuffer CodeBuffer
pasting bool
pasteBuffer bytes.Buffer
}
func ( CodeAreaSpec) CodeArea {
if .Bindings == nil {
.Bindings = DummyBindings{}
}
if .Highlighter == nil {
.Highlighter = func( string) (ui.Text, []error) { return ui.T(), nil }
}
if .Prompt == nil {
.Prompt = func() ui.Text { return nil }
}
if .RPrompt == nil {
.RPrompt = func() ui.Text { return nil }
}
if .Abbreviations == nil {
.Abbreviations = func(func(, string)) {}
}
if .SmallWordAbbreviations == nil {
.SmallWordAbbreviations = func(func(, string)) {}
}
if .QuotePaste == nil {
.QuotePaste = func() bool { return false }
}
if .OnSubmit == nil {
.OnSubmit = func() {}
}
return &codeArea{CodeAreaSpec: }
}
func ( *codeArea) () {
.OnSubmit()
}
func ( *codeArea) (, int) *term.Buffer {
:= getView()
:= term.NewBufferBuilder()
renderView(, )
:= .Buffer()
truncateToHeight(, )
return
}
func ( *codeArea) ( term.Event) bool {
switch event := .(type) {
case term.PasteSetting:
return .handlePasteSetting(bool())
case term.KeyEvent:
return .handleKeyEvent(ui.Key())
}
return false
}
func ( *codeArea) ( func(*CodeAreaState)) {
.StateMutex.Lock()
defer .StateMutex.Unlock()
(&.State)
}
func ( *codeArea) () CodeAreaState {
.StateMutex.RLock()
defer .StateMutex.RUnlock()
return .State
}
func ( *codeArea) () {
.inserts = ""
.lastCodeBuffer = CodeBuffer{}
}
func ( *codeArea) ( bool) bool {
.resetInserts()
if {
.pasting = true
} else {
:= .pasteBuffer.String()
if .QuotePaste() {
= parse.Quote()
}
.MutateState(func( *CodeAreaState) { .Buffer.InsertAtDot() })
.pasting = false
.pasteBuffer = bytes.Buffer{}
}
return true
}
func ( *codeArea) () {
var , string
.Abbreviations(func(, string) {
if strings.HasSuffix(.inserts, ) && len() > len() {
, = ,
}
})
if len() > 0 {
:= &.State.Buffer
* = CodeBuffer{
Content: .Content[:.Dot-len()] + + .Content[.Dot:],
Dot: .Dot - len() + len(),
}
.resetInserts()
}
}
func ( *codeArea) ( rune, func(rune) int) {
:= &.State.Buffer
if .Dot < len(.Content) {
return
}
:= len(string())
if >= len(.inserts) {
return
}
:= .inserts[:len(.inserts)-]
var , string
.SmallWordAbbreviations(func(, string) {
if len() <= len() {
return
}
if !strings.HasSuffix(, ) {
return
}
, := utf8.DecodeLastRuneInString()
if () == () {
return
}
if len(.Content) > len()+ {
, := utf8.DecodeLastRuneInString(.Content[:len(.Content)-len()-])
, := utf8.DecodeRuneInString()
if () == () {
return
}
}
, = ,
})
if len() > 0 {
* = CodeBuffer{
Content: .Content[:.Dot-len()-] + + string(),
Dot: .Dot - len() + len(),
}
.resetInserts()
}
}
func ( *codeArea) ( ui.Key) bool {
:= .Mod != 0 || .Rune < 0
if .pasting {
if {
} else {
.pasteBuffer.WriteRune(.Rune)
}
return true
}
if .Bindings.Handle(, term.KeyEvent()) {
return true
}
switch {
case ui.K('\n'):
.resetInserts()
.Submit()
return true
case ui.K(ui.Backspace), ui.K('H', ui.Ctrl):
.resetInserts()
.MutateState(func( *CodeAreaState) {
:= &.Buffer
, := utf8.DecodeLastRuneInString(.Content[:.Dot])
* = CodeBuffer{
Content: .Content[:.Dot-] + .Content[.Dot:],
Dot: .Dot - ,
}
})
return true
default:
if || !unicode.IsGraphic(.Rune) {
.resetInserts()
return false
}
.StateMutex.Lock()
defer .StateMutex.Unlock()
if .lastCodeBuffer != .State.Buffer {
.resetInserts()
}
:= string(.Rune)
.State.Buffer.InsertAtDot()
.inserts +=
.lastCodeBuffer = .State.Buffer
.expandSimpleAbbr()
.expandWordAbbr(.Rune, CategorizeSmallWord)
return true
}
}
func ( rune) bool {
return unicode.IsLetter() || unicode.IsNumber()
}
func ( rune) int {
switch {
case unicode.IsSpace():
return 0
case IsAlnum():
return 1
default:
return 2
}
}