forked from toolshed/abra
chore: vendor
This commit is contained in:
1
vendor/github.com/charmbracelet/lipgloss/.gitignore
generated
vendored
Normal file
1
vendor/github.com/charmbracelet/lipgloss/.gitignore
generated
vendored
Normal file
@ -0,0 +1 @@
|
||||
ssh_example_ed25519*
|
46
vendor/github.com/charmbracelet/lipgloss/.golangci-soft.yml
generated
vendored
Normal file
46
vendor/github.com/charmbracelet/lipgloss/.golangci-soft.yml
generated
vendored
Normal file
@ -0,0 +1,46 @@
|
||||
run:
|
||||
tests: false
|
||||
|
||||
issues:
|
||||
include:
|
||||
- EXC0001
|
||||
- EXC0005
|
||||
- EXC0011
|
||||
- EXC0012
|
||||
- EXC0013
|
||||
|
||||
max-issues-per-linter: 0
|
||||
max-same-issues: 0
|
||||
|
||||
linters:
|
||||
enable:
|
||||
# - dupl
|
||||
- exhaustive
|
||||
# - exhaustivestruct
|
||||
- goconst
|
||||
- godot
|
||||
- godox
|
||||
- gomnd
|
||||
- gomoddirectives
|
||||
- goprintffuncname
|
||||
# - lll
|
||||
- misspell
|
||||
- nakedret
|
||||
- nestif
|
||||
- noctx
|
||||
- nolintlint
|
||||
- prealloc
|
||||
- wrapcheck
|
||||
|
||||
# disable default linters, they are already enabled in .golangci.yml
|
||||
disable:
|
||||
- deadcode
|
||||
- errcheck
|
||||
- gosimple
|
||||
- govet
|
||||
- ineffassign
|
||||
- staticcheck
|
||||
- structcheck
|
||||
- typecheck
|
||||
- unused
|
||||
- varcheck
|
30
vendor/github.com/charmbracelet/lipgloss/.golangci.yml
generated
vendored
Normal file
30
vendor/github.com/charmbracelet/lipgloss/.golangci.yml
generated
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
run:
|
||||
tests: false
|
||||
|
||||
issues:
|
||||
include:
|
||||
- EXC0001
|
||||
- EXC0005
|
||||
- EXC0011
|
||||
- EXC0012
|
||||
- EXC0013
|
||||
|
||||
max-issues-per-linter: 0
|
||||
max-same-issues: 0
|
||||
|
||||
linters:
|
||||
enable:
|
||||
- bodyclose
|
||||
- exportloopref
|
||||
- gofumpt
|
||||
- goimports
|
||||
- gosec
|
||||
- nilerr
|
||||
- predeclared
|
||||
- revive
|
||||
- rowserrcheck
|
||||
- sqlclosecheck
|
||||
- tparallel
|
||||
- unconvert
|
||||
- unparam
|
||||
- whitespace
|
21
vendor/github.com/charmbracelet/lipgloss/LICENSE
generated
vendored
Normal file
21
vendor/github.com/charmbracelet/lipgloss/LICENSE
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021-2023 Charmbracelet, Inc
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
751
vendor/github.com/charmbracelet/lipgloss/README.md
generated
vendored
Normal file
751
vendor/github.com/charmbracelet/lipgloss/README.md
generated
vendored
Normal file
@ -0,0 +1,751 @@
|
||||
# Lip Gloss
|
||||
|
||||
<p>
|
||||
<a href="https://stuff.charm.sh/lipgloss/lipgloss-mascot-2k.png"><img width="340" alt="Lip Gloss title treatment" src="https://github.com/charmbracelet/lipgloss/assets/25087/147cadb1-4254-43ec-ae6b-8d6ca7b029a1"></a><br>
|
||||
<a href="https://github.com/charmbracelet/lipgloss/releases"><img src="https://img.shields.io/github/release/charmbracelet/lipgloss.svg" alt="Latest Release"></a>
|
||||
<a href="https://pkg.go.dev/github.com/charmbracelet/lipgloss?tab=doc"><img src="https://godoc.org/github.com/golang/gddo?status.svg" alt="GoDoc"></a>
|
||||
<a href="https://github.com/charmbracelet/lipgloss/actions"><img src="https://github.com/charmbracelet/lipgloss/workflows/build/badge.svg" alt="Build Status"></a>
|
||||
<a href="https://www.phorm.ai/query?projectId=a0e324b6-b706-4546-b951-6671ea60c13f"><img src="https://stuff.charm.sh/misc/phorm-badge.svg" alt="phorm.ai"></a>
|
||||
</p>
|
||||
|
||||
Style definitions for nice terminal layouts. Built with TUIs in mind.
|
||||
|
||||

|
||||
|
||||
Lip Gloss takes an expressive, declarative approach to terminal rendering.
|
||||
Users familiar with CSS will feel at home with Lip Gloss.
|
||||
|
||||
```go
|
||||
|
||||
import "github.com/charmbracelet/lipgloss"
|
||||
|
||||
var style = lipgloss.NewStyle().
|
||||
Bold(true).
|
||||
Foreground(lipgloss.Color("#FAFAFA")).
|
||||
Background(lipgloss.Color("#7D56F4")).
|
||||
PaddingTop(2).
|
||||
PaddingLeft(4).
|
||||
Width(22)
|
||||
|
||||
fmt.Println(style.Render("Hello, kitty"))
|
||||
```
|
||||
|
||||
## Colors
|
||||
|
||||
Lip Gloss supports the following color profiles:
|
||||
|
||||
### ANSI 16 colors (4-bit)
|
||||
|
||||
```go
|
||||
lipgloss.Color("5") // magenta
|
||||
lipgloss.Color("9") // red
|
||||
lipgloss.Color("12") // light blue
|
||||
```
|
||||
|
||||
### ANSI 256 Colors (8-bit)
|
||||
|
||||
```go
|
||||
lipgloss.Color("86") // aqua
|
||||
lipgloss.Color("201") // hot pink
|
||||
lipgloss.Color("202") // orange
|
||||
```
|
||||
|
||||
### True Color (16,777,216 colors; 24-bit)
|
||||
|
||||
```go
|
||||
lipgloss.Color("#0000FF") // good ol' 100% blue
|
||||
lipgloss.Color("#04B575") // a green
|
||||
lipgloss.Color("#3C3C3C") // a dark gray
|
||||
```
|
||||
|
||||
...as well as a 1-bit ASCII profile, which is black and white only.
|
||||
|
||||
The terminal's color profile will be automatically detected, and colors outside
|
||||
the gamut of the current palette will be automatically coerced to their closest
|
||||
available value.
|
||||
|
||||
### Adaptive Colors
|
||||
|
||||
You can also specify color options for light and dark backgrounds:
|
||||
|
||||
```go
|
||||
lipgloss.AdaptiveColor{Light: "236", Dark: "248"}
|
||||
```
|
||||
|
||||
The terminal's background color will automatically be detected and the
|
||||
appropriate color will be chosen at runtime.
|
||||
|
||||
### Complete Colors
|
||||
|
||||
CompleteColor specifies exact values for truecolor, ANSI256, and ANSI color
|
||||
profiles.
|
||||
|
||||
```go
|
||||
lipgloss.CompleteColor{True: "#0000FF", ANSI256: "86", ANSI: "5"}
|
||||
```
|
||||
|
||||
Automatic color degradation will not be performed in this case and it will be
|
||||
based on the color specified.
|
||||
|
||||
### Complete Adaptive Colors
|
||||
|
||||
You can use CompleteColor with AdaptiveColor to specify the exact values for
|
||||
light and dark backgrounds without automatic color degradation.
|
||||
|
||||
```go
|
||||
lipgloss.CompleteAdaptiveColor{
|
||||
Light: CompleteColor{TrueColor: "#d7ffae", ANSI256: "193", ANSI: "11"},
|
||||
Dark: CompleteColor{TrueColor: "#d75fee", ANSI256: "163", ANSI: "5"},
|
||||
}
|
||||
```
|
||||
|
||||
## Inline Formatting
|
||||
|
||||
Lip Gloss supports the usual ANSI text formatting options:
|
||||
|
||||
```go
|
||||
var style = lipgloss.NewStyle().
|
||||
Bold(true).
|
||||
Italic(true).
|
||||
Faint(true).
|
||||
Blink(true).
|
||||
Strikethrough(true).
|
||||
Underline(true).
|
||||
Reverse(true)
|
||||
```
|
||||
|
||||
## Block-Level Formatting
|
||||
|
||||
Lip Gloss also supports rules for block-level formatting:
|
||||
|
||||
```go
|
||||
// Padding
|
||||
var style = lipgloss.NewStyle().
|
||||
PaddingTop(2).
|
||||
PaddingRight(4).
|
||||
PaddingBottom(2).
|
||||
PaddingLeft(4)
|
||||
|
||||
// Margins
|
||||
var style = lipgloss.NewStyle().
|
||||
MarginTop(2).
|
||||
MarginRight(4).
|
||||
MarginBottom(2).
|
||||
MarginLeft(4)
|
||||
```
|
||||
|
||||
There is also shorthand syntax for margins and padding, which follows the same
|
||||
format as CSS:
|
||||
|
||||
```go
|
||||
// 2 cells on all sides
|
||||
lipgloss.NewStyle().Padding(2)
|
||||
|
||||
// 2 cells on the top and bottom, 4 cells on the left and right
|
||||
lipgloss.NewStyle().Margin(2, 4)
|
||||
|
||||
// 1 cell on the top, 4 cells on the sides, 2 cells on the bottom
|
||||
lipgloss.NewStyle().Padding(1, 4, 2)
|
||||
|
||||
// Clockwise, starting from the top: 2 cells on the top, 4 on the right, 3 on
|
||||
// the bottom, and 1 on the left
|
||||
lipgloss.NewStyle().Margin(2, 4, 3, 1)
|
||||
```
|
||||
|
||||
## Aligning Text
|
||||
|
||||
You can align paragraphs of text to the left, right, or center.
|
||||
|
||||
```go
|
||||
var style = lipgloss.NewStyle().
|
||||
Width(24).
|
||||
Align(lipgloss.Left). // align it left
|
||||
Align(lipgloss.Right). // no wait, align it right
|
||||
Align(lipgloss.Center) // just kidding, align it in the center
|
||||
```
|
||||
|
||||
## Width and Height
|
||||
|
||||
Setting a minimum width and height is simple and straightforward.
|
||||
|
||||
```go
|
||||
var style = lipgloss.NewStyle().
|
||||
SetString("What’s for lunch?").
|
||||
Width(24).
|
||||
Height(32).
|
||||
Foreground(lipgloss.Color("63"))
|
||||
```
|
||||
|
||||
## Borders
|
||||
|
||||
Adding borders is easy:
|
||||
|
||||
```go
|
||||
// Add a purple, rectangular border
|
||||
var style = lipgloss.NewStyle().
|
||||
BorderStyle(lipgloss.NormalBorder()).
|
||||
BorderForeground(lipgloss.Color("63"))
|
||||
|
||||
// Set a rounded, yellow-on-purple border to the top and left
|
||||
var anotherStyle = lipgloss.NewStyle().
|
||||
BorderStyle(lipgloss.RoundedBorder()).
|
||||
BorderForeground(lipgloss.Color("228")).
|
||||
BorderBackground(lipgloss.Color("63")).
|
||||
BorderTop(true).
|
||||
BorderLeft(true)
|
||||
|
||||
// Make your own border
|
||||
var myCuteBorder = lipgloss.Border{
|
||||
Top: "._.:*:",
|
||||
Bottom: "._.:*:",
|
||||
Left: "|*",
|
||||
Right: "|*",
|
||||
TopLeft: "*",
|
||||
TopRight: "*",
|
||||
BottomLeft: "*",
|
||||
BottomRight: "*",
|
||||
}
|
||||
```
|
||||
|
||||
There are also shorthand functions for defining borders, which follow a similar
|
||||
pattern to the margin and padding shorthand functions.
|
||||
|
||||
```go
|
||||
// Add a thick border to the top and bottom
|
||||
lipgloss.NewStyle().
|
||||
Border(lipgloss.ThickBorder(), true, false)
|
||||
|
||||
// Add a double border to the top and left sides. Rules are set clockwise
|
||||
// from top.
|
||||
lipgloss.NewStyle().
|
||||
Border(lipgloss.DoubleBorder(), true, false, false, true)
|
||||
```
|
||||
|
||||
For more on borders see [the docs][docs].
|
||||
|
||||
## Copying Styles
|
||||
|
||||
Just use assignment:
|
||||
|
||||
```go
|
||||
style := lipgloss.NewStyle().Foreground(lipgloss.Color("219"))
|
||||
|
||||
copiedStyle := style // this is a true copy
|
||||
|
||||
wildStyle := style.Blink(true) // this is also true copy, with blink added
|
||||
|
||||
```
|
||||
|
||||
Since `Style` data structures contains only primitive types, assigning a style
|
||||
to another effectively creates a new copy of the style without mutating the
|
||||
original.
|
||||
|
||||
## Inheritance
|
||||
|
||||
Styles can inherit rules from other styles. When inheriting, only unset rules
|
||||
on the receiver are inherited.
|
||||
|
||||
```go
|
||||
var styleA = lipgloss.NewStyle().
|
||||
Foreground(lipgloss.Color("229")).
|
||||
Background(lipgloss.Color("63"))
|
||||
|
||||
// Only the background color will be inherited here, because the foreground
|
||||
// color will have been already set:
|
||||
var styleB = lipgloss.NewStyle().
|
||||
Foreground(lipgloss.Color("201")).
|
||||
Inherit(styleA)
|
||||
```
|
||||
|
||||
## Unsetting Rules
|
||||
|
||||
All rules can be unset:
|
||||
|
||||
```go
|
||||
var style = lipgloss.NewStyle().
|
||||
Bold(true). // make it bold
|
||||
UnsetBold(). // jk don't make it bold
|
||||
Background(lipgloss.Color("227")). // yellow background
|
||||
UnsetBackground() // never mind
|
||||
```
|
||||
|
||||
When a rule is unset, it won't be inherited or copied.
|
||||
|
||||
## Enforcing Rules
|
||||
|
||||
Sometimes, such as when developing a component, you want to make sure style
|
||||
definitions respect their intended purpose in the UI. This is where `Inline`
|
||||
and `MaxWidth`, and `MaxHeight` come in:
|
||||
|
||||
```go
|
||||
// Force rendering onto a single line, ignoring margins, padding, and borders.
|
||||
someStyle.Inline(true).Render("yadda yadda")
|
||||
|
||||
// Also limit rendering to five cells
|
||||
someStyle.Inline(true).MaxWidth(5).Render("yadda yadda")
|
||||
|
||||
// Limit rendering to a 5x5 cell block
|
||||
someStyle.MaxWidth(5).MaxHeight(5).Render("yadda yadda")
|
||||
```
|
||||
|
||||
## Tabs
|
||||
|
||||
The tab character (`\t`) is rendered differently in different terminals (often
|
||||
as 8 spaces, sometimes 4). Because of this inconsistency, Lip Gloss converts
|
||||
tabs to 4 spaces at render time. This behavior can be changed on a per-style
|
||||
basis, however:
|
||||
|
||||
```go
|
||||
style := lipgloss.NewStyle() // tabs will render as 4 spaces, the default
|
||||
style = style.TabWidth(2) // render tabs as 2 spaces
|
||||
style = style.TabWidth(0) // remove tabs entirely
|
||||
style = style.TabWidth(lipgloss.NoTabConversion) // leave tabs intact
|
||||
```
|
||||
|
||||
## Rendering
|
||||
|
||||
Generally, you just call the `Render(string...)` method on a `lipgloss.Style`:
|
||||
|
||||
```go
|
||||
style := lipgloss.NewStyle().Bold(true).SetString("Hello,")
|
||||
fmt.Println(style.Render("kitty.")) // Hello, kitty.
|
||||
fmt.Println(style.Render("puppy.")) // Hello, puppy.
|
||||
```
|
||||
|
||||
But you could also use the Stringer interface:
|
||||
|
||||
```go
|
||||
var style = lipgloss.NewStyle().SetString("你好,猫咪。").Bold(true)
|
||||
fmt.Println(style) // 你好,猫咪。
|
||||
```
|
||||
|
||||
### Custom Renderers
|
||||
|
||||
Custom renderers allow you to render to a specific outputs. This is
|
||||
particularly important when you want to render to different outputs and
|
||||
correctly detect the color profile and dark background status for each, such as
|
||||
in a server-client situation.
|
||||
|
||||
```go
|
||||
func myLittleHandler(sess ssh.Session) {
|
||||
// Create a renderer for the client.
|
||||
renderer := lipgloss.NewRenderer(sess)
|
||||
|
||||
// Create a new style on the renderer.
|
||||
style := renderer.NewStyle().Background(lipgloss.AdaptiveColor{Light: "63", Dark: "228"})
|
||||
|
||||
// Render. The color profile and dark background state will be correctly detected.
|
||||
io.WriteString(sess, style.Render("Heyyyyyyy"))
|
||||
}
|
||||
```
|
||||
|
||||
For an example on using a custom renderer over SSH with [Wish][wish] see the
|
||||
[SSH example][ssh-example].
|
||||
|
||||
## Utilities
|
||||
|
||||
In addition to pure styling, Lip Gloss also ships with some utilities to help
|
||||
assemble your layouts.
|
||||
|
||||
### Joining Paragraphs
|
||||
|
||||
Horizontally and vertically joining paragraphs is a cinch.
|
||||
|
||||
```go
|
||||
// Horizontally join three paragraphs along their bottom edges
|
||||
lipgloss.JoinHorizontal(lipgloss.Bottom, paragraphA, paragraphB, paragraphC)
|
||||
|
||||
// Vertically join two paragraphs along their center axes
|
||||
lipgloss.JoinVertical(lipgloss.Center, paragraphA, paragraphB)
|
||||
|
||||
// Horizontally join three paragraphs, with the shorter ones aligning 20%
|
||||
// from the top of the tallest
|
||||
lipgloss.JoinHorizontal(0.2, paragraphA, paragraphB, paragraphC)
|
||||
```
|
||||
|
||||
### Measuring Width and Height
|
||||
|
||||
Sometimes you’ll want to know the width and height of text blocks when building
|
||||
your layouts.
|
||||
|
||||
```go
|
||||
// Render a block of text.
|
||||
var style = lipgloss.NewStyle().
|
||||
Width(40).
|
||||
Padding(2)
|
||||
var block string = style.Render(someLongString)
|
||||
|
||||
// Get the actual, physical dimensions of the text block.
|
||||
width := lipgloss.Width(block)
|
||||
height := lipgloss.Height(block)
|
||||
|
||||
// Here's a shorthand function.
|
||||
w, h := lipgloss.Size(block)
|
||||
```
|
||||
|
||||
### Placing Text in Whitespace
|
||||
|
||||
Sometimes you’ll simply want to place a block of text in whitespace.
|
||||
|
||||
```go
|
||||
// Center a paragraph horizontally in a space 80 cells wide. The height of
|
||||
// the block returned will be as tall as the input paragraph.
|
||||
block := lipgloss.PlaceHorizontal(80, lipgloss.Center, fancyStyledParagraph)
|
||||
|
||||
// Place a paragraph at the bottom of a space 30 cells tall. The width of
|
||||
// the text block returned will be as wide as the input paragraph.
|
||||
block := lipgloss.PlaceVertical(30, lipgloss.Bottom, fancyStyledParagraph)
|
||||
|
||||
// Place a paragraph in the bottom right corner of a 30x80 cell space.
|
||||
block := lipgloss.Place(30, 80, lipgloss.Right, lipgloss.Bottom, fancyStyledParagraph)
|
||||
```
|
||||
|
||||
You can also style the whitespace. For details, see [the docs][docs].
|
||||
|
||||
### Rendering Tables
|
||||
|
||||
Lip Gloss ships with a table rendering sub-package.
|
||||
|
||||
```go
|
||||
import "github.com/charmbracelet/lipgloss/table"
|
||||
```
|
||||
|
||||
Define some rows of data.
|
||||
|
||||
```go
|
||||
rows := [][]string{
|
||||
{"Chinese", "您好", "你好"},
|
||||
{"Japanese", "こんにちは", "やあ"},
|
||||
{"Arabic", "أهلين", "أهلا"},
|
||||
{"Russian", "Здравствуйте", "Привет"},
|
||||
{"Spanish", "Hola", "¿Qué tal?"},
|
||||
}
|
||||
```
|
||||
|
||||
Use the table package to style and render the table.
|
||||
|
||||
```go
|
||||
t := table.New().
|
||||
Border(lipgloss.NormalBorder()).
|
||||
BorderStyle(lipgloss.NewStyle().Foreground(lipgloss.Color("99"))).
|
||||
StyleFunc(func(row, col int) lipgloss.Style {
|
||||
switch {
|
||||
case row == 0:
|
||||
return HeaderStyle
|
||||
case row%2 == 0:
|
||||
return EvenRowStyle
|
||||
default:
|
||||
return OddRowStyle
|
||||
}
|
||||
}).
|
||||
Headers("LANGUAGE", "FORMAL", "INFORMAL").
|
||||
Rows(rows...)
|
||||
|
||||
// You can also add tables row-by-row
|
||||
t.Row("English", "You look absolutely fabulous.", "How's it going?")
|
||||
```
|
||||
|
||||
Print the table.
|
||||
|
||||
```go
|
||||
fmt.Println(t)
|
||||
```
|
||||
|
||||

|
||||
|
||||
For more on tables see [the docs](https://pkg.go.dev/github.com/charmbracelet/lipgloss?tab=doc) and [examples](https://github.com/charmbracelet/lipgloss/tree/master/examples/table).
|
||||
|
||||
## Rendering Trees
|
||||
|
||||
Lip Gloss ships with a tree rendering sub-package.
|
||||
|
||||
```go
|
||||
import "github.com/charmbracelet/lipgloss/tree"
|
||||
```
|
||||
|
||||
Define a new tree.
|
||||
|
||||
```go
|
||||
t := tree.New("root", "child 1", "child 2", tree.New("child 3", "child 3.1"))
|
||||
```
|
||||
|
||||
Print the tree.
|
||||
|
||||
```go
|
||||
fmt.Println(t)
|
||||
|
||||
// root
|
||||
// ├── child 1
|
||||
// ├── child 2
|
||||
// └── child 3
|
||||
// └── child 3.1
|
||||
```
|
||||
|
||||
### Customization
|
||||
|
||||
Trees can be customized via their enumeration function as well as using
|
||||
`lipgloss.Style`s.
|
||||
|
||||
```go
|
||||
style1 := lipgloss.NewStyle().Foreground(lipgloss.Color("99")).MarginRight(1)
|
||||
style2 := lipgloss.NewStyle().Foreground(lipgloss.Color("10")).MarginRight(1)
|
||||
|
||||
t := tree.New().
|
||||
Items(
|
||||
"Glossier",
|
||||
"Claire’s Boutique",
|
||||
tree.New().
|
||||
Root("Nyx").
|
||||
Items("Qux", "Quux").
|
||||
EnumeratorStyle(style2),
|
||||
"Mac",
|
||||
"Milk",
|
||||
).
|
||||
EnumeratorStyle(style1)
|
||||
```
|
||||
|
||||
Print the tree:
|
||||
|
||||
<p align="center">
|
||||
<img
|
||||
width="600"
|
||||
alt="Tree example"
|
||||
src="https://github.com/charmbracelet/lipgloss/assets/245435/5a875269-f6d6-43fa-9916-5d8360e66964"
|
||||
/>
|
||||
</p>
|
||||
You may also define custom enumerator implementations:
|
||||
|
||||
```go
|
||||
t := tree.New().
|
||||
Items(
|
||||
"Glossier",
|
||||
"Claire’s Boutique",
|
||||
tree.New().
|
||||
Root("Nyx").
|
||||
Items(
|
||||
"Qux",
|
||||
"Quux",
|
||||
),
|
||||
"Mac",
|
||||
"Milk",
|
||||
).
|
||||
Enumerator(func(tree.Data, int) (string, string) {
|
||||
return "->", "->"
|
||||
})
|
||||
```
|
||||
|
||||
Print the tree.
|
||||
|
||||
<p align="center">
|
||||
<img
|
||||
width="600"
|
||||
alt="Tree example"
|
||||
src="https://github.com/charmbracelet/lipgloss/assets/245435/811e8b39-124f-48bb-b3dd-e015a65b1065"
|
||||
/>
|
||||
</p>
|
||||
|
||||
### Building
|
||||
|
||||
If you need, you can also build trees incrementally:
|
||||
|
||||
```go
|
||||
t := tree.New("")
|
||||
|
||||
for i := 0; i < repeat; i++ {
|
||||
t.Item("Lip Gloss")
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## Rendering Lists
|
||||
|
||||
Lip Gloss ships with a list rendering sub-package.
|
||||
Implementation-wise, lists are still trees.
|
||||
The `list` package provides many common `Enumerator` implementations, as well as
|
||||
some syntactic sugar.
|
||||
|
||||
```go
|
||||
import "github.com/charmbracelet/lipgloss/list"
|
||||
```
|
||||
|
||||
Define a new list.
|
||||
|
||||
```go
|
||||
l := list.New("A", "B", "C")
|
||||
```
|
||||
|
||||
Print the list.
|
||||
|
||||
```go
|
||||
fmt.Println(l)
|
||||
|
||||
// • A
|
||||
// • B
|
||||
// • C
|
||||
```
|
||||
|
||||
|
||||
### Customization
|
||||
|
||||
Lists can be customized via their enumeration function as well as using
|
||||
`lipgloss.Style`s.
|
||||
|
||||
```go
|
||||
enumeratorStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("99")).MarginRight(1)
|
||||
itemStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("10")).MarginRight(1)
|
||||
|
||||
l := list.New(
|
||||
"Glossier",
|
||||
"Claire’s Boutique",
|
||||
"Nyx",
|
||||
"Mac",
|
||||
"Milk",
|
||||
).
|
||||
Enumerator(list.Roman).
|
||||
EnumeratorStyle(enumeratorStyle).
|
||||
ItemStyle(itemStyle)
|
||||
```
|
||||
|
||||
Print the list.
|
||||
|
||||
<p align="center">
|
||||
<img
|
||||
width="600"
|
||||
alt="List example"
|
||||
src="https://github.com/charmbracelet/lipgloss/assets/245435/8f5e5e0b-7bf9-4e3b-a8ba-3af10825320e"
|
||||
/>
|
||||
</p>
|
||||
In addition to the predefined enumerators (`Arabic`, `Alphabet`, `Roman`, `Bullet`, `Tree`),
|
||||
you may also define your own custom enumerator:
|
||||
|
||||
```go
|
||||
var DuckDuckGooseEnumerator Enumerator = func(l *List, i int) string {
|
||||
if l.At(i) == "Goose" {
|
||||
return "Honk →"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
```
|
||||
|
||||
Use it in a list:
|
||||
|
||||
```go
|
||||
l := list.New("Duck", "Duck", "Duck", "Duck", "Goose", "Duck", "Duck")
|
||||
l.Enumerator(DuckDuckGooseEnumerator)
|
||||
```
|
||||
|
||||
Print the list:
|
||||
|
||||
<p align="center">
|
||||
<img
|
||||
width="600"
|
||||
alt="image"
|
||||
src="https://github.com/charmbracelet/lipgloss/assets/245435/44e37a5b-5124-4f49-a332-1756a355002e"
|
||||
/>
|
||||
</p>
|
||||
|
||||
### Building
|
||||
|
||||
If you need, you can also build trees incrementally:
|
||||
|
||||
```go
|
||||
l := list.New()
|
||||
|
||||
for i := 0; i < repeat; i++ {
|
||||
l.Item("Lip Gloss")
|
||||
}
|
||||
```
|
||||
---
|
||||
|
||||
## FAQ
|
||||
|
||||
<details>
|
||||
<summary>
|
||||
Why are things misaligning? Why are borders at the wrong widths?
|
||||
</summary>
|
||||
<p>This is most likely due to your locale and encoding, particularly with
|
||||
regard to Chinese, Japanese, and Korean (for example, <code>zh_CN.UTF-8</code>
|
||||
or <code>ja_JP.UTF-8</code>). The most direct way to fix this is to set
|
||||
<code>RUNEWIDTH_EASTASIAN=0</code> in your environment.</p>
|
||||
|
||||
<p>For details see <a href="https://github.com/charmbracelet/lipgloss/issues/40">https://github.com/charmbracelet/lipgloss/issues/40.</a></p>
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>
|
||||
Why isn't Lip Gloss displaying colors?
|
||||
</summary>
|
||||
<p>Lip Gloss automatically degrades colors to the best available option in the
|
||||
given terminal, and if output's not a TTY it will remove color output entirely.
|
||||
This is common when running tests, CI, or when piping output elsewhere.</p>
|
||||
|
||||
<p>If necessary, you can force a color profile in your tests with
|
||||
<a href="https://pkg.go.dev/github.com/charmbracelet/lipgloss#SetColorProfile"><code>SetColorProfile</code></a>.</p>
|
||||
|
||||
```go
|
||||
import (
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
"github.com/muesli/termenv"
|
||||
)
|
||||
|
||||
lipgloss.SetColorProfile(termenv.TrueColor)
|
||||
```
|
||||
|
||||
_Note:_ this option limits the flexibility of your application and can cause
|
||||
ANSI escape codes to be output in cases where that might not be desired. Take
|
||||
careful note of your use case and environment before choosing to force a color
|
||||
profile.
|
||||
|
||||
</details>
|
||||
|
||||
## What about [Bubble Tea][tea]?
|
||||
|
||||
Lip Gloss doesn’t replace Bubble Tea. Rather, it is an excellent Bubble Tea
|
||||
companion. It was designed to make assembling terminal user interface views as
|
||||
simple and fun as possible so that you can focus on building your application
|
||||
instead of concerning yourself with low-level layout details.
|
||||
|
||||
In simple terms, you can use Lip Gloss to help build your Bubble Tea views.
|
||||
|
||||
[tea]: https://github.com/charmbracelet/tea
|
||||
|
||||
## Under the Hood
|
||||
|
||||
Lip Gloss is built on the excellent [Termenv][termenv] and [Reflow][reflow]
|
||||
libraries which deal with color and ANSI-aware text operations, respectively.
|
||||
For many use cases Termenv and Reflow will be sufficient for your needs.
|
||||
|
||||
[termenv]: https://github.com/muesli/termenv
|
||||
[reflow]: https://github.com/muesli/reflow
|
||||
|
||||
## Rendering Markdown
|
||||
|
||||
For a more document-centric rendering solution with support for things like
|
||||
lists, tables, and syntax-highlighted code have a look at [Glamour][glamour],
|
||||
the stylesheet-based Markdown renderer.
|
||||
|
||||
[glamour]: https://github.com/charmbracelet/glamour
|
||||
|
||||
## Feedback
|
||||
|
||||
We’d love to hear your thoughts on this project. Feel free to drop us a note!
|
||||
|
||||
- [Twitter](https://twitter.com/charmcli)
|
||||
- [The Fediverse](https://mastodon.social/@charmcli)
|
||||
- [Discord](https://charm.sh/chat)
|
||||
|
||||
## License
|
||||
|
||||
[MIT](https://github.com/charmbracelet/lipgloss/raw/master/LICENSE)
|
||||
|
||||
---
|
||||
|
||||
Part of [Charm](https://charm.sh).
|
||||
|
||||
<a href="https://charm.sh/"><img alt="The Charm logo" src="https://stuff.charm.sh/charm-badge.jpg" width="400"></a>
|
||||
|
||||
Charm热爱开源 • Charm loves open source
|
||||
|
||||
[docs]: https://pkg.go.dev/github.com/charmbracelet/lipgloss?tab=doc
|
||||
[wish]: https://github.com/charmbracelet/wish
|
||||
[ssh-example]: examples/ssh
|
83
vendor/github.com/charmbracelet/lipgloss/align.go
generated
vendored
Normal file
83
vendor/github.com/charmbracelet/lipgloss/align.go
generated
vendored
Normal file
@ -0,0 +1,83 @@
|
||||
package lipgloss
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/charmbracelet/x/ansi"
|
||||
"github.com/muesli/termenv"
|
||||
)
|
||||
|
||||
// Perform text alignment. If the string is multi-lined, we also make all lines
|
||||
// the same width by padding them with spaces. If a termenv style is passed,
|
||||
// use that to style the spaces added.
|
||||
func alignTextHorizontal(str string, pos Position, width int, style *termenv.Style) string {
|
||||
lines, widestLine := getLines(str)
|
||||
var b strings.Builder
|
||||
|
||||
for i, l := range lines {
|
||||
lineWidth := ansi.StringWidth(l)
|
||||
|
||||
shortAmount := widestLine - lineWidth // difference from the widest line
|
||||
shortAmount += max(0, width-(shortAmount+lineWidth)) // difference from the total width, if set
|
||||
|
||||
if shortAmount > 0 {
|
||||
switch pos { //nolint:exhaustive
|
||||
case Right:
|
||||
s := strings.Repeat(" ", shortAmount)
|
||||
if style != nil {
|
||||
s = style.Styled(s)
|
||||
}
|
||||
l = s + l
|
||||
case Center:
|
||||
// Note: remainder goes on the right.
|
||||
left := shortAmount / 2 //nolint:gomnd
|
||||
right := left + shortAmount%2 //nolint:gomnd
|
||||
|
||||
leftSpaces := strings.Repeat(" ", left)
|
||||
rightSpaces := strings.Repeat(" ", right)
|
||||
|
||||
if style != nil {
|
||||
leftSpaces = style.Styled(leftSpaces)
|
||||
rightSpaces = style.Styled(rightSpaces)
|
||||
}
|
||||
l = leftSpaces + l + rightSpaces
|
||||
default: // Left
|
||||
s := strings.Repeat(" ", shortAmount)
|
||||
if style != nil {
|
||||
s = style.Styled(s)
|
||||
}
|
||||
l += s
|
||||
}
|
||||
}
|
||||
|
||||
b.WriteString(l)
|
||||
if i < len(lines)-1 {
|
||||
b.WriteRune('\n')
|
||||
}
|
||||
}
|
||||
|
||||
return b.String()
|
||||
}
|
||||
|
||||
func alignTextVertical(str string, pos Position, height int, _ *termenv.Style) string {
|
||||
strHeight := strings.Count(str, "\n") + 1
|
||||
if height < strHeight {
|
||||
return str
|
||||
}
|
||||
|
||||
switch pos {
|
||||
case Top:
|
||||
return str + strings.Repeat("\n", height-strHeight)
|
||||
case Center:
|
||||
topPadding, bottomPadding := (height-strHeight)/2, (height-strHeight)/2 //nolint:gomnd
|
||||
if strHeight+topPadding+bottomPadding > height {
|
||||
topPadding--
|
||||
} else if strHeight+topPadding+bottomPadding < height {
|
||||
bottomPadding++
|
||||
}
|
||||
return strings.Repeat("\n", topPadding) + str + strings.Repeat("\n", bottomPadding)
|
||||
case Bottom:
|
||||
return strings.Repeat("\n", height-strHeight) + str
|
||||
}
|
||||
return str
|
||||
}
|
7
vendor/github.com/charmbracelet/lipgloss/ansi_unix.go
generated
vendored
Normal file
7
vendor/github.com/charmbracelet/lipgloss/ansi_unix.go
generated
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package lipgloss
|
||||
|
||||
// enableLegacyWindowsANSI is only needed on Windows.
|
||||
func enableLegacyWindowsANSI() {}
|
22
vendor/github.com/charmbracelet/lipgloss/ansi_windows.go
generated
vendored
Normal file
22
vendor/github.com/charmbracelet/lipgloss/ansi_windows.go
generated
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package lipgloss
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/muesli/termenv"
|
||||
)
|
||||
|
||||
var enableANSI sync.Once
|
||||
|
||||
// enableANSIColors enables support for ANSI color sequences in the Windows
|
||||
// default console (cmd.exe and the PowerShell application). Note that this
|
||||
// only works with Windows 10. Also note that Windows Terminal supports colors
|
||||
// by default.
|
||||
func enableLegacyWindowsANSI() {
|
||||
enableANSI.Do(func() {
|
||||
_, _ = termenv.EnableWindowsANSIConsole()
|
||||
})
|
||||
}
|
443
vendor/github.com/charmbracelet/lipgloss/borders.go
generated
vendored
Normal file
443
vendor/github.com/charmbracelet/lipgloss/borders.go
generated
vendored
Normal file
@ -0,0 +1,443 @@
|
||||
package lipgloss
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/charmbracelet/x/ansi"
|
||||
"github.com/muesli/termenv"
|
||||
"github.com/rivo/uniseg"
|
||||
)
|
||||
|
||||
// Border contains a series of values which comprise the various parts of a
|
||||
// border.
|
||||
type Border struct {
|
||||
Top string
|
||||
Bottom string
|
||||
Left string
|
||||
Right string
|
||||
TopLeft string
|
||||
TopRight string
|
||||
BottomLeft string
|
||||
BottomRight string
|
||||
MiddleLeft string
|
||||
MiddleRight string
|
||||
Middle string
|
||||
MiddleTop string
|
||||
MiddleBottom string
|
||||
}
|
||||
|
||||
// GetTopSize returns the width of the top border. If borders contain runes of
|
||||
// varying widths, the widest rune is returned. If no border exists on the top
|
||||
// edge, 0 is returned.
|
||||
func (b Border) GetTopSize() int {
|
||||
return getBorderEdgeWidth(b.TopLeft, b.Top, b.TopRight)
|
||||
}
|
||||
|
||||
// GetRightSize returns the width of the right border. If borders contain
|
||||
// runes of varying widths, the widest rune is returned. If no border exists on
|
||||
// the right edge, 0 is returned.
|
||||
func (b Border) GetRightSize() int {
|
||||
return getBorderEdgeWidth(b.TopRight, b.Right, b.BottomRight)
|
||||
}
|
||||
|
||||
// GetBottomSize returns the width of the bottom border. If borders contain
|
||||
// runes of varying widths, the widest rune is returned. If no border exists on
|
||||
// the bottom edge, 0 is returned.
|
||||
func (b Border) GetBottomSize() int {
|
||||
return getBorderEdgeWidth(b.BottomLeft, b.Bottom, b.BottomRight)
|
||||
}
|
||||
|
||||
// GetLeftSize returns the width of the left border. If borders contain runes
|
||||
// of varying widths, the widest rune is returned. If no border exists on the
|
||||
// left edge, 0 is returned.
|
||||
func (b Border) GetLeftSize() int {
|
||||
return getBorderEdgeWidth(b.TopLeft, b.Left, b.BottomLeft)
|
||||
}
|
||||
|
||||
func getBorderEdgeWidth(borderParts ...string) (maxWidth int) {
|
||||
for _, piece := range borderParts {
|
||||
w := maxRuneWidth(piece)
|
||||
if w > maxWidth {
|
||||
maxWidth = w
|
||||
}
|
||||
}
|
||||
return maxWidth
|
||||
}
|
||||
|
||||
var (
|
||||
noBorder = Border{}
|
||||
|
||||
normalBorder = Border{
|
||||
Top: "─",
|
||||
Bottom: "─",
|
||||
Left: "│",
|
||||
Right: "│",
|
||||
TopLeft: "┌",
|
||||
TopRight: "┐",
|
||||
BottomLeft: "└",
|
||||
BottomRight: "┘",
|
||||
MiddleLeft: "├",
|
||||
MiddleRight: "┤",
|
||||
Middle: "┼",
|
||||
MiddleTop: "┬",
|
||||
MiddleBottom: "┴",
|
||||
}
|
||||
|
||||
roundedBorder = Border{
|
||||
Top: "─",
|
||||
Bottom: "─",
|
||||
Left: "│",
|
||||
Right: "│",
|
||||
TopLeft: "╭",
|
||||
TopRight: "╮",
|
||||
BottomLeft: "╰",
|
||||
BottomRight: "╯",
|
||||
MiddleLeft: "├",
|
||||
MiddleRight: "┤",
|
||||
Middle: "┼",
|
||||
MiddleTop: "┬",
|
||||
MiddleBottom: "┴",
|
||||
}
|
||||
|
||||
blockBorder = Border{
|
||||
Top: "█",
|
||||
Bottom: "█",
|
||||
Left: "█",
|
||||
Right: "█",
|
||||
TopLeft: "█",
|
||||
TopRight: "█",
|
||||
BottomLeft: "█",
|
||||
BottomRight: "█",
|
||||
}
|
||||
|
||||
outerHalfBlockBorder = Border{
|
||||
Top: "▀",
|
||||
Bottom: "▄",
|
||||
Left: "▌",
|
||||
Right: "▐",
|
||||
TopLeft: "▛",
|
||||
TopRight: "▜",
|
||||
BottomLeft: "▙",
|
||||
BottomRight: "▟",
|
||||
}
|
||||
|
||||
innerHalfBlockBorder = Border{
|
||||
Top: "▄",
|
||||
Bottom: "▀",
|
||||
Left: "▐",
|
||||
Right: "▌",
|
||||
TopLeft: "▗",
|
||||
TopRight: "▖",
|
||||
BottomLeft: "▝",
|
||||
BottomRight: "▘",
|
||||
}
|
||||
|
||||
thickBorder = Border{
|
||||
Top: "━",
|
||||
Bottom: "━",
|
||||
Left: "┃",
|
||||
Right: "┃",
|
||||
TopLeft: "┏",
|
||||
TopRight: "┓",
|
||||
BottomLeft: "┗",
|
||||
BottomRight: "┛",
|
||||
MiddleLeft: "┣",
|
||||
MiddleRight: "┫",
|
||||
Middle: "╋",
|
||||
MiddleTop: "┳",
|
||||
MiddleBottom: "┻",
|
||||
}
|
||||
|
||||
doubleBorder = Border{
|
||||
Top: "═",
|
||||
Bottom: "═",
|
||||
Left: "║",
|
||||
Right: "║",
|
||||
TopLeft: "╔",
|
||||
TopRight: "╗",
|
||||
BottomLeft: "╚",
|
||||
BottomRight: "╝",
|
||||
MiddleLeft: "╠",
|
||||
MiddleRight: "╣",
|
||||
Middle: "╬",
|
||||
MiddleTop: "╦",
|
||||
MiddleBottom: "╩",
|
||||
}
|
||||
|
||||
hiddenBorder = Border{
|
||||
Top: " ",
|
||||
Bottom: " ",
|
||||
Left: " ",
|
||||
Right: " ",
|
||||
TopLeft: " ",
|
||||
TopRight: " ",
|
||||
BottomLeft: " ",
|
||||
BottomRight: " ",
|
||||
MiddleLeft: " ",
|
||||
MiddleRight: " ",
|
||||
Middle: " ",
|
||||
MiddleTop: " ",
|
||||
MiddleBottom: " ",
|
||||
}
|
||||
)
|
||||
|
||||
// NormalBorder returns a standard-type border with a normal weight and 90
|
||||
// degree corners.
|
||||
func NormalBorder() Border {
|
||||
return normalBorder
|
||||
}
|
||||
|
||||
// RoundedBorder returns a border with rounded corners.
|
||||
func RoundedBorder() Border {
|
||||
return roundedBorder
|
||||
}
|
||||
|
||||
// BlockBorder returns a border that takes the whole block.
|
||||
func BlockBorder() Border {
|
||||
return blockBorder
|
||||
}
|
||||
|
||||
// OuterHalfBlockBorder returns a half-block border that sits outside the frame.
|
||||
func OuterHalfBlockBorder() Border {
|
||||
return outerHalfBlockBorder
|
||||
}
|
||||
|
||||
// InnerHalfBlockBorder returns a half-block border that sits inside the frame.
|
||||
func InnerHalfBlockBorder() Border {
|
||||
return innerHalfBlockBorder
|
||||
}
|
||||
|
||||
// ThickBorder returns a border that's thicker than the one returned by
|
||||
// NormalBorder.
|
||||
func ThickBorder() Border {
|
||||
return thickBorder
|
||||
}
|
||||
|
||||
// DoubleBorder returns a border comprised of two thin strokes.
|
||||
func DoubleBorder() Border {
|
||||
return doubleBorder
|
||||
}
|
||||
|
||||
// HiddenBorder returns a border that renders as a series of single-cell
|
||||
// spaces. It's useful for cases when you want to remove a standard border but
|
||||
// maintain layout positioning. This said, you can still apply a background
|
||||
// color to a hidden border.
|
||||
func HiddenBorder() Border {
|
||||
return hiddenBorder
|
||||
}
|
||||
|
||||
func (s Style) applyBorder(str string) string {
|
||||
var (
|
||||
topSet = s.isSet(borderTopKey)
|
||||
rightSet = s.isSet(borderRightKey)
|
||||
bottomSet = s.isSet(borderBottomKey)
|
||||
leftSet = s.isSet(borderLeftKey)
|
||||
|
||||
border = s.getBorderStyle()
|
||||
hasTop = s.getAsBool(borderTopKey, false)
|
||||
hasRight = s.getAsBool(borderRightKey, false)
|
||||
hasBottom = s.getAsBool(borderBottomKey, false)
|
||||
hasLeft = s.getAsBool(borderLeftKey, false)
|
||||
|
||||
topFG = s.getAsColor(borderTopForegroundKey)
|
||||
rightFG = s.getAsColor(borderRightForegroundKey)
|
||||
bottomFG = s.getAsColor(borderBottomForegroundKey)
|
||||
leftFG = s.getAsColor(borderLeftForegroundKey)
|
||||
|
||||
topBG = s.getAsColor(borderTopBackgroundKey)
|
||||
rightBG = s.getAsColor(borderRightBackgroundKey)
|
||||
bottomBG = s.getAsColor(borderBottomBackgroundKey)
|
||||
leftBG = s.getAsColor(borderLeftBackgroundKey)
|
||||
)
|
||||
|
||||
// If a border is set and no sides have been specifically turned on or off
|
||||
// render borders on all sides.
|
||||
if border != noBorder && !(topSet || rightSet || bottomSet || leftSet) {
|
||||
hasTop = true
|
||||
hasRight = true
|
||||
hasBottom = true
|
||||
hasLeft = true
|
||||
}
|
||||
|
||||
// If no border is set or all borders are been disabled, abort.
|
||||
if border == noBorder || (!hasTop && !hasRight && !hasBottom && !hasLeft) {
|
||||
return str
|
||||
}
|
||||
|
||||
lines, width := getLines(str)
|
||||
|
||||
if hasLeft {
|
||||
if border.Left == "" {
|
||||
border.Left = " "
|
||||
}
|
||||
width += maxRuneWidth(border.Left)
|
||||
}
|
||||
|
||||
if hasRight && border.Right == "" {
|
||||
border.Right = " "
|
||||
}
|
||||
|
||||
// If corners should be rendered but are set with the empty string, fill them
|
||||
// with a single space.
|
||||
if hasTop && hasLeft && border.TopLeft == "" {
|
||||
border.TopLeft = " "
|
||||
}
|
||||
if hasTop && hasRight && border.TopRight == "" {
|
||||
border.TopRight = " "
|
||||
}
|
||||
if hasBottom && hasLeft && border.BottomLeft == "" {
|
||||
border.BottomLeft = " "
|
||||
}
|
||||
if hasBottom && hasRight && border.BottomRight == "" {
|
||||
border.BottomRight = " "
|
||||
}
|
||||
|
||||
// Figure out which corners we should actually be using based on which
|
||||
// sides are set to show.
|
||||
if hasTop {
|
||||
switch {
|
||||
case !hasLeft && !hasRight:
|
||||
border.TopLeft = ""
|
||||
border.TopRight = ""
|
||||
case !hasLeft:
|
||||
border.TopLeft = ""
|
||||
case !hasRight:
|
||||
border.TopRight = ""
|
||||
}
|
||||
}
|
||||
if hasBottom {
|
||||
switch {
|
||||
case !hasLeft && !hasRight:
|
||||
border.BottomLeft = ""
|
||||
border.BottomRight = ""
|
||||
case !hasLeft:
|
||||
border.BottomLeft = ""
|
||||
case !hasRight:
|
||||
border.BottomRight = ""
|
||||
}
|
||||
}
|
||||
|
||||
// For now, limit corners to one rune.
|
||||
border.TopLeft = getFirstRuneAsString(border.TopLeft)
|
||||
border.TopRight = getFirstRuneAsString(border.TopRight)
|
||||
border.BottomRight = getFirstRuneAsString(border.BottomRight)
|
||||
border.BottomLeft = getFirstRuneAsString(border.BottomLeft)
|
||||
|
||||
var out strings.Builder
|
||||
|
||||
// Render top
|
||||
if hasTop {
|
||||
top := renderHorizontalEdge(border.TopLeft, border.Top, border.TopRight, width)
|
||||
top = s.styleBorder(top, topFG, topBG)
|
||||
out.WriteString(top)
|
||||
out.WriteRune('\n')
|
||||
}
|
||||
|
||||
leftRunes := []rune(border.Left)
|
||||
leftIndex := 0
|
||||
|
||||
rightRunes := []rune(border.Right)
|
||||
rightIndex := 0
|
||||
|
||||
// Render sides
|
||||
for i, l := range lines {
|
||||
if hasLeft {
|
||||
r := string(leftRunes[leftIndex])
|
||||
leftIndex++
|
||||
if leftIndex >= len(leftRunes) {
|
||||
leftIndex = 0
|
||||
}
|
||||
out.WriteString(s.styleBorder(r, leftFG, leftBG))
|
||||
}
|
||||
out.WriteString(l)
|
||||
if hasRight {
|
||||
r := string(rightRunes[rightIndex])
|
||||
rightIndex++
|
||||
if rightIndex >= len(rightRunes) {
|
||||
rightIndex = 0
|
||||
}
|
||||
out.WriteString(s.styleBorder(r, rightFG, rightBG))
|
||||
}
|
||||
if i < len(lines)-1 {
|
||||
out.WriteRune('\n')
|
||||
}
|
||||
}
|
||||
|
||||
// Render bottom
|
||||
if hasBottom {
|
||||
bottom := renderHorizontalEdge(border.BottomLeft, border.Bottom, border.BottomRight, width)
|
||||
bottom = s.styleBorder(bottom, bottomFG, bottomBG)
|
||||
out.WriteRune('\n')
|
||||
out.WriteString(bottom)
|
||||
}
|
||||
|
||||
return out.String()
|
||||
}
|
||||
|
||||
// Render the horizontal (top or bottom) portion of a border.
|
||||
func renderHorizontalEdge(left, middle, right string, width int) string {
|
||||
if middle == "" {
|
||||
middle = " "
|
||||
}
|
||||
|
||||
leftWidth := ansi.StringWidth(left)
|
||||
rightWidth := ansi.StringWidth(right)
|
||||
|
||||
runes := []rune(middle)
|
||||
j := 0
|
||||
|
||||
out := strings.Builder{}
|
||||
out.WriteString(left)
|
||||
for i := leftWidth + rightWidth; i < width+rightWidth; {
|
||||
out.WriteRune(runes[j])
|
||||
j++
|
||||
if j >= len(runes) {
|
||||
j = 0
|
||||
}
|
||||
i += ansi.StringWidth(string(runes[j]))
|
||||
}
|
||||
out.WriteString(right)
|
||||
|
||||
return out.String()
|
||||
}
|
||||
|
||||
// Apply foreground and background styling to a border.
|
||||
func (s Style) styleBorder(border string, fg, bg TerminalColor) string {
|
||||
if fg == noColor && bg == noColor {
|
||||
return border
|
||||
}
|
||||
|
||||
style := termenv.Style{}
|
||||
|
||||
if fg != noColor {
|
||||
style = style.Foreground(fg.color(s.r))
|
||||
}
|
||||
if bg != noColor {
|
||||
style = style.Background(bg.color(s.r))
|
||||
}
|
||||
|
||||
return style.Styled(border)
|
||||
}
|
||||
|
||||
func maxRuneWidth(str string) int {
|
||||
var width int
|
||||
|
||||
state := -1
|
||||
for len(str) > 0 {
|
||||
var w int
|
||||
_, str, w, state = uniseg.FirstGraphemeClusterInString(str, state)
|
||||
if w > width {
|
||||
width = w
|
||||
}
|
||||
}
|
||||
|
||||
return width
|
||||
}
|
||||
|
||||
func getFirstRuneAsString(str string) string {
|
||||
if str == "" {
|
||||
return str
|
||||
}
|
||||
r := []rune(str)
|
||||
return string(r[0])
|
||||
}
|
172
vendor/github.com/charmbracelet/lipgloss/color.go
generated
vendored
Normal file
172
vendor/github.com/charmbracelet/lipgloss/color.go
generated
vendored
Normal file
@ -0,0 +1,172 @@
|
||||
package lipgloss
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/muesli/termenv"
|
||||
)
|
||||
|
||||
// TerminalColor is a color intended to be rendered in the terminal.
|
||||
type TerminalColor interface {
|
||||
color(*Renderer) termenv.Color
|
||||
RGBA() (r, g, b, a uint32)
|
||||
}
|
||||
|
||||
var noColor = NoColor{}
|
||||
|
||||
// NoColor is used to specify the absence of color styling. When this is active
|
||||
// foreground colors will be rendered with the terminal's default text color,
|
||||
// and background colors will not be drawn at all.
|
||||
//
|
||||
// Example usage:
|
||||
//
|
||||
// var style = someStyle.Background(lipgloss.NoColor{})
|
||||
type NoColor struct{}
|
||||
|
||||
func (NoColor) color(*Renderer) termenv.Color {
|
||||
return termenv.NoColor{}
|
||||
}
|
||||
|
||||
// RGBA returns the RGBA value of this color. Because we have to return
|
||||
// something, despite this color being the absence of color, we're returning
|
||||
// black with 100% opacity.
|
||||
//
|
||||
// Red: 0x0, Green: 0x0, Blue: 0x0, Alpha: 0xFFFF.
|
||||
//
|
||||
// Deprecated.
|
||||
func (n NoColor) RGBA() (r, g, b, a uint32) {
|
||||
return 0x0, 0x0, 0x0, 0xFFFF //nolint:gomnd
|
||||
}
|
||||
|
||||
// Color specifies a color by hex or ANSI value. For example:
|
||||
//
|
||||
// ansiColor := lipgloss.Color("21")
|
||||
// hexColor := lipgloss.Color("#0000ff")
|
||||
type Color string
|
||||
|
||||
func (c Color) color(r *Renderer) termenv.Color {
|
||||
return r.ColorProfile().Color(string(c))
|
||||
}
|
||||
|
||||
// RGBA returns the RGBA value of this color. This satisfies the Go Color
|
||||
// interface. Note that on error we return black with 100% opacity, or:
|
||||
//
|
||||
// Red: 0x0, Green: 0x0, Blue: 0x0, Alpha: 0xFFFF.
|
||||
//
|
||||
// Deprecated.
|
||||
func (c Color) RGBA() (r, g, b, a uint32) {
|
||||
return termenv.ConvertToRGB(c.color(renderer)).RGBA()
|
||||
}
|
||||
|
||||
// ANSIColor is a color specified by an ANSI color value. It's merely syntactic
|
||||
// sugar for the more general Color function. Invalid colors will render as
|
||||
// black.
|
||||
//
|
||||
// Example usage:
|
||||
//
|
||||
// // These two statements are equivalent.
|
||||
// colorA := lipgloss.ANSIColor(21)
|
||||
// colorB := lipgloss.Color("21")
|
||||
type ANSIColor uint
|
||||
|
||||
func (ac ANSIColor) color(r *Renderer) termenv.Color {
|
||||
return Color(strconv.FormatUint(uint64(ac), 10)).color(r)
|
||||
}
|
||||
|
||||
// RGBA returns the RGBA value of this color. This satisfies the Go Color
|
||||
// interface. Note that on error we return black with 100% opacity, or:
|
||||
//
|
||||
// Red: 0x0, Green: 0x0, Blue: 0x0, Alpha: 0xFFFF.
|
||||
//
|
||||
// Deprecated.
|
||||
func (ac ANSIColor) RGBA() (r, g, b, a uint32) {
|
||||
cf := Color(strconv.FormatUint(uint64(ac), 10))
|
||||
return cf.RGBA()
|
||||
}
|
||||
|
||||
// AdaptiveColor provides color options for light and dark backgrounds. The
|
||||
// appropriate color will be returned at runtime based on the darkness of the
|
||||
// terminal background color.
|
||||
//
|
||||
// Example usage:
|
||||
//
|
||||
// color := lipgloss.AdaptiveColor{Light: "#0000ff", Dark: "#000099"}
|
||||
type AdaptiveColor struct {
|
||||
Light string
|
||||
Dark string
|
||||
}
|
||||
|
||||
func (ac AdaptiveColor) color(r *Renderer) termenv.Color {
|
||||
if r.HasDarkBackground() {
|
||||
return Color(ac.Dark).color(r)
|
||||
}
|
||||
return Color(ac.Light).color(r)
|
||||
}
|
||||
|
||||
// RGBA returns the RGBA value of this color. This satisfies the Go Color
|
||||
// interface. Note that on error we return black with 100% opacity, or:
|
||||
//
|
||||
// Red: 0x0, Green: 0x0, Blue: 0x0, Alpha: 0xFFFF.
|
||||
//
|
||||
// Deprecated.
|
||||
func (ac AdaptiveColor) RGBA() (r, g, b, a uint32) {
|
||||
return termenv.ConvertToRGB(ac.color(renderer)).RGBA()
|
||||
}
|
||||
|
||||
// CompleteColor specifies exact values for truecolor, ANSI256, and ANSI color
|
||||
// profiles. Automatic color degradation will not be performed.
|
||||
type CompleteColor struct {
|
||||
TrueColor string
|
||||
ANSI256 string
|
||||
ANSI string
|
||||
}
|
||||
|
||||
func (c CompleteColor) color(r *Renderer) termenv.Color {
|
||||
p := r.ColorProfile()
|
||||
switch p { //nolint:exhaustive
|
||||
case termenv.TrueColor:
|
||||
return p.Color(c.TrueColor)
|
||||
case termenv.ANSI256:
|
||||
return p.Color(c.ANSI256)
|
||||
case termenv.ANSI:
|
||||
return p.Color(c.ANSI)
|
||||
default:
|
||||
return termenv.NoColor{}
|
||||
}
|
||||
}
|
||||
|
||||
// RGBA returns the RGBA value of this color. This satisfies the Go Color
|
||||
// interface. Note that on error we return black with 100% opacity, or:
|
||||
//
|
||||
// Red: 0x0, Green: 0x0, Blue: 0x0, Alpha: 0xFFFF.
|
||||
// CompleteAdaptiveColor specifies exact values for truecolor, ANSI256, and ANSI color
|
||||
//
|
||||
// Deprecated.
|
||||
func (c CompleteColor) RGBA() (r, g, b, a uint32) {
|
||||
return termenv.ConvertToRGB(c.color(renderer)).RGBA()
|
||||
}
|
||||
|
||||
// CompleteAdaptiveColor specifies exact values for truecolor, ANSI256, and ANSI color
|
||||
// profiles, with separate options for light and dark backgrounds. Automatic
|
||||
// color degradation will not be performed.
|
||||
type CompleteAdaptiveColor struct {
|
||||
Light CompleteColor
|
||||
Dark CompleteColor
|
||||
}
|
||||
|
||||
func (cac CompleteAdaptiveColor) color(r *Renderer) termenv.Color {
|
||||
if r.HasDarkBackground() {
|
||||
return cac.Dark.color(r)
|
||||
}
|
||||
return cac.Light.color(r)
|
||||
}
|
||||
|
||||
// RGBA returns the RGBA value of this color. This satisfies the Go Color
|
||||
// interface. Note that on error we return black with 100% opacity, or:
|
||||
//
|
||||
// Red: 0x0, Green: 0x0, Blue: 0x0, Alpha: 0xFFFF.
|
||||
//
|
||||
// Deprecated.
|
||||
func (cac CompleteAdaptiveColor) RGBA() (r, g, b, a uint32) {
|
||||
return termenv.ConvertToRGB(cac.color(renderer)).RGBA()
|
||||
}
|
542
vendor/github.com/charmbracelet/lipgloss/get.go
generated
vendored
Normal file
542
vendor/github.com/charmbracelet/lipgloss/get.go
generated
vendored
Normal file
@ -0,0 +1,542 @@
|
||||
package lipgloss
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/charmbracelet/x/ansi"
|
||||
)
|
||||
|
||||
// GetBold returns the style's bold value. If no value is set false is returned.
|
||||
func (s Style) GetBold() bool {
|
||||
return s.getAsBool(boldKey, false)
|
||||
}
|
||||
|
||||
// GetItalic returns the style's italic value. If no value is set false is
|
||||
// returned.
|
||||
func (s Style) GetItalic() bool {
|
||||
return s.getAsBool(italicKey, false)
|
||||
}
|
||||
|
||||
// GetUnderline returns the style's underline value. If no value is set false is
|
||||
// returned.
|
||||
func (s Style) GetUnderline() bool {
|
||||
return s.getAsBool(underlineKey, false)
|
||||
}
|
||||
|
||||
// GetStrikethrough returns the style's strikethrough value. If no value is set false
|
||||
// is returned.
|
||||
func (s Style) GetStrikethrough() bool {
|
||||
return s.getAsBool(strikethroughKey, false)
|
||||
}
|
||||
|
||||
// GetReverse returns the style's reverse value. If no value is set false is
|
||||
// returned.
|
||||
func (s Style) GetReverse() bool {
|
||||
return s.getAsBool(reverseKey, false)
|
||||
}
|
||||
|
||||
// GetBlink returns the style's blink value. If no value is set false is
|
||||
// returned.
|
||||
func (s Style) GetBlink() bool {
|
||||
return s.getAsBool(blinkKey, false)
|
||||
}
|
||||
|
||||
// GetFaint returns the style's faint value. If no value is set false is
|
||||
// returned.
|
||||
func (s Style) GetFaint() bool {
|
||||
return s.getAsBool(faintKey, false)
|
||||
}
|
||||
|
||||
// GetForeground returns the style's foreground color. If no value is set
|
||||
// NoColor{} is returned.
|
||||
func (s Style) GetForeground() TerminalColor {
|
||||
return s.getAsColor(foregroundKey)
|
||||
}
|
||||
|
||||
// GetBackground returns the style's background color. If no value is set
|
||||
// NoColor{} is returned.
|
||||
func (s Style) GetBackground() TerminalColor {
|
||||
return s.getAsColor(backgroundKey)
|
||||
}
|
||||
|
||||
// GetWidth returns the style's width setting. If no width is set 0 is
|
||||
// returned.
|
||||
func (s Style) GetWidth() int {
|
||||
return s.getAsInt(widthKey)
|
||||
}
|
||||
|
||||
// GetHeight returns the style's height setting. If no height is set 0 is
|
||||
// returned.
|
||||
func (s Style) GetHeight() int {
|
||||
return s.getAsInt(heightKey)
|
||||
}
|
||||
|
||||
// GetAlign returns the style's implicit horizontal alignment setting.
|
||||
// If no alignment is set Position.Left is returned.
|
||||
func (s Style) GetAlign() Position {
|
||||
v := s.getAsPosition(alignHorizontalKey)
|
||||
if v == Position(0) {
|
||||
return Left
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// GetAlignHorizontal returns the style's implicit horizontal alignment setting.
|
||||
// If no alignment is set Position.Left is returned.
|
||||
func (s Style) GetAlignHorizontal() Position {
|
||||
v := s.getAsPosition(alignHorizontalKey)
|
||||
if v == Position(0) {
|
||||
return Left
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// GetAlignVertical returns the style's implicit vertical alignment setting.
|
||||
// If no alignment is set Position.Top is returned.
|
||||
func (s Style) GetAlignVertical() Position {
|
||||
v := s.getAsPosition(alignVerticalKey)
|
||||
if v == Position(0) {
|
||||
return Top
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// GetPadding returns the style's top, right, bottom, and left padding values,
|
||||
// in that order. 0 is returned for unset values.
|
||||
func (s Style) GetPadding() (top, right, bottom, left int) {
|
||||
return s.getAsInt(paddingTopKey),
|
||||
s.getAsInt(paddingRightKey),
|
||||
s.getAsInt(paddingBottomKey),
|
||||
s.getAsInt(paddingLeftKey)
|
||||
}
|
||||
|
||||
// GetPaddingTop returns the style's top padding. If no value is set 0 is
|
||||
// returned.
|
||||
func (s Style) GetPaddingTop() int {
|
||||
return s.getAsInt(paddingTopKey)
|
||||
}
|
||||
|
||||
// GetPaddingRight returns the style's right padding. If no value is set 0 is
|
||||
// returned.
|
||||
func (s Style) GetPaddingRight() int {
|
||||
return s.getAsInt(paddingRightKey)
|
||||
}
|
||||
|
||||
// GetPaddingBottom returns the style's bottom padding. If no value is set 0 is
|
||||
// returned.
|
||||
func (s Style) GetPaddingBottom() int {
|
||||
return s.getAsInt(paddingBottomKey)
|
||||
}
|
||||
|
||||
// GetPaddingLeft returns the style's left padding. If no value is set 0 is
|
||||
// returned.
|
||||
func (s Style) GetPaddingLeft() int {
|
||||
return s.getAsInt(paddingLeftKey)
|
||||
}
|
||||
|
||||
// GetHorizontalPadding returns the style's left and right padding. Unset
|
||||
// values are measured as 0.
|
||||
func (s Style) GetHorizontalPadding() int {
|
||||
return s.getAsInt(paddingLeftKey) + s.getAsInt(paddingRightKey)
|
||||
}
|
||||
|
||||
// GetVerticalPadding returns the style's top and bottom padding. Unset values
|
||||
// are measured as 0.
|
||||
func (s Style) GetVerticalPadding() int {
|
||||
return s.getAsInt(paddingTopKey) + s.getAsInt(paddingBottomKey)
|
||||
}
|
||||
|
||||
// GetColorWhitespace returns the style's whitespace coloring setting. If no
|
||||
// value is set false is returned.
|
||||
func (s Style) GetColorWhitespace() bool {
|
||||
return s.getAsBool(colorWhitespaceKey, false)
|
||||
}
|
||||
|
||||
// GetMargin returns the style's top, right, bottom, and left margins, in that
|
||||
// order. 0 is returned for unset values.
|
||||
func (s Style) GetMargin() (top, right, bottom, left int) {
|
||||
return s.getAsInt(marginTopKey),
|
||||
s.getAsInt(marginRightKey),
|
||||
s.getAsInt(marginBottomKey),
|
||||
s.getAsInt(marginLeftKey)
|
||||
}
|
||||
|
||||
// GetMarginTop returns the style's top margin. If no value is set 0 is
|
||||
// returned.
|
||||
func (s Style) GetMarginTop() int {
|
||||
return s.getAsInt(marginTopKey)
|
||||
}
|
||||
|
||||
// GetMarginRight returns the style's right margin. If no value is set 0 is
|
||||
// returned.
|
||||
func (s Style) GetMarginRight() int {
|
||||
return s.getAsInt(marginRightKey)
|
||||
}
|
||||
|
||||
// GetMarginBottom returns the style's bottom margin. If no value is set 0 is
|
||||
// returned.
|
||||
func (s Style) GetMarginBottom() int {
|
||||
return s.getAsInt(marginBottomKey)
|
||||
}
|
||||
|
||||
// GetMarginLeft returns the style's left margin. If no value is set 0 is
|
||||
// returned.
|
||||
func (s Style) GetMarginLeft() int {
|
||||
return s.getAsInt(marginLeftKey)
|
||||
}
|
||||
|
||||
// GetHorizontalMargins returns the style's left and right margins. Unset
|
||||
// values are measured as 0.
|
||||
func (s Style) GetHorizontalMargins() int {
|
||||
return s.getAsInt(marginLeftKey) + s.getAsInt(marginRightKey)
|
||||
}
|
||||
|
||||
// GetVerticalMargins returns the style's top and bottom margins. Unset values
|
||||
// are measured as 0.
|
||||
func (s Style) GetVerticalMargins() int {
|
||||
return s.getAsInt(marginTopKey) + s.getAsInt(marginBottomKey)
|
||||
}
|
||||
|
||||
// GetBorder returns the style's border style (type Border) and value for the
|
||||
// top, right, bottom, and left in that order. If no value is set for the
|
||||
// border style, Border{} is returned. For all other unset values false is
|
||||
// returned.
|
||||
func (s Style) GetBorder() (b Border, top, right, bottom, left bool) {
|
||||
return s.getBorderStyle(),
|
||||
s.getAsBool(borderTopKey, false),
|
||||
s.getAsBool(borderRightKey, false),
|
||||
s.getAsBool(borderBottomKey, false),
|
||||
s.getAsBool(borderLeftKey, false)
|
||||
}
|
||||
|
||||
// GetBorderStyle returns the style's border style (type Border). If no value
|
||||
// is set Border{} is returned.
|
||||
func (s Style) GetBorderStyle() Border {
|
||||
return s.getBorderStyle()
|
||||
}
|
||||
|
||||
// GetBorderTop returns the style's top border setting. If no value is set
|
||||
// false is returned.
|
||||
func (s Style) GetBorderTop() bool {
|
||||
return s.getAsBool(borderTopKey, false)
|
||||
}
|
||||
|
||||
// GetBorderRight returns the style's right border setting. If no value is set
|
||||
// false is returned.
|
||||
func (s Style) GetBorderRight() bool {
|
||||
return s.getAsBool(borderRightKey, false)
|
||||
}
|
||||
|
||||
// GetBorderBottom returns the style's bottom border setting. If no value is
|
||||
// set false is returned.
|
||||
func (s Style) GetBorderBottom() bool {
|
||||
return s.getAsBool(borderBottomKey, false)
|
||||
}
|
||||
|
||||
// GetBorderLeft returns the style's left border setting. If no value is
|
||||
// set false is returned.
|
||||
func (s Style) GetBorderLeft() bool {
|
||||
return s.getAsBool(borderLeftKey, false)
|
||||
}
|
||||
|
||||
// GetBorderTopForeground returns the style's border top foreground color. If
|
||||
// no value is set NoColor{} is returned.
|
||||
func (s Style) GetBorderTopForeground() TerminalColor {
|
||||
return s.getAsColor(borderTopForegroundKey)
|
||||
}
|
||||
|
||||
// GetBorderRightForeground returns the style's border right foreground color.
|
||||
// If no value is set NoColor{} is returned.
|
||||
func (s Style) GetBorderRightForeground() TerminalColor {
|
||||
return s.getAsColor(borderRightForegroundKey)
|
||||
}
|
||||
|
||||
// GetBorderBottomForeground returns the style's border bottom foreground
|
||||
// color. If no value is set NoColor{} is returned.
|
||||
func (s Style) GetBorderBottomForeground() TerminalColor {
|
||||
return s.getAsColor(borderBottomForegroundKey)
|
||||
}
|
||||
|
||||
// GetBorderLeftForeground returns the style's border left foreground
|
||||
// color. If no value is set NoColor{} is returned.
|
||||
func (s Style) GetBorderLeftForeground() TerminalColor {
|
||||
return s.getAsColor(borderLeftForegroundKey)
|
||||
}
|
||||
|
||||
// GetBorderTopBackground returns the style's border top background color. If
|
||||
// no value is set NoColor{} is returned.
|
||||
func (s Style) GetBorderTopBackground() TerminalColor {
|
||||
return s.getAsColor(borderTopBackgroundKey)
|
||||
}
|
||||
|
||||
// GetBorderRightBackground returns the style's border right background color.
|
||||
// If no value is set NoColor{} is returned.
|
||||
func (s Style) GetBorderRightBackground() TerminalColor {
|
||||
return s.getAsColor(borderRightBackgroundKey)
|
||||
}
|
||||
|
||||
// GetBorderBottomBackground returns the style's border bottom background
|
||||
// color. If no value is set NoColor{} is returned.
|
||||
func (s Style) GetBorderBottomBackground() TerminalColor {
|
||||
return s.getAsColor(borderBottomBackgroundKey)
|
||||
}
|
||||
|
||||
// GetBorderLeftBackground returns the style's border left background
|
||||
// color. If no value is set NoColor{} is returned.
|
||||
func (s Style) GetBorderLeftBackground() TerminalColor {
|
||||
return s.getAsColor(borderLeftBackgroundKey)
|
||||
}
|
||||
|
||||
// GetBorderTopWidth returns the width of the top border. If borders contain
|
||||
// runes of varying widths, the widest rune is returned. If no border exists on
|
||||
// the top edge, 0 is returned.
|
||||
//
|
||||
// Deprecated: This function simply calls Style.GetBorderTopSize.
|
||||
func (s Style) GetBorderTopWidth() int {
|
||||
return s.GetBorderTopSize()
|
||||
}
|
||||
|
||||
// GetBorderTopSize returns the width of the top border. If borders contain
|
||||
// runes of varying widths, the widest rune is returned. If no border exists on
|
||||
// the top edge, 0 is returned.
|
||||
func (s Style) GetBorderTopSize() int {
|
||||
if !s.getAsBool(borderTopKey, false) {
|
||||
return 0
|
||||
}
|
||||
return s.getBorderStyle().GetTopSize()
|
||||
}
|
||||
|
||||
// GetBorderLeftSize returns the width of the left border. If borders contain
|
||||
// runes of varying widths, the widest rune is returned. If no border exists on
|
||||
// the left edge, 0 is returned.
|
||||
func (s Style) GetBorderLeftSize() int {
|
||||
if !s.getAsBool(borderLeftKey, false) {
|
||||
return 0
|
||||
}
|
||||
return s.getBorderStyle().GetLeftSize()
|
||||
}
|
||||
|
||||
// GetBorderBottomSize returns the width of the bottom border. If borders
|
||||
// contain runes of varying widths, the widest rune is returned. If no border
|
||||
// exists on the left edge, 0 is returned.
|
||||
func (s Style) GetBorderBottomSize() int {
|
||||
if !s.getAsBool(borderBottomKey, false) {
|
||||
return 0
|
||||
}
|
||||
return s.getBorderStyle().GetBottomSize()
|
||||
}
|
||||
|
||||
// GetBorderRightSize returns the width of the right border. If borders
|
||||
// contain runes of varying widths, the widest rune is returned. If no border
|
||||
// exists on the right edge, 0 is returned.
|
||||
func (s Style) GetBorderRightSize() int {
|
||||
if !s.getAsBool(borderRightKey, false) {
|
||||
return 0
|
||||
}
|
||||
return s.getBorderStyle().GetRightSize()
|
||||
}
|
||||
|
||||
// GetHorizontalBorderSize returns the width of the horizontal borders. If
|
||||
// borders contain runes of varying widths, the widest rune is returned. If no
|
||||
// border exists on the horizontal edges, 0 is returned.
|
||||
func (s Style) GetHorizontalBorderSize() int {
|
||||
return s.GetBorderLeftSize() + s.GetBorderRightSize()
|
||||
}
|
||||
|
||||
// GetVerticalBorderSize returns the width of the vertical borders. If
|
||||
// borders contain runes of varying widths, the widest rune is returned. If no
|
||||
// border exists on the vertical edges, 0 is returned.
|
||||
func (s Style) GetVerticalBorderSize() int {
|
||||
return s.GetBorderTopSize() + s.GetBorderBottomSize()
|
||||
}
|
||||
|
||||
// GetInline returns the style's inline setting. If no value is set false is
|
||||
// returned.
|
||||
func (s Style) GetInline() bool {
|
||||
return s.getAsBool(inlineKey, false)
|
||||
}
|
||||
|
||||
// GetMaxWidth returns the style's max width setting. If no value is set 0 is
|
||||
// returned.
|
||||
func (s Style) GetMaxWidth() int {
|
||||
return s.getAsInt(maxWidthKey)
|
||||
}
|
||||
|
||||
// GetMaxHeight returns the style's max height setting. If no value is set 0 is
|
||||
// returned.
|
||||
func (s Style) GetMaxHeight() int {
|
||||
return s.getAsInt(maxHeightKey)
|
||||
}
|
||||
|
||||
// GetTabWidth returns the style's tab width setting. If no value is set 4 is
|
||||
// returned which is the implicit default.
|
||||
func (s Style) GetTabWidth() int {
|
||||
return s.getAsInt(tabWidthKey)
|
||||
}
|
||||
|
||||
// GetUnderlineSpaces returns whether or not the style is set to underline
|
||||
// spaces. If not value is set false is returned.
|
||||
func (s Style) GetUnderlineSpaces() bool {
|
||||
return s.getAsBool(underlineSpacesKey, false)
|
||||
}
|
||||
|
||||
// GetStrikethroughSpaces returns whether or not the style is set to strikethrough
|
||||
// spaces. If not value is set false is returned.
|
||||
func (s Style) GetStrikethroughSpaces() bool {
|
||||
return s.getAsBool(strikethroughSpacesKey, false)
|
||||
}
|
||||
|
||||
// GetHorizontalFrameSize returns the sum of the style's horizontal margins, padding
|
||||
// and border widths.
|
||||
//
|
||||
// Provisional: this method may be renamed.
|
||||
func (s Style) GetHorizontalFrameSize() int {
|
||||
return s.GetHorizontalMargins() + s.GetHorizontalPadding() + s.GetHorizontalBorderSize()
|
||||
}
|
||||
|
||||
// GetVerticalFrameSize returns the sum of the style's vertical margins, padding
|
||||
// and border widths.
|
||||
//
|
||||
// Provisional: this method may be renamed.
|
||||
func (s Style) GetVerticalFrameSize() int {
|
||||
return s.GetVerticalMargins() + s.GetVerticalPadding() + s.GetVerticalBorderSize()
|
||||
}
|
||||
|
||||
// GetFrameSize returns the sum of the margins, padding and border width for
|
||||
// both the horizontal and vertical margins.
|
||||
func (s Style) GetFrameSize() (x, y int) {
|
||||
return s.GetHorizontalFrameSize(), s.GetVerticalFrameSize()
|
||||
}
|
||||
|
||||
// GetTransform returns the transform set on the style. If no transform is set
|
||||
// nil is returned.
|
||||
func (s Style) GetTransform() func(string) string {
|
||||
return s.getAsTransform(transformKey)
|
||||
}
|
||||
|
||||
// Returns whether or not the given property is set.
|
||||
func (s Style) isSet(k propKey) bool {
|
||||
return s.props.has(k)
|
||||
}
|
||||
|
||||
func (s Style) getAsBool(k propKey, defaultVal bool) bool {
|
||||
if !s.isSet(k) {
|
||||
return defaultVal
|
||||
}
|
||||
return s.attrs&int(k) != 0
|
||||
}
|
||||
|
||||
func (s Style) getAsColor(k propKey) TerminalColor {
|
||||
if !s.isSet(k) {
|
||||
return noColor
|
||||
}
|
||||
|
||||
var c TerminalColor
|
||||
switch k { //nolint:exhaustive
|
||||
case foregroundKey:
|
||||
c = s.fgColor
|
||||
case backgroundKey:
|
||||
c = s.bgColor
|
||||
case marginBackgroundKey:
|
||||
c = s.marginBgColor
|
||||
case borderTopForegroundKey:
|
||||
c = s.borderTopFgColor
|
||||
case borderRightForegroundKey:
|
||||
c = s.borderRightFgColor
|
||||
case borderBottomForegroundKey:
|
||||
c = s.borderBottomFgColor
|
||||
case borderLeftForegroundKey:
|
||||
c = s.borderLeftFgColor
|
||||
case borderTopBackgroundKey:
|
||||
c = s.borderTopBgColor
|
||||
case borderRightBackgroundKey:
|
||||
c = s.borderRightBgColor
|
||||
case borderBottomBackgroundKey:
|
||||
c = s.borderBottomBgColor
|
||||
case borderLeftBackgroundKey:
|
||||
c = s.borderLeftBgColor
|
||||
}
|
||||
|
||||
if c != nil {
|
||||
return c
|
||||
}
|
||||
|
||||
return noColor
|
||||
}
|
||||
|
||||
func (s Style) getAsInt(k propKey) int {
|
||||
if !s.isSet(k) {
|
||||
return 0
|
||||
}
|
||||
switch k { //nolint:exhaustive
|
||||
case widthKey:
|
||||
return s.width
|
||||
case heightKey:
|
||||
return s.height
|
||||
case paddingTopKey:
|
||||
return s.paddingTop
|
||||
case paddingRightKey:
|
||||
return s.paddingRight
|
||||
case paddingBottomKey:
|
||||
return s.paddingBottom
|
||||
case paddingLeftKey:
|
||||
return s.paddingLeft
|
||||
case marginTopKey:
|
||||
return s.marginTop
|
||||
case marginRightKey:
|
||||
return s.marginRight
|
||||
case marginBottomKey:
|
||||
return s.marginBottom
|
||||
case marginLeftKey:
|
||||
return s.marginLeft
|
||||
case maxWidthKey:
|
||||
return s.maxWidth
|
||||
case maxHeightKey:
|
||||
return s.maxHeight
|
||||
case tabWidthKey:
|
||||
return s.tabWidth
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (s Style) getAsPosition(k propKey) Position {
|
||||
if !s.isSet(k) {
|
||||
return Position(0)
|
||||
}
|
||||
switch k { //nolint:exhaustive
|
||||
case alignHorizontalKey:
|
||||
return s.alignHorizontal
|
||||
case alignVerticalKey:
|
||||
return s.alignVertical
|
||||
}
|
||||
return Position(0)
|
||||
}
|
||||
|
||||
func (s Style) getBorderStyle() Border {
|
||||
if !s.isSet(borderStyleKey) {
|
||||
return noBorder
|
||||
}
|
||||
return s.borderStyle
|
||||
}
|
||||
|
||||
func (s Style) getAsTransform(propKey) func(string) string {
|
||||
if !s.isSet(transformKey) {
|
||||
return nil
|
||||
}
|
||||
return s.transform
|
||||
}
|
||||
|
||||
// Split a string into lines, additionally returning the size of the widest
|
||||
// line.
|
||||
func getLines(s string) (lines []string, widest int) {
|
||||
lines = strings.Split(s, "\n")
|
||||
|
||||
for _, l := range lines {
|
||||
w := ansi.StringWidth(l)
|
||||
if widest < w {
|
||||
widest = w
|
||||
}
|
||||
}
|
||||
|
||||
return lines, widest
|
||||
}
|
175
vendor/github.com/charmbracelet/lipgloss/join.go
generated
vendored
Normal file
175
vendor/github.com/charmbracelet/lipgloss/join.go
generated
vendored
Normal file
@ -0,0 +1,175 @@
|
||||
package lipgloss
|
||||
|
||||
import (
|
||||
"math"
|
||||
"strings"
|
||||
|
||||
"github.com/charmbracelet/x/ansi"
|
||||
)
|
||||
|
||||
// JoinHorizontal is a utility function for horizontally joining two
|
||||
// potentially multi-lined strings along a vertical axis. The first argument is
|
||||
// the position, with 0 being all the way at the top and 1 being all the way
|
||||
// at the bottom.
|
||||
//
|
||||
// If you just want to align to the top, center or bottom you may as well just
|
||||
// use the helper constants Top, Center, and Bottom.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// blockB := "...\n...\n..."
|
||||
// blockA := "...\n...\n...\n...\n..."
|
||||
//
|
||||
// // Join 20% from the top
|
||||
// str := lipgloss.JoinHorizontal(0.2, blockA, blockB)
|
||||
//
|
||||
// // Join on the top edge
|
||||
// str := lipgloss.JoinHorizontal(lipgloss.Top, blockA, blockB)
|
||||
func JoinHorizontal(pos Position, strs ...string) string {
|
||||
if len(strs) == 0 {
|
||||
return ""
|
||||
}
|
||||
if len(strs) == 1 {
|
||||
return strs[0]
|
||||
}
|
||||
|
||||
var (
|
||||
// Groups of strings broken into multiple lines
|
||||
blocks = make([][]string, len(strs))
|
||||
|
||||
// Max line widths for the above text blocks
|
||||
maxWidths = make([]int, len(strs))
|
||||
|
||||
// Height of the tallest block
|
||||
maxHeight int
|
||||
)
|
||||
|
||||
// Break text blocks into lines and get max widths for each text block
|
||||
for i, str := range strs {
|
||||
blocks[i], maxWidths[i] = getLines(str)
|
||||
if len(blocks[i]) > maxHeight {
|
||||
maxHeight = len(blocks[i])
|
||||
}
|
||||
}
|
||||
|
||||
// Add extra lines to make each side the same height
|
||||
for i := range blocks {
|
||||
if len(blocks[i]) >= maxHeight {
|
||||
continue
|
||||
}
|
||||
|
||||
extraLines := make([]string, maxHeight-len(blocks[i]))
|
||||
|
||||
switch pos { //nolint:exhaustive
|
||||
case Top:
|
||||
blocks[i] = append(blocks[i], extraLines...)
|
||||
|
||||
case Bottom:
|
||||
blocks[i] = append(extraLines, blocks[i]...)
|
||||
|
||||
default: // Somewhere in the middle
|
||||
n := len(extraLines)
|
||||
split := int(math.Round(float64(n) * pos.value()))
|
||||
top := n - split
|
||||
bottom := n - top
|
||||
|
||||
blocks[i] = append(extraLines[top:], blocks[i]...)
|
||||
blocks[i] = append(blocks[i], extraLines[bottom:]...)
|
||||
}
|
||||
}
|
||||
|
||||
// Merge lines
|
||||
var b strings.Builder
|
||||
for i := range blocks[0] { // remember, all blocks have the same number of members now
|
||||
for j, block := range blocks {
|
||||
b.WriteString(block[i])
|
||||
|
||||
// Also make lines the same length
|
||||
b.WriteString(strings.Repeat(" ", maxWidths[j]-ansi.StringWidth(block[i])))
|
||||
}
|
||||
if i < len(blocks[0])-1 {
|
||||
b.WriteRune('\n')
|
||||
}
|
||||
}
|
||||
|
||||
return b.String()
|
||||
}
|
||||
|
||||
// JoinVertical is a utility function for vertically joining two potentially
|
||||
// multi-lined strings along a horizontal axis. The first argument is the
|
||||
// position, with 0 being all the way to the left and 1 being all the way to
|
||||
// the right.
|
||||
//
|
||||
// If you just want to align to the left, right or center you may as well just
|
||||
// use the helper constants Left, Center, and Right.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// blockB := "...\n...\n..."
|
||||
// blockA := "...\n...\n...\n...\n..."
|
||||
//
|
||||
// // Join 20% from the top
|
||||
// str := lipgloss.JoinVertical(0.2, blockA, blockB)
|
||||
//
|
||||
// // Join on the right edge
|
||||
// str := lipgloss.JoinVertical(lipgloss.Right, blockA, blockB)
|
||||
func JoinVertical(pos Position, strs ...string) string {
|
||||
if len(strs) == 0 {
|
||||
return ""
|
||||
}
|
||||
if len(strs) == 1 {
|
||||
return strs[0]
|
||||
}
|
||||
|
||||
var (
|
||||
blocks = make([][]string, len(strs))
|
||||
maxWidth int
|
||||
)
|
||||
|
||||
for i := range strs {
|
||||
var w int
|
||||
blocks[i], w = getLines(strs[i])
|
||||
if w > maxWidth {
|
||||
maxWidth = w
|
||||
}
|
||||
}
|
||||
|
||||
var b strings.Builder
|
||||
for i, block := range blocks {
|
||||
for j, line := range block {
|
||||
w := maxWidth - ansi.StringWidth(line)
|
||||
|
||||
switch pos { //nolint:exhaustive
|
||||
case Left:
|
||||
b.WriteString(line)
|
||||
b.WriteString(strings.Repeat(" ", w))
|
||||
|
||||
case Right:
|
||||
b.WriteString(strings.Repeat(" ", w))
|
||||
b.WriteString(line)
|
||||
|
||||
default: // Somewhere in the middle
|
||||
if w < 1 {
|
||||
b.WriteString(line)
|
||||
break
|
||||
}
|
||||
|
||||
split := int(math.Round(float64(w) * pos.value()))
|
||||
right := w - split
|
||||
left := w - right
|
||||
|
||||
b.WriteString(strings.Repeat(" ", left))
|
||||
b.WriteString(line)
|
||||
b.WriteString(strings.Repeat(" ", right))
|
||||
}
|
||||
|
||||
// Write a newline as long as we're not on the last line of the
|
||||
// last block.
|
||||
if !(i == len(blocks)-1 && j == len(block)-1) {
|
||||
b.WriteRune('\n')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return b.String()
|
||||
}
|
154
vendor/github.com/charmbracelet/lipgloss/position.go
generated
vendored
Normal file
154
vendor/github.com/charmbracelet/lipgloss/position.go
generated
vendored
Normal file
@ -0,0 +1,154 @@
|
||||
package lipgloss
|
||||
|
||||
import (
|
||||
"math"
|
||||
"strings"
|
||||
|
||||
"github.com/charmbracelet/x/ansi"
|
||||
)
|
||||
|
||||
// Position represents a position along a horizontal or vertical axis. It's in
|
||||
// situations where an axis is involved, like alignment, joining, placement and
|
||||
// so on.
|
||||
//
|
||||
// A value of 0 represents the start (the left or top) and 1 represents the end
|
||||
// (the right or bottom). 0.5 represents the center.
|
||||
//
|
||||
// There are constants Top, Bottom, Center, Left and Right in this package that
|
||||
// can be used to aid readability.
|
||||
type Position float64
|
||||
|
||||
func (p Position) value() float64 {
|
||||
return math.Min(1, math.Max(0, float64(p)))
|
||||
}
|
||||
|
||||
// Position aliases.
|
||||
const (
|
||||
Top Position = 0.0
|
||||
Bottom Position = 1.0
|
||||
Center Position = 0.5
|
||||
Left Position = 0.0
|
||||
Right Position = 1.0
|
||||
)
|
||||
|
||||
// Place places a string or text block vertically in an unstyled box of a given
|
||||
// width or height.
|
||||
func Place(width, height int, hPos, vPos Position, str string, opts ...WhitespaceOption) string {
|
||||
return renderer.Place(width, height, hPos, vPos, str, opts...)
|
||||
}
|
||||
|
||||
// Place places a string or text block vertically in an unstyled box of a given
|
||||
// width or height.
|
||||
func (r *Renderer) Place(width, height int, hPos, vPos Position, str string, opts ...WhitespaceOption) string {
|
||||
return r.PlaceVertical(height, vPos, r.PlaceHorizontal(width, hPos, str, opts...), opts...)
|
||||
}
|
||||
|
||||
// PlaceHorizontal places a string or text block horizontally in an unstyled
|
||||
// block of a given width. If the given width is shorter than the max width of
|
||||
// the string (measured by its longest line) this will be a noop.
|
||||
func PlaceHorizontal(width int, pos Position, str string, opts ...WhitespaceOption) string {
|
||||
return renderer.PlaceHorizontal(width, pos, str, opts...)
|
||||
}
|
||||
|
||||
// PlaceHorizontal places a string or text block horizontally in an unstyled
|
||||
// block of a given width. If the given width is shorter than the max width of
|
||||
// the string (measured by its longest line) this will be a noöp.
|
||||
func (r *Renderer) PlaceHorizontal(width int, pos Position, str string, opts ...WhitespaceOption) string {
|
||||
lines, contentWidth := getLines(str)
|
||||
gap := width - contentWidth
|
||||
|
||||
if gap <= 0 {
|
||||
return str
|
||||
}
|
||||
|
||||
ws := newWhitespace(r, opts...)
|
||||
|
||||
var b strings.Builder
|
||||
for i, l := range lines {
|
||||
// Is this line shorter than the longest line?
|
||||
short := max(0, contentWidth-ansi.StringWidth(l))
|
||||
|
||||
switch pos { //nolint:exhaustive
|
||||
case Left:
|
||||
b.WriteString(l)
|
||||
b.WriteString(ws.render(gap + short))
|
||||
|
||||
case Right:
|
||||
b.WriteString(ws.render(gap + short))
|
||||
b.WriteString(l)
|
||||
|
||||
default: // somewhere in the middle
|
||||
totalGap := gap + short
|
||||
|
||||
split := int(math.Round(float64(totalGap) * pos.value()))
|
||||
left := totalGap - split
|
||||
right := totalGap - left
|
||||
|
||||
b.WriteString(ws.render(left))
|
||||
b.WriteString(l)
|
||||
b.WriteString(ws.render(right))
|
||||
}
|
||||
|
||||
if i < len(lines)-1 {
|
||||
b.WriteRune('\n')
|
||||
}
|
||||
}
|
||||
|
||||
return b.String()
|
||||
}
|
||||
|
||||
// PlaceVertical places a string or text block vertically in an unstyled block
|
||||
// of a given height. If the given height is shorter than the height of the
|
||||
// string (measured by its newlines) then this will be a noop.
|
||||
func PlaceVertical(height int, pos Position, str string, opts ...WhitespaceOption) string {
|
||||
return renderer.PlaceVertical(height, pos, str, opts...)
|
||||
}
|
||||
|
||||
// PlaceVertical places a string or text block vertically in an unstyled block
|
||||
// of a given height. If the given height is shorter than the height of the
|
||||
// string (measured by its newlines) then this will be a noöp.
|
||||
func (r *Renderer) PlaceVertical(height int, pos Position, str string, opts ...WhitespaceOption) string {
|
||||
contentHeight := strings.Count(str, "\n") + 1
|
||||
gap := height - contentHeight
|
||||
|
||||
if gap <= 0 {
|
||||
return str
|
||||
}
|
||||
|
||||
ws := newWhitespace(r, opts...)
|
||||
|
||||
_, width := getLines(str)
|
||||
emptyLine := ws.render(width)
|
||||
b := strings.Builder{}
|
||||
|
||||
switch pos { //nolint:exhaustive
|
||||
case Top:
|
||||
b.WriteString(str)
|
||||
b.WriteRune('\n')
|
||||
for i := 0; i < gap; i++ {
|
||||
b.WriteString(emptyLine)
|
||||
if i < gap-1 {
|
||||
b.WriteRune('\n')
|
||||
}
|
||||
}
|
||||
|
||||
case Bottom:
|
||||
b.WriteString(strings.Repeat(emptyLine+"\n", gap))
|
||||
b.WriteString(str)
|
||||
|
||||
default: // Somewhere in the middle
|
||||
split := int(math.Round(float64(gap) * pos.value()))
|
||||
top := gap - split
|
||||
bottom := gap - top
|
||||
|
||||
b.WriteString(strings.Repeat(emptyLine+"\n", top))
|
||||
b.WriteString(str)
|
||||
|
||||
for i := 0; i < bottom; i++ {
|
||||
b.WriteRune('\n')
|
||||
b.WriteString(emptyLine)
|
||||
}
|
||||
}
|
||||
|
||||
return b.String()
|
||||
}
|
181
vendor/github.com/charmbracelet/lipgloss/renderer.go
generated
vendored
Normal file
181
vendor/github.com/charmbracelet/lipgloss/renderer.go
generated
vendored
Normal file
@ -0,0 +1,181 @@
|
||||
package lipgloss
|
||||
|
||||
import (
|
||||
"io"
|
||||
"sync"
|
||||
|
||||
"github.com/muesli/termenv"
|
||||
)
|
||||
|
||||
// We're manually creating the struct here to avoid initializing the output and
|
||||
// query the terminal multiple times.
|
||||
var renderer = &Renderer{
|
||||
output: termenv.DefaultOutput(),
|
||||
}
|
||||
|
||||
// Renderer is a lipgloss terminal renderer.
|
||||
type Renderer struct {
|
||||
output *termenv.Output
|
||||
colorProfile termenv.Profile
|
||||
hasDarkBackground bool
|
||||
|
||||
getColorProfile sync.Once
|
||||
explicitColorProfile bool
|
||||
|
||||
getBackgroundColor sync.Once
|
||||
explicitBackgroundColor bool
|
||||
|
||||
mtx sync.RWMutex
|
||||
}
|
||||
|
||||
// DefaultRenderer returns the default renderer.
|
||||
func DefaultRenderer() *Renderer {
|
||||
return renderer
|
||||
}
|
||||
|
||||
// SetDefaultRenderer sets the default global renderer.
|
||||
func SetDefaultRenderer(r *Renderer) {
|
||||
renderer = r
|
||||
}
|
||||
|
||||
// NewRenderer creates a new Renderer.
|
||||
//
|
||||
// w will be used to determine the terminal's color capabilities.
|
||||
func NewRenderer(w io.Writer, opts ...termenv.OutputOption) *Renderer {
|
||||
r := &Renderer{
|
||||
output: termenv.NewOutput(w, opts...),
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// Output returns the termenv output.
|
||||
func (r *Renderer) Output() *termenv.Output {
|
||||
r.mtx.RLock()
|
||||
defer r.mtx.RUnlock()
|
||||
return r.output
|
||||
}
|
||||
|
||||
// SetOutput sets the termenv output.
|
||||
func (r *Renderer) SetOutput(o *termenv.Output) {
|
||||
r.mtx.Lock()
|
||||
defer r.mtx.Unlock()
|
||||
r.output = o
|
||||
}
|
||||
|
||||
// ColorProfile returns the detected termenv color profile.
|
||||
func (r *Renderer) ColorProfile() termenv.Profile {
|
||||
r.mtx.RLock()
|
||||
defer r.mtx.RUnlock()
|
||||
|
||||
if !r.explicitColorProfile {
|
||||
r.getColorProfile.Do(func() {
|
||||
// NOTE: we don't need to lock here because sync.Once provides its
|
||||
// own locking mechanism.
|
||||
r.colorProfile = r.output.EnvColorProfile()
|
||||
})
|
||||
}
|
||||
|
||||
return r.colorProfile
|
||||
}
|
||||
|
||||
// ColorProfile returns the detected termenv color profile.
|
||||
func ColorProfile() termenv.Profile {
|
||||
return renderer.ColorProfile()
|
||||
}
|
||||
|
||||
// SetColorProfile sets the color profile on the renderer. This function exists
|
||||
// mostly for testing purposes so that you can assure you're testing against
|
||||
// a specific profile.
|
||||
//
|
||||
// Outside of testing you likely won't want to use this function as the color
|
||||
// profile will detect and cache the terminal's color capabilities and choose
|
||||
// the best available profile.
|
||||
//
|
||||
// Available color profiles are:
|
||||
//
|
||||
// termenv.Ascii // no color, 1-bit
|
||||
// termenv.ANSI //16 colors, 4-bit
|
||||
// termenv.ANSI256 // 256 colors, 8-bit
|
||||
// termenv.TrueColor // 16,777,216 colors, 24-bit
|
||||
//
|
||||
// This function is thread-safe.
|
||||
func (r *Renderer) SetColorProfile(p termenv.Profile) {
|
||||
r.mtx.Lock()
|
||||
defer r.mtx.Unlock()
|
||||
|
||||
r.colorProfile = p
|
||||
r.explicitColorProfile = true
|
||||
}
|
||||
|
||||
// SetColorProfile sets the color profile on the default renderer. This
|
||||
// function exists mostly for testing purposes so that you can assure you're
|
||||
// testing against a specific profile.
|
||||
//
|
||||
// Outside of testing you likely won't want to use this function as the color
|
||||
// profile will detect and cache the terminal's color capabilities and choose
|
||||
// the best available profile.
|
||||
//
|
||||
// Available color profiles are:
|
||||
//
|
||||
// termenv.Ascii // no color, 1-bit
|
||||
// termenv.ANSI //16 colors, 4-bit
|
||||
// termenv.ANSI256 // 256 colors, 8-bit
|
||||
// termenv.TrueColor // 16,777,216 colors, 24-bit
|
||||
//
|
||||
// This function is thread-safe.
|
||||
func SetColorProfile(p termenv.Profile) {
|
||||
renderer.SetColorProfile(p)
|
||||
}
|
||||
|
||||
// HasDarkBackground returns whether or not the terminal has a dark background.
|
||||
func HasDarkBackground() bool {
|
||||
return renderer.HasDarkBackground()
|
||||
}
|
||||
|
||||
// HasDarkBackground returns whether or not the renderer will render to a dark
|
||||
// background. A dark background can either be auto-detected, or set explicitly
|
||||
// on the renderer.
|
||||
func (r *Renderer) HasDarkBackground() bool {
|
||||
r.mtx.RLock()
|
||||
defer r.mtx.RUnlock()
|
||||
|
||||
if !r.explicitBackgroundColor {
|
||||
r.getBackgroundColor.Do(func() {
|
||||
// NOTE: we don't need to lock here because sync.Once provides its
|
||||
// own locking mechanism.
|
||||
r.hasDarkBackground = r.output.HasDarkBackground()
|
||||
})
|
||||
}
|
||||
|
||||
return r.hasDarkBackground
|
||||
}
|
||||
|
||||
// SetHasDarkBackground sets the background color detection value for the
|
||||
// default renderer. This function exists mostly for testing purposes so that
|
||||
// you can assure you're testing against a specific background color setting.
|
||||
//
|
||||
// Outside of testing you likely won't want to use this function as the
|
||||
// backgrounds value will be automatically detected and cached against the
|
||||
// terminal's current background color setting.
|
||||
//
|
||||
// This function is thread-safe.
|
||||
func SetHasDarkBackground(b bool) {
|
||||
renderer.SetHasDarkBackground(b)
|
||||
}
|
||||
|
||||
// SetHasDarkBackground sets the background color detection value on the
|
||||
// renderer. This function exists mostly for testing purposes so that you can
|
||||
// assure you're testing against a specific background color setting.
|
||||
//
|
||||
// Outside of testing you likely won't want to use this function as the
|
||||
// backgrounds value will be automatically detected and cached against the
|
||||
// terminal's current background color setting.
|
||||
//
|
||||
// This function is thread-safe.
|
||||
func (r *Renderer) SetHasDarkBackground(b bool) {
|
||||
r.mtx.Lock()
|
||||
defer r.mtx.Unlock()
|
||||
|
||||
r.hasDarkBackground = b
|
||||
r.explicitBackgroundColor = true
|
||||
}
|
43
vendor/github.com/charmbracelet/lipgloss/runes.go
generated
vendored
Normal file
43
vendor/github.com/charmbracelet/lipgloss/runes.go
generated
vendored
Normal file
@ -0,0 +1,43 @@
|
||||
package lipgloss
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// StyleRunes apply a given style to runes at the given indices in the string.
|
||||
// Note that you must provide styling options for both matched and unmatched
|
||||
// runes. Indices out of bounds will be ignored.
|
||||
func StyleRunes(str string, indices []int, matched, unmatched Style) string {
|
||||
// Convert slice of indices to a map for easier lookups
|
||||
m := make(map[int]struct{})
|
||||
for _, i := range indices {
|
||||
m[i] = struct{}{}
|
||||
}
|
||||
|
||||
var (
|
||||
out strings.Builder
|
||||
group strings.Builder
|
||||
style Style
|
||||
runes = []rune(str)
|
||||
)
|
||||
|
||||
for i, r := range runes {
|
||||
group.WriteRune(r)
|
||||
|
||||
_, matches := m[i]
|
||||
_, nextMatches := m[i+1]
|
||||
|
||||
if matches != nextMatches || i == len(runes)-1 {
|
||||
// Flush
|
||||
if matches {
|
||||
style = matched
|
||||
} else {
|
||||
style = unmatched
|
||||
}
|
||||
out.WriteString(style.Render(group.String()))
|
||||
group.Reset()
|
||||
}
|
||||
}
|
||||
|
||||
return out.String()
|
||||
}
|
799
vendor/github.com/charmbracelet/lipgloss/set.go
generated
vendored
Normal file
799
vendor/github.com/charmbracelet/lipgloss/set.go
generated
vendored
Normal file
@ -0,0 +1,799 @@
|
||||
package lipgloss
|
||||
|
||||
// Set a value on the underlying rules map.
|
||||
func (s *Style) set(key propKey, value interface{}) {
|
||||
// We don't allow negative integers on any of our other values, so just keep
|
||||
// them at zero or above. We could use uints instead, but the
|
||||
// conversions are a little tedious, so we're sticking with ints for
|
||||
// sake of usability.
|
||||
switch key { //nolint:exhaustive
|
||||
case foregroundKey:
|
||||
s.fgColor = colorOrNil(value)
|
||||
case backgroundKey:
|
||||
s.bgColor = colorOrNil(value)
|
||||
case widthKey:
|
||||
s.width = max(0, value.(int))
|
||||
case heightKey:
|
||||
s.height = max(0, value.(int))
|
||||
case alignHorizontalKey:
|
||||
s.alignHorizontal = value.(Position)
|
||||
case alignVerticalKey:
|
||||
s.alignVertical = value.(Position)
|
||||
case paddingTopKey:
|
||||
s.paddingTop = max(0, value.(int))
|
||||
case paddingRightKey:
|
||||
s.paddingRight = max(0, value.(int))
|
||||
case paddingBottomKey:
|
||||
s.paddingBottom = max(0, value.(int))
|
||||
case paddingLeftKey:
|
||||
s.paddingLeft = max(0, value.(int))
|
||||
case marginTopKey:
|
||||
s.marginTop = max(0, value.(int))
|
||||
case marginRightKey:
|
||||
s.marginRight = max(0, value.(int))
|
||||
case marginBottomKey:
|
||||
s.marginBottom = max(0, value.(int))
|
||||
case marginLeftKey:
|
||||
s.marginLeft = max(0, value.(int))
|
||||
case marginBackgroundKey:
|
||||
s.marginBgColor = colorOrNil(value)
|
||||
case borderStyleKey:
|
||||
s.borderStyle = value.(Border)
|
||||
case borderTopForegroundKey:
|
||||
s.borderTopFgColor = colorOrNil(value)
|
||||
case borderRightForegroundKey:
|
||||
s.borderRightFgColor = colorOrNil(value)
|
||||
case borderBottomForegroundKey:
|
||||
s.borderBottomFgColor = colorOrNil(value)
|
||||
case borderLeftForegroundKey:
|
||||
s.borderLeftFgColor = colorOrNil(value)
|
||||
case borderTopBackgroundKey:
|
||||
s.borderTopBgColor = colorOrNil(value)
|
||||
case borderRightBackgroundKey:
|
||||
s.borderRightBgColor = colorOrNil(value)
|
||||
case borderBottomBackgroundKey:
|
||||
s.borderBottomBgColor = colorOrNil(value)
|
||||
case borderLeftBackgroundKey:
|
||||
s.borderLeftBgColor = colorOrNil(value)
|
||||
case maxWidthKey:
|
||||
s.maxWidth = max(0, value.(int))
|
||||
case maxHeightKey:
|
||||
s.maxHeight = max(0, value.(int))
|
||||
case tabWidthKey:
|
||||
// TabWidth is the only property that may have a negative value (and
|
||||
// that negative value can be no less than -1).
|
||||
s.tabWidth = value.(int)
|
||||
case transformKey:
|
||||
s.transform = value.(func(string) string)
|
||||
default:
|
||||
if v, ok := value.(bool); ok { //nolint:nestif
|
||||
if v {
|
||||
s.attrs |= int(key)
|
||||
} else {
|
||||
s.attrs &^= int(key)
|
||||
}
|
||||
} else if attrs, ok := value.(int); ok {
|
||||
// bool attrs
|
||||
if attrs&int(key) != 0 {
|
||||
s.attrs |= int(key)
|
||||
} else {
|
||||
s.attrs &^= int(key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set the prop on
|
||||
s.props = s.props.set(key)
|
||||
}
|
||||
|
||||
// setFrom sets the property from another style.
|
||||
func (s *Style) setFrom(key propKey, i Style) {
|
||||
switch key { //nolint:exhaustive
|
||||
case foregroundKey:
|
||||
s.set(foregroundKey, i.fgColor)
|
||||
case backgroundKey:
|
||||
s.set(backgroundKey, i.bgColor)
|
||||
case widthKey:
|
||||
s.set(widthKey, i.width)
|
||||
case heightKey:
|
||||
s.set(heightKey, i.height)
|
||||
case alignHorizontalKey:
|
||||
s.set(alignHorizontalKey, i.alignHorizontal)
|
||||
case alignVerticalKey:
|
||||
s.set(alignVerticalKey, i.alignVertical)
|
||||
case paddingTopKey:
|
||||
s.set(paddingTopKey, i.paddingTop)
|
||||
case paddingRightKey:
|
||||
s.set(paddingRightKey, i.paddingRight)
|
||||
case paddingBottomKey:
|
||||
s.set(paddingBottomKey, i.paddingBottom)
|
||||
case paddingLeftKey:
|
||||
s.set(paddingLeftKey, i.paddingLeft)
|
||||
case marginTopKey:
|
||||
s.set(marginTopKey, i.marginTop)
|
||||
case marginRightKey:
|
||||
s.set(marginRightKey, i.marginRight)
|
||||
case marginBottomKey:
|
||||
s.set(marginBottomKey, i.marginBottom)
|
||||
case marginLeftKey:
|
||||
s.set(marginLeftKey, i.marginLeft)
|
||||
case marginBackgroundKey:
|
||||
s.set(marginBackgroundKey, i.marginBgColor)
|
||||
case borderStyleKey:
|
||||
s.set(borderStyleKey, i.borderStyle)
|
||||
case borderTopForegroundKey:
|
||||
s.set(borderTopForegroundKey, i.borderTopFgColor)
|
||||
case borderRightForegroundKey:
|
||||
s.set(borderRightForegroundKey, i.borderRightFgColor)
|
||||
case borderBottomForegroundKey:
|
||||
s.set(borderBottomForegroundKey, i.borderBottomFgColor)
|
||||
case borderLeftForegroundKey:
|
||||
s.set(borderLeftForegroundKey, i.borderLeftFgColor)
|
||||
case borderTopBackgroundKey:
|
||||
s.set(borderTopBackgroundKey, i.borderTopBgColor)
|
||||
case borderRightBackgroundKey:
|
||||
s.set(borderRightBackgroundKey, i.borderRightBgColor)
|
||||
case borderBottomBackgroundKey:
|
||||
s.set(borderBottomBackgroundKey, i.borderBottomBgColor)
|
||||
case borderLeftBackgroundKey:
|
||||
s.set(borderLeftBackgroundKey, i.borderLeftBgColor)
|
||||
case maxWidthKey:
|
||||
s.set(maxWidthKey, i.maxWidth)
|
||||
case maxHeightKey:
|
||||
s.set(maxHeightKey, i.maxHeight)
|
||||
case tabWidthKey:
|
||||
s.set(tabWidthKey, i.tabWidth)
|
||||
case transformKey:
|
||||
s.set(transformKey, i.transform)
|
||||
default:
|
||||
// Set attributes for set bool properties
|
||||
s.set(key, i.attrs)
|
||||
}
|
||||
}
|
||||
|
||||
func colorOrNil(c interface{}) TerminalColor {
|
||||
if c, ok := c.(TerminalColor); ok {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Bold sets a bold formatting rule.
|
||||
func (s Style) Bold(v bool) Style {
|
||||
s.set(boldKey, v)
|
||||
return s
|
||||
}
|
||||
|
||||
// Italic sets an italic formatting rule. In some terminal emulators this will
|
||||
// render with "reverse" coloring if not italic font variant is available.
|
||||
func (s Style) Italic(v bool) Style {
|
||||
s.set(italicKey, v)
|
||||
return s
|
||||
}
|
||||
|
||||
// Underline sets an underline rule. By default, underlines will not be drawn on
|
||||
// whitespace like margins and padding. To change this behavior set
|
||||
// UnderlineSpaces.
|
||||
func (s Style) Underline(v bool) Style {
|
||||
s.set(underlineKey, v)
|
||||
return s
|
||||
}
|
||||
|
||||
// Strikethrough sets a strikethrough rule. By default, strikes will not be
|
||||
// drawn on whitespace like margins and padding. To change this behavior set
|
||||
// StrikethroughSpaces.
|
||||
func (s Style) Strikethrough(v bool) Style {
|
||||
s.set(strikethroughKey, v)
|
||||
return s
|
||||
}
|
||||
|
||||
// Reverse sets a rule for inverting foreground and background colors.
|
||||
func (s Style) Reverse(v bool) Style {
|
||||
s.set(reverseKey, v)
|
||||
return s
|
||||
}
|
||||
|
||||
// Blink sets a rule for blinking foreground text.
|
||||
func (s Style) Blink(v bool) Style {
|
||||
s.set(blinkKey, v)
|
||||
return s
|
||||
}
|
||||
|
||||
// Faint sets a rule for rendering the foreground color in a dimmer shade.
|
||||
func (s Style) Faint(v bool) Style {
|
||||
s.set(faintKey, v)
|
||||
return s
|
||||
}
|
||||
|
||||
// Foreground sets a foreground color.
|
||||
//
|
||||
// // Sets the foreground to blue
|
||||
// s := lipgloss.NewStyle().Foreground(lipgloss.Color("#0000ff"))
|
||||
//
|
||||
// // Removes the foreground color
|
||||
// s.Foreground(lipgloss.NoColor)
|
||||
func (s Style) Foreground(c TerminalColor) Style {
|
||||
s.set(foregroundKey, c)
|
||||
return s
|
||||
}
|
||||
|
||||
// Background sets a background color.
|
||||
func (s Style) Background(c TerminalColor) Style {
|
||||
s.set(backgroundKey, c)
|
||||
return s
|
||||
}
|
||||
|
||||
// Width sets the width of the block before applying margins. The width, if
|
||||
// set, also determines where text will wrap.
|
||||
func (s Style) Width(i int) Style {
|
||||
s.set(widthKey, i)
|
||||
return s
|
||||
}
|
||||
|
||||
// Height sets the height of the block before applying margins. If the height of
|
||||
// the text block is less than this value after applying padding (or not), the
|
||||
// block will be set to this height.
|
||||
func (s Style) Height(i int) Style {
|
||||
s.set(heightKey, i)
|
||||
return s
|
||||
}
|
||||
|
||||
// Align is a shorthand method for setting horizontal and vertical alignment.
|
||||
//
|
||||
// With one argument, the position value is applied to the horizontal alignment.
|
||||
//
|
||||
// With two arguments, the value is applied to the horizontal and vertical
|
||||
// alignments, in that order.
|
||||
func (s Style) Align(p ...Position) Style {
|
||||
if len(p) > 0 {
|
||||
s.set(alignHorizontalKey, p[0])
|
||||
}
|
||||
if len(p) > 1 {
|
||||
s.set(alignVerticalKey, p[1])
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// AlignHorizontal sets a horizontal text alignment rule.
|
||||
func (s Style) AlignHorizontal(p Position) Style {
|
||||
s.set(alignHorizontalKey, p)
|
||||
return s
|
||||
}
|
||||
|
||||
// AlignVertical sets a vertical text alignment rule.
|
||||
func (s Style) AlignVertical(p Position) Style {
|
||||
s.set(alignVerticalKey, p)
|
||||
return s
|
||||
}
|
||||
|
||||
// Padding is a shorthand method for setting padding on all sides at once.
|
||||
//
|
||||
// With one argument, the value is applied to all sides.
|
||||
//
|
||||
// With two arguments, the value is applied to the vertical and horizontal
|
||||
// sides, in that order.
|
||||
//
|
||||
// With three arguments, the value is applied to the top side, the horizontal
|
||||
// sides, and the bottom side, in that order.
|
||||
//
|
||||
// With four arguments, the value is applied clockwise starting from the top
|
||||
// side, followed by the right side, then the bottom, and finally the left.
|
||||
//
|
||||
// With more than four arguments no padding will be added.
|
||||
func (s Style) Padding(i ...int) Style {
|
||||
top, right, bottom, left, ok := whichSidesInt(i...)
|
||||
if !ok {
|
||||
return s
|
||||
}
|
||||
|
||||
s.set(paddingTopKey, top)
|
||||
s.set(paddingRightKey, right)
|
||||
s.set(paddingBottomKey, bottom)
|
||||
s.set(paddingLeftKey, left)
|
||||
return s
|
||||
}
|
||||
|
||||
// PaddingLeft adds padding on the left.
|
||||
func (s Style) PaddingLeft(i int) Style {
|
||||
s.set(paddingLeftKey, i)
|
||||
return s
|
||||
}
|
||||
|
||||
// PaddingRight adds padding on the right.
|
||||
func (s Style) PaddingRight(i int) Style {
|
||||
s.set(paddingRightKey, i)
|
||||
return s
|
||||
}
|
||||
|
||||
// PaddingTop adds padding to the top of the block.
|
||||
func (s Style) PaddingTop(i int) Style {
|
||||
s.set(paddingTopKey, i)
|
||||
return s
|
||||
}
|
||||
|
||||
// PaddingBottom adds padding to the bottom of the block.
|
||||
func (s Style) PaddingBottom(i int) Style {
|
||||
s.set(paddingBottomKey, i)
|
||||
return s
|
||||
}
|
||||
|
||||
// ColorWhitespace determines whether or not the background color should be
|
||||
// applied to the padding. This is true by default as it's more than likely the
|
||||
// desired and expected behavior, but it can be disabled for certain graphic
|
||||
// effects.
|
||||
//
|
||||
// Deprecated: Just use margins and padding.
|
||||
func (s Style) ColorWhitespace(v bool) Style {
|
||||
s.set(colorWhitespaceKey, v)
|
||||
return s
|
||||
}
|
||||
|
||||
// Margin is a shorthand method for setting margins on all sides at once.
|
||||
//
|
||||
// With one argument, the value is applied to all sides.
|
||||
//
|
||||
// With two arguments, the value is applied to the vertical and horizontal
|
||||
// sides, in that order.
|
||||
//
|
||||
// With three arguments, the value is applied to the top side, the horizontal
|
||||
// sides, and the bottom side, in that order.
|
||||
//
|
||||
// With four arguments, the value is applied clockwise starting from the top
|
||||
// side, followed by the right side, then the bottom, and finally the left.
|
||||
//
|
||||
// With more than four arguments no margin will be added.
|
||||
func (s Style) Margin(i ...int) Style {
|
||||
top, right, bottom, left, ok := whichSidesInt(i...)
|
||||
if !ok {
|
||||
return s
|
||||
}
|
||||
|
||||
s.set(marginTopKey, top)
|
||||
s.set(marginRightKey, right)
|
||||
s.set(marginBottomKey, bottom)
|
||||
s.set(marginLeftKey, left)
|
||||
return s
|
||||
}
|
||||
|
||||
// MarginLeft sets the value of the left margin.
|
||||
func (s Style) MarginLeft(i int) Style {
|
||||
s.set(marginLeftKey, i)
|
||||
return s
|
||||
}
|
||||
|
||||
// MarginRight sets the value of the right margin.
|
||||
func (s Style) MarginRight(i int) Style {
|
||||
s.set(marginRightKey, i)
|
||||
return s
|
||||
}
|
||||
|
||||
// MarginTop sets the value of the top margin.
|
||||
func (s Style) MarginTop(i int) Style {
|
||||
s.set(marginTopKey, i)
|
||||
return s
|
||||
}
|
||||
|
||||
// MarginBottom sets the value of the bottom margin.
|
||||
func (s Style) MarginBottom(i int) Style {
|
||||
s.set(marginBottomKey, i)
|
||||
return s
|
||||
}
|
||||
|
||||
// MarginBackground sets the background color of the margin. Note that this is
|
||||
// also set when inheriting from a style with a background color. In that case
|
||||
// the background color on that style will set the margin color on this style.
|
||||
func (s Style) MarginBackground(c TerminalColor) Style {
|
||||
s.set(marginBackgroundKey, c)
|
||||
return s
|
||||
}
|
||||
|
||||
// Border is shorthand for setting the border style and which sides should
|
||||
// have a border at once. The variadic argument sides works as follows:
|
||||
//
|
||||
// With one value, the value is applied to all sides.
|
||||
//
|
||||
// With two values, the values are applied to the vertical and horizontal
|
||||
// sides, in that order.
|
||||
//
|
||||
// With three values, the values are applied to the top side, the horizontal
|
||||
// sides, and the bottom side, in that order.
|
||||
//
|
||||
// With four values, the values are applied clockwise starting from the top
|
||||
// side, followed by the right side, then the bottom, and finally the left.
|
||||
//
|
||||
// With more than four arguments the border will be applied to all sides.
|
||||
//
|
||||
// Examples:
|
||||
//
|
||||
// // Applies borders to the top and bottom only
|
||||
// lipgloss.NewStyle().Border(lipgloss.NormalBorder(), true, false)
|
||||
//
|
||||
// // Applies rounded borders to the right and bottom only
|
||||
// lipgloss.NewStyle().Border(lipgloss.RoundedBorder(), false, true, true, false)
|
||||
func (s Style) Border(b Border, sides ...bool) Style {
|
||||
s.set(borderStyleKey, b)
|
||||
|
||||
top, right, bottom, left, ok := whichSidesBool(sides...)
|
||||
if !ok {
|
||||
top = true
|
||||
right = true
|
||||
bottom = true
|
||||
left = true
|
||||
}
|
||||
|
||||
s.set(borderTopKey, top)
|
||||
s.set(borderRightKey, right)
|
||||
s.set(borderBottomKey, bottom)
|
||||
s.set(borderLeftKey, left)
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
// BorderStyle defines the Border on a style. A Border contains a series of
|
||||
// definitions for the sides and corners of a border.
|
||||
//
|
||||
// Note that if border visibility has not been set for any sides when setting
|
||||
// the border style, the border will be enabled for all sides during rendering.
|
||||
//
|
||||
// You can define border characters as you'd like, though several default
|
||||
// styles are included: NormalBorder(), RoundedBorder(), BlockBorder(),
|
||||
// OuterHalfBlockBorder(), InnerHalfBlockBorder(), ThickBorder(),
|
||||
// and DoubleBorder().
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// lipgloss.NewStyle().BorderStyle(lipgloss.ThickBorder())
|
||||
func (s Style) BorderStyle(b Border) Style {
|
||||
s.set(borderStyleKey, b)
|
||||
return s
|
||||
}
|
||||
|
||||
// BorderTop determines whether or not to draw a top border.
|
||||
func (s Style) BorderTop(v bool) Style {
|
||||
s.set(borderTopKey, v)
|
||||
return s
|
||||
}
|
||||
|
||||
// BorderRight determines whether or not to draw a right border.
|
||||
func (s Style) BorderRight(v bool) Style {
|
||||
s.set(borderRightKey, v)
|
||||
return s
|
||||
}
|
||||
|
||||
// BorderBottom determines whether or not to draw a bottom border.
|
||||
func (s Style) BorderBottom(v bool) Style {
|
||||
s.set(borderBottomKey, v)
|
||||
return s
|
||||
}
|
||||
|
||||
// BorderLeft determines whether or not to draw a left border.
|
||||
func (s Style) BorderLeft(v bool) Style {
|
||||
s.set(borderLeftKey, v)
|
||||
return s
|
||||
}
|
||||
|
||||
// BorderForeground is a shorthand function for setting all of the
|
||||
// foreground colors of the borders at once. The arguments work as follows:
|
||||
//
|
||||
// With one argument, the argument is applied to all sides.
|
||||
//
|
||||
// With two arguments, the arguments are applied to the vertical and horizontal
|
||||
// sides, in that order.
|
||||
//
|
||||
// With three arguments, the arguments are applied to the top side, the
|
||||
// horizontal sides, and the bottom side, in that order.
|
||||
//
|
||||
// With four arguments, the arguments are applied clockwise starting from the
|
||||
// top side, followed by the right side, then the bottom, and finally the left.
|
||||
//
|
||||
// With more than four arguments nothing will be set.
|
||||
func (s Style) BorderForeground(c ...TerminalColor) Style {
|
||||
if len(c) == 0 {
|
||||
return s
|
||||
}
|
||||
|
||||
top, right, bottom, left, ok := whichSidesColor(c...)
|
||||
if !ok {
|
||||
return s
|
||||
}
|
||||
|
||||
s.set(borderTopForegroundKey, top)
|
||||
s.set(borderRightForegroundKey, right)
|
||||
s.set(borderBottomForegroundKey, bottom)
|
||||
s.set(borderLeftForegroundKey, left)
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
// BorderTopForeground set the foreground color for the top of the border.
|
||||
func (s Style) BorderTopForeground(c TerminalColor) Style {
|
||||
s.set(borderTopForegroundKey, c)
|
||||
return s
|
||||
}
|
||||
|
||||
// BorderRightForeground sets the foreground color for the right side of the
|
||||
// border.
|
||||
func (s Style) BorderRightForeground(c TerminalColor) Style {
|
||||
s.set(borderRightForegroundKey, c)
|
||||
return s
|
||||
}
|
||||
|
||||
// BorderBottomForeground sets the foreground color for the bottom of the
|
||||
// border.
|
||||
func (s Style) BorderBottomForeground(c TerminalColor) Style {
|
||||
s.set(borderBottomForegroundKey, c)
|
||||
return s
|
||||
}
|
||||
|
||||
// BorderLeftForeground sets the foreground color for the left side of the
|
||||
// border.
|
||||
func (s Style) BorderLeftForeground(c TerminalColor) Style {
|
||||
s.set(borderLeftForegroundKey, c)
|
||||
return s
|
||||
}
|
||||
|
||||
// BorderBackground is a shorthand function for setting all of the
|
||||
// background colors of the borders at once. The arguments work as follows:
|
||||
//
|
||||
// With one argument, the argument is applied to all sides.
|
||||
//
|
||||
// With two arguments, the arguments are applied to the vertical and horizontal
|
||||
// sides, in that order.
|
||||
//
|
||||
// With three arguments, the arguments are applied to the top side, the
|
||||
// horizontal sides, and the bottom side, in that order.
|
||||
//
|
||||
// With four arguments, the arguments are applied clockwise starting from the
|
||||
// top side, followed by the right side, then the bottom, and finally the left.
|
||||
//
|
||||
// With more than four arguments nothing will be set.
|
||||
func (s Style) BorderBackground(c ...TerminalColor) Style {
|
||||
if len(c) == 0 {
|
||||
return s
|
||||
}
|
||||
|
||||
top, right, bottom, left, ok := whichSidesColor(c...)
|
||||
if !ok {
|
||||
return s
|
||||
}
|
||||
|
||||
s.set(borderTopBackgroundKey, top)
|
||||
s.set(borderRightBackgroundKey, right)
|
||||
s.set(borderBottomBackgroundKey, bottom)
|
||||
s.set(borderLeftBackgroundKey, left)
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
// BorderTopBackground sets the background color of the top of the border.
|
||||
func (s Style) BorderTopBackground(c TerminalColor) Style {
|
||||
s.set(borderTopBackgroundKey, c)
|
||||
return s
|
||||
}
|
||||
|
||||
// BorderRightBackground sets the background color of right side the border.
|
||||
func (s Style) BorderRightBackground(c TerminalColor) Style {
|
||||
s.set(borderRightBackgroundKey, c)
|
||||
return s
|
||||
}
|
||||
|
||||
// BorderBottomBackground sets the background color of the bottom of the
|
||||
// border.
|
||||
func (s Style) BorderBottomBackground(c TerminalColor) Style {
|
||||
s.set(borderBottomBackgroundKey, c)
|
||||
return s
|
||||
}
|
||||
|
||||
// BorderLeftBackground set the background color of the left side of the
|
||||
// border.
|
||||
func (s Style) BorderLeftBackground(c TerminalColor) Style {
|
||||
s.set(borderLeftBackgroundKey, c)
|
||||
return s
|
||||
}
|
||||
|
||||
// Inline makes rendering output one line and disables the rendering of
|
||||
// margins, padding and borders. This is useful when you need a style to apply
|
||||
// only to font rendering and don't want it to change any physical dimensions.
|
||||
// It works well with Style.MaxWidth.
|
||||
//
|
||||
// Because this in intended to be used at the time of render, this method will
|
||||
// not mutate the style and instead return a copy.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// var userInput string = "..."
|
||||
// var userStyle = text.Style{ /* ... */ }
|
||||
// fmt.Println(userStyle.Inline(true).Render(userInput))
|
||||
func (s Style) Inline(v bool) Style {
|
||||
o := s // copy
|
||||
o.set(inlineKey, v)
|
||||
return o
|
||||
}
|
||||
|
||||
// MaxWidth applies a max width to a given style. This is useful in enforcing
|
||||
// a certain width at render time, particularly with arbitrary strings and
|
||||
// styles.
|
||||
//
|
||||
// Because this in intended to be used at the time of render, this method will
|
||||
// not mutate the style and instead return a copy.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// var userInput string = "..."
|
||||
// var userStyle = text.Style{ /* ... */ }
|
||||
// fmt.Println(userStyle.MaxWidth(16).Render(userInput))
|
||||
func (s Style) MaxWidth(n int) Style {
|
||||
o := s // copy
|
||||
o.set(maxWidthKey, n)
|
||||
return o
|
||||
}
|
||||
|
||||
// MaxHeight applies a max height to a given style. This is useful in enforcing
|
||||
// a certain height at render time, particularly with arbitrary strings and
|
||||
// styles.
|
||||
//
|
||||
// Because this in intended to be used at the time of render, this method will
|
||||
// not mutate the style and instead returns a copy.
|
||||
func (s Style) MaxHeight(n int) Style {
|
||||
o := s // copy
|
||||
o.set(maxHeightKey, n)
|
||||
return o
|
||||
}
|
||||
|
||||
// NoTabConversion can be passed to [Style.TabWidth] to disable the replacement
|
||||
// of tabs with spaces at render time.
|
||||
const NoTabConversion = -1
|
||||
|
||||
// TabWidth sets the number of spaces that a tab (/t) should be rendered as.
|
||||
// When set to 0, tabs will be removed. To disable the replacement of tabs with
|
||||
// spaces entirely, set this to [NoTabConversion].
|
||||
//
|
||||
// By default, tabs will be replaced with 4 spaces.
|
||||
func (s Style) TabWidth(n int) Style {
|
||||
if n <= -1 {
|
||||
n = -1
|
||||
}
|
||||
s.set(tabWidthKey, n)
|
||||
return s
|
||||
}
|
||||
|
||||
// UnderlineSpaces determines whether to underline spaces between words. By
|
||||
// default, this is true. Spaces can also be underlined without underlining the
|
||||
// text itself.
|
||||
func (s Style) UnderlineSpaces(v bool) Style {
|
||||
s.set(underlineSpacesKey, v)
|
||||
return s
|
||||
}
|
||||
|
||||
// StrikethroughSpaces determines whether to apply strikethroughs to spaces
|
||||
// between words. By default, this is true. Spaces can also be struck without
|
||||
// underlining the text itself.
|
||||
func (s Style) StrikethroughSpaces(v bool) Style {
|
||||
s.set(strikethroughSpacesKey, v)
|
||||
return s
|
||||
}
|
||||
|
||||
// Transform applies a given function to a string at render time, allowing for
|
||||
// the string being rendered to be manipuated.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// s := NewStyle().Transform(strings.ToUpper)
|
||||
// fmt.Println(s.Render("raow!") // "RAOW!"
|
||||
func (s Style) Transform(fn func(string) string) Style {
|
||||
s.set(transformKey, fn)
|
||||
return s
|
||||
}
|
||||
|
||||
// Renderer sets the renderer for the style. This is useful for changing the
|
||||
// renderer for a style that is being used in a different context.
|
||||
func (s Style) Renderer(r *Renderer) Style {
|
||||
s.r = r
|
||||
return s
|
||||
}
|
||||
|
||||
// whichSidesInt is a helper method for setting values on sides of a block based
|
||||
// on the number of arguments. It follows the CSS shorthand rules for blocks
|
||||
// like margin, padding. and borders. Here are how the rules work:
|
||||
//
|
||||
// 0 args: do nothing
|
||||
// 1 arg: all sides
|
||||
// 2 args: top -> bottom
|
||||
// 3 args: top -> horizontal -> bottom
|
||||
// 4 args: top -> right -> bottom -> left
|
||||
// 5+ args: do nothing.
|
||||
func whichSidesInt(i ...int) (top, right, bottom, left int, ok bool) {
|
||||
switch len(i) {
|
||||
case 1:
|
||||
top = i[0]
|
||||
bottom = i[0]
|
||||
left = i[0]
|
||||
right = i[0]
|
||||
ok = true
|
||||
case 2: //nolint:gomnd
|
||||
top = i[0]
|
||||
bottom = i[0]
|
||||
left = i[1]
|
||||
right = i[1]
|
||||
ok = true
|
||||
case 3: //nolint:gomnd
|
||||
top = i[0]
|
||||
left = i[1]
|
||||
right = i[1]
|
||||
bottom = i[2]
|
||||
ok = true
|
||||
case 4: //nolint:gomnd
|
||||
top = i[0]
|
||||
right = i[1]
|
||||
bottom = i[2]
|
||||
left = i[3]
|
||||
ok = true
|
||||
}
|
||||
return top, right, bottom, left, ok
|
||||
}
|
||||
|
||||
// whichSidesBool is like whichSidesInt, except it operates on a series of
|
||||
// boolean values. See the comment on whichSidesInt for details on how this
|
||||
// works.
|
||||
func whichSidesBool(i ...bool) (top, right, bottom, left bool, ok bool) {
|
||||
switch len(i) {
|
||||
case 1:
|
||||
top = i[0]
|
||||
bottom = i[0]
|
||||
left = i[0]
|
||||
right = i[0]
|
||||
ok = true
|
||||
case 2: //nolint:gomnd
|
||||
top = i[0]
|
||||
bottom = i[0]
|
||||
left = i[1]
|
||||
right = i[1]
|
||||
ok = true
|
||||
case 3: //nolint:gomnd
|
||||
top = i[0]
|
||||
left = i[1]
|
||||
right = i[1]
|
||||
bottom = i[2]
|
||||
ok = true
|
||||
case 4: //nolint:gomnd
|
||||
top = i[0]
|
||||
right = i[1]
|
||||
bottom = i[2]
|
||||
left = i[3]
|
||||
ok = true
|
||||
}
|
||||
return top, right, bottom, left, ok
|
||||
}
|
||||
|
||||
// whichSidesColor is like whichSides, except it operates on a series of
|
||||
// boolean values. See the comment on whichSidesInt for details on how this
|
||||
// works.
|
||||
func whichSidesColor(i ...TerminalColor) (top, right, bottom, left TerminalColor, ok bool) {
|
||||
switch len(i) {
|
||||
case 1:
|
||||
top = i[0]
|
||||
bottom = i[0]
|
||||
left = i[0]
|
||||
right = i[0]
|
||||
ok = true
|
||||
case 2: //nolint:gomnd
|
||||
top = i[0]
|
||||
bottom = i[0]
|
||||
left = i[1]
|
||||
right = i[1]
|
||||
ok = true
|
||||
case 3: //nolint:gomnd
|
||||
top = i[0]
|
||||
left = i[1]
|
||||
right = i[1]
|
||||
bottom = i[2]
|
||||
ok = true
|
||||
case 4: //nolint:gomnd
|
||||
top = i[0]
|
||||
right = i[1]
|
||||
bottom = i[2]
|
||||
left = i[3]
|
||||
ok = true
|
||||
}
|
||||
return top, right, bottom, left, ok
|
||||
}
|
41
vendor/github.com/charmbracelet/lipgloss/size.go
generated
vendored
Normal file
41
vendor/github.com/charmbracelet/lipgloss/size.go
generated
vendored
Normal file
@ -0,0 +1,41 @@
|
||||
package lipgloss
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/charmbracelet/x/ansi"
|
||||
)
|
||||
|
||||
// Width returns the cell width of characters in the string. ANSI sequences are
|
||||
// ignored and characters wider than one cell (such as Chinese characters and
|
||||
// emojis) are appropriately measured.
|
||||
//
|
||||
// You should use this instead of len(string) len([]rune(string) as neither
|
||||
// will give you accurate results.
|
||||
func Width(str string) (width int) {
|
||||
for _, l := range strings.Split(str, "\n") {
|
||||
w := ansi.StringWidth(l)
|
||||
if w > width {
|
||||
width = w
|
||||
}
|
||||
}
|
||||
|
||||
return width
|
||||
}
|
||||
|
||||
// Height returns height of a string in cells. This is done simply by
|
||||
// counting \n characters. If your strings use \r\n for newlines you should
|
||||
// convert them to \n first, or simply write a separate function for measuring
|
||||
// height.
|
||||
func Height(str string) int {
|
||||
return strings.Count(str, "\n") + 1
|
||||
}
|
||||
|
||||
// Size returns the width and height of the string in cells. ANSI sequences are
|
||||
// ignored and characters wider than one cell (such as Chinese characters and
|
||||
// emojis) are appropriately measured.
|
||||
func Size(str string) (width, height int) {
|
||||
width = Width(str)
|
||||
height = Height(str)
|
||||
return width, height
|
||||
}
|
587
vendor/github.com/charmbracelet/lipgloss/style.go
generated
vendored
Normal file
587
vendor/github.com/charmbracelet/lipgloss/style.go
generated
vendored
Normal file
@ -0,0 +1,587 @@
|
||||
package lipgloss
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"github.com/charmbracelet/x/ansi"
|
||||
"github.com/muesli/termenv"
|
||||
)
|
||||
|
||||
const tabWidthDefault = 4
|
||||
|
||||
// Property for a key.
|
||||
type propKey int64
|
||||
|
||||
// Available properties.
|
||||
const (
|
||||
// Boolean props come first.
|
||||
boldKey propKey = 1 << iota
|
||||
italicKey
|
||||
underlineKey
|
||||
strikethroughKey
|
||||
reverseKey
|
||||
blinkKey
|
||||
faintKey
|
||||
underlineSpacesKey
|
||||
strikethroughSpacesKey
|
||||
colorWhitespaceKey
|
||||
|
||||
// Non-boolean props.
|
||||
foregroundKey
|
||||
backgroundKey
|
||||
widthKey
|
||||
heightKey
|
||||
alignHorizontalKey
|
||||
alignVerticalKey
|
||||
|
||||
// Padding.
|
||||
paddingTopKey
|
||||
paddingRightKey
|
||||
paddingBottomKey
|
||||
paddingLeftKey
|
||||
|
||||
// Margins.
|
||||
marginTopKey
|
||||
marginRightKey
|
||||
marginBottomKey
|
||||
marginLeftKey
|
||||
marginBackgroundKey
|
||||
|
||||
// Border runes.
|
||||
borderStyleKey
|
||||
|
||||
// Border edges.
|
||||
borderTopKey
|
||||
borderRightKey
|
||||
borderBottomKey
|
||||
borderLeftKey
|
||||
|
||||
// Border foreground colors.
|
||||
borderTopForegroundKey
|
||||
borderRightForegroundKey
|
||||
borderBottomForegroundKey
|
||||
borderLeftForegroundKey
|
||||
|
||||
// Border background colors.
|
||||
borderTopBackgroundKey
|
||||
borderRightBackgroundKey
|
||||
borderBottomBackgroundKey
|
||||
borderLeftBackgroundKey
|
||||
|
||||
inlineKey
|
||||
maxWidthKey
|
||||
maxHeightKey
|
||||
tabWidthKey
|
||||
|
||||
transformKey
|
||||
)
|
||||
|
||||
// props is a set of properties.
|
||||
type props int64
|
||||
|
||||
// set sets a property.
|
||||
func (p props) set(k propKey) props {
|
||||
return p | props(k)
|
||||
}
|
||||
|
||||
// unset unsets a property.
|
||||
func (p props) unset(k propKey) props {
|
||||
return p &^ props(k)
|
||||
}
|
||||
|
||||
// has checks if a property is set.
|
||||
func (p props) has(k propKey) bool {
|
||||
return p&props(k) != 0
|
||||
}
|
||||
|
||||
// NewStyle returns a new, empty Style. While it's syntactic sugar for the
|
||||
// Style{} primitive, it's recommended to use this function for creating styles
|
||||
// in case the underlying implementation changes. It takes an optional string
|
||||
// value to be set as the underlying string value for this style.
|
||||
func NewStyle() Style {
|
||||
return renderer.NewStyle()
|
||||
}
|
||||
|
||||
// NewStyle returns a new, empty Style. While it's syntactic sugar for the
|
||||
// Style{} primitive, it's recommended to use this function for creating styles
|
||||
// in case the underlying implementation changes. It takes an optional string
|
||||
// value to be set as the underlying string value for this style.
|
||||
func (r *Renderer) NewStyle() Style {
|
||||
s := Style{r: r}
|
||||
return s
|
||||
}
|
||||
|
||||
// Style contains a set of rules that comprise a style as a whole.
|
||||
type Style struct {
|
||||
r *Renderer
|
||||
props props
|
||||
value string
|
||||
|
||||
// we store bool props values here
|
||||
attrs int
|
||||
|
||||
// props that have values
|
||||
fgColor TerminalColor
|
||||
bgColor TerminalColor
|
||||
|
||||
width int
|
||||
height int
|
||||
|
||||
alignHorizontal Position
|
||||
alignVertical Position
|
||||
|
||||
paddingTop int
|
||||
paddingRight int
|
||||
paddingBottom int
|
||||
paddingLeft int
|
||||
|
||||
marginTop int
|
||||
marginRight int
|
||||
marginBottom int
|
||||
marginLeft int
|
||||
marginBgColor TerminalColor
|
||||
|
||||
borderStyle Border
|
||||
borderTopFgColor TerminalColor
|
||||
borderRightFgColor TerminalColor
|
||||
borderBottomFgColor TerminalColor
|
||||
borderLeftFgColor TerminalColor
|
||||
borderTopBgColor TerminalColor
|
||||
borderRightBgColor TerminalColor
|
||||
borderBottomBgColor TerminalColor
|
||||
borderLeftBgColor TerminalColor
|
||||
|
||||
maxWidth int
|
||||
maxHeight int
|
||||
tabWidth int
|
||||
|
||||
transform func(string) string
|
||||
}
|
||||
|
||||
// joinString joins a list of strings into a single string separated with a
|
||||
// space.
|
||||
func joinString(strs ...string) string {
|
||||
return strings.Join(strs, " ")
|
||||
}
|
||||
|
||||
// SetString sets the underlying string value for this style. To render once
|
||||
// the underlying string is set, use the Style.String. This method is
|
||||
// a convenience for cases when having a stringer implementation is handy, such
|
||||
// as when using fmt.Sprintf. You can also simply define a style and render out
|
||||
// strings directly with Style.Render.
|
||||
func (s Style) SetString(strs ...string) Style {
|
||||
s.value = joinString(strs...)
|
||||
return s
|
||||
}
|
||||
|
||||
// Value returns the raw, unformatted, underlying string value for this style.
|
||||
func (s Style) Value() string {
|
||||
return s.value
|
||||
}
|
||||
|
||||
// String implements stringer for a Style, returning the rendered result based
|
||||
// on the rules in this style. An underlying string value must be set with
|
||||
// Style.SetString prior to using this method.
|
||||
func (s Style) String() string {
|
||||
return s.Render()
|
||||
}
|
||||
|
||||
// Copy returns a copy of this style, including any underlying string values.
|
||||
//
|
||||
// Deprecated: to copy just use assignment (i.e. a := b). All methods also
|
||||
// return a new style.
|
||||
func (s Style) Copy() Style {
|
||||
return s
|
||||
}
|
||||
|
||||
// Inherit overlays the style in the argument onto this style by copying each explicitly
|
||||
// set value from the argument style onto this style if it is not already explicitly set.
|
||||
// Existing set values are kept intact and not overwritten.
|
||||
//
|
||||
// Margins, padding, and underlying string values are not inherited.
|
||||
func (s Style) Inherit(i Style) Style {
|
||||
for k := boldKey; k <= transformKey; k <<= 1 {
|
||||
if !i.isSet(k) {
|
||||
continue
|
||||
}
|
||||
|
||||
switch k { //nolint:exhaustive
|
||||
case marginTopKey, marginRightKey, marginBottomKey, marginLeftKey:
|
||||
// Margins are not inherited
|
||||
continue
|
||||
case paddingTopKey, paddingRightKey, paddingBottomKey, paddingLeftKey:
|
||||
// Padding is not inherited
|
||||
continue
|
||||
case backgroundKey:
|
||||
// The margins also inherit the background color
|
||||
if !s.isSet(marginBackgroundKey) && !i.isSet(marginBackgroundKey) {
|
||||
s.set(marginBackgroundKey, i.bgColor)
|
||||
}
|
||||
}
|
||||
|
||||
if s.isSet(k) {
|
||||
continue
|
||||
}
|
||||
|
||||
s.setFrom(k, i)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// Render applies the defined style formatting to a given string.
|
||||
func (s Style) Render(strs ...string) string {
|
||||
if s.r == nil {
|
||||
s.r = renderer
|
||||
}
|
||||
if s.value != "" {
|
||||
strs = append([]string{s.value}, strs...)
|
||||
}
|
||||
|
||||
var (
|
||||
str = joinString(strs...)
|
||||
|
||||
p = s.r.ColorProfile()
|
||||
te = p.String()
|
||||
teSpace = p.String()
|
||||
teWhitespace = p.String()
|
||||
|
||||
bold = s.getAsBool(boldKey, false)
|
||||
italic = s.getAsBool(italicKey, false)
|
||||
underline = s.getAsBool(underlineKey, false)
|
||||
strikethrough = s.getAsBool(strikethroughKey, false)
|
||||
reverse = s.getAsBool(reverseKey, false)
|
||||
blink = s.getAsBool(blinkKey, false)
|
||||
faint = s.getAsBool(faintKey, false)
|
||||
|
||||
fg = s.getAsColor(foregroundKey)
|
||||
bg = s.getAsColor(backgroundKey)
|
||||
|
||||
width = s.getAsInt(widthKey)
|
||||
height = s.getAsInt(heightKey)
|
||||
horizontalAlign = s.getAsPosition(alignHorizontalKey)
|
||||
verticalAlign = s.getAsPosition(alignVerticalKey)
|
||||
|
||||
topPadding = s.getAsInt(paddingTopKey)
|
||||
rightPadding = s.getAsInt(paddingRightKey)
|
||||
bottomPadding = s.getAsInt(paddingBottomKey)
|
||||
leftPadding = s.getAsInt(paddingLeftKey)
|
||||
|
||||
colorWhitespace = s.getAsBool(colorWhitespaceKey, true)
|
||||
inline = s.getAsBool(inlineKey, false)
|
||||
maxWidth = s.getAsInt(maxWidthKey)
|
||||
maxHeight = s.getAsInt(maxHeightKey)
|
||||
|
||||
underlineSpaces = s.getAsBool(underlineSpacesKey, false) || (underline && s.getAsBool(underlineSpacesKey, true))
|
||||
strikethroughSpaces = s.getAsBool(strikethroughSpacesKey, false) || (strikethrough && s.getAsBool(strikethroughSpacesKey, true))
|
||||
|
||||
// Do we need to style whitespace (padding and space outside
|
||||
// paragraphs) separately?
|
||||
styleWhitespace = reverse
|
||||
|
||||
// Do we need to style spaces separately?
|
||||
useSpaceStyler = (underline && !underlineSpaces) || (strikethrough && !strikethroughSpaces) || underlineSpaces || strikethroughSpaces
|
||||
|
||||
transform = s.getAsTransform(transformKey)
|
||||
)
|
||||
|
||||
if transform != nil {
|
||||
str = transform(str)
|
||||
}
|
||||
|
||||
if s.props == 0 {
|
||||
return s.maybeConvertTabs(str)
|
||||
}
|
||||
|
||||
// Enable support for ANSI on the legacy Windows cmd.exe console. This is a
|
||||
// no-op on non-Windows systems and on Windows runs only once.
|
||||
enableLegacyWindowsANSI()
|
||||
|
||||
if bold {
|
||||
te = te.Bold()
|
||||
}
|
||||
if italic {
|
||||
te = te.Italic()
|
||||
}
|
||||
if underline {
|
||||
te = te.Underline()
|
||||
}
|
||||
if reverse {
|
||||
if reverse {
|
||||
teWhitespace = teWhitespace.Reverse()
|
||||
}
|
||||
te = te.Reverse()
|
||||
}
|
||||
if blink {
|
||||
te = te.Blink()
|
||||
}
|
||||
if faint {
|
||||
te = te.Faint()
|
||||
}
|
||||
|
||||
if fg != noColor {
|
||||
te = te.Foreground(fg.color(s.r))
|
||||
if styleWhitespace {
|
||||
teWhitespace = teWhitespace.Foreground(fg.color(s.r))
|
||||
}
|
||||
if useSpaceStyler {
|
||||
teSpace = teSpace.Foreground(fg.color(s.r))
|
||||
}
|
||||
}
|
||||
|
||||
if bg != noColor {
|
||||
te = te.Background(bg.color(s.r))
|
||||
if colorWhitespace {
|
||||
teWhitespace = teWhitespace.Background(bg.color(s.r))
|
||||
}
|
||||
if useSpaceStyler {
|
||||
teSpace = teSpace.Background(bg.color(s.r))
|
||||
}
|
||||
}
|
||||
|
||||
if underline {
|
||||
te = te.Underline()
|
||||
}
|
||||
if strikethrough {
|
||||
te = te.CrossOut()
|
||||
}
|
||||
|
||||
if underlineSpaces {
|
||||
teSpace = teSpace.Underline()
|
||||
}
|
||||
if strikethroughSpaces {
|
||||
teSpace = teSpace.CrossOut()
|
||||
}
|
||||
|
||||
// Potentially convert tabs to spaces
|
||||
str = s.maybeConvertTabs(str)
|
||||
|
||||
// Strip newlines in single line mode
|
||||
if inline {
|
||||
str = strings.ReplaceAll(str, "\n", "")
|
||||
}
|
||||
|
||||
// Word wrap
|
||||
if !inline && width > 0 {
|
||||
wrapAt := width - leftPadding - rightPadding
|
||||
str = ansi.Wrap(str, wrapAt, "")
|
||||
}
|
||||
|
||||
// Render core text
|
||||
{
|
||||
var b strings.Builder
|
||||
|
||||
l := strings.Split(str, "\n")
|
||||
for i := range l {
|
||||
if useSpaceStyler {
|
||||
// Look for spaces and apply a different styler
|
||||
for _, r := range l[i] {
|
||||
if unicode.IsSpace(r) {
|
||||
b.WriteString(teSpace.Styled(string(r)))
|
||||
continue
|
||||
}
|
||||
b.WriteString(te.Styled(string(r)))
|
||||
}
|
||||
} else {
|
||||
b.WriteString(te.Styled(l[i]))
|
||||
}
|
||||
if i != len(l)-1 {
|
||||
b.WriteRune('\n')
|
||||
}
|
||||
}
|
||||
|
||||
str = b.String()
|
||||
}
|
||||
|
||||
// Padding
|
||||
if !inline { //nolint:nestif
|
||||
if leftPadding > 0 {
|
||||
var st *termenv.Style
|
||||
if colorWhitespace || styleWhitespace {
|
||||
st = &teWhitespace
|
||||
}
|
||||
str = padLeft(str, leftPadding, st)
|
||||
}
|
||||
|
||||
if rightPadding > 0 {
|
||||
var st *termenv.Style
|
||||
if colorWhitespace || styleWhitespace {
|
||||
st = &teWhitespace
|
||||
}
|
||||
str = padRight(str, rightPadding, st)
|
||||
}
|
||||
|
||||
if topPadding > 0 {
|
||||
str = strings.Repeat("\n", topPadding) + str
|
||||
}
|
||||
|
||||
if bottomPadding > 0 {
|
||||
str += strings.Repeat("\n", bottomPadding)
|
||||
}
|
||||
}
|
||||
|
||||
// Height
|
||||
if height > 0 {
|
||||
str = alignTextVertical(str, verticalAlign, height, nil)
|
||||
}
|
||||
|
||||
// Set alignment. This will also pad short lines with spaces so that all
|
||||
// lines are the same length, so we run it under a few different conditions
|
||||
// beyond alignment.
|
||||
{
|
||||
numLines := strings.Count(str, "\n")
|
||||
|
||||
if !(numLines == 0 && width == 0) {
|
||||
var st *termenv.Style
|
||||
if colorWhitespace || styleWhitespace {
|
||||
st = &teWhitespace
|
||||
}
|
||||
str = alignTextHorizontal(str, horizontalAlign, width, st)
|
||||
}
|
||||
}
|
||||
|
||||
if !inline {
|
||||
str = s.applyBorder(str)
|
||||
str = s.applyMargins(str, inline)
|
||||
}
|
||||
|
||||
// Truncate according to MaxWidth
|
||||
if maxWidth > 0 {
|
||||
lines := strings.Split(str, "\n")
|
||||
|
||||
for i := range lines {
|
||||
lines[i] = ansi.Truncate(lines[i], maxWidth, "")
|
||||
}
|
||||
|
||||
str = strings.Join(lines, "\n")
|
||||
}
|
||||
|
||||
// Truncate according to MaxHeight
|
||||
if maxHeight > 0 {
|
||||
lines := strings.Split(str, "\n")
|
||||
height := min(maxHeight, len(lines))
|
||||
if len(lines) > 0 {
|
||||
str = strings.Join(lines[:height], "\n")
|
||||
}
|
||||
}
|
||||
|
||||
return str
|
||||
}
|
||||
|
||||
func (s Style) maybeConvertTabs(str string) string {
|
||||
tw := tabWidthDefault
|
||||
if s.isSet(tabWidthKey) {
|
||||
tw = s.getAsInt(tabWidthKey)
|
||||
}
|
||||
switch tw {
|
||||
case -1:
|
||||
return str
|
||||
case 0:
|
||||
return strings.ReplaceAll(str, "\t", "")
|
||||
default:
|
||||
return strings.ReplaceAll(str, "\t", strings.Repeat(" ", tw))
|
||||
}
|
||||
}
|
||||
|
||||
func (s Style) applyMargins(str string, inline bool) string {
|
||||
var (
|
||||
topMargin = s.getAsInt(marginTopKey)
|
||||
rightMargin = s.getAsInt(marginRightKey)
|
||||
bottomMargin = s.getAsInt(marginBottomKey)
|
||||
leftMargin = s.getAsInt(marginLeftKey)
|
||||
|
||||
styler termenv.Style
|
||||
)
|
||||
|
||||
bgc := s.getAsColor(marginBackgroundKey)
|
||||
if bgc != noColor {
|
||||
styler = styler.Background(bgc.color(s.r))
|
||||
}
|
||||
|
||||
// Add left and right margin
|
||||
str = padLeft(str, leftMargin, &styler)
|
||||
str = padRight(str, rightMargin, &styler)
|
||||
|
||||
// Top/bottom margin
|
||||
if !inline {
|
||||
_, width := getLines(str)
|
||||
spaces := strings.Repeat(" ", width)
|
||||
|
||||
if topMargin > 0 {
|
||||
str = styler.Styled(strings.Repeat(spaces+"\n", topMargin)) + str
|
||||
}
|
||||
if bottomMargin > 0 {
|
||||
str += styler.Styled(strings.Repeat("\n"+spaces, bottomMargin))
|
||||
}
|
||||
}
|
||||
|
||||
return str
|
||||
}
|
||||
|
||||
// Apply left padding.
|
||||
func padLeft(str string, n int, style *termenv.Style) string {
|
||||
return pad(str, -n, style)
|
||||
}
|
||||
|
||||
// Apply right padding.
|
||||
func padRight(str string, n int, style *termenv.Style) string {
|
||||
return pad(str, n, style)
|
||||
}
|
||||
|
||||
// pad adds padding to either the left or right side of a string.
|
||||
// Positive values add to the right side while negative values
|
||||
// add to the left side.
|
||||
func pad(str string, n int, style *termenv.Style) string {
|
||||
if n == 0 {
|
||||
return str
|
||||
}
|
||||
|
||||
sp := strings.Repeat(" ", abs(n))
|
||||
if style != nil {
|
||||
sp = style.Styled(sp)
|
||||
}
|
||||
|
||||
b := strings.Builder{}
|
||||
l := strings.Split(str, "\n")
|
||||
|
||||
for i := range l {
|
||||
switch {
|
||||
// pad right
|
||||
case n > 0:
|
||||
b.WriteString(l[i])
|
||||
b.WriteString(sp)
|
||||
// pad left
|
||||
default:
|
||||
b.WriteString(sp)
|
||||
b.WriteString(l[i])
|
||||
}
|
||||
|
||||
if i != len(l)-1 {
|
||||
b.WriteRune('\n')
|
||||
}
|
||||
}
|
||||
|
||||
return b.String()
|
||||
}
|
||||
|
||||
func max(a, b int) int { //nolint:unparam
|
||||
if a > b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func min(a, b int) int {
|
||||
if a < b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func abs(a int) int {
|
||||
if a < 0 {
|
||||
return -a
|
||||
}
|
||||
|
||||
return a
|
||||
}
|
113
vendor/github.com/charmbracelet/lipgloss/table/rows.go
generated
vendored
Normal file
113
vendor/github.com/charmbracelet/lipgloss/table/rows.go
generated
vendored
Normal file
@ -0,0 +1,113 @@
|
||||
package table
|
||||
|
||||
// Data is the interface that wraps the basic methods of a table model.
|
||||
type Data interface {
|
||||
// At returns the contents of the cell at the given index.
|
||||
At(row, cell int) string
|
||||
|
||||
// Rows returns the number of rows in the table.
|
||||
Rows() int
|
||||
|
||||
// Columns returns the number of columns in the table.
|
||||
Columns() int
|
||||
}
|
||||
|
||||
// StringData is a string-based implementation of the Data interface.
|
||||
type StringData struct {
|
||||
rows [][]string
|
||||
columns int
|
||||
}
|
||||
|
||||
// NewStringData creates a new StringData with the given number of columns.
|
||||
func NewStringData(rows ...[]string) *StringData {
|
||||
m := StringData{columns: 0}
|
||||
|
||||
for _, row := range rows {
|
||||
m.columns = max(m.columns, len(row))
|
||||
m.rows = append(m.rows, row)
|
||||
}
|
||||
|
||||
return &m
|
||||
}
|
||||
|
||||
// Append appends the given row to the table.
|
||||
func (m *StringData) Append(row []string) {
|
||||
m.columns = max(m.columns, len(row))
|
||||
m.rows = append(m.rows, row)
|
||||
}
|
||||
|
||||
// At returns the contents of the cell at the given index.
|
||||
func (m *StringData) At(row, cell int) string {
|
||||
if row >= len(m.rows) || cell >= len(m.rows[row]) {
|
||||
return ""
|
||||
}
|
||||
|
||||
return m.rows[row][cell]
|
||||
}
|
||||
|
||||
// Columns returns the number of columns in the table.
|
||||
func (m *StringData) Columns() int {
|
||||
return m.columns
|
||||
}
|
||||
|
||||
// Item appends the given row to the table.
|
||||
func (m *StringData) Item(rows ...string) *StringData {
|
||||
m.columns = max(m.columns, len(rows))
|
||||
m.rows = append(m.rows, rows)
|
||||
return m
|
||||
}
|
||||
|
||||
// Rows returns the number of rows in the table.
|
||||
func (m *StringData) Rows() int {
|
||||
return len(m.rows)
|
||||
}
|
||||
|
||||
// Filter applies a filter on some data.
|
||||
type Filter struct {
|
||||
data Data
|
||||
filter func(row int) bool
|
||||
}
|
||||
|
||||
// NewFilter initializes a new Filter.
|
||||
func NewFilter(data Data) *Filter {
|
||||
return &Filter{data: data}
|
||||
}
|
||||
|
||||
// Filter applies the given filter function to the data.
|
||||
func (m *Filter) Filter(f func(row int) bool) *Filter {
|
||||
m.filter = f
|
||||
return m
|
||||
}
|
||||
|
||||
// At returns the row at the given index.
|
||||
func (m *Filter) At(row, cell int) string {
|
||||
j := 0
|
||||
for i := 0; i < m.data.Rows(); i++ {
|
||||
if m.filter(i) {
|
||||
if j == row {
|
||||
return m.data.At(i, cell)
|
||||
}
|
||||
|
||||
j++
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
// Columns returns the number of columns in the table.
|
||||
func (m *Filter) Columns() int {
|
||||
return m.data.Columns()
|
||||
}
|
||||
|
||||
// Rows returns the number of rows in the table.
|
||||
func (m *Filter) Rows() int {
|
||||
j := 0
|
||||
for i := 0; i < m.data.Rows(); i++ {
|
||||
if m.filter(i) {
|
||||
j++
|
||||
}
|
||||
}
|
||||
|
||||
return j
|
||||
}
|
521
vendor/github.com/charmbracelet/lipgloss/table/table.go
generated
vendored
Normal file
521
vendor/github.com/charmbracelet/lipgloss/table/table.go
generated
vendored
Normal file
@ -0,0 +1,521 @@
|
||||
package table
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
"github.com/charmbracelet/x/ansi"
|
||||
)
|
||||
|
||||
// StyleFunc is the style function that determines the style of a Cell.
|
||||
//
|
||||
// It takes the row and column of the cell as an input and determines the
|
||||
// lipgloss Style to use for that cell position.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// t := table.New().
|
||||
// Headers("Name", "Age").
|
||||
// Row("Kini", 4).
|
||||
// Row("Eli", 1).
|
||||
// Row("Iris", 102).
|
||||
// StyleFunc(func(row, col int) lipgloss.Style {
|
||||
// switch {
|
||||
// case row == 0:
|
||||
// return HeaderStyle
|
||||
// case row%2 == 0:
|
||||
// return EvenRowStyle
|
||||
// default:
|
||||
// return OddRowStyle
|
||||
// }
|
||||
// })
|
||||
type StyleFunc func(row, col int) lipgloss.Style
|
||||
|
||||
// DefaultStyles is a TableStyleFunc that returns a new Style with no attributes.
|
||||
func DefaultStyles(_, _ int) lipgloss.Style {
|
||||
return lipgloss.NewStyle()
|
||||
}
|
||||
|
||||
// Table is a type for rendering tables.
|
||||
type Table struct {
|
||||
styleFunc StyleFunc
|
||||
border lipgloss.Border
|
||||
|
||||
borderTop bool
|
||||
borderBottom bool
|
||||
borderLeft bool
|
||||
borderRight bool
|
||||
borderHeader bool
|
||||
borderColumn bool
|
||||
borderRow bool
|
||||
|
||||
borderStyle lipgloss.Style
|
||||
headers []string
|
||||
data Data
|
||||
|
||||
width int
|
||||
height int
|
||||
offset int
|
||||
|
||||
// widths tracks the width of each column.
|
||||
widths []int
|
||||
|
||||
// heights tracks the height of each row.
|
||||
heights []int
|
||||
}
|
||||
|
||||
// New returns a new Table that can be modified through different
|
||||
// attributes.
|
||||
//
|
||||
// By default, a table has no border, no styling, and no rows.
|
||||
func New() *Table {
|
||||
return &Table{
|
||||
styleFunc: DefaultStyles,
|
||||
border: lipgloss.RoundedBorder(),
|
||||
borderBottom: true,
|
||||
borderColumn: true,
|
||||
borderHeader: true,
|
||||
borderLeft: true,
|
||||
borderRight: true,
|
||||
borderTop: true,
|
||||
data: NewStringData(),
|
||||
}
|
||||
}
|
||||
|
||||
// ClearRows clears the table rows.
|
||||
func (t *Table) ClearRows() *Table {
|
||||
t.data = nil
|
||||
return t
|
||||
}
|
||||
|
||||
// StyleFunc sets the style for a cell based on it's position (row, column).
|
||||
func (t *Table) StyleFunc(style StyleFunc) *Table {
|
||||
t.styleFunc = style
|
||||
return t
|
||||
}
|
||||
|
||||
// style returns the style for a cell based on it's position (row, column).
|
||||
func (t *Table) style(row, col int) lipgloss.Style {
|
||||
if t.styleFunc == nil {
|
||||
return lipgloss.NewStyle()
|
||||
}
|
||||
return t.styleFunc(row, col)
|
||||
}
|
||||
|
||||
// Data sets the table data.
|
||||
func (t *Table) Data(data Data) *Table {
|
||||
t.data = data
|
||||
return t
|
||||
}
|
||||
|
||||
// Rows appends rows to the table data.
|
||||
func (t *Table) Rows(rows ...[]string) *Table {
|
||||
for _, row := range rows {
|
||||
switch t.data.(type) {
|
||||
case *StringData:
|
||||
t.data.(*StringData).Append(row)
|
||||
}
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
// Row appends a row to the table data.
|
||||
func (t *Table) Row(row ...string) *Table {
|
||||
switch t.data.(type) {
|
||||
case *StringData:
|
||||
t.data.(*StringData).Append(row)
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
// Headers sets the table headers.
|
||||
func (t *Table) Headers(headers ...string) *Table {
|
||||
t.headers = headers
|
||||
return t
|
||||
}
|
||||
|
||||
// Border sets the table border.
|
||||
func (t *Table) Border(border lipgloss.Border) *Table {
|
||||
t.border = border
|
||||
return t
|
||||
}
|
||||
|
||||
// BorderTop sets the top border.
|
||||
func (t *Table) BorderTop(v bool) *Table {
|
||||
t.borderTop = v
|
||||
return t
|
||||
}
|
||||
|
||||
// BorderBottom sets the bottom border.
|
||||
func (t *Table) BorderBottom(v bool) *Table {
|
||||
t.borderBottom = v
|
||||
return t
|
||||
}
|
||||
|
||||
// BorderLeft sets the left border.
|
||||
func (t *Table) BorderLeft(v bool) *Table {
|
||||
t.borderLeft = v
|
||||
return t
|
||||
}
|
||||
|
||||
// BorderRight sets the right border.
|
||||
func (t *Table) BorderRight(v bool) *Table {
|
||||
t.borderRight = v
|
||||
return t
|
||||
}
|
||||
|
||||
// BorderHeader sets the header separator border.
|
||||
func (t *Table) BorderHeader(v bool) *Table {
|
||||
t.borderHeader = v
|
||||
return t
|
||||
}
|
||||
|
||||
// BorderColumn sets the column border separator.
|
||||
func (t *Table) BorderColumn(v bool) *Table {
|
||||
t.borderColumn = v
|
||||
return t
|
||||
}
|
||||
|
||||
// BorderRow sets the row border separator.
|
||||
func (t *Table) BorderRow(v bool) *Table {
|
||||
t.borderRow = v
|
||||
return t
|
||||
}
|
||||
|
||||
// BorderStyle sets the style for the table border.
|
||||
func (t *Table) BorderStyle(style lipgloss.Style) *Table {
|
||||
t.borderStyle = style
|
||||
return t
|
||||
}
|
||||
|
||||
// Width sets the table width, this auto-sizes the columns to fit the width by
|
||||
// either expanding or contracting the widths of each column as a best effort
|
||||
// approach.
|
||||
func (t *Table) Width(w int) *Table {
|
||||
t.width = w
|
||||
return t
|
||||
}
|
||||
|
||||
// Height sets the table height.
|
||||
func (t *Table) Height(h int) *Table {
|
||||
t.height = h
|
||||
return t
|
||||
}
|
||||
|
||||
// Offset sets the table rendering offset.
|
||||
func (t *Table) Offset(o int) *Table {
|
||||
t.offset = o
|
||||
return t
|
||||
}
|
||||
|
||||
// String returns the table as a string.
|
||||
func (t *Table) String() string {
|
||||
hasHeaders := t.headers != nil && len(t.headers) > 0
|
||||
hasRows := t.data != nil && t.data.Rows() > 0
|
||||
|
||||
if !hasHeaders && !hasRows {
|
||||
return ""
|
||||
}
|
||||
|
||||
var s strings.Builder
|
||||
|
||||
// Add empty cells to the headers, until it's the same length as the longest
|
||||
// row (only if there are at headers in the first place).
|
||||
if hasHeaders {
|
||||
for i := len(t.headers); i < t.data.Columns(); i++ {
|
||||
t.headers = append(t.headers, "")
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize the widths.
|
||||
t.widths = make([]int, max(len(t.headers), t.data.Columns()))
|
||||
t.heights = make([]int, btoi(hasHeaders)+t.data.Rows())
|
||||
|
||||
// The style function may affect width of the table. It's possible to set
|
||||
// the StyleFunc after the headers and rows. Update the widths for a final
|
||||
// time.
|
||||
for i, cell := range t.headers {
|
||||
t.widths[i] = max(t.widths[i], lipgloss.Width(t.style(0, i).Render(cell)))
|
||||
t.heights[0] = max(t.heights[0], lipgloss.Height(t.style(0, i).Render(cell)))
|
||||
}
|
||||
|
||||
for r := 0; r < t.data.Rows(); r++ {
|
||||
for i := 0; i < t.data.Columns(); i++ {
|
||||
cell := t.data.At(r, i)
|
||||
|
||||
rendered := t.style(r+1, i).Render(cell)
|
||||
t.heights[r+btoi(hasHeaders)] = max(t.heights[r+btoi(hasHeaders)], lipgloss.Height(rendered))
|
||||
t.widths[i] = max(t.widths[i], lipgloss.Width(rendered))
|
||||
}
|
||||
}
|
||||
|
||||
// Table Resizing Logic.
|
||||
//
|
||||
// Given a user defined table width, we must ensure the table is exactly that
|
||||
// width. This must account for all borders, column, separators, and column
|
||||
// data.
|
||||
//
|
||||
// In the case where the table is narrower than the specified table width,
|
||||
// we simply expand the columns evenly to fit the width.
|
||||
// For example, a table with 3 columns takes up 50 characters total, and the
|
||||
// width specified is 80, we expand each column by 10 characters, adding 30
|
||||
// to the total width.
|
||||
//
|
||||
// In the case where the table is wider than the specified table width, we
|
||||
// _could_ simply shrink the columns evenly but this would result in data
|
||||
// being truncated (perhaps unnecessarily). The naive approach could result
|
||||
// in very poor cropping of the table data. So, instead of shrinking columns
|
||||
// evenly, we calculate the median non-whitespace length of each column, and
|
||||
// shrink the columns based on the largest median.
|
||||
//
|
||||
// For example,
|
||||
// ┌──────┬───────────────┬──────────┐
|
||||
// │ Name │ Age of Person │ Location │
|
||||
// ├──────┼───────────────┼──────────┤
|
||||
// │ Kini │ 40 │ New York │
|
||||
// │ Eli │ 30 │ London │
|
||||
// │ Iris │ 20 │ Paris │
|
||||
// └──────┴───────────────┴──────────┘
|
||||
//
|
||||
// Median non-whitespace length vs column width of each column:
|
||||
//
|
||||
// Name: 4 / 5
|
||||
// Age of Person: 2 / 15
|
||||
// Location: 6 / 10
|
||||
//
|
||||
// The biggest difference is 15 - 2, so we can shrink the 2nd column by 13.
|
||||
|
||||
width := t.computeWidth()
|
||||
|
||||
if width < t.width && t.width > 0 {
|
||||
// Table is too narrow, expand the columns evenly until it reaches the
|
||||
// desired width.
|
||||
var i int
|
||||
for width < t.width {
|
||||
t.widths[i]++
|
||||
width++
|
||||
i = (i + 1) % len(t.widths)
|
||||
}
|
||||
} else if width > t.width && t.width > 0 {
|
||||
// Table is too wide, calculate the median non-whitespace length of each
|
||||
// column, and shrink the columns based on the largest difference.
|
||||
columnMedians := make([]int, len(t.widths))
|
||||
for c := range t.widths {
|
||||
trimmedWidth := make([]int, t.data.Rows())
|
||||
for r := 0; r < t.data.Rows(); r++ {
|
||||
renderedCell := t.style(r+btoi(hasHeaders), c).Render(t.data.At(r, c))
|
||||
nonWhitespaceChars := lipgloss.Width(strings.TrimRight(renderedCell, " "))
|
||||
trimmedWidth[r] = nonWhitespaceChars + 1
|
||||
}
|
||||
|
||||
columnMedians[c] = median(trimmedWidth)
|
||||
}
|
||||
|
||||
// Find the biggest differences between the median and the column width.
|
||||
// Shrink the columns based on the largest difference.
|
||||
differences := make([]int, len(t.widths))
|
||||
for i := range t.widths {
|
||||
differences[i] = t.widths[i] - columnMedians[i]
|
||||
}
|
||||
|
||||
for width > t.width {
|
||||
index, _ := largest(differences)
|
||||
if differences[index] < 1 {
|
||||
break
|
||||
}
|
||||
|
||||
shrink := min(differences[index], width-t.width)
|
||||
t.widths[index] -= shrink
|
||||
width -= shrink
|
||||
differences[index] = 0
|
||||
}
|
||||
|
||||
// Table is still too wide, begin shrinking the columns based on the
|
||||
// largest column.
|
||||
for width > t.width {
|
||||
index, _ := largest(t.widths)
|
||||
if t.widths[index] < 1 {
|
||||
break
|
||||
}
|
||||
t.widths[index]--
|
||||
width--
|
||||
}
|
||||
}
|
||||
|
||||
if t.borderTop {
|
||||
s.WriteString(t.constructTopBorder())
|
||||
s.WriteString("\n")
|
||||
}
|
||||
|
||||
if hasHeaders {
|
||||
s.WriteString(t.constructHeaders())
|
||||
s.WriteString("\n")
|
||||
}
|
||||
|
||||
for r := t.offset; r < t.data.Rows(); r++ {
|
||||
s.WriteString(t.constructRow(r))
|
||||
}
|
||||
|
||||
if t.borderBottom {
|
||||
s.WriteString(t.constructBottomBorder())
|
||||
}
|
||||
|
||||
return lipgloss.NewStyle().
|
||||
MaxHeight(t.computeHeight()).
|
||||
MaxWidth(t.width).Render(s.String())
|
||||
}
|
||||
|
||||
// computeWidth computes the width of the table in it's current configuration.
|
||||
func (t *Table) computeWidth() int {
|
||||
width := sum(t.widths) + btoi(t.borderLeft) + btoi(t.borderRight)
|
||||
if t.borderColumn {
|
||||
width += len(t.widths) - 1
|
||||
}
|
||||
return width
|
||||
}
|
||||
|
||||
// computeHeight computes the height of the table in it's current configuration.
|
||||
func (t *Table) computeHeight() int {
|
||||
hasHeaders := t.headers != nil && len(t.headers) > 0
|
||||
return sum(t.heights) - 1 + btoi(hasHeaders) +
|
||||
btoi(t.borderTop) + btoi(t.borderBottom) +
|
||||
btoi(t.borderHeader) + t.data.Rows()*btoi(t.borderRow)
|
||||
}
|
||||
|
||||
// Render returns the table as a string.
|
||||
func (t *Table) Render() string {
|
||||
return t.String()
|
||||
}
|
||||
|
||||
// constructTopBorder constructs the top border for the table given it's current
|
||||
// border configuration and data.
|
||||
func (t *Table) constructTopBorder() string {
|
||||
var s strings.Builder
|
||||
if t.borderLeft {
|
||||
s.WriteString(t.borderStyle.Render(t.border.TopLeft))
|
||||
}
|
||||
for i := 0; i < len(t.widths); i++ {
|
||||
s.WriteString(t.borderStyle.Render(strings.Repeat(t.border.Top, t.widths[i])))
|
||||
if i < len(t.widths)-1 && t.borderColumn {
|
||||
s.WriteString(t.borderStyle.Render(t.border.MiddleTop))
|
||||
}
|
||||
}
|
||||
if t.borderRight {
|
||||
s.WriteString(t.borderStyle.Render(t.border.TopRight))
|
||||
}
|
||||
return s.String()
|
||||
}
|
||||
|
||||
// constructBottomBorder constructs the bottom border for the table given it's current
|
||||
// border configuration and data.
|
||||
func (t *Table) constructBottomBorder() string {
|
||||
var s strings.Builder
|
||||
if t.borderLeft {
|
||||
s.WriteString(t.borderStyle.Render(t.border.BottomLeft))
|
||||
}
|
||||
for i := 0; i < len(t.widths); i++ {
|
||||
s.WriteString(t.borderStyle.Render(strings.Repeat(t.border.Bottom, t.widths[i])))
|
||||
if i < len(t.widths)-1 && t.borderColumn {
|
||||
s.WriteString(t.borderStyle.Render(t.border.MiddleBottom))
|
||||
}
|
||||
}
|
||||
if t.borderRight {
|
||||
s.WriteString(t.borderStyle.Render(t.border.BottomRight))
|
||||
}
|
||||
return s.String()
|
||||
}
|
||||
|
||||
// constructHeaders constructs the headers for the table given it's current
|
||||
// header configuration and data.
|
||||
func (t *Table) constructHeaders() string {
|
||||
var s strings.Builder
|
||||
if t.borderLeft {
|
||||
s.WriteString(t.borderStyle.Render(t.border.Left))
|
||||
}
|
||||
for i, header := range t.headers {
|
||||
s.WriteString(t.style(0, i).
|
||||
MaxHeight(1).
|
||||
Width(t.widths[i]).
|
||||
MaxWidth(t.widths[i]).
|
||||
Render(ansi.Truncate(header, t.widths[i], "…")))
|
||||
if i < len(t.headers)-1 && t.borderColumn {
|
||||
s.WriteString(t.borderStyle.Render(t.border.Left))
|
||||
}
|
||||
}
|
||||
if t.borderHeader {
|
||||
if t.borderRight {
|
||||
s.WriteString(t.borderStyle.Render(t.border.Right))
|
||||
}
|
||||
s.WriteString("\n")
|
||||
if t.borderLeft {
|
||||
s.WriteString(t.borderStyle.Render(t.border.MiddleLeft))
|
||||
}
|
||||
for i := 0; i < len(t.headers); i++ {
|
||||
s.WriteString(t.borderStyle.Render(strings.Repeat(t.border.Top, t.widths[i])))
|
||||
if i < len(t.headers)-1 && t.borderColumn {
|
||||
s.WriteString(t.borderStyle.Render(t.border.Middle))
|
||||
}
|
||||
}
|
||||
if t.borderRight {
|
||||
s.WriteString(t.borderStyle.Render(t.border.MiddleRight))
|
||||
}
|
||||
}
|
||||
if t.borderRight && !t.borderHeader {
|
||||
s.WriteString(t.borderStyle.Render(t.border.Right))
|
||||
}
|
||||
return s.String()
|
||||
}
|
||||
|
||||
// constructRow constructs the row for the table given an index and row data
|
||||
// based on the current configuration.
|
||||
func (t *Table) constructRow(index int) string {
|
||||
var s strings.Builder
|
||||
|
||||
hasHeaders := t.headers != nil && len(t.headers) > 0
|
||||
height := t.heights[index+btoi(hasHeaders)]
|
||||
|
||||
var cells []string
|
||||
left := strings.Repeat(t.borderStyle.Render(t.border.Left)+"\n", height)
|
||||
if t.borderLeft {
|
||||
cells = append(cells, left)
|
||||
}
|
||||
|
||||
for c := 0; c < t.data.Columns(); c++ {
|
||||
cell := t.data.At(index, c)
|
||||
|
||||
cells = append(cells, t.style(index+1, c).
|
||||
Height(height).
|
||||
MaxHeight(height).
|
||||
Width(t.widths[c]).
|
||||
MaxWidth(t.widths[c]).
|
||||
Render(ansi.Truncate(cell, t.widths[c]*height, "…")))
|
||||
|
||||
if c < t.data.Columns()-1 && t.borderColumn {
|
||||
cells = append(cells, left)
|
||||
}
|
||||
}
|
||||
|
||||
if t.borderRight {
|
||||
right := strings.Repeat(t.borderStyle.Render(t.border.Right)+"\n", height)
|
||||
cells = append(cells, right)
|
||||
}
|
||||
|
||||
for i, cell := range cells {
|
||||
cells[i] = strings.TrimRight(cell, "\n")
|
||||
}
|
||||
|
||||
s.WriteString(lipgloss.JoinHorizontal(lipgloss.Top, cells...) + "\n")
|
||||
|
||||
if t.borderRow && index < t.data.Rows()-1 {
|
||||
s.WriteString(t.borderStyle.Render(t.border.MiddleLeft))
|
||||
for i := 0; i < len(t.widths); i++ {
|
||||
s.WriteString(t.borderStyle.Render(strings.Repeat(t.border.Bottom, t.widths[i])))
|
||||
if i < len(t.widths)-1 && t.borderColumn {
|
||||
s.WriteString(t.borderStyle.Render(t.border.Middle))
|
||||
}
|
||||
}
|
||||
s.WriteString(t.borderStyle.Render(t.border.MiddleRight) + "\n")
|
||||
}
|
||||
|
||||
return s.String()
|
||||
}
|
64
vendor/github.com/charmbracelet/lipgloss/table/util.go
generated
vendored
Normal file
64
vendor/github.com/charmbracelet/lipgloss/table/util.go
generated
vendored
Normal file
@ -0,0 +1,64 @@
|
||||
package table
|
||||
|
||||
import (
|
||||
"sort"
|
||||
)
|
||||
|
||||
// btoi converts a boolean to an integer, 1 if true, 0 if false.
|
||||
func btoi(b bool) int {
|
||||
if b {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// max returns the greater of two integers.
|
||||
func max(a, b int) int {
|
||||
if a > b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// min returns the greater of two integers.
|
||||
func min(a, b int) int {
|
||||
if a < b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// sum returns the sum of all integers in a slice.
|
||||
func sum(n []int) int {
|
||||
var sum int
|
||||
for _, i := range n {
|
||||
sum += i
|
||||
}
|
||||
return sum
|
||||
}
|
||||
|
||||
// median returns the median of a slice of integers.
|
||||
func median(n []int) int {
|
||||
sort.Ints(n)
|
||||
|
||||
if len(n) <= 0 {
|
||||
return 0
|
||||
}
|
||||
if len(n)%2 == 0 {
|
||||
h := len(n) / 2 //nolint:gomnd
|
||||
return (n[h-1] + n[h]) / 2 //nolint:gomnd
|
||||
}
|
||||
return n[len(n)/2]
|
||||
}
|
||||
|
||||
// largest returns the largest element and it's index from a slice of integers.
|
||||
func largest(n []int) (int, int) { //nolint:unparam
|
||||
var largest, index int
|
||||
for i, e := range n {
|
||||
if n[i] > n[index] {
|
||||
largest = e
|
||||
index = i
|
||||
}
|
||||
}
|
||||
return index, largest
|
||||
}
|
331
vendor/github.com/charmbracelet/lipgloss/unset.go
generated
vendored
Normal file
331
vendor/github.com/charmbracelet/lipgloss/unset.go
generated
vendored
Normal file
@ -0,0 +1,331 @@
|
||||
package lipgloss
|
||||
|
||||
// unset unsets a property from a style.
|
||||
func (s *Style) unset(key propKey) {
|
||||
s.props = s.props.unset(key)
|
||||
}
|
||||
|
||||
// UnsetBold removes the bold style rule, if set.
|
||||
func (s Style) UnsetBold() Style {
|
||||
s.unset(boldKey)
|
||||
return s
|
||||
}
|
||||
|
||||
// UnsetItalic removes the italic style rule, if set.
|
||||
func (s Style) UnsetItalic() Style {
|
||||
s.unset(italicKey)
|
||||
return s
|
||||
}
|
||||
|
||||
// UnsetUnderline removes the underline style rule, if set.
|
||||
func (s Style) UnsetUnderline() Style {
|
||||
s.unset(underlineKey)
|
||||
return s
|
||||
}
|
||||
|
||||
// UnsetStrikethrough removes the strikethrough style rule, if set.
|
||||
func (s Style) UnsetStrikethrough() Style {
|
||||
s.unset(strikethroughKey)
|
||||
return s
|
||||
}
|
||||
|
||||
// UnsetReverse removes the reverse style rule, if set.
|
||||
func (s Style) UnsetReverse() Style {
|
||||
s.unset(reverseKey)
|
||||
return s
|
||||
}
|
||||
|
||||
// UnsetBlink removes the blink style rule, if set.
|
||||
func (s Style) UnsetBlink() Style {
|
||||
s.unset(blinkKey)
|
||||
return s
|
||||
}
|
||||
|
||||
// UnsetFaint removes the faint style rule, if set.
|
||||
func (s Style) UnsetFaint() Style {
|
||||
s.unset(faintKey)
|
||||
return s
|
||||
}
|
||||
|
||||
// UnsetForeground removes the foreground style rule, if set.
|
||||
func (s Style) UnsetForeground() Style {
|
||||
s.unset(foregroundKey)
|
||||
return s
|
||||
}
|
||||
|
||||
// UnsetBackground removes the background style rule, if set.
|
||||
func (s Style) UnsetBackground() Style {
|
||||
s.unset(backgroundKey)
|
||||
return s
|
||||
}
|
||||
|
||||
// UnsetWidth removes the width style rule, if set.
|
||||
func (s Style) UnsetWidth() Style {
|
||||
s.unset(widthKey)
|
||||
return s
|
||||
}
|
||||
|
||||
// UnsetHeight removes the height style rule, if set.
|
||||
func (s Style) UnsetHeight() Style {
|
||||
s.unset(heightKey)
|
||||
return s
|
||||
}
|
||||
|
||||
// UnsetAlign removes the horizontal and vertical text alignment style rule, if set.
|
||||
func (s Style) UnsetAlign() Style {
|
||||
s.unset(alignHorizontalKey)
|
||||
s.unset(alignVerticalKey)
|
||||
return s
|
||||
}
|
||||
|
||||
// UnsetAlignHorizontal removes the horizontal text alignment style rule, if set.
|
||||
func (s Style) UnsetAlignHorizontal() Style {
|
||||
s.unset(alignHorizontalKey)
|
||||
return s
|
||||
}
|
||||
|
||||
// UnsetAlignVertical removes the vertical text alignment style rule, if set.
|
||||
func (s Style) UnsetAlignVertical() Style {
|
||||
s.unset(alignVerticalKey)
|
||||
return s
|
||||
}
|
||||
|
||||
// UnsetPadding removes all padding style rules.
|
||||
func (s Style) UnsetPadding() Style {
|
||||
s.unset(paddingLeftKey)
|
||||
s.unset(paddingRightKey)
|
||||
s.unset(paddingTopKey)
|
||||
s.unset(paddingBottomKey)
|
||||
return s
|
||||
}
|
||||
|
||||
// UnsetPaddingLeft removes the left padding style rule, if set.
|
||||
func (s Style) UnsetPaddingLeft() Style {
|
||||
s.unset(paddingLeftKey)
|
||||
return s
|
||||
}
|
||||
|
||||
// UnsetPaddingRight removes the right padding style rule, if set.
|
||||
func (s Style) UnsetPaddingRight() Style {
|
||||
s.unset(paddingRightKey)
|
||||
return s
|
||||
}
|
||||
|
||||
// UnsetPaddingTop removes the top padding style rule, if set.
|
||||
func (s Style) UnsetPaddingTop() Style {
|
||||
s.unset(paddingTopKey)
|
||||
return s
|
||||
}
|
||||
|
||||
// UnsetPaddingBottom removes the bottom padding style rule, if set.
|
||||
func (s Style) UnsetPaddingBottom() Style {
|
||||
s.unset(paddingBottomKey)
|
||||
return s
|
||||
}
|
||||
|
||||
// UnsetColorWhitespace removes the rule for coloring padding, if set.
|
||||
func (s Style) UnsetColorWhitespace() Style {
|
||||
s.unset(colorWhitespaceKey)
|
||||
return s
|
||||
}
|
||||
|
||||
// UnsetMargins removes all margin style rules.
|
||||
func (s Style) UnsetMargins() Style {
|
||||
s.unset(marginLeftKey)
|
||||
s.unset(marginRightKey)
|
||||
s.unset(marginTopKey)
|
||||
s.unset(marginBottomKey)
|
||||
return s
|
||||
}
|
||||
|
||||
// UnsetMarginLeft removes the left margin style rule, if set.
|
||||
func (s Style) UnsetMarginLeft() Style {
|
||||
s.unset(marginLeftKey)
|
||||
return s
|
||||
}
|
||||
|
||||
// UnsetMarginRight removes the right margin style rule, if set.
|
||||
func (s Style) UnsetMarginRight() Style {
|
||||
s.unset(marginRightKey)
|
||||
return s
|
||||
}
|
||||
|
||||
// UnsetMarginTop removes the top margin style rule, if set.
|
||||
func (s Style) UnsetMarginTop() Style {
|
||||
s.unset(marginTopKey)
|
||||
return s
|
||||
}
|
||||
|
||||
// UnsetMarginBottom removes the bottom margin style rule, if set.
|
||||
func (s Style) UnsetMarginBottom() Style {
|
||||
s.unset(marginBottomKey)
|
||||
return s
|
||||
}
|
||||
|
||||
// UnsetMarginBackground removes the margin's background color. Note that the
|
||||
// margin's background color can be set from the background color of another
|
||||
// style during inheritance.
|
||||
func (s Style) UnsetMarginBackground() Style {
|
||||
s.unset(marginBackgroundKey)
|
||||
return s
|
||||
}
|
||||
|
||||
// UnsetBorderStyle removes the border style rule, if set.
|
||||
func (s Style) UnsetBorderStyle() Style {
|
||||
s.unset(borderStyleKey)
|
||||
return s
|
||||
}
|
||||
|
||||
// UnsetBorderTop removes the border top style rule, if set.
|
||||
func (s Style) UnsetBorderTop() Style {
|
||||
s.unset(borderTopKey)
|
||||
return s
|
||||
}
|
||||
|
||||
// UnsetBorderRight removes the border right style rule, if set.
|
||||
func (s Style) UnsetBorderRight() Style {
|
||||
s.unset(borderRightKey)
|
||||
return s
|
||||
}
|
||||
|
||||
// UnsetBorderBottom removes the border bottom style rule, if set.
|
||||
func (s Style) UnsetBorderBottom() Style {
|
||||
s.unset(borderBottomKey)
|
||||
return s
|
||||
}
|
||||
|
||||
// UnsetBorderLeft removes the border left style rule, if set.
|
||||
func (s Style) UnsetBorderLeft() Style {
|
||||
s.unset(borderLeftKey)
|
||||
return s
|
||||
}
|
||||
|
||||
// UnsetBorderForeground removes all border foreground color styles, if set.
|
||||
func (s Style) UnsetBorderForeground() Style {
|
||||
s.unset(borderTopForegroundKey)
|
||||
s.unset(borderRightForegroundKey)
|
||||
s.unset(borderBottomForegroundKey)
|
||||
s.unset(borderLeftForegroundKey)
|
||||
return s
|
||||
}
|
||||
|
||||
// UnsetBorderTopForeground removes the top border foreground color rule,
|
||||
// if set.
|
||||
func (s Style) UnsetBorderTopForeground() Style {
|
||||
s.unset(borderTopForegroundKey)
|
||||
return s
|
||||
}
|
||||
|
||||
// UnsetBorderRightForeground removes the right border foreground color rule,
|
||||
// if set.
|
||||
func (s Style) UnsetBorderRightForeground() Style {
|
||||
s.unset(borderRightForegroundKey)
|
||||
return s
|
||||
}
|
||||
|
||||
// UnsetBorderBottomForeground removes the bottom border foreground color
|
||||
// rule, if set.
|
||||
func (s Style) UnsetBorderBottomForeground() Style {
|
||||
s.unset(borderBottomForegroundKey)
|
||||
return s
|
||||
}
|
||||
|
||||
// UnsetBorderLeftForeground removes the left border foreground color rule,
|
||||
// if set.
|
||||
func (s Style) UnsetBorderLeftForeground() Style {
|
||||
s.unset(borderLeftForegroundKey)
|
||||
return s
|
||||
}
|
||||
|
||||
// UnsetBorderBackground removes all border background color styles, if
|
||||
// set.
|
||||
func (s Style) UnsetBorderBackground() Style {
|
||||
s.unset(borderTopBackgroundKey)
|
||||
s.unset(borderRightBackgroundKey)
|
||||
s.unset(borderBottomBackgroundKey)
|
||||
s.unset(borderLeftBackgroundKey)
|
||||
return s
|
||||
}
|
||||
|
||||
// UnsetBorderTopBackgroundColor removes the top border background color rule,
|
||||
// if set.
|
||||
//
|
||||
// Deprecated: This function simply calls Style.UnsetBorderTopBackground.
|
||||
func (s Style) UnsetBorderTopBackgroundColor() Style {
|
||||
return s.UnsetBorderTopBackground()
|
||||
}
|
||||
|
||||
// UnsetBorderTopBackground removes the top border background color rule,
|
||||
// if set.
|
||||
func (s Style) UnsetBorderTopBackground() Style {
|
||||
s.unset(borderTopBackgroundKey)
|
||||
return s
|
||||
}
|
||||
|
||||
// UnsetBorderRightBackground removes the right border background color
|
||||
// rule, if set.
|
||||
func (s Style) UnsetBorderRightBackground() Style {
|
||||
s.unset(borderRightBackgroundKey)
|
||||
return s
|
||||
}
|
||||
|
||||
// UnsetBorderBottomBackground removes the bottom border background color
|
||||
// rule, if set.
|
||||
func (s Style) UnsetBorderBottomBackground() Style {
|
||||
s.unset(borderBottomBackgroundKey)
|
||||
return s
|
||||
}
|
||||
|
||||
// UnsetBorderLeftBackground removes the left border color rule, if set.
|
||||
func (s Style) UnsetBorderLeftBackground() Style {
|
||||
s.unset(borderLeftBackgroundKey)
|
||||
return s
|
||||
}
|
||||
|
||||
// UnsetInline removes the inline style rule, if set.
|
||||
func (s Style) UnsetInline() Style {
|
||||
s.unset(inlineKey)
|
||||
return s
|
||||
}
|
||||
|
||||
// UnsetMaxWidth removes the max width style rule, if set.
|
||||
func (s Style) UnsetMaxWidth() Style {
|
||||
s.unset(maxWidthKey)
|
||||
return s
|
||||
}
|
||||
|
||||
// UnsetMaxHeight removes the max height style rule, if set.
|
||||
func (s Style) UnsetMaxHeight() Style {
|
||||
s.unset(maxHeightKey)
|
||||
return s
|
||||
}
|
||||
|
||||
// UnsetTabWidth removes the tab width style rule, if set.
|
||||
func (s Style) UnsetTabWidth() Style {
|
||||
s.unset(tabWidthKey)
|
||||
return s
|
||||
}
|
||||
|
||||
// UnsetUnderlineSpaces removes the value set by UnderlineSpaces.
|
||||
func (s Style) UnsetUnderlineSpaces() Style {
|
||||
s.unset(underlineSpacesKey)
|
||||
return s
|
||||
}
|
||||
|
||||
// UnsetStrikethroughSpaces removes the value set by StrikethroughSpaces.
|
||||
func (s Style) UnsetStrikethroughSpaces() Style {
|
||||
s.unset(strikethroughSpacesKey)
|
||||
return s
|
||||
}
|
||||
|
||||
// UnsetTransform removes the value set by Transform.
|
||||
func (s Style) UnsetTransform() Style {
|
||||
s.unset(transformKey)
|
||||
return s
|
||||
}
|
||||
|
||||
// UnsetString sets the underlying string value to the empty string.
|
||||
func (s Style) UnsetString() Style {
|
||||
s.value = ""
|
||||
return s
|
||||
}
|
83
vendor/github.com/charmbracelet/lipgloss/whitespace.go
generated
vendored
Normal file
83
vendor/github.com/charmbracelet/lipgloss/whitespace.go
generated
vendored
Normal file
@ -0,0 +1,83 @@
|
||||
package lipgloss
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/charmbracelet/x/ansi"
|
||||
"github.com/muesli/termenv"
|
||||
)
|
||||
|
||||
// whitespace is a whitespace renderer.
|
||||
type whitespace struct {
|
||||
re *Renderer
|
||||
style termenv.Style
|
||||
chars string
|
||||
}
|
||||
|
||||
// newWhitespace creates a new whitespace renderer. The order of the options
|
||||
// matters, if you're using WithWhitespaceRenderer, make sure it comes first as
|
||||
// other options might depend on it.
|
||||
func newWhitespace(r *Renderer, opts ...WhitespaceOption) *whitespace {
|
||||
w := &whitespace{
|
||||
re: r,
|
||||
style: r.ColorProfile().String(),
|
||||
}
|
||||
for _, opt := range opts {
|
||||
opt(w)
|
||||
}
|
||||
return w
|
||||
}
|
||||
|
||||
// Render whitespaces.
|
||||
func (w whitespace) render(width int) string {
|
||||
if w.chars == "" {
|
||||
w.chars = " "
|
||||
}
|
||||
|
||||
r := []rune(w.chars)
|
||||
j := 0
|
||||
b := strings.Builder{}
|
||||
|
||||
// Cycle through runes and print them into the whitespace.
|
||||
for i := 0; i < width; {
|
||||
b.WriteRune(r[j])
|
||||
j++
|
||||
if j >= len(r) {
|
||||
j = 0
|
||||
}
|
||||
i += ansi.StringWidth(string(r[j]))
|
||||
}
|
||||
|
||||
// Fill any extra gaps white spaces. This might be necessary if any runes
|
||||
// are more than one cell wide, which could leave a one-rune gap.
|
||||
short := width - ansi.StringWidth(b.String())
|
||||
if short > 0 {
|
||||
b.WriteString(strings.Repeat(" ", short))
|
||||
}
|
||||
|
||||
return w.style.Styled(b.String())
|
||||
}
|
||||
|
||||
// WhitespaceOption sets a styling rule for rendering whitespace.
|
||||
type WhitespaceOption func(*whitespace)
|
||||
|
||||
// WithWhitespaceForeground sets the color of the characters in the whitespace.
|
||||
func WithWhitespaceForeground(c TerminalColor) WhitespaceOption {
|
||||
return func(w *whitespace) {
|
||||
w.style = w.style.Foreground(c.color(w.re))
|
||||
}
|
||||
}
|
||||
|
||||
// WithWhitespaceBackground sets the background color of the whitespace.
|
||||
func WithWhitespaceBackground(c TerminalColor) WhitespaceOption {
|
||||
return func(w *whitespace) {
|
||||
w.style = w.style.Background(c.color(w.re))
|
||||
}
|
||||
}
|
||||
|
||||
// WithWhitespaceChars sets the characters to be rendered in the whitespace.
|
||||
func WithWhitespaceChars(s string) WhitespaceOption {
|
||||
return func(w *whitespace) {
|
||||
w.chars = s
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user