Skip to content

Commit

Permalink
Use enter to switch display modes (#17)
Browse files Browse the repository at this point in the history
* Use enter to switch display modes

* Documentation clean-up

* Previous result printing and reader indexes

* Clean-up
  • Loading branch information
spacez320 authored Jan 12, 2024
1 parent 3719670 commit 23157b6
Show file tree
Hide file tree
Showing 6 changed files with 123 additions and 61 deletions.
86 changes: 49 additions & 37 deletions internal/lib/display.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,24 +14,12 @@ import (
"golang.org/x/exp/slog"
)

///////////////////////////////////////////////////////////////////////////////////////////////////
//
// Types
//
///////////////////////////////////////////////////////////////////////////////////////////////////

// Represents the display driver.
type DisplayDriver int

// Represents the display mode.
type DisplayMode int

///////////////////////////////////////////////////////////////////////////////////////////////////
//
// Variables
//
///////////////////////////////////////////////////////////////////////////////////////////////////

// Display driver constants. Each display mode uses a specific display driver.
const (
DISPLAY_RAW DisplayDriver = iota + 1 // Used for direct output.
Expand All @@ -48,6 +36,7 @@ const (
)

const (
HELP_TEXT = "(ESC) Quit | (ENTER) Next Display | (TAB) Next Query"
HELP_SIZE = 10 // Proportional size of the logs widget.
LOGS_SIZE = 15 // Proportional size of the logs widget.
OUTER_PADDING_LEFT = 10 // Left padding for the full display.
Expand All @@ -60,20 +49,14 @@ const (

var (
activeDisplayModes = []DisplayMode{
DISPLAY_MODE_RAW,
// DISPLAY_MODE_RAW, // It's impossible to escape raw mode, so we exclude it.
DISPLAY_MODE_STREAM,
DISPLAY_MODE_TABLE,
DISPLAY_MODE_GRAPH,
} // Display modes considered for use in the current session.
interruptChan = make(chan bool) // Channel for interrupting displays.
)

///////////////////////////////////////////////////////////////////////////////////////////////////
//
// Private
//
///////////////////////////////////////////////////////////////////////////////////////////////////

// Starts the display. Applies contextual logic depending on the provided
// display driver. Expects a function to execute within a goroutine to update
// the display.
Expand All @@ -92,12 +75,6 @@ func display(driver DisplayDriver, f func()) {
}
}

///////////////////////////////////////////////////////////////////////////////////////////////////
//
// Public
//
///////////////////////////////////////////////////////////////////////////////////////////////////

// Presents raw output.
func RawDisplay(query string) {
go func() {
Expand All @@ -110,11 +87,9 @@ func RawDisplay(query string) {
// Update the results pane with new results as they are generated.
func StreamDisplay(query string) {
var (
helpText = "(ESC) Quit | (TAB) Next Query" // Text to display in the help pane.
helpText = HELP_TEXT + fmt.Sprintf("\nQuery: %v", query) // Text to display in the help pane.
)

helpText += fmt.Sprintf("\nQuery: %v", query)

// Initialize the display.
resultsView, _, _ := initDisplayTviewText(helpText)

Expand All @@ -129,6 +104,11 @@ func StreamDisplay(query string) {
})
}

// Print all previous results.
for _, result := range store.GetToIndex(query, readerIndexes[query]) {
fmt.Fprintln(resultsView, result.Value)
}

// Print results.
for {
// Listen for an interrupt event to stop result consumption in
Expand All @@ -149,13 +129,11 @@ func StreamDisplay(query string) {
// Creates a table of results for the results pane.
func TableDisplay(query string, filters []string) {
var (
helpText = "(ESC) Quit | (TAB) Next Query" // Text to display in the help pane.
tableCellPadding = strings.Repeat(" ", TABLE_PADDING) // Padding to add to table cell content.
valueIndexes = []int{} // Indexes of the result values to add to the table.
helpText = HELP_TEXT + fmt.Sprintf("\nQuery: %v", query) // Text to display in the help pane.
tableCellPadding = strings.Repeat(" ", TABLE_PADDING) // Padding to add to table cell content.
valueIndexes = []int{} // Indexes of the result values to add to the table.
)

helpText += fmt.Sprintf("\nQuery: %v", query)

// Initialize the display.
resultsView, _, _ := initDisplayTviewTable(helpText)

Expand Down Expand Up @@ -189,6 +167,29 @@ func TableDisplay(query string, filters []string) {
i += 1
}

// Print all previous results.
for _, result := range store.GetToIndex(query, readerIndexes[query]) {
appTview.QueueUpdateDraw(func() {
var (
row = resultsView.InsertRow(i) // Row to contain the result.
)

for j, value := range FilterSlice(result.Values, valueIndexes) {
// Extrapolate the field types in order to print them out.
switch value.(type) {
case int64:
nextCellContent = strconv.FormatInt(value.(int64), 10)
case float64:
nextCellContent = strconv.FormatFloat(value.(float64), 'f', -1, 64)
default:
nextCellContent = value.(string)
}
row.SetCellSimple(i, j, tableCellPadding+nextCellContent+tableCellPadding)
}
})
i += 1
}

// Print results.
for {
// Listen for an interrupt event to stop result consumption in
Expand Down Expand Up @@ -227,12 +228,10 @@ func TableDisplay(query string, filters []string) {
// Creates a graph of results for the results pane.
func GraphDisplay(query string, filters []string) {
var (
helpText = "(ESC) Quit | (TAB) Next Query" // Text to display in the help pane.
valueIndex = 0 // Index of the result value to graph.
helpText = HELP_TEXT + fmt.Sprintf("\nQuery: %v", query) // Text to display in the help pane.
valueIndex = 0 // Index of the result value to graph.
)

helpText += fmt.Sprintf("\nQuery: %v", query)

// Determine the values to populate into the graph. If no filter is provided,
// the first value is taken.
if len(filters) > 0 {
Expand Down Expand Up @@ -264,6 +263,19 @@ func GraphDisplay(query string, filters []string) {
display(
DISPLAY_TERMDASH,
func() {
// Print all previous results.
for _, result := range store.GetToIndex(query, readerIndexes[query]) {
// We can display the next result.
value := result.Values[valueIndex]

switch value.(type) {
case int64:
resultWidget.Add([]int{int(value.(int64))})
case float64:
resultWidget.Add([]int{int(value.(float64))})
}
}

for {
// Listen for an interrupt event to stop result consumption in
// preparation for some display change.
Expand Down
24 changes: 23 additions & 1 deletion internal/lib/display_termdash.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"github.com/mum4k/termdash/terminal/terminalapi"
"github.com/mum4k/termdash/widgetapi"
"github.com/mum4k/termdash/widgets/text"
"golang.org/x/exp/slog"
)

// Used to provide an io.Writer implementation of termdash text widgets.
Expand Down Expand Up @@ -46,9 +47,24 @@ func keyboardTermdashHandler(key *terminalapi.Keyboard) {
currentCtx = context.WithValue(currentCtx, "advanceQuery", true)
cancel()
appTermdash.Close()
case keyboard.KeyEnter:
// When a user presses Enter, stop the display but continue running.
interruptChan <- true
currentCtx = context.WithValue(currentCtx, "advanceDisplayMode", true)
cancel()
appTermdash.Close()
}
}

// Error management for termdash.
func errorTermdashHandler(e error) {
// If we hit an error from termdash, just log it and try to continue. Cases
// of errors seen so far make sense to ignore:
//
// - Unimplemented key-strokes.
slog.Error(e.Error())
}

// Sets-up the termdash container, which defines the overall layout, and begins
// running the display.
func initDisplayTermdash(resultsWidget, helpWidget, logsWidget widgetapi.Widget) {
Expand Down Expand Up @@ -101,5 +117,11 @@ func initDisplayTermdash(resultsWidget, helpWidget, logsWidget widgetapi.Widget)
e(err)

// Run the display.
termdash.Run(ctx, appTermdash, c, termdash.KeyboardSubscriber(keyboardTermdashHandler))
termdash.Run(
ctx,
appTermdash,
c,
termdash.ErrorHandler(errorTermdashHandler),
termdash.KeyboardSubscriber(keyboardTermdashHandler),
)
}
10 changes: 10 additions & 0 deletions internal/lib/display_tview.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,16 @@ func keyboardTviewHandler(key tcell.Key) {
currentCtx = context.WithValue(currentCtx, "advanceQuery", true)
appTview.Stop()
}()
case tcell.KeyEnter:
// This is wrapped in a goroutine to avoid deadlocks with tview.
//
// See: https://github.com/rivo/tview/issues/784
go func() {
// When a user presses Enter, stop the display but continue running.
interruptChan <- true
currentCtx = context.WithValue(currentCtx, "advanceDisplayMode", true)
appTview.Stop()
}()
}
}

Expand Down
19 changes: 14 additions & 5 deletions internal/lib/results.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,10 @@ import (
)

var (
config Config // Global configuration.
currentCtx context.Context // Current context.
driver DisplayDriver // Display driver, dictated by the results.
config Config // Global configuration.
currentCtx context.Context // Current context.
driver DisplayDriver // Display driver, dictated by the results.
readerIndexes map[string]*storage.ReaderIndex // Reader indexes for queries.

ctxDefaults = map[string]interface{}{
"advanceDisplayMode": false,
Expand All @@ -52,7 +53,7 @@ func AddResult(query, result string) {

// Retrieves a next result.
func GetResult(query string) storage.Result {
return <-storage.PutEvents[query]
return store.Next(query, readerIndexes[query])
}

// Creates a result with filtered values.
Expand Down Expand Up @@ -126,6 +127,14 @@ func Results(
) {
// Assign global config.
config = inputConfig
// Capture queries from context.
queries := ctx.Value("queries").([]string)

// Iniitialize reader indexes.
readerIndexes = make(map[string]*storage.ReaderIndex, len(queries))
for _, query := range queries {
readerIndexes[query] = store.NewReaderIndex()
}

for {
// Assign current context and restore default values.
Expand Down Expand Up @@ -167,7 +176,7 @@ func Results(
}
if currentCtx.Value("advanceQuery").(bool) {
// Adjust the query.
query = GetNextSliceRing(ctx.Value("queries").([]string), query)
query = GetNextSliceRing(queries, query)
}
}
}
18 changes: 0 additions & 18 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,6 @@ import (
"golang.org/x/exp/slog"
)

////////////////////////////////////////////////////////////////////////////////
//
// Types
//
////////////////////////////////////////////////////////////////////////////////

// Represents the mode value.
type queryMode int

Expand All @@ -43,12 +37,6 @@ func (q *queriesArg) ToStrings() (q_strings []string) {
return
}

////////////////////////////////////////////////////////////////////////////////
//
// Variables
//
////////////////////////////////////////////////////////////////////////////////

// Mode constants.
const (
MODE_QUERY queryMode = iota + 1 // For running in 'query' mode.
Expand Down Expand Up @@ -76,12 +64,6 @@ var (
} // Log levels acceptable as a flag.
)

////////////////////////////////////////////////////////////////////////////////
//
// Private
//
////////////////////////////////////////////////////////////////////////////////

// Parses a comma delimited argument string, returning a slice of strings if
// any are found, or an empty slice if not.
func parseCommaDelimitedArg(arg string) []string {
Expand Down
27 changes: 27 additions & 0 deletions pkg/storage/storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ type Results struct {
Results []Result
}

type ReaderIndex int

// Collection of results mapped to their queries.
type Storage map[string]*Results

Expand Down Expand Up @@ -122,6 +124,11 @@ func (r *Results) show() {
}
}

// Incremement a reader index, likely after a read.
func (i *ReaderIndex) inc() {
(*i)++
}

///////////////////////////////////////////////////////////////////////////////////////////////////
//
// Public
Expand All @@ -138,6 +145,10 @@ func (s *Storage) Get(query string, time time.Time) Result {
return (*s)[query].get(time)
}

func (s *Storage) GetAll(query string) []Result {
return (*s)[query].Results
}

// Get a result's labels.
func (s *Storage) GetLabels(query string) []string {
return (*s)[query].Labels
Expand All @@ -148,11 +159,21 @@ func (s *Storage) GetRange(query string, startTime, endTime time.Time) []Result
return (*s)[query].getRange(startTime, endTime)
}

func (s *Storage) GetToIndex(query string, index *ReaderIndex) []Result {
return (*s)[query].Results[:*index]
}

// Given a filter, return the corresponding value index.
func (s *Storage) GetValueIndex(query, filter string) int {
return (*s)[query].getValueIndex(filter)
}

// Initialize a new reader index.
func (s *Storage) NewReaderIndex() *ReaderIndex {
r := ReaderIndex(0)
return &r
}

// Initializes a new results series in a storage.
func (s *Storage) NewResults(query string) {
if _, ok := (*s)[query]; !ok {
Expand All @@ -162,6 +183,12 @@ func (s *Storage) NewResults(query string) {
}
}

func (s *Storage) Next(query string, index *ReaderIndex) (next Result) {
next = <-PutEvents[query]
index.inc()
return
}

// Put a new compound result.
func (s *Storage) Put(query string, value string, values ...interface{}) Result {
s.NewResults(query)
Expand Down

0 comments on commit 23157b6

Please sign in to comment.