turbo-editors/turbo-corepublic Fork 0
v1.0.1
Commits
Clone
git clone https://git.rickub.com/turbo-editors/turbo-core.git
git clone ssh://git@rickub.com/turbo-editors/turbo-core.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

🛟 Updated. 28d5985 · on v1.0.1 · k33g · 14h ago
parser_escape.go · 100 lines · 2.7 KBGo Blame HistoryRaw
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
package terminal

// stepAfterEscape decides what kind of sequence an ESC begins.
func (p *Parser) stepAfterEscape(b byte) {
	switch b {
	case '[':
		p.state = inCSI
	case ']':
		p.state = inOSC
		p.osc = p.osc[:0]
	case 'P', 'X', '^', '_':
		// A device control, privacy message or application command. None is
		// implemented, and all of them run to a string terminator.
		p.state = inOSC
		p.osc = p.osc[:0]
	case '(', ')', '*', '+', '%', '#', ' ':
		// A character-set or size selector: one more byte follows, and this
		// emulator works in UTF-8 regardless.
		p.state = ignoring
	default:
		p.simpleEscape(b)
		p.state = ground
	}
}

// simpleEscape runs the escape sequences that are one byte long.
func (p *Parser) simpleEscape(b byte) {
	switch b {
	case 'D': // IND, index: down one line, scrolling if need be.
		p.screen.LineFeed()
	case 'M': // RI, reverse index: up one line, scrolling if need be.
		p.screen.ReverseLineFeed()
	case 'E': // NEL, next line.
		p.screen.CarriageReturn()
		p.screen.LineFeed()
	case '7': // DECSC, save the cursor and the style.
		p.screen.SaveCursor()
	case '8': // DECRC, restore them.
		p.screen.RestoreCursor()
	case 'c': // RIS, reset to the initial state.
		p.screen.Reset()
	}
	// Anything else — keypad modes, character sets — changes nothing this
	// emulator models, and is dropped rather than printed.
}

// stepIgnoring skips the one byte that completes a sequence being ignored.
func (p *Parser) stepIgnoring(byte) {
	p.state = ground
}

// stepOSC collects an operating-system command until its terminator.
func (p *Parser) stepOSC(b byte) {
	switch {
	case b == bell:
		p.endOSC()
	case b == escape:
		p.state = inOSCEscape
	case len(p.osc) >= maxOSC:
		// A program that has sent this much without a terminator is not going
		// to send one.
		p.state = ground
	default:
		p.osc = append(p.osc, b)
	}
}

// stepOSCEscape decides whether the ESC inside an OSC ended it.
func (p *Parser) stepOSCEscape(b byte) {
	if b == '\\' { // ST, the string terminator.
		p.endOSC()
		return
	}

	// It was not a terminator, so the OSC is abandoned and this byte starts
	// whatever the ESC really began.
	p.state = ground
	p.step(escape)
	p.step(b)
}

// endOSC acts on a completed operating-system command.
//
// Only the window title is kept. The rest — colour palettes, clipboard
// requests, working-directory hints — is dropped, which is what a terminal
// that does not implement them is expected to do.
func (p *Parser) endOSC() {
	p.state = ground

	command := string(p.osc)
	p.osc = p.osc[:0]

	// "0;title" sets both the icon name and the title; "2;title" the title.
	for _, prefix := range []string{"0;", "2;"} {
		if len(command) >= len(prefix) && command[:len(prefix)] == prefix {
			p.title = command[len(prefix):]
			return
		}
	}
}