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 · 12h ago
screen_resize.go · 95 lines · 3.1 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
package terminal

import "github.com/gdamore/tcell/v2"

// Resize changes the screen's size, keeping as much of what is on it as fits.
//
// Lines that no longer fit at the bottom go into the scrollback rather than
// being dropped, which is what makes shrinking a window and growing it again
// leave the session's history intact. Columns beyond the new width are lost;
// no terminal reflows them, and pretending otherwise would move text the
// program believes it has already placed.
func (s *Screen) Resize(width, height int) {
	width, height = max(width, 1), max(height, 1)
	if width == s.width && height == s.height {
		return
	}

	previous := s.lines
	s.width, s.height = width, height

	dropped := rowsToDrop(len(previous), height, s.cursor.Row)
	for i := range dropped {
		s.remember(fitLine(previous[i], width, s.style))
	}

	s.lines = make([]Line, height)
	for row := range height {
		if source := row + dropped; source < len(previous) {
			s.lines[row] = fitLine(previous[source], width, s.style)
		} else {
			s.lines[row] = s.blankLine()
		}
	}

	s.cursor.Row -= dropped
	s.resetScrollRegion()
	s.fitCursor()
	s.resizePrimary(width, height)
}

// resizePrimary keeps the grid put aside for the alternate screen the same
// size as the visible one, so that leaving the alternate screen does not
// restore a grid of the wrong shape.
func (s *Screen) resizePrimary(width, height int) {
	if !s.alternate {
		return
	}

	lines := make([]Line, height)
	for row := range height {
		if row < len(s.primaryLines) {
			lines[row] = fitLine(s.primaryLines[row], width, s.primaryStyle)
		} else {
			lines[row] = blankLineOf(width, s.primaryStyle)
		}
	}
	s.primaryLines = lines

	s.primaryCursor.Row = min(s.primaryCursor.Row, height-1)
	s.primaryCursor.Col = min(s.primaryCursor.Col, width-1)
}

// fitLine returns a copy of a line at a new width, padded or cut.
// rowsToDrop returns how many rows must come off the top when a screen shrinks.
//
// Only as many as it takes to keep the cursor on screen — never more.
//
// Dropping every row that no longer fits is the obvious rule and is wrong for
// the common case: a command that prints one line and exits leaves the cursor
// near the top and the rest of the screen blank, and dropping from the top then
// throws the output away while keeping the blank rows below it. That is how
// `echo TADA` in a tool window came out empty, the window having been created
// at 80×24 and resized to fit its frame a moment later.
//
// A screen that really is full behaves as before, because a cursor at the
// bottom needs exactly the rows that no longer fit to be dropped.
func rowsToDrop(previous, height, cursorRow int) int {
	needed := cursorRow - height + 1
	return min(max(needed, 0), max(previous-height, 0))
}

func fitLine(line Line, width int, style tcell.Style) Line {
	out := blankLineOf(width, style)
	copy(out, line[:min(len(line), width)])
	return out
}

// blankLineOf returns an empty line of a given width.
func blankLineOf(width int, style tcell.Style) Line {
	line := make(Line, width)
	for i := range line {
		line[i] = blank(style)
	}
	return line
}