mirror of https://github.com/liamg/aminal.git
added Tab Stops support (#175)
This commit is contained in:
parent
4e7b8b40e7
commit
d2214b7915
|
@ -717,22 +717,11 @@ func (buffer *Buffer) CarriageReturn() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (buffer *Buffer) Tab() {
|
func (buffer *Buffer) Tab() {
|
||||||
tabSize := 4
|
for buffer.terminalState.cursorX < buffer.terminalState.viewWidth-1 { // @todo rightMargin
|
||||||
max := tabSize
|
|
||||||
|
|
||||||
// @todo rightMargin
|
|
||||||
if buffer.terminalState.cursorX < buffer.terminalState.viewWidth {
|
|
||||||
max = int(buffer.terminalState.viewWidth - buffer.terminalState.cursorX - 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
shift := tabSize - (int(buffer.terminalState.cursorX+1) % tabSize)
|
|
||||||
|
|
||||||
if shift > max {
|
|
||||||
shift = max
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 0; i < shift; i++ {
|
|
||||||
buffer.Write(' ')
|
buffer.Write(' ')
|
||||||
|
if buffer.terminalState.IsTabSetAtCursor() {
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,8 +26,8 @@ func TestTabbing(t *testing.T) {
|
||||||
b.CarriageReturn()
|
b.CarriageReturn()
|
||||||
b.NewLine()
|
b.NewLine()
|
||||||
expected := `
|
expected := `
|
||||||
hello x goodbye
|
hello x goodbye
|
||||||
hell xxx good
|
hell xxx good
|
||||||
`
|
`
|
||||||
|
|
||||||
lines := b.GetVisibleLines()
|
lines := b.GetVisibleLines()
|
||||||
|
|
|
@ -15,6 +15,7 @@ type TerminalState struct {
|
||||||
LineFeedMode bool
|
LineFeedMode bool
|
||||||
AutoWrap bool
|
AutoWrap bool
|
||||||
maxLines uint64
|
maxLines uint64
|
||||||
|
tabStops map[uint16]struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewTerminalMode creates a new terminal state
|
// NewTerminalMode creates a new terminal state
|
||||||
|
@ -31,6 +32,7 @@ func NewTerminalState(viewCols uint16, viewLines uint16, attr CellAttributes, ma
|
||||||
topMargin: 0,
|
topMargin: 0,
|
||||||
bottomMargin: uint(viewLines - 1),
|
bottomMargin: uint(viewLines - 1),
|
||||||
}
|
}
|
||||||
|
b.TabReset()
|
||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,3 +49,48 @@ func (terminalState *TerminalState) ResetVerticalMargins() {
|
||||||
func (terminalState *TerminalState) IsNewLineMode() bool {
|
func (terminalState *TerminalState) IsNewLineMode() bool {
|
||||||
return terminalState.LineFeedMode == false
|
return terminalState.LineFeedMode == false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (terminalState *TerminalState) TabZonk() {
|
||||||
|
terminalState.tabStops = make(map[uint16]struct{})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (terminalState *TerminalState) TabSet(index uint16) {
|
||||||
|
terminalState.tabStops[index] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (terminalState *TerminalState) TabClear(index uint16) {
|
||||||
|
delete(terminalState.tabStops, index)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (terminalState *TerminalState) getTabIndexFromCursor() uint16 {
|
||||||
|
index := terminalState.cursorX
|
||||||
|
if index == terminalState.viewWidth {
|
||||||
|
index = 0
|
||||||
|
}
|
||||||
|
return index
|
||||||
|
}
|
||||||
|
|
||||||
|
func (terminalState *TerminalState) IsTabSetAtCursor() bool {
|
||||||
|
index := terminalState.getTabIndexFromCursor()
|
||||||
|
_, ok := terminalState.tabStops[index]
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (terminalState *TerminalState) TabClearAtCursor() {
|
||||||
|
terminalState.TabClear(terminalState.getTabIndexFromCursor())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (terminalState *TerminalState) TabSetAtCursor() {
|
||||||
|
terminalState.TabSet(terminalState.getTabIndexFromCursor())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (terminalState *TerminalState) TabReset() {
|
||||||
|
terminalState.TabZonk()
|
||||||
|
const MaxTabs uint16 = 1024
|
||||||
|
const TabStep = 4
|
||||||
|
var i uint16
|
||||||
|
for i < MaxTabs {
|
||||||
|
terminalState.TabSet(i)
|
||||||
|
i += TabStep
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@ var ansiSequenceMap = map[rune]escapeSequenceHandler{
|
||||||
'8': restoreCursorHandler,
|
'8': restoreCursorHandler,
|
||||||
'D': indexHandler,
|
'D': indexHandler,
|
||||||
'E': nextLineHandler, // NEL
|
'E': nextLineHandler, // NEL
|
||||||
|
'H': tabSetHandler, // HTS
|
||||||
'M': reverseIndexHandler,
|
'M': reverseIndexHandler,
|
||||||
'P': sixelHandler,
|
'P': sixelHandler,
|
||||||
'c': risHandler, //RIS
|
'c': risHandler, //RIS
|
||||||
|
@ -75,3 +76,8 @@ func nextLineHandler(pty chan rune, terminal *Terminal) error {
|
||||||
terminal.ActiveBuffer().NewLineEx(true)
|
terminal.ActiveBuffer().NewLineEx(true)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func tabSetHandler(pty chan rune, terminal *Terminal) error {
|
||||||
|
terminal.terminalState.TabSetAtCursor()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ var csiSequences = []csiMapping{
|
||||||
{id: 'c', handler: csiSendDeviceAttributesHandler, description: " Send Device Attributes (Primary/Secondary/Tertiary DA)"},
|
{id: 'c', handler: csiSendDeviceAttributesHandler, description: " Send Device Attributes (Primary/Secondary/Tertiary DA)"},
|
||||||
{id: 'd', handler: csiLinePositionAbsolute, expectedParams: &expectedParams{min: 0, max: 1}, description: "Line Position Absolute [row] (default = [1,column]) (VPA)"},
|
{id: 'd', handler: csiLinePositionAbsolute, expectedParams: &expectedParams{min: 0, max: 1}, description: "Line Position Absolute [row] (default = [1,column]) (VPA)"},
|
||||||
{id: 'f', handler: csiCursorPositionHandler, description: "Horizontal and Vertical Position [row;column] (default = [1,1]) (HVP)"},
|
{id: 'f', handler: csiCursorPositionHandler, description: "Horizontal and Vertical Position [row;column] (default = [1,1]) (HVP)"},
|
||||||
|
{id: 'g', handler: csiTabClearHandler, description: "Tab Clear (TBC)"},
|
||||||
{id: 'h', handler: csiSetModeHandler, expectedParams: &expectedParams{min: 1, max: 1}, description: "Set Mode (SM)"},
|
{id: 'h', handler: csiSetModeHandler, expectedParams: &expectedParams{min: 1, max: 1}, description: "Set Mode (SM)"},
|
||||||
{id: 'l', handler: csiResetModeHandler, expectedParams: &expectedParams{min: 1, max: 1}, description: "Reset Mode (RM)"},
|
{id: 'l', handler: csiResetModeHandler, expectedParams: &expectedParams{min: 1, max: 1}, description: "Reset Mode (RM)"},
|
||||||
{id: 'm', handler: sgrSequenceHandler, description: "Character Attributes (SGR)"},
|
{id: 'm', handler: sgrSequenceHandler, description: "Character Attributes (SGR)"},
|
||||||
|
@ -463,6 +464,24 @@ func csiDeleteHandler(params []string, terminal *Terminal) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func csiTabClearHandler(params []string, terminal *Terminal) error {
|
||||||
|
n := "0"
|
||||||
|
if len(params) > 0 {
|
||||||
|
n = params[0]
|
||||||
|
}
|
||||||
|
switch n {
|
||||||
|
|
||||||
|
case "0", "":
|
||||||
|
terminal.terminalState.TabClearAtCursor()
|
||||||
|
case "3":
|
||||||
|
terminal.terminalState.TabZonk()
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("Ignored TBC: CSI %s g", n)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// CSI Ps J
|
// CSI Ps J
|
||||||
func csiEraseInDisplayHandler(params []string, terminal *Terminal) error {
|
func csiEraseInDisplayHandler(params []string, terminal *Terminal) error {
|
||||||
n := "0"
|
n := "0"
|
||||||
|
|
Loading…
Reference in New Issue