diff --git a/cmd/mop/main.go b/cmd/mop/main.go index 5b7cc4a..2418a52 100644 --- a/cmd/mop/main.go +++ b/cmd/mop/main.go @@ -143,5 +143,5 @@ func main() { defer screen.Close() mainLoop(screen, profile) - profile.Save() + profile.Save() } diff --git a/layout.go b/layout.go index 5478981..7d495ae 100644 --- a/layout.go +++ b/layout.go @@ -168,7 +168,7 @@ func (layout *Layout) prettify(quotes *Quotes) []Stock { profile := quotes.profile - if profile.Filter != ""{ // Fix for blank display if invalid filter expression was cleared. + if profile.Filter != "" { // Fix for blank display if invalid filter expression was cleared. if profile.filterExpression != nil { if layout.filter == nil { // Initialize filter on first invocation. layout.filter = NewFilter(profile) @@ -218,11 +218,11 @@ func buildMarketTemplate() *template.Template { //----------------------------------------------------------------------------- func buildQuotesTemplate() *template.Template { - markup := `{{.Now}} + markup := ` -{{.Header}} +
{{.Header}} {{range.Stocks}}{{if eq .Direction 1}}{{else if eq .Direction -1}}{{end}}{{.Ticker}}{{.LastTrade}}{{.Change}}{{.ChangePct}}{{.Open}}{{.Low}}{{.High}}{{.Low52}}{{.High52}}{{.Volume}}{{.AvgVolume}}{{.PeRatio}}{{.Dividend}}{{.Yield}}{{.MarketCap}}{{.PreOpen}}{{.AfterHours}} {{end}}` @@ -234,7 +234,7 @@ func highlight(collections ...map[string]string) { for _, collection := range collections { change := collection[`change`] if change[len(change)-1:] == `%` { - change = change[0:len(change)-1] + change = change[0 : len(change)-1] } adv, err := strconv.ParseFloat(change, 64) if err == nil { @@ -272,9 +272,9 @@ func group(stocks []Stock) []Stock { func arrowFor(column int, profile *Profile) string { if column == profile.SortColumn { if profile.Ascending { - return string('\U00002191') + return string('▲') } - return string('\U00002193') + return string('▼') } return `` } diff --git a/markup.go b/markup.go index c99dbb4..331926f 100644 --- a/markup.go +++ b/markup.go @@ -5,9 +5,10 @@ package mop import ( - `github.com/nsf/termbox-go` - `regexp` - `strings` + "regexp" + "strings" + + "github.com/nsf/termbox-go" ) // Markup implements some minimalistic text formatting conventions that @@ -35,9 +36,6 @@ type Markup struct { // colors and column alignments. func NewMarkup(profile *Profile) *Markup { markup := &Markup{} - markup.Foreground = termbox.ColorDefault - markup.Background = termbox.ColorDefault - markup.RightAligned = false markup.tags = make(map[string]termbox.Attribute) markup.tags[`/`] = termbox.ColorDefault @@ -67,6 +65,14 @@ func NewMarkup(profile *Profile) *Markup { markup.tags[`gain`] = markup.tags[profile.Colors.Gain] markup.tags[`loss`] = markup.tags[profile.Colors.Loss] markup.tags[`tag`] = markup.tags[profile.Colors.Tag] + markup.tags[`header`] = markup.tags[profile.Colors.Header] + markup.tags[`time`] = markup.tags[profile.Colors.Time] + markup.tags[`default`] = markup.tags[profile.Colors.Default] + + markup.Foreground = markup.tags[profile.Colors.Default] + + markup.Background = termbox.ColorDefault + markup.RightAligned = false markup.regex = markup.supportedTags() // Once we have the hash we could build the regex. @@ -138,7 +144,7 @@ func (markup *Markup) process(tag string, open bool) bool { if attribute >= termbox.AttrBold { markup.Foreground &= ^attribute // Clear the Termbox attribute. } else { - markup.Foreground = termbox.ColorDefault + markup.Foreground = markup.tags[`default`] } } } diff --git a/profile.go b/profile.go index 4be37d0..d6a3779 100644 --- a/profile.go +++ b/profile.go @@ -16,22 +16,28 @@ import ( const defaultGainColor = "green" const defaultLossColor = "red" const defaultTagColor = "yellow" +const defaultHeaderColor = "lightgray" +const defaultTimeColor = "lightgray" +const defaultColor = "lightgray" // Profile manages Mop program settings as defined by user (ex. list of // stock tickers). The settings are serialized using JSON and saved in // the ~/.moprc file. type Profile struct { - Tickers []string // List of stock tickers to display. - MarketRefresh int // Time interval to refresh market data. - QuotesRefresh int // Time interval to refresh stock quotes. - SortColumn int // Column number by which we sort stock quotes. - Ascending bool // True when sort order is ascending. - Grouped bool // True when stocks are grouped by advancing/declining. - Filter string // Filter in human form - Colors struct { // User defined colors - Gain string - Loss string - Tag string + Tickers []string // List of stock tickers to display. + MarketRefresh int // Time interval to refresh market data. + QuotesRefresh int // Time interval to refresh stock quotes. + SortColumn int // Column number by which we sort stock quotes. + Ascending bool // True when sort order is ascending. + Grouped bool // True when stocks are grouped by advancing/declining. + Filter string // Filter in human form + Colors struct { // User defined colors + Gain string + Loss string + Tag string + Header string + Time string + Default string } filterExpression *govaluate.EvaluableExpression // The filter as a govaluate expression selectedColumn int // Stores selected column number when the column editor is active. @@ -40,24 +46,24 @@ type Profile struct { func IsSupportedColor(colorName string) bool { switch colorName { - case - "black", - "red", - "green", - "yellow", - "blue", - "magenta", - "cyan", - "white", - "darkgray", - "lightred", - "lightgreen", - "lightyellow", - "lightblue", - "lightmagenta", - "lightcyan", - "lightgray": - return true + case + "black", + "red", + "green", + "yellow", + "blue", + "magenta", + "cyan", + "white", + "darkgray", + "lightred", + "lightgreen", + "lightyellow", + "lightblue", + "lightmagenta", + "lightcyan", + "lightgray": + return true } return false } @@ -78,13 +84,19 @@ func NewProfile(filename string) *Profile { profile.Colors.Gain = defaultGainColor profile.Colors.Loss = defaultLossColor profile.Colors.Tag = defaultTagColor + profile.Colors.Header = defaultHeaderColor + profile.Colors.Time = defaultTimeColor + profile.Colors.Default = defaultColor profile.Save() } else { json.Unmarshal(data, profile) - InitColor(profile.Colors.Gain, defaultGainColor) - InitColor(profile.Colors.Loss, defaultLossColor) - InitColor(profile.Colors.Tag, defaultTagColor) + InitColor(&profile.Colors.Gain, defaultGainColor) + InitColor(&profile.Colors.Loss, defaultLossColor) + InitColor(&profile.Colors.Tag, defaultTagColor) + InitColor(&profile.Colors.Header, defaultHeaderColor) + InitColor(&profile.Colors.Time, defaultTimeColor) + InitColor(&profile.Colors.Default, defaultColor) profile.SetFilter(profile.Filter) } @@ -93,10 +105,10 @@ func NewProfile(filename string) *Profile { return profile } -func InitColor(color string, defaultValue string) { - color = strings.ToLower(color) - if !IsSupportedColor(color) { - color = defaultValue; +func InitColor(color *string, defaultValue string) { + *color = strings.ToLower(*color) + if !IsSupportedColor(*color) { + *color = defaultValue } } diff --git a/screen.go b/screen.go index 91a2932..9a9b45d 100644 --- a/screen.go +++ b/screen.go @@ -5,11 +5,12 @@ package mop import ( - `github.com/nsf/termbox-go` - `strings` - `time` - `strconv` - `fmt` + "fmt" + "strconv" + "strings" + "time" + + "github.com/nsf/termbox-go" ) // Screen is thin wrapper around Termbox library to provide basic display @@ -90,7 +91,7 @@ func (screen *Screen) ClearLine(x int, y int) *Screen { func (screen *Screen) Draw(objects ...interface{}) *Screen { zonename, _ := time.Now().In(time.Local).Zone() if screen.pausedAt != nil { - defer screen.DrawLine(0, 0, ``+screen.pausedAt.Format(`3:04:05pm ` + zonename)+``) + defer screen.DrawLine(0, 0, ``+screen.pausedAt.Format(`3:04:05pm `+zonename)+``) } for _, ptr := range objects { switch ptr.(type) { @@ -102,7 +103,7 @@ func (screen *Screen) Draw(objects ...interface{}) *Screen { screen.draw(screen.layout.Quotes(object.Fetch())) case time.Time: timestamp := ptr.(time.Time).Format(`3:04:05pm ` + zonename) - screen.DrawLine(0, 0, ``+timestamp+``) + screen.DrawLine(0, 0, ``) default: screen.draw(ptr.(string)) } @@ -146,7 +147,7 @@ func (screen *Screen) draw(str string) { drewHeading := false tempFormat := "%" + strconv.Itoa(screen.width) + "s" - blankLine := fmt.Sprintf(tempFormat,"") + blankLine := fmt.Sprintf(tempFormat, "") allLines = strings.Split(str, "\n") // Write the lines being updated. @@ -154,9 +155,9 @@ func (screen *Screen) draw(str string) { screen.DrawLine(0, row, allLines[row]) // Did we draw the underlined heading row? This is a crude // check, but--see comments below... - if strings.Contains(allLines[row],"Ticker") && - strings.Contains(allLines[row],"Last") && - strings.Contains(allLines[row],"Change") { + if strings.Contains(allLines[row], "Ticker") && + strings.Contains(allLines[row], "Last") && + strings.Contains(allLines[row], "Change") { drewHeading = true } } @@ -173,7 +174,7 @@ func (screen *Screen) draw(str string) { // cycle. In that case, padding with blank lines would overwrite the // stocks list.) if drewHeading { - for i := len(allLines)-1; i < screen.height; i++ { + for i := len(allLines) - 1; i < screen.height; i++ { screen.DrawLine(0, i, blankLine) } }