Skip to content

Commit 45d48bb

Browse files
authored
feat: add optional statistics (#13)
* feat: add current state * feat: add current state * refactor(git.go): remove function and simplify return * refactor(gui.go): decrease indent for title text this slightly dedents everything to align properly * refactor(gui.go): change deprecated methods * refactor(gui.go): change selected item's prefix indicator this now matches the other input fields * refactor: clean up how the configuration is used it was a real mess before * feat: add session as statistics field * refactor(gui.go): remove colons they kind of don't make sense in the location they're at * refactor(gui.go): remove filtering didn't work anyway * feat: simplify stat display and add options * docs: describe new configuration options * feat(main.go): add '-s' flag to just show statistics * docs(README.md): add note about new '-s' flag
1 parent ec9111c commit 45d48bb

8 files changed

Lines changed: 347 additions & 111 deletions

File tree

README.md

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,14 +42,27 @@ rm -rf "${GOPATH}/pkg/mod/github.com/usrme/cometary*"
4242

4343
There is an additional `comet.json` file that includes the prefixes and descriptions that I most prefer myself, which can be added to either the root of a repository, to one's home directory as `.comet.json` or to `${XDG_CONFIG_HOME}/cometary/config.json`. Omitting this means that the same defaults are used as in the original.
4444

45-
- To adjust the character limit of the scope, add the key `scopeInputCharLimit` into the configuration file with the desired limit
46-
- Omitting the key uses a default value of 16 characters
47-
- To adjust the character limit of the message, add the key `commitInputCharLimit` into the configuration file with the desired limit
48-
- Omitting the key uses a default value of 100 characters
49-
- To adjust the total limit of characters in the *resulting* commit message, add the key `totalInputCharLimit` into the configuration file with the desired limit
45+
- To adjust the character limit of the scope, add the key `scopeInputCharLimit` with the desired limit
46+
- Default: 16
47+
- To adjust the character limit of the message, add the key `commitInputCharLimit` with the desired limit
48+
- Default: 100
49+
- To adjust the total limit of characters in the *resulting* commit message, add the key `totalInputCharLimit` with the desired limit
5050
- Adding this key overrides scope- and message-specific limits
51-
- To adjust the order of the scope completion values (i.e. longer or shorter strings first), then add the key `scopeOrderCompletion` into the configuration file with either `ascending` or `descending` as the values
52-
- Omitting the key uses a default order of descending
51+
- To adjust the order of the scope completion values (i.e. longer or shorter strings first), add the key `scopeOrderCompletion` with either `"ascending"` or `"descending"`
52+
- Default: `"descending"`
53+
- To enable the storing of runtime statistics, add the key `storeRuntime` with the value `true`
54+
- Default: `false`
55+
- This will create a `stats.json` file next to the configuration file with aggregated statistics across days, weeks, months, and years
56+
- To show the session runtime statistics after each commit, add the key `showRuntime` with the value `true`
57+
- Default: `false`
58+
- This will show `> Session: N seconds` after the commit was successful
59+
- To show the all-time runtime statistics after each commit, add the key `showStats` with the value `true`
60+
- Default: `false`
61+
- To just show the all-time runtime statistics and quit the program, run the program with the `-s` flag
62+
- To adjust the format of the statistics from seconds to hours or minutes, add the key `showStatsFormat` with either `"minutes"` or `"hours"`
63+
- Default: `"seconds"`
64+
- To always show session runtime statistics as seconds but keep everything else as defined by `showStatsFormat`, add the key `sessionStatAsSeconds` with the value `true`
65+
- Default: `false`
5366

5467
There is also a `-m` flag that takes a string that will be used as the basis for a search among all commit messages. For example: if you're committing something of a chore and always just use the message "update dependencies", you can do `cometary -m update` (use quotation marks if argument to `-m` includes spaces) and Cometary will populate the list of possible messages with those that include "update", which can then be cycled through with the Tab key. This is similar to the search you could make with `git log --grep="update"`.
5568

comet.json

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
11
{
22
"signOffCommits": false,
3-
"scopeInputCharLimit": 100,
4-
"commitInputCharLimit": 120,
5-
"totalInputCharLimit": 50,
6-
"scopeCompletionOrder": "ascending",
7-
"findAllCommitMessages": true,
3+
"scopeInputCharLimit": 16,
4+
"commitInputCharLimit": 100,
5+
"totalInputCharLimit": 0,
6+
"scopeCompletionOrder": "descending",
7+
"findAllCommitMessages": false,
8+
"storeRuntime": true,
9+
"showRuntime": true,
10+
"showStats": true,
11+
"showStatsFormat": "minutes",
12+
"sessionStatAsSeconds": false,
813
"prefixes": [
914
{
1015
"title": "fix",

config.go

Lines changed: 44 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,8 @@ package main
22

33
import (
44
"encoding/json"
5-
"fmt"
65
"os"
76
"path/filepath"
8-
9-
"github.com/charmbracelet/bubbles/list"
107
)
118

129
type prefix struct {
@@ -22,62 +19,67 @@ type config struct {
2219
TotalInputCharLimit int `json:"totalInputCharLimit"`
2320
ScopeCompletionOrder string `json:"scopeCompletionOrder"`
2421
FindAllCommitMessages bool `json:"findAllCommitMessages"`
22+
StoreRuntime bool `json:"storeRuntime"`
23+
ShowRuntime bool `json:"showRuntime"`
24+
ShowStats bool `json:"showStats"`
25+
ShowStatsFormat string `json:"showStatsFormat"`
26+
SessionStatAsSeconds bool `json:"sessionStatAsSeconds"`
2527
}
2628

2729
func (i prefix) Title() string { return i.T }
2830
func (i prefix) Description() string { return i.D }
2931
func (i prefix) FilterValue() string { return i.T }
3032

31-
var defaultPrefixes = []list.Item{
32-
prefix{
33+
var defaultPrefixes = []prefix{
34+
{
3335
T: "feat",
3436
D: "Introduces a new feature",
3537
},
36-
prefix{
38+
{
3739
T: "fix",
3840
D: "Patches a bug",
3941
},
40-
prefix{
42+
{
4143
T: "docs",
4244
D: "Documentation changes only",
4345
},
44-
prefix{
46+
{
4547
T: "test",
4648
D: "Adding missing tests or correcting existing tests",
4749
},
48-
prefix{
50+
{
4951
T: "build",
5052
D: "Changes that affect the build system",
5153
},
52-
prefix{
54+
{
5355
T: "ci",
5456
D: "Changes to CI configuration files and scripts",
5557
},
56-
prefix{
58+
{
5759
T: "perf",
5860
D: "A code change that improves performance",
5961
},
60-
prefix{
62+
{
6163
T: "refactor",
6264
D: "A code change that neither fixes a bug nor adds a feature",
6365
},
64-
prefix{
66+
{
6567
T: "revert",
6668
D: "Reverts a previous change",
6769
},
68-
prefix{
70+
{
6971
T: "style",
7072
D: "Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)",
7173
},
72-
prefix{
74+
{
7375
T: "chore",
7476
D: "A minor change which does not fit into any other category",
7577
},
7678
}
7779

7880
const applicationName = "cometary"
7981

80-
func loadConfig() ([]list.Item, bool, *config, error) {
82+
func loadConfig() *config {
8183
nonXdgConfigFile := ".comet.json"
8284

8385
// Check for configuration file local to current directory
@@ -94,20 +96,36 @@ func loadConfig() ([]list.Item, bool, *config, error) {
9496
}
9597

9698
// Check for configuration file according to XDG Base Directory Specification
97-
if cfgDir, err := getConfigDir(); err == nil {
99+
if cfgDir, err := GetConfigDir(); err == nil {
98100
path := filepath.Join(cfgDir, "config.json")
99101
if _, err := os.Stat(path); err == nil {
100102
return loadConfigFile(path)
101103
}
102104
}
103105

104-
return defaultPrefixes, false, nil, nil
106+
return newConfig()
107+
}
108+
109+
func newConfig() *config {
110+
return &config{
111+
Prefixes: defaultPrefixes,
112+
SignOffCommits: false,
113+
ScopeInputCharLimit: 16,
114+
CommitInputCharLimit: 100,
115+
TotalInputCharLimit: 0,
116+
ScopeCompletionOrder: "descending",
117+
FindAllCommitMessages: false,
118+
StoreRuntime: false,
119+
ShowRuntime: false,
120+
ShowStats: false,
121+
ShowStatsFormat: "seconds",
122+
SessionStatAsSeconds: true,
123+
}
105124
}
106125

107-
func getConfigDir() (string, error) {
126+
func GetConfigDir() (string, error) {
108127
configDir := os.Getenv("XDG_CONFIG_HOME")
109128

110-
// If the value of the environment variable is unset, empty, or not an absolute path, use the default
111129
if configDir == "" || configDir[0:1] != "/" {
112130
homeDir, err := os.UserHomeDir()
113131
if err != nil {
@@ -116,30 +134,19 @@ func getConfigDir() (string, error) {
116134
return filepath.Join(homeDir, ".config", applicationName), nil
117135
}
118136

119-
// The value of the environment variable is valid; use it
120137
return filepath.Join(configDir, applicationName), nil
121138
}
122139

123-
func loadConfigFile(path string) ([]list.Item, bool, *config, error) {
140+
func loadConfigFile(path string) *config {
141+
var c config
124142
data, err := os.ReadFile(path)
125143
if err != nil {
126-
return nil, false, nil, fmt.Errorf("failed to read config file: %w", err)
144+
return &c
127145
}
128-
var c config
146+
129147
if err := json.Unmarshal(data, &c); err != nil {
130-
return nil, false, nil, fmt.Errorf("invalid json in config file '%s': %w", path, err)
148+
return &c
131149
}
132150

133-
return convertPrefixes(c.Prefixes), c.SignOffCommits, &c, nil
134-
}
135-
136-
func convertPrefixes(prefixes []prefix) []list.Item {
137-
var output []list.Item
138-
for _, prefix := range prefixes {
139-
output = append(output, prefix)
140-
}
141-
if len(output) == 0 {
142-
return defaultPrefixes
143-
}
144-
return output
151+
return &c
145152
}

git.go

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import (
1010
func filesInStaging() ([]string, error) {
1111
cmd := exec.Command("git", "diff", "--no-ext-diff", "--cached", "--name-only")
1212
output, err := cmd.CombinedOutput()
13-
1413
if err != nil {
1514
return []string{}, fmt.Errorf(string(output))
1615
}
@@ -21,22 +20,13 @@ func filesInStaging() ([]string, error) {
2120
return strings.Split(lines, "\n"), nil
2221
}
2322

24-
func checkGitInPath() error {
25-
if _, err := exec.LookPath("git"); err != nil {
26-
return fmt.Errorf("cannot find git in PATH: %w", err)
27-
}
28-
return nil
29-
}
30-
31-
func findGitDir() (string, error) {
23+
func findGitDir() error {
3224
cmd := exec.Command("git", "rev-parse", "--show-toplevel")
3325
output, err := cmd.CombinedOutput()
34-
3526
if err != nil {
36-
return "", fmt.Errorf(string(output))
27+
return fmt.Errorf(string(output))
3728
}
38-
39-
return strings.TrimSpace(string(output)), nil
29+
return nil
4030
}
4131

4232
func commit(msg string, body bool, signOff bool) error {

go.sum

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@ github.com/charmbracelet/bubbles v0.16.1 h1:6uzpAAaT9ZqKssntbvZMlksWHruQLNxg49H5
66
github.com/charmbracelet/bubbles v0.16.1/go.mod h1:2QCp9LFlEsBQMvIYERr7Ww2H2bA7xen1idUDIzm/+Xc=
77
github.com/charmbracelet/bubbletea v0.24.2 h1:uaQIKx9Ai6Gdh5zpTbGiWpytMU+CfsPp06RaW2cx/SY=
88
github.com/charmbracelet/bubbletea v0.24.2/go.mod h1:XdrNrV4J8GiyshTtx3DNuYkR1FDaJmO3l2nejekbsgg=
9-
github.com/charmbracelet/lipgloss v0.7.1 h1:17WMwi7N1b1rVWOjMT+rCh7sQkvDU75B2hbZpc5Kc1E=
10-
github.com/charmbracelet/lipgloss v0.7.1/go.mod h1:yG0k3giv8Qj8edTCbbg6AlQ5e8KNWpFujkNawKNhE2c=
119
github.com/charmbracelet/lipgloss v0.8.0 h1:IS00fk4XAHcf8uZKc3eHeMUTCxUH6NkaTrdyCQk84RU=
1210
github.com/charmbracelet/lipgloss v0.8.0/go.mod h1:p4eYUZZJ/0oXTuCQKFF8mqyKCz0ja6y+7DniDDw5KKU=
1311
github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 h1:q2hJAaP1k2wIvVRd/hEHD7lacgqrCPS+k8g1MndzfWY=
@@ -42,15 +40,9 @@ golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
4240
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
4341
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
4442
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
45-
golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
46-
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
4743
golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
4844
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
49-
golang.org/x/term v0.11.0 h1:F9tnn/DA/Im8nCwm+fX+1/eBwi4qFjRT++MhtVC4ZX0=
50-
golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU=
5145
golang.org/x/term v0.12.0 h1:/ZfYdc3zq+q02Rv9vGqTeSItdzZTSNDmfTi0mBAuidU=
5246
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
53-
golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc=
54-
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
5547
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
5648
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=

0 commit comments

Comments
 (0)