2021-03-26 06:52:01 +00:00
|
|
|
|
# beacon_chain
|
2023-01-20 14:14:37 +00:00
|
|
|
|
# Copyright (c) 2018-2023 Status Research & Development GmbH
|
2021-03-26 06:52:01 +00:00
|
|
|
|
# Licensed and distributed under either of
|
|
|
|
|
# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT).
|
|
|
|
|
# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
|
|
|
|
|
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
|
|
|
|
|
2023-01-20 14:14:37 +00:00
|
|
|
|
{.push raises: [].}
|
2021-03-24 16:20:55 +00:00
|
|
|
|
|
2019-10-03 01:51:44 +00:00
|
|
|
|
import
|
2021-03-24 16:20:55 +00:00
|
|
|
|
std/[strutils, parseutils, sequtils, terminal, colors]
|
2019-10-03 01:51:44 +00:00
|
|
|
|
|
|
|
|
|
type
|
|
|
|
|
ContentFragments = seq[tuple[kind: InterpolatedKind, value: string]]
|
|
|
|
|
|
|
|
|
|
StatusBarCell = object
|
|
|
|
|
label, content: string
|
|
|
|
|
contentFragments: ContentFragments
|
|
|
|
|
|
|
|
|
|
Layout = object
|
|
|
|
|
cellsLeft: seq[StatusBarCell]
|
|
|
|
|
cellsRight: seq[StatusBarCell]
|
|
|
|
|
|
2021-03-24 16:20:55 +00:00
|
|
|
|
DataItemResolver* = proc (dataItem: string): string {.
|
2023-08-25 09:29:07 +00:00
|
|
|
|
gcsafe, raises: [].}
|
2019-10-03 01:51:44 +00:00
|
|
|
|
|
|
|
|
|
StatusBarView* = object
|
|
|
|
|
model: DataItemResolver
|
|
|
|
|
layout: Layout
|
|
|
|
|
consumedLines: int
|
|
|
|
|
|
|
|
|
|
const
|
|
|
|
|
sepLeft = "❯"
|
|
|
|
|
sepRight = "❮"
|
|
|
|
|
|
|
|
|
|
# sepLeft = "|"
|
|
|
|
|
# sepRight = "|"
|
|
|
|
|
|
|
|
|
|
backgroundColor = rgb(36, 36, 36)
|
|
|
|
|
foregroundColor = colWhiteSmoke
|
|
|
|
|
|
2021-03-24 16:20:55 +00:00
|
|
|
|
func loadFragmentsLayout(contentLayout: string): ContentFragments {.
|
2023-08-25 09:29:07 +00:00
|
|
|
|
raises: [ValueError].} =
|
2021-08-17 09:51:39 +00:00
|
|
|
|
toSeq(interpolatedFragments(strip contentLayout))
|
2019-10-03 01:51:44 +00:00
|
|
|
|
|
2021-03-24 16:20:55 +00:00
|
|
|
|
func loadCellsLayout(cellsLayout: string): seq[StatusBarCell] {.
|
2023-08-25 09:29:07 +00:00
|
|
|
|
raises: [ValueError].} =
|
2022-12-08 16:21:53 +00:00
|
|
|
|
let cells = cellsLayout.split(';')
|
2019-10-03 01:51:44 +00:00
|
|
|
|
for cell in cells:
|
2022-12-08 16:21:53 +00:00
|
|
|
|
let columns = cell.split(':', maxSplit = 1)
|
2019-10-03 01:51:44 +00:00
|
|
|
|
if columns.len == 2:
|
|
|
|
|
result.add StatusBarCell(
|
|
|
|
|
label: strip(columns[0]),
|
|
|
|
|
contentFragments: loadFragmentsLayout(columns[1]))
|
|
|
|
|
else:
|
|
|
|
|
result.add StatusBarCell(
|
|
|
|
|
contentFragments: loadFragmentsLayout(columns[0]))
|
|
|
|
|
|
2023-08-25 09:29:07 +00:00
|
|
|
|
func loadLayout(layout: string): Layout {.raises: [ValueError].} =
|
2022-12-08 16:21:53 +00:00
|
|
|
|
let sections = layout.split('|', maxSplit = 1)
|
2019-10-03 01:51:44 +00:00
|
|
|
|
result.cellsLeft = loadCellsLayout(sections[0])
|
|
|
|
|
if sections.len == 2: result.cellsRight = loadCellsLayout(sections[1])
|
|
|
|
|
|
2021-03-24 16:20:55 +00:00
|
|
|
|
proc updateContent(cell: var StatusBarCell, model: DataItemResolver) =
|
2019-10-03 01:51:44 +00:00
|
|
|
|
cell.content.setLen 0
|
|
|
|
|
for fragment in cell.contentFragments:
|
|
|
|
|
case fragment[0]
|
|
|
|
|
of ikStr, ikDollar:
|
|
|
|
|
cell.content.add fragment[1]
|
|
|
|
|
of ikExpr, ikVar:
|
|
|
|
|
cell.content.add model(fragment[1])
|
|
|
|
|
|
2021-03-24 16:20:55 +00:00
|
|
|
|
proc updateCells(cells: var seq[StatusBarCell], model: DataItemResolver) =
|
2019-10-03 01:51:44 +00:00
|
|
|
|
for cell in mitems(cells):
|
|
|
|
|
cell.updateContent(model)
|
|
|
|
|
|
2021-03-24 16:20:55 +00:00
|
|
|
|
proc update*(s: var StatusBarView) =
|
2019-10-03 01:51:44 +00:00
|
|
|
|
updateCells s.layout.cellsLeft, s.model
|
|
|
|
|
updateCells s.layout.cellsRight, s.model
|
|
|
|
|
|
|
|
|
|
func width(cell: StatusBarCell): int =
|
|
|
|
|
cell.label.len + cell.content.len + 4 # separator + pading
|
|
|
|
|
|
|
|
|
|
func width(cells: seq[StatusBarCell]): int =
|
|
|
|
|
result = max(0, cells.len - 1) # the number of separators
|
|
|
|
|
for cell in cells: result += cell.width
|
|
|
|
|
|
2021-03-24 16:20:55 +00:00
|
|
|
|
var complained = false
|
|
|
|
|
template ignoreException(body: untyped) =
|
|
|
|
|
try:
|
|
|
|
|
body
|
|
|
|
|
except Exception as exc:
|
|
|
|
|
if not complained:
|
|
|
|
|
# TODO terminal.nim exception leak
|
|
|
|
|
echo "Unable to update status bar: ", exc.msg
|
|
|
|
|
complained = true
|
|
|
|
|
|
2019-10-03 01:51:44 +00:00
|
|
|
|
proc renderCells(cells: seq[StatusBarCell], sep: string) =
|
|
|
|
|
for i, cell in cells:
|
2021-03-24 16:20:55 +00:00
|
|
|
|
ignoreException:
|
|
|
|
|
stdout.setBackgroundColor backgroundColor
|
|
|
|
|
stdout.setForegroundColor foregroundColor
|
|
|
|
|
stdout.setStyle {styleDim}
|
|
|
|
|
if i > 0: stdout.write sep
|
|
|
|
|
stdout.write " ", cell.label, ": "
|
|
|
|
|
stdout.setStyle {styleBright}
|
|
|
|
|
stdout.write cell.content, " "
|
|
|
|
|
stdout.resetAttributes()
|
|
|
|
|
|
2023-08-25 09:29:07 +00:00
|
|
|
|
proc render*(s: var StatusBarView) {.raises: [ValueError].} =
|
2019-10-03 01:51:44 +00:00
|
|
|
|
doAssert s.consumedLines == 0
|
|
|
|
|
|
|
|
|
|
let
|
|
|
|
|
termWidth = terminalWidth()
|
|
|
|
|
allCellsWidth = s.layout.cellsLeft.width + s.layout.cellsRight.width
|
|
|
|
|
|
2020-02-04 22:59:02 +00:00
|
|
|
|
if allCellsWidth > 0:
|
2021-03-24 16:20:55 +00:00
|
|
|
|
ignoreException:
|
|
|
|
|
renderCells(s.layout.cellsLeft, sepLeft)
|
|
|
|
|
stdout.setBackgroundColor backgroundColor
|
|
|
|
|
if termWidth > allCellsWidth:
|
|
|
|
|
stdout.write spaces(termWidth - allCellsWidth)
|
|
|
|
|
s.consumedLines = 1
|
|
|
|
|
else:
|
|
|
|
|
stdout.write spaces(max(0, termWidth - s.layout.cellsLeft.width)), "\p"
|
|
|
|
|
s.consumedLines = 2
|
|
|
|
|
renderCells(s.layout.cellsRight, sepRight)
|
|
|
|
|
stdout.flushFile
|
2019-10-03 01:51:44 +00:00
|
|
|
|
|
|
|
|
|
proc erase*(s: var StatusBarView) =
|
2021-03-24 16:20:55 +00:00
|
|
|
|
ignoreException:
|
|
|
|
|
for i in 1 ..< s.consumedLines: cursorUp()
|
|
|
|
|
for i in 0 ..< s.consumedLines: eraseLine()
|
|
|
|
|
s.consumedLines = 0
|
2019-10-03 01:51:44 +00:00
|
|
|
|
|
|
|
|
|
func init*(T: type StatusBarView,
|
|
|
|
|
layout: string,
|
2023-08-25 09:29:07 +00:00
|
|
|
|
model: DataItemResolver): T {.raises: [ValueError].} =
|
2019-10-03 01:51:44 +00:00
|
|
|
|
StatusBarView(model: model, consumedLines: 1, layout: loadLayout(layout))
|