diff --git a/CHANGELOG.md b/CHANGELOG.md
index b956c39..b765055 100755
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,3 +9,5 @@
- Something This is unexpected
- Something really good
- foo bar foo
+- Something
+- a `monospaced` item
diff --git a/README.md b/README.md
index 320c9dd..296253f 100644
--- a/README.md
+++ b/README.md
@@ -4,7 +4,7 @@ An opiniated way of managing changelogs adhering to the [keepachangelog](https:/
-
+
## Installation
@@ -67,6 +67,17 @@ While quite basic, this command can be used in conjunction with other tools to q
Renders the nearest changelog right inside your terminal using [charmbracelet/glamour](https://github.com/charmbracelet/glamour).
+### `changelog search`
+
+Searches for changes matching the given search query.
+
+
+
+
+
+
+Matches are displayed with contextual information, such as the version the change was released in and its type of change.
+
### `changelog edit`
Opens the nearest changelog inside your `$EDITOR`.
@@ -79,7 +90,7 @@ Adds a new entry to one of your sections in the changelog.
-
+
Now we arrive at the more useful features of keepac.
diff --git a/cmd/search.go b/cmd/search.go
new file mode 100644
index 0000000..10bc64d
--- /dev/null
+++ b/cmd/search.go
@@ -0,0 +1,36 @@
+package cmd
+
+import (
+ "fmt"
+
+ clog "github.com/niclasvaneyk/keepac/internal/changelog"
+ "github.com/spf13/cobra"
+)
+
+// searchCmd represents the grep command
+var searchCmd = &cobra.Command{
+ Use: "search [query]",
+ Aliases: []string{"grep"},
+ Short: "Searches for strings in the nearest changelog and prints matches within their context",
+ Long: ``,
+ Args: cobra.ExactArgs(1),
+ RunE: func(cmd *cobra.Command, args []string) error {
+ changelog, _, err := clog.ResolveChangelog()
+ if err != nil {
+ return err
+ }
+
+ query := args[0]
+ result := clog.Search(changelog, query)
+
+ if result == "" {
+ fmt.Println("Nothing matched your query!")
+ }
+
+ return clog.Show(result + "\n")
+ },
+}
+
+func init() {
+ rootCmd.AddCommand(searchCmd)
+}
diff --git a/go.mod b/go.mod
index 8636cf7..6f0850a 100644
--- a/go.mod
+++ b/go.mod
@@ -5,6 +5,7 @@ go 1.20
require (
github.com/spf13/cobra v1.7.0
github.com/yuin/goldmark v1.5.4
+ golang.org/x/crypto v0.10.0
)
require (
@@ -28,11 +29,11 @@ require (
github.com/rivo/uniseg v0.2.0 // indirect
github.com/sahilm/fuzzy v0.1.0 // indirect
github.com/yuin/goldmark-emoji v1.0.1 // indirect
- golang.org/x/net v0.7.0 // indirect
+ golang.org/x/net v0.10.0 // indirect
golang.org/x/sync v0.1.0 // indirect
- golang.org/x/sys v0.6.0 // indirect
- golang.org/x/term v0.6.0 // indirect
- golang.org/x/text v0.7.0 // indirect
+ golang.org/x/sys v0.9.0 // indirect
+ golang.org/x/term v0.9.0 // indirect
+ golang.org/x/text v0.10.0 // indirect
)
require (
diff --git a/go.sum b/go.sum
index 7314f28..5e80a56 100644
--- a/go.sum
+++ b/go.sum
@@ -21,7 +21,6 @@ github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 h1:q2hJAaP1k2
github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dlclark/regexp2 v1.4.0 h1:F1rxgk7p4uKjwIQxBs9oAXe5CqrXlCduYEJvrF4u93E=
github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
@@ -29,7 +28,6 @@ github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY=
github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
-github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
@@ -54,7 +52,6 @@ github.com/muesli/termenv v0.15.1 h1:UzuTb/+hhlBugQz28rpzey4ZuKcZ03MeKsoG7IJZIxs
github.com/muesli/termenv v0.15.1/go.mod h1:HeAQPTzpfs016yGtA4g00CsdYnVLJvxsS4ANqrZs2sQ=
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
-github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
@@ -67,7 +64,6 @@ github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRM
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.5.2/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
@@ -75,25 +71,27 @@ github.com/yuin/goldmark v1.5.4 h1:2uY/xC0roWy8IBEGLgB1ywIoEJFGmRrX21YQcvGZzjU=
github.com/yuin/goldmark v1.5.4/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yuin/goldmark-emoji v1.0.1 h1:ctuWEyzGBwiucEqxzwe0SOYDXPAucOrE9NQC18Wa1os=
github.com/yuin/goldmark-emoji v1.0.1/go.mod h1:2w1E6FEWLcDQkoTE+7HU6QF1F6SLlNGjRIBbIZQFqkQ=
+golang.org/x/crypto v0.10.0 h1:LKqV2xt9+kDzSTfOhx4FrkEBcMrAgHSYgzywV9zcGmM=
+golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I=
golang.org/x/net v0.0.0-20221002022538-bcab6841153b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
-golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
-golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
+golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
+golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s=
+golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
-golang.org/x/term v0.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw=
-golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
+golang.org/x/term v0.9.0 h1:GRRCnKYhdQrD8kfRAdQ6Zcw1P0OcELxGLKJvtjVMZ28=
+golang.org/x/term v0.9.0/go.mod h1:M6DEAAIenWoTxdKrOltXcmDY3rSplQUkrvaDU5FcQyo=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
-golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
-golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
+golang.org/x/text v0.10.0 h1:UpjohKhiEgNc0CSauXmwYftY1+LlaC75SJwh0SgCX58=
+golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/internal/changelog/parser.go b/internal/changelog/parser.go
index d97fbce..4fcaeb7 100644
--- a/internal/changelog/parser.go
+++ b/internal/changelog/parser.go
@@ -116,7 +116,11 @@ type Bounds struct {
type Section struct {
Type ChangeType
- Items []string
+ Items []Item
+ Bounds Bounds
+}
+
+type Item struct {
Bounds Bounds
}
@@ -212,7 +216,8 @@ func Parse(source []byte) Changelog {
Stop: r.HeadlineBounds.Stop, // This will be incremented later
}
currentRelease = &r
- currentReleaseIsNextRelease = string(heading.Text(source)) == "[Unreleased]"
+ text := string(heading.Text(source))
+ currentReleaseIsNextRelease = text == "[Unreleased]" || text == "Unreleased"
}
if heading.Level == 3 && entering {
@@ -220,7 +225,7 @@ func Parse(source []byte) Changelog {
bounds.Start = bounds.Start - 4 // we subtract the length of "### " to achieve better insertion points
section := Section{
Type: ParseChangeType(string(heading.Text(source))),
- Items: make([]string, 0),
+ Items: make([]Item, 0),
Bounds: bounds,
}
(*currentRelease).Sections = append((*currentRelease).Sections, section)
@@ -237,8 +242,7 @@ func Parse(source []byte) Changelog {
}
if node.Kind() == ast.KindListItem && currentRelease != nil && len(currentRelease.Sections) > 0 {
- item := node.(*ast.ListItem)
- change := string(item.Text(source))
+ change := Item{Bounds: ComputeBounds(node)}
section := ¤tRelease.Sections[len(currentRelease.Sections)-1]
section.Items = append(section.Items, change)
}
diff --git a/internal/changelog/search.go b/internal/changelog/search.go
new file mode 100644
index 0000000..8dfd1e1
--- /dev/null
+++ b/internal/changelog/search.go
@@ -0,0 +1,59 @@
+package changelog
+
+import (
+ "strings"
+)
+
+func Search(changelog *Changelog, query string) string {
+ output := make([]string, 0)
+
+ nextRelease := changelog.Releases.Next
+ if nextRelease != nil {
+ includedRelease := false
+ for _, section := range nextRelease.Sections {
+ includedSection := false
+ for _, item := range section.Items {
+ text := changelog.ContentWithin(&item.Bounds)
+ if strings.Contains(text, query) {
+ if !includedRelease {
+ includedRelease = true
+ output = append(output, "## "+changelog.ContentWithin(&nextRelease.HeadlineBounds))
+ }
+
+ if !includedSection {
+ includedSection = true
+ output = append(output, "### "+ChangeTypeLabel(section.Type))
+ }
+
+ output = append(output, "- "+text)
+ }
+ }
+ }
+ }
+
+ for _, release := range changelog.Releases.Past {
+ includedRelease := false
+ for _, section := range release.Sections {
+ includedSection := false
+ for _, item := range section.Items {
+ text := changelog.ContentWithin(&item.Bounds)
+ if strings.Contains(text, query) {
+ if !includedRelease {
+ includedRelease = true
+ output = append(output, "## "+changelog.ContentWithin(&release.HeadlineBounds))
+ output = append(output, "")
+ }
+
+ if !includedSection {
+ includedSection = true
+ output = append(output, "### "+ChangeTypeLabel(section.Type))
+ output = append(output, "")
+ }
+ output = append(output, "- "+text)
+ }
+ }
+ }
+ }
+
+ return strings.Join(output, "\n")
+}
diff --git a/internal/changelog/search_test.go b/internal/changelog/search_test.go
new file mode 100644
index 0000000..95041c9
--- /dev/null
+++ b/internal/changelog/search_test.go
@@ -0,0 +1,36 @@
+package changelog
+
+import "testing"
+
+func TestItCanSearchForItems(t *testing.T) {
+ changelog := Parse([]byte(`
+# Changelog
+
+## [Unreleased]
+
+### Added
+
+- Something cool
+- Another thing
+
+### Removed
+
+- Support for Go > 1.0
+- Support for Windows
+ `))
+
+ actual := Search(&changelog, "Windows")
+ expected := `
+# Changelog
+
+## [Unreleased]
+
+### Removed
+
+- Support for Windows
+ `
+
+ if actual != expected {
+ t.Errorf("Expected does not match actual:\n\n%s", actual)
+ }
+}
diff --git a/internal/changelog/show.go b/internal/changelog/show.go
index b64d0c1..ca76abd 100644
--- a/internal/changelog/show.go
+++ b/internal/changelog/show.go
@@ -2,8 +2,10 @@ package changelog
import (
"fmt"
+ "os"
"github.com/charmbracelet/glamour"
+ "golang.org/x/crypto/ssh/terminal"
)
func (changelog *Changelog) ContentWithin(bounds *Bounds) string {
@@ -11,11 +13,14 @@ func (changelog *Changelog) ContentWithin(bounds *Bounds) string {
}
func Show(contents string) error {
- renderer, _ := glamour.NewTermRenderer(
- // detect background color and pick either the default dark or light theme
+ renderer, err := glamour.NewTermRenderer(
glamour.WithAutoStyle(),
glamour.WithEnvironmentConfig(),
+ glamour.WithWordWrap(getWordWrapLimit()),
)
+ if err != nil {
+ return err
+ }
out, err := renderer.Render(contents)
if err != nil {
@@ -25,3 +30,17 @@ func Show(contents string) error {
fmt.Print(out)
return nil
}
+
+func getWordWrapLimit() int {
+ current := int(os.Stdin.Fd())
+ width, _, err := terminal.GetSize(current)
+ if err != nil {
+ return 80
+ }
+
+ if width > 80 {
+ return 80
+ }
+
+ return width
+}
diff --git a/tapes/dark/CHANGELOG.md.1 b/tapes/dark/CHANGELOG.md.1
new file mode 100644
index 0000000..09a9580
--- /dev/null
+++ b/tapes/dark/CHANGELOG.md.1
@@ -0,0 +1,2479 @@
+# Changelog
+
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
+and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+
+## [Unreleased]
+
+### Fixed
+
+- Fix issue where some pseudo-element variants generated the wrong selector ([#10943](https://github.com/tailwindlabs/tailwindcss/pull/10943), [#10962](https://github.com/tailwindlabs/tailwindcss/pull/10962))
+- Make font settings propagate into buttons, inputs, etc. ([#10940](https://github.com/tailwindlabs/tailwindcss/pull/10940))
+- Fix parsing of `theme()` inside `calc()` when there are no spaces around operators ([#11157](https://github.com/tailwindlabs/tailwindcss/pull/11157))
+- Ensure `repeating-conic-gradient` is detected as an image ([#11180](https://github.com/tailwindlabs/tailwindcss/pull/11180))
+- Remove `autoprefixer` dependency ([#11315](https://github.com/tailwindlabs/tailwindcss/pull/11315))
+- Fix source maps issue resulting in a crash ([#11319](https://github.com/tailwindlabs/tailwindcss/pull/11319))
+- Fallback to RegEx based parser when using custom transformers or extractors ([#11335](https://github.com/tailwindlabs/tailwindcss/pull/11335))
+- Move unknown pseudo-elements outside of `:is` by default ([#11345](https://github.com/tailwindlabs/tailwindcss/pull/11345))
+- Escape animation names when prefixes contain special characters ([#11470](https://github.com/tailwindlabs/tailwindcss/pull/11470))
+- Don't prefix arbitrary classes in `group` and `peer` variants ([#11454](https://github.com/tailwindlabs/tailwindcss/pull/11454))
+- Sort classes using position of first matching rule ([#11504](https://github.com/tailwindlabs/tailwindcss/pull/11504))
+
+### Added
+
+- Add `aria-busy` utility ([#10966](https://github.com/tailwindlabs/tailwindcss/pull/10966))
+- Support `@import "tailwindcss"` using top-level `index.css` file ([#11205](https://github.com/tailwindlabs/tailwindcss/pull/11205), ([#11260](https://github.com/tailwindlabs/tailwindcss/pull/11260)))
+- Use `lightningcss` for nesting and vendor prefixes in PostCSS plugin ([#10399](https://github.com/tailwindlabs/tailwindcss/pull/10399))
+- Automatically detect content paths when no `content` configuration is provided ([#11173](https://github.com/tailwindlabs/tailwindcss/pull/11173), [#11221](https://github.com/tailwindlabs/tailwindcss/pull/11221))
+- Process and inline `@import` at-rules natively ([#11239](https://github.com/tailwindlabs/tailwindcss/pull/11239))
+- Add `svh`, `lvh`, and `dvh` values to default `height`/`min-height`/`max-height` theme ([#11317](https://github.com/tailwindlabs/tailwindcss/pull/11317))
+- Add `has-*` variants for `:has(...)` pseudo-class ([#11318](https://github.com/tailwindlabs/tailwindcss/pull/11318))
+- Add `text-wrap` utilities including `text-balance` ([#11320](https://github.com/tailwindlabs/tailwindcss/pull/11320))
+- Explicitly configure Lightning CSS features, and prefer user browserslist over default browserslist ([#11402](https://github.com/tailwindlabs/tailwindcss/pull/11402), [#11412](https://github.com/tailwindlabs/tailwindcss/pull/11412))
+
+### Changed
+
+- Reset padding for `