mirror of
				https://github.com/jesseduffield/lazygit.git
				synced 2025-10-29 15:09:22 +03:00 
			
		
		
		
	
		
			
				
	
	
		
			632 lines
		
	
	
		
			40 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			632 lines
		
	
	
		
			40 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package helpers
 | |
| 
 | |
| import (
 | |
| 	"cmp"
 | |
| 	"fmt"
 | |
| 	"slices"
 | |
| 	"strings"
 | |
| 	"testing"
 | |
| 
 | |
| 	"github.com/jesseduffield/lazycore/pkg/boxlayout"
 | |
| 	"github.com/jesseduffield/lazygit/pkg/config"
 | |
| 	"github.com/jesseduffield/lazygit/pkg/gui/types"
 | |
| 	"github.com/samber/lo"
 | |
| )
 | |
| 
 | |
| // The best way to add test cases here is to set your args and then get the
 | |
| // test to fail and copy+paste the output into the test case's expected string.
 | |
| // TODO: add more test cases
 | |
| func TestGetWindowDimensions(t *testing.T) {
 | |
| 	getDefaultArgs := func() WindowArrangementArgs {
 | |
| 		return WindowArrangementArgs{
 | |
| 			Width:               75,
 | |
| 			Height:              30,
 | |
| 			UserConfig:          config.GetDefaultConfig(),
 | |
| 			CurrentWindow:       "files",
 | |
| 			CurrentSideWindow:   "files",
 | |
| 			CurrentStaticWindow: "files",
 | |
| 			SplitMainPanel:      false,
 | |
| 			ScreenMode:          types.SCREEN_NORMAL,
 | |
| 			AppStatus:           "",
 | |
| 			InformationStr:      "information",
 | |
| 			ShowExtrasWindow:    false,
 | |
| 			InDemo:              false,
 | |
| 			IsAnyModeActive:     false,
 | |
| 			InSearchPrompt:      false,
 | |
| 			SearchPrefix:        "",
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	type Test struct {
 | |
| 		name       string
 | |
| 		mutateArgs func(*WindowArrangementArgs)
 | |
| 		expected   string
 | |
| 	}
 | |
| 
 | |
| 	tests := []Test{
 | |
| 		{
 | |
| 			name:       "default",
 | |
| 			mutateArgs: func(args *WindowArrangementArgs) {},
 | |
| 			expected: `
 | |
| 			╭status─────────────────╮╭main────────────────────────────────────────────╮
 | |
| 			│                       ││                                                │
 | |
| 			╰───────────────────────╯│                                                │
 | |
| 			╭files──────────────────╮│                                                │
 | |
| 			│                       ││                                                │
 | |
| 			│                       ││                                                │
 | |
| 			│                       ││                                                │
 | |
| 			│                       ││                                                │
 | |
| 			│                       ││                                                │
 | |
| 			│                       ││                                                │
 | |
| 			╰───────────────────────╯│                                                │
 | |
| 			╭branches───────────────╮│                                                │
 | |
| 			│                       ││                                                │
 | |
| 			│                       ││                                                │
 | |
| 			│                       ││                                                │
 | |
| 			│                       ││                                                │
 | |
| 			│                       ││                                                │
 | |
| 			│                       ││                                                │
 | |
| 			╰───────────────────────╯│                                                │
 | |
| 			╭commits────────────────╮│                                                │
 | |
| 			│                       ││                                                │
 | |
| 			│                       ││                                                │
 | |
| 			│                       ││                                                │
 | |
| 			│                       ││                                                │
 | |
| 			│                       ││                                                │
 | |
| 			╰───────────────────────╯│                                                │
 | |
| 			╭stash──────────────────╮│                                                │
 | |
| 			│                       ││                                                │
 | |
| 			╰───────────────────────╯╰────────────────────────────────────────────────╯
 | |
| 			<options──────────────────────────────────────────────────────>A<B────────>
 | |
| 			A: statusSpacer1
 | |
| 			B: information
 | |
| 			`,
 | |
| 		},
 | |
| 		{
 | |
| 			name: "stash focused",
 | |
| 			mutateArgs: func(args *WindowArrangementArgs) {
 | |
| 				args.CurrentSideWindow = "stash"
 | |
| 			},
 | |
| 			expected: `
 | |
| 			╭status─────────────────╮╭main────────────────────────────────────────────╮
 | |
| 			│                       ││                                                │
 | |
| 			╰───────────────────────╯│                                                │
 | |
| 			╭files──────────────────╮│                                                │
 | |
| 			│                       ││                                                │
 | |
| 			│                       ││                                                │
 | |
| 			│                       ││                                                │
 | |
| 			│                       ││                                                │
 | |
| 			│                       ││                                                │
 | |
| 			╰───────────────────────╯│                                                │
 | |
| 			╭branches───────────────╮│                                                │
 | |
| 			│                       ││                                                │
 | |
| 			│                       ││                                                │
 | |
| 			│                       ││                                                │
 | |
| 			│                       ││                                                │
 | |
| 			│                       ││                                                │
 | |
| 			╰───────────────────────╯│                                                │
 | |
| 			╭commits────────────────╮│                                                │
 | |
| 			│                       ││                                                │
 | |
| 			│                       ││                                                │
 | |
| 			│                       ││                                                │
 | |
| 			│                       ││                                                │
 | |
| 			╰───────────────────────╯│                                                │
 | |
| 			╭stash──────────────────╮│                                                │
 | |
| 			│                       ││                                                │
 | |
| 			│                       ││                                                │
 | |
| 			│                       ││                                                │
 | |
| 			│                       ││                                                │
 | |
| 			╰───────────────────────╯╰────────────────────────────────────────────────╯
 | |
| 			<options──────────────────────────────────────────────────────>A<B────────>
 | |
| 			A: statusSpacer1
 | |
| 			B: information
 | |
| 			`,
 | |
| 		},
 | |
| 		{
 | |
| 			name: "expandFocusedSidePanel",
 | |
| 			mutateArgs: func(args *WindowArrangementArgs) {
 | |
| 				args.UserConfig.Gui.ExpandFocusedSidePanel = true
 | |
| 			},
 | |
| 			expected: `
 | |
| 			╭status─────────────────╮╭main────────────────────────────────────────────╮
 | |
| 			│                       ││                                                │
 | |
| 			╰───────────────────────╯│                                                │
 | |
| 			╭files──────────────────╮│                                                │
 | |
| 			│                       ││                                                │
 | |
| 			│                       ││                                                │
 | |
| 			│                       ││                                                │
 | |
| 			│                       ││                                                │
 | |
| 			│                       ││                                                │
 | |
| 			│                       ││                                                │
 | |
| 			│                       ││                                                │
 | |
| 			│                       ││                                                │
 | |
| 			│                       ││                                                │
 | |
| 			╰───────────────────────╯│                                                │
 | |
| 			╭branches───────────────╮│                                                │
 | |
| 			│                       ││                                                │
 | |
| 			│                       ││                                                │
 | |
| 			│                       ││                                                │
 | |
| 			│                       ││                                                │
 | |
| 			╰───────────────────────╯│                                                │
 | |
| 			╭commits────────────────╮│                                                │
 | |
| 			│                       ││                                                │
 | |
| 			│                       ││                                                │
 | |
| 			│                       ││                                                │
 | |
| 			│                       ││                                                │
 | |
| 			╰───────────────────────╯│                                                │
 | |
| 			╭stash──────────────────╮│                                                │
 | |
| 			│                       ││                                                │
 | |
| 			╰───────────────────────╯╰────────────────────────────────────────────────╯
 | |
| 			<options──────────────────────────────────────────────────────>A<B────────>
 | |
| 			A: statusSpacer1
 | |
| 			B: information
 | |
| 			`,
 | |
| 		},
 | |
| 		{
 | |
| 			name: "expandSidePanelWeight",
 | |
| 			mutateArgs: func(args *WindowArrangementArgs) {
 | |
| 				args.UserConfig.Gui.ExpandFocusedSidePanel = true
 | |
| 				args.UserConfig.Gui.ExpandedSidePanelWeight = 4
 | |
| 			},
 | |
| 			expected: `
 | |
| 			╭status─────────────────╮╭main────────────────────────────────────────────╮
 | |
| 			│                       ││                                                │
 | |
| 			╰───────────────────────╯│                                                │
 | |
| 			╭files──────────────────╮│                                                │
 | |
| 			│                       ││                                                │
 | |
| 			│                       ││                                                │
 | |
| 			│                       ││                                                │
 | |
| 			│                       ││                                                │
 | |
| 			│                       ││                                                │
 | |
| 			│                       ││                                                │
 | |
| 			│                       ││                                                │
 | |
| 			│                       ││                                                │
 | |
| 			│                       ││                                                │
 | |
| 			│                       ││                                                │
 | |
| 			│                       ││                                                │
 | |
| 			│                       ││                                                │
 | |
| 			│                       ││                                                │
 | |
| 			╰───────────────────────╯│                                                │
 | |
| 			╭branches───────────────╮│                                                │
 | |
| 			│                       ││                                                │
 | |
| 			│                       ││                                                │
 | |
| 			╰───────────────────────╯│                                                │
 | |
| 			╭commits────────────────╮│                                                │
 | |
| 			│                       ││                                                │
 | |
| 			│                       ││                                                │
 | |
| 			╰───────────────────────╯│                                                │
 | |
| 			╭stash──────────────────╮│                                                │
 | |
| 			│                       ││                                                │
 | |
| 			╰───────────────────────╯╰────────────────────────────────────────────────╯
 | |
| 			<options──────────────────────────────────────────────────────>A<B────────>
 | |
| 			A: statusSpacer1
 | |
| 			B: information
 | |
| 			`,
 | |
| 		},
 | |
| 		{
 | |
| 			name: "0.5 SidePanelWidth",
 | |
| 			mutateArgs: func(args *WindowArrangementArgs) {
 | |
| 				args.UserConfig.Gui.SidePanelWidth = 0.5
 | |
| 			},
 | |
| 			expected: `
 | |
| 			╭status──────────────────────────────╮╭main───────────────────────────────╮
 | |
| 			│                                    ││                                   │
 | |
| 			╰────────────────────────────────────╯│                                   │
 | |
| 			╭files───────────────────────────────╮│                                   │
 | |
| 			│                                    ││                                   │
 | |
| 			│                                    ││                                   │
 | |
| 			│                                    ││                                   │
 | |
| 			│                                    ││                                   │
 | |
| 			│                                    ││                                   │
 | |
| 			│                                    ││                                   │
 | |
| 			╰────────────────────────────────────╯│                                   │
 | |
| 			╭branches────────────────────────────╮│                                   │
 | |
| 			│                                    ││                                   │
 | |
| 			│                                    ││                                   │
 | |
| 			│                                    ││                                   │
 | |
| 			│                                    ││                                   │
 | |
| 			│                                    ││                                   │
 | |
| 			│                                    ││                                   │
 | |
| 			╰────────────────────────────────────╯│                                   │
 | |
| 			╭commits─────────────────────────────╮│                                   │
 | |
| 			│                                    ││                                   │
 | |
| 			│                                    ││                                   │
 | |
| 			│                                    ││                                   │
 | |
| 			│                                    ││                                   │
 | |
| 			│                                    ││                                   │
 | |
| 			╰────────────────────────────────────╯│                                   │
 | |
| 			╭stash───────────────────────────────╮│                                   │
 | |
| 			│                                    ││                                   │
 | |
| 			╰────────────────────────────────────╯╰───────────────────────────────────╯
 | |
| 			<options──────────────────────────────────────────────────────>A<B────────>
 | |
| 			A: statusSpacer1
 | |
| 			B: information
 | |
| 			`,
 | |
| 		},
 | |
| 		{
 | |
| 			name: "0.8 SidePanelWidth",
 | |
| 			mutateArgs: func(args *WindowArrangementArgs) {
 | |
| 				args.UserConfig.Gui.SidePanelWidth = 0.8
 | |
| 			},
 | |
| 			expected: `
 | |
| 			╭status────────────────────────────────────────────────────╮╭main─────────╮
 | |
| 			│                                                          ││             │
 | |
| 			╰──────────────────────────────────────────────────────────╯│             │
 | |
| 			╭files─────────────────────────────────────────────────────╮│             │
 | |
| 			│                                                          ││             │
 | |
| 			│                                                          ││             │
 | |
| 			│                                                          ││             │
 | |
| 			│                                                          ││             │
 | |
| 			│                                                          ││             │
 | |
| 			│                                                          ││             │
 | |
| 			╰──────────────────────────────────────────────────────────╯│             │
 | |
| 			╭branches──────────────────────────────────────────────────╮│             │
 | |
| 			│                                                          ││             │
 | |
| 			│                                                          ││             │
 | |
| 			│                                                          ││             │
 | |
| 			│                                                          ││             │
 | |
| 			│                                                          ││             │
 | |
| 			│                                                          ││             │
 | |
| 			╰──────────────────────────────────────────────────────────╯│             │
 | |
| 			╭commits───────────────────────────────────────────────────╮│             │
 | |
| 			│                                                          ││             │
 | |
| 			│                                                          ││             │
 | |
| 			│                                                          ││             │
 | |
| 			│                                                          ││             │
 | |
| 			│                                                          ││             │
 | |
| 			╰──────────────────────────────────────────────────────────╯│             │
 | |
| 			╭stash─────────────────────────────────────────────────────╮│             │
 | |
| 			│                                                          ││             │
 | |
| 			╰──────────────────────────────────────────────────────────╯╰─────────────╯
 | |
| 			<options──────────────────────────────────────────────────────>A<B────────>
 | |
| 			A: statusSpacer1
 | |
| 			B: information
 | |
| 			`,
 | |
| 		},
 | |
| 		{
 | |
| 			name: "half screen mode, enlargedSideViewLocation left",
 | |
| 			mutateArgs: func(args *WindowArrangementArgs) {
 | |
| 				args.Height = 20 // smaller height because we don't more here
 | |
| 				args.ScreenMode = types.SCREEN_HALF
 | |
| 				args.UserConfig.Gui.EnlargedSideViewLocation = "left"
 | |
| 			},
 | |
| 			expected: `
 | |
| 			╭status──────────────────────────────╮╭main───────────────────────────────╮
 | |
| 			│                                    ││                                   │
 | |
| 			│                                    ││                                   │
 | |
| 			│                                    ││                                   │
 | |
| 			│                                    ││                                   │
 | |
| 			│                                    ││                                   │
 | |
| 			│                                    ││                                   │
 | |
| 			│                                    ││                                   │
 | |
| 			│                                    ││                                   │
 | |
| 			│                                    ││                                   │
 | |
| 			│                                    ││                                   │
 | |
| 			│                                    ││                                   │
 | |
| 			│                                    ││                                   │
 | |
| 			│                                    ││                                   │
 | |
| 			│                                    ││                                   │
 | |
| 			│                                    ││                                   │
 | |
| 			│                                    ││                                   │
 | |
| 			│                                    ││                                   │
 | |
| 			╰────────────────────────────────────╯╰───────────────────────────────────╯
 | |
| 			<options──────────────────────────────────────────────────────>A<B────────>
 | |
| 			A: statusSpacer1
 | |
| 			B: information
 | |
| 			`,
 | |
| 		},
 | |
| 		{
 | |
| 			name: "half screen mode, enlargedSideViewLocation top",
 | |
| 			mutateArgs: func(args *WindowArrangementArgs) {
 | |
| 				args.Height = 20 // smaller height because we don't more here
 | |
| 				args.ScreenMode = types.SCREEN_HALF
 | |
| 				args.UserConfig.Gui.EnlargedSideViewLocation = "top"
 | |
| 			},
 | |
| 			expected: `
 | |
| 			╭status───────────────────────────────────────────────────────────────────╮
 | |
| 			│                                                                         │
 | |
| 			│                                                                         │
 | |
| 			│                                                                         │
 | |
| 			│                                                                         │
 | |
| 			│                                                                         │
 | |
| 			╰─────────────────────────────────────────────────────────────────────────╯
 | |
| 			╭main─────────────────────────────────────────────────────────────────────╮
 | |
| 			│                                                                         │
 | |
| 			│                                                                         │
 | |
| 			│                                                                         │
 | |
| 			│                                                                         │
 | |
| 			│                                                                         │
 | |
| 			│                                                                         │
 | |
| 			│                                                                         │
 | |
| 			│                                                                         │
 | |
| 			│                                                                         │
 | |
| 			│                                                                         │
 | |
| 			╰─────────────────────────────────────────────────────────────────────────╯
 | |
| 			<options──────────────────────────────────────────────────────>A<B────────>
 | |
| 			A: statusSpacer1
 | |
| 			B: information
 | |
| 			`,
 | |
| 		},
 | |
| 		{
 | |
| 			name: "search mode",
 | |
| 			mutateArgs: func(args *WindowArrangementArgs) {
 | |
| 				args.InSearchPrompt = true
 | |
| 				args.SearchPrefix = "Search: "
 | |
| 				args.Height = 6 // small height cos we only care about the bottom line
 | |
| 			},
 | |
| 			expected: `
 | |
| 			<status─────────────────>╭main────────────────────────────────────────────╮
 | |
| 			<files──────────────────>│                                                │
 | |
| 			<branches───────────────>│                                                │
 | |
| 			<commits────────────────>│                                                │
 | |
| 			<stash──────────────────>╰────────────────────────────────────────────────╯
 | |
| 			<A─────><search───────────────────────────────────────────────────────────>
 | |
| 			A: searchPrefix
 | |
| 			`,
 | |
| 		},
 | |
| 		{
 | |
| 			name: "app status present",
 | |
| 			mutateArgs: func(args *WindowArrangementArgs) {
 | |
| 				args.AppStatus = "Rebasing /"
 | |
| 				args.Height = 6 // small height cos we only care about the bottom line
 | |
| 			},
 | |
| 			// We expect single-character spacers between the windows of the bottom line
 | |
| 			expected: `
 | |
| 			<status─────────────────>╭main────────────────────────────────────────────╮
 | |
| 			<files──────────────────>│                                                │
 | |
| 			<branches───────────────>│                                                │
 | |
| 			<commits────────────────>│                                                │
 | |
| 			<stash──────────────────>╰────────────────────────────────────────────────╯
 | |
| 			<A───────>B<options───────────────────────────────────────────>C<D────────>
 | |
| 			A: appStatus
 | |
| 			B: statusSpacer2
 | |
| 			C: statusSpacer1
 | |
| 			D: information
 | |
| 			`,
 | |
| 		},
 | |
| 		{
 | |
| 			name: "information present without options",
 | |
| 			mutateArgs: func(args *WindowArrangementArgs) {
 | |
| 				args.Height = 6                            // small height cos we only care about the bottom line
 | |
| 				args.UserConfig.Gui.ShowBottomLine = false // this hides the options window
 | |
| 				args.IsAnyModeActive = true                // this means we show the bottom line despite the user config
 | |
| 			},
 | |
| 			// We expect a spacer on the left of the bottom line so that the information
 | |
| 			// window is right-aligned
 | |
| 			expected: `
 | |
| 			<status─────────────────>╭main────────────────────────────────────────────╮
 | |
| 			<files──────────────────>│                                                │
 | |
| 			<branches───────────────>│                                                │
 | |
| 			<commits────────────────>│                                                │
 | |
| 			<stash──────────────────>╰────────────────────────────────────────────────╯
 | |
| 			<statusSpacer1────────────────────────────────────────────────>A<B────────>
 | |
| 			A: statusSpacer2
 | |
| 			B: information
 | |
| 			`,
 | |
| 		},
 | |
| 		{
 | |
| 			name: "app status present without information or options",
 | |
| 			mutateArgs: func(args *WindowArrangementArgs) {
 | |
| 				args.Height = 6                            // small height cos we only care about the bottom line
 | |
| 				args.UserConfig.Gui.ShowBottomLine = false // this hides the options window
 | |
| 				args.IsAnyModeActive = false
 | |
| 				args.AppStatus = "Rebasing /"
 | |
| 			},
 | |
| 			// We expect the app status window to take up all the available space
 | |
| 			expected: `
 | |
| 			<status─────────────────>╭main────────────────────────────────────────────╮
 | |
| 			<files──────────────────>│                                                │
 | |
| 			<branches───────────────>│                                                │
 | |
| 			<commits────────────────>│                                                │
 | |
| 			<stash──────────────────>╰────────────────────────────────────────────────╯
 | |
| 			<appStatus────────────────────────────────────────────────────────────────>
 | |
| 			`,
 | |
| 		},
 | |
| 		{
 | |
| 			name: "app status present with information but without options",
 | |
| 			mutateArgs: func(args *WindowArrangementArgs) {
 | |
| 				args.Height = 6                            // small height cos we only care about the bottom line
 | |
| 				args.UserConfig.Gui.ShowBottomLine = false // this hides the options window
 | |
| 				args.IsAnyModeActive = true
 | |
| 				args.AppStatus = "Rebasing /"
 | |
| 			},
 | |
| 			expected: `
 | |
| 			<status─────────────────>╭main────────────────────────────────────────────╮
 | |
| 			<files──────────────────>│                                                │
 | |
| 			<branches───────────────>│                                                │
 | |
| 			<commits────────────────>│                                                │
 | |
| 			<stash──────────────────>╰────────────────────────────────────────────────╯
 | |
| 			<A───────><statusSpacer1──────────────────────────────────────>B<C────────>
 | |
| 			A: appStatus
 | |
| 			B: statusSpacer2
 | |
| 			C: information
 | |
| 			`,
 | |
| 		},
 | |
| 		{
 | |
| 			name: "app status present with very long information but without options",
 | |
| 			mutateArgs: func(args *WindowArrangementArgs) {
 | |
| 				args.Height = 6                            // small height cos we only care about the bottom line
 | |
| 				args.Width = 55                            // smaller width so that not all bottom line views fit
 | |
| 				args.UserConfig.Gui.ShowBottomLine = false // this hides the options window
 | |
| 				args.IsAnyModeActive = true
 | |
| 				args.AppStatus = "Rebasing /"
 | |
| 				args.InformationStr = "Showing output for: git diff deadbeef fa1afe1 -- (Reset)"
 | |
| 			},
 | |
| 			expected: `
 | |
| 			<status───────────>╭main──────────────────────────────╮
 | |
| 			<files────────────>│                                  │
 | |
| 			<branches─────────>│                                  │
 | |
| 			<commits──────────>│                                  │
 | |
| 			<stash────────────>╰──────────────────────────────────╯
 | |
| 			<A───────>B<information──────────────────────────────────────────>
 | |
| 			A: appStatus
 | |
| 			B: statusSpacer2
 | |
| 			`,
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	for _, test := range tests {
 | |
| 		t.Run(test.name, func(t *testing.T) {
 | |
| 			args := getDefaultArgs()
 | |
| 			test.mutateArgs(&args)
 | |
| 			windows := GetWindowDimensions(args)
 | |
| 			output := renderLayout(windows)
 | |
| 			// removing tabs so that it's easier to paste the expected output
 | |
| 			expected := strings.ReplaceAll(test.expected, "\t", "")
 | |
| 			expected = strings.TrimSpace(expected)
 | |
| 			if output != expected {
 | |
| 				fmt.Println(output)
 | |
| 				t.Errorf("Expected:\n%s\n\nGot:\n%s", expected, output)
 | |
| 			}
 | |
| 		})
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func renderLayout(windows map[string]boxlayout.Dimensions) string {
 | |
| 	// Each window will be represented by a letter.
 | |
| 	windowMarkers := map[string]string{}
 | |
| 	shortLabels := []string{"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"}
 | |
| 	currentShortLabelIdx := 0
 | |
| 	windowNames := lo.Keys(windows)
 | |
| 	// Sort first by name, then by position. This means our short labels will
 | |
| 	// increment in the order that the windows appear on the screen.
 | |
| 	slices.Sort(windowNames)
 | |
| 	slices.SortStableFunc(windowNames, func(a, b string) int {
 | |
| 		dimensionsA := windows[a]
 | |
| 		dimensionsB := windows[b]
 | |
| 		if dimensionsA.Y0 != dimensionsB.Y0 {
 | |
| 			return cmp.Compare(dimensionsA.Y0, dimensionsB.Y0)
 | |
| 		}
 | |
| 		return cmp.Compare(dimensionsA.X0, dimensionsB.X0)
 | |
| 	})
 | |
| 
 | |
| 	// Uniquify windows by dimensions (so perfectly overlapping windows are de-duped). This prevents getting 'fileshes' as a label where the files and branches windows overlap.
 | |
| 	// branches windows overlap.
 | |
| 	windowNames = lo.UniqBy(windowNames, func(windowName string) boxlayout.Dimensions {
 | |
| 		return windows[windowName]
 | |
| 	})
 | |
| 
 | |
| 	// excluding the limit window because it overlaps with everything. In future
 | |
| 	// we should have a concept of layers and then our test can assert against
 | |
| 	// each layer.
 | |
| 	windowNames = lo.Without(windowNames, "limit")
 | |
| 
 | |
| 	// get width/height by getting the max values of the dimensions
 | |
| 	width := 0
 | |
| 	height := 0
 | |
| 	for _, dimensions := range windows {
 | |
| 		if dimensions.X1+1 > width {
 | |
| 			width = dimensions.X1 + 1
 | |
| 		}
 | |
| 		if dimensions.Y1+1 > height {
 | |
| 			height = dimensions.Y1 + 1
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	screen := make([][]string, height)
 | |
| 	for i := range screen {
 | |
| 		screen[i] = make([]string, width)
 | |
| 	}
 | |
| 
 | |
| 	// Draw each window
 | |
| 	for _, windowName := range windowNames {
 | |
| 		dimensions := windows[windowName]
 | |
| 
 | |
| 		zeroWidth := dimensions.X0 == dimensions.X1+1
 | |
| 		if zeroWidth {
 | |
| 			continue
 | |
| 		}
 | |
| 
 | |
| 		singleRow := dimensions.Y0 == dimensions.Y1
 | |
| 		oneOrTwoColumns := dimensions.X0 == dimensions.X1 || dimensions.X0+1 == dimensions.X1
 | |
| 
 | |
| 		assignShortLabel := func(windowName string) string {
 | |
| 			windowMarkers[windowName] = shortLabels[currentShortLabelIdx]
 | |
| 			currentShortLabelIdx++
 | |
| 			return windowMarkers[windowName]
 | |
| 		}
 | |
| 
 | |
| 		if singleRow {
 | |
| 			y := dimensions.Y0
 | |
| 			// If our window only occupies one (or two) columns we'll just use the short
 | |
| 			// label once (or twice) i.e. 'A' or 'AA'.
 | |
| 			if oneOrTwoColumns {
 | |
| 				shortLabel := assignShortLabel(windowName)
 | |
| 
 | |
| 				for x := dimensions.X0; x <= dimensions.X1; x++ {
 | |
| 					screen[y][x] = shortLabel
 | |
| 				}
 | |
| 			} else {
 | |
| 				screen[y][dimensions.X0] = "<"
 | |
| 				screen[y][dimensions.X1] = ">"
 | |
| 				for x := dimensions.X0 + 1; x < dimensions.X1; x++ {
 | |
| 					screen[y][x] = "─"
 | |
| 				}
 | |
| 
 | |
| 				// Now add the label
 | |
| 				label := windowName
 | |
| 				// If we can't fit the label we'll use a one-character short label
 | |
| 				if len(label) > dimensions.X1-dimensions.X0-1 {
 | |
| 					label = assignShortLabel(windowName)
 | |
| 				}
 | |
| 				for i, char := range label {
 | |
| 					screen[y][dimensions.X0+1+i] = string(char)
 | |
| 				}
 | |
| 			}
 | |
| 		} else {
 | |
| 			// Draw box border
 | |
| 			for y := dimensions.Y0; y <= dimensions.Y1; y++ {
 | |
| 				for x := dimensions.X0; x <= dimensions.X1; x++ {
 | |
| 					if x == dimensions.X0 && y == dimensions.Y0 {
 | |
| 						screen[y][x] = "╭"
 | |
| 					} else if x == dimensions.X1 && y == dimensions.Y0 {
 | |
| 						screen[y][x] = "╮"
 | |
| 					} else if x == dimensions.X0 && y == dimensions.Y1 {
 | |
| 						screen[y][x] = "╰"
 | |
| 					} else if x == dimensions.X1 && y == dimensions.Y1 {
 | |
| 						screen[y][x] = "╯"
 | |
| 					} else if y == dimensions.Y0 || y == dimensions.Y1 {
 | |
| 						screen[y][x] = "─"
 | |
| 					} else if x == dimensions.X0 || x == dimensions.X1 {
 | |
| 						screen[y][x] = "│"
 | |
| 					} else {
 | |
| 						screen[y][x] = " "
 | |
| 					}
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			// Add the label
 | |
| 			label := windowName
 | |
| 			// If we can't fit the label we'll use a one-character short label
 | |
| 			if len(label) > dimensions.X1-dimensions.X0-1 {
 | |
| 				label = assignShortLabel(windowName)
 | |
| 			}
 | |
| 			for i, char := range label {
 | |
| 				screen[dimensions.Y0][dimensions.X0+1+i] = string(char)
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	// Draw the screen
 | |
| 	output := ""
 | |
| 	for _, row := range screen {
 | |
| 		for _, marker := range row {
 | |
| 			output += marker
 | |
| 		}
 | |
| 		output += "\n"
 | |
| 	}
 | |
| 
 | |
| 	// Add a legend
 | |
| 	for _, windowName := range windowNames {
 | |
| 		if !lo.Contains(lo.Keys(windowMarkers), windowName) {
 | |
| 			continue
 | |
| 		}
 | |
| 		marker := windowMarkers[windowName]
 | |
| 		output += fmt.Sprintf("%s: %s\n", marker, windowName)
 | |
| 	}
 | |
| 
 | |
| 	output = strings.TrimSpace(output)
 | |
| 
 | |
| 	return output
 | |
| }
 |