/
githubmirror
/
client
Обзор
Документация
Войти
/
githubmirror
/
client
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
go/flexibletable/table.go
239 строк
6 KB
zoom-ua
gofumpt (#28710)
17 дек 2025, 17:16
Не верифицирован
17 дек 2025, 17:16
cda510b
Код
Авторство
О чём код?
// Copyright 2016 Keybase, Inc. All rights reserved. Use of // this source code is governed by the included BSD license. package flexibletable import ( "errors" "fmt" "io" "strings" ) // ColumnConstraint specifies how a column should behave while being rendered. // Use positive to specify a maximum width for the column, or one of const // values for expandable width. type ColumnConstraint int const ( // Expandable is a special ColumnConstraint where the column and may expand // automatically if other columns end up taking less actual width. Expandable ColumnConstraint = 0 // ExpandableWrappable is a special ColumnConstraint where the column is // expandable. In addition, it can wrap into multiple lines if needed. ExpandableWrappable ColumnConstraint = -1 ) // Row defines a row type Row []Cell // Table defines a table and is used to do the rendering type Table struct { rows []Row nInserts int } // Insert inserts a row into the table func (t *Table) Insert(row Row) error { if len(t.rows) > 0 && len(t.rows[0]) != len(row) { return InconsistentRowsError{existingRows: len(t.rows), newRow: len(row)} } t.rows = append(t.rows, row) t.nInserts++ return nil } func (t *Table) NumInserts() int { return t.nInserts } func (t *Table) breakOnLineBreaks() error { // so that there's no need to resize if there's no line break broken := make([]Row, 0, len(t.rows)) for _, row := range t.rows { notEmpty := true for notEmpty { newRow := make(Row, 0, len(row)) notEmpty = false for iCell := range row { switch content := row[iCell].Content.(type) { case emptyCell: newRow = append(newRow, Cell{ Alignment: row[iCell].Alignment, Frame: [2]string{"", ""}, Content: row[iCell].Content, }) case MultiCell: notEmpty = true for iItem := range content.Items { // we are replacing line breaks with spaces for MultiCell for now content.Items[iItem] = strings.ReplaceAll(content.Items[iItem], "\n", " ") } newRow = append(newRow, Cell{ Alignment: row[iCell].Alignment, Frame: row[iCell].Frame, Content: content, }) row[iCell].Content = emptyCell{} case SingleCell: notEmpty = true lb := strings.Index(content.Item, "\n") current := "" if lb >= 0 { current = content.Item[:lb] row[iCell].Content = SingleCell{Item: content.Item[lb+1:]} } else { current = content.Item row[iCell].Content = emptyCell{} } newRow = append(newRow, Cell{ Alignment: row[iCell].Alignment, Frame: row[iCell].Frame, Content: SingleCell{Item: current}, }) default: // unexported error because this shouldn't happen unless we make a // mistake in code return errors.New("unexpected cell content") } } if notEmpty { broken = append(broken, newRow) } } } t.rows = broken return nil } func (t Table) renderFirstPass(cellSep string, maxWidth int, constraints []ColumnConstraint) (widths []int, err error) { numOfNoConstraints := 0 for _, c := range constraints { if c <= 0 { numOfNoConstraints++ } } if numOfNoConstraints == 0 { numOfNoConstraints = 1 } // first pass; determine smallest width for each column under constraints widths = make([]int, len(t.rows[0])) for _, row := range t.rows { for i, c := range row { if constraints[i] > 0 { str, err := c.render(int(constraints[i])) if err != nil { return nil, err } if widths[i] < len(str) { widths[i] = len(str) } } } } // calculate width for un-constrained columns rest := maxWidth - len(cellSep)*(len(widths)-1) // take out cellSeps for _, w := range widths { rest -= w } each := rest / numOfNoConstraints last := -1 for i := range widths { if constraints[i] <= 0 { widths[i] = each last = i } } if last != -1 { widths[last] = rest - each*(numOfNoConstraints-1) } return widths, nil } func (t Table) renderSecondPass(constraints []ColumnConstraint, widths []int) (rows [][]string, err error) { // actually rendering for _, row := range t.rows { var strs []string for ic, c := range row { if constraints[ic] >= 0 { str, err := c.renderWithPadding(widths[ic]) if err != nil { return nil, err } strs = append(strs, str) } else { // need wrapping! strs = append(strs, c.full()) } } wrapping := true for wrapping { var toAppend []string wrapping = false for i := range strs { if widths[i] < len(strs[i]) { toAppend = append(toAppend, strs[i][:widths[i]]) strs[i] = strs[i][widths[i]:] wrapping = true } else { str, err := row[i].addPadding(strs[i], widths[i]) if err != nil { return nil, err } toAppend = append(toAppend, str) strs[i] = strings.Repeat(" ", widths[i]) } } rows = append(rows, toAppend) } } return rows, nil } // Render renders the table into writer. The constraints parameter specifies // how each column should be constrained while being rendered. Positive values // limit the maximum width. func (t Table) Render(w io.Writer, cellSep string, maxWidth int, constraints []ColumnConstraint) error { if len(t.rows) == 0 { return NoRowsError{} } if len(constraints) != len(t.rows[0]) { return InconsistentRowsError{existingRows: len(t.rows[0]), newRow: len(constraints)} } err := t.breakOnLineBreaks() if err != nil { return err } widths, err := t.renderFirstPass(cellSep, maxWidth, constraints) if err != nil { return err } rows, err := t.renderSecondPass(constraints, widths) if err != nil { return err } // write out for _, row := range rows { fmt.Fprint(w, strings.Join(row, cellSep)) fmt.Fprintln(w) } return nil }