Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
262 changes: 116 additions & 146 deletions skills/go-documentation/SKILL.md

Large diffs are not rendered by default.

67 changes: 67 additions & 0 deletions skills/go-documentation/evals/evals.json

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions skills/go-documentation/evals/files/jsdoc-comments/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module example.com/go-documentation-evals/tokenbucket

go 1.23
97 changes: 97 additions & 0 deletions skills/go-documentation/evals/files/jsdoc-comments/tokenbucket.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
// A rate limiter based on the token bucket algorithm.
// See https://en.wikipedia.org/wiki/Token_bucket for the algorithm.
package tokenbucket

import (
"errors"
"time"
)

/**
* The maximum number of tokens a bucket can hold by default.
*/
const DefaultCapacity = 100

// `ErrEmpty` is returned by `Take` when there are not enough tokens available.
var ErrEmpty = errors.New("Not enough tokens available.")

// A Bucket holds tokens that refill over time.
//
// It is **not** safe for concurrent use. Wrap it in a mutex if you
// share it between goroutines.
var bucketSchemaVersion = 1

type Bucket struct {
capacity int
tokens int
rate time.Duration
last time.Time
}

// Creates a new Bucket with the given capacity and refill rate.
//
// @param capacity The maximum number of tokens the bucket can hold.
// @param rate How often a single token is added to the bucket.
// @returns A pointer to the newly created Bucket.
func New(capacity int, rate time.Duration) *Bucket {
return &Bucket{
capacity: capacity,
tokens: capacity,
rate: rate,
last: time.Now(),
}
}

// Take removes `n` tokens from the bucket.
//
// @param n the number of tokens to remove
// @throws ErrEmpty if there are fewer than n tokens left
//
// Example usage:
// ```go
// if err := b.Take(1); err != nil {
// log.Fatal(err)
// }
// ```
func (b *Bucket) Take(n int) error {
b.refill()
if b.tokens < n {
return ErrEmpty
}
b.tokens -= n
return nil
}

// Available returns true if at least one token is currently available.
func (b *Bucket) Available() bool {
b.refill()
return b.tokens > 0
}

// SetRate changes how often tokens are added.
// For more details see [the tuning guide](https://example.com/tokenbucket/tuning).
func (b *Bucket) SetRate(rate time.Duration) {
b.rate = rate
}

// Refill tops the bucket up based on elapsed time.
//
// @deprecated Use Take instead, which refills the bucket automatically.
func (b *Bucket) Refill() {
b.refill()
}

func (b *Bucket) refill() {
now := time.Now()
if b.rate <= 0 {
return
}
added := int(now.Sub(b.last) / b.rate)
if added > 0 {
b.tokens += added
if b.tokens > b.capacity {
b.tokens = b.capacity
}
b.last = now
}
}
59 changes: 59 additions & 0 deletions skills/go-documentation/evals/files/package-overview/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# kvstore

[![Go Reference](https://pkg.go.dev/badge/example.com/go-documentation-evals/kvstore.svg)](https://pkg.go.dev/example.com/go-documentation-evals/kvstore)

**kvstore** is a tiny, dependency-free, concurrency-safe in-memory key-value
store for Go. It's the store I reach for in tests, small CLIs, and prototypes
where reaching for Redis or SQLite would be overkill.

## Why kvstore?

- **Zero dependencies** — just the standard library.
- **Concurrency-safe** — every operation is guarded by a `sync.RWMutex`, so you
can share one `Store` across goroutines without your own locking.
- **Tiny API** — five methods, nothing to learn.

## Install

```bash
go get example.com/go-documentation-evals/kvstore
```

## Quick start

```go
package main

import (
"fmt"

"example.com/go-documentation-evals/kvstore"
)

func main() {
s := kvstore.Open()
s.Set("greeting", []byte("hello"))

v, err := s.Get("greeting")
if err != nil {
panic(err)
}
fmt.Println(string(v)) // hello
}
```

## How it works

A `Store` wraps a `map[string][]byte` behind a read-write mutex. Reads
(`Get`, `Len`) take the read lock so they can run concurrently; writes
(`Set`, `Delete`) take the write lock. A missing key from `Get` returns the
sentinel `ErrNotFound` rather than an empty slice, so callers can tell "no such
key" apart from "the value is empty".

The store keeps everything in memory — there is no persistence. When the
process exits, the data is gone. That's intentional: kvstore is for ephemeral
state, not durable storage.

## License

MIT
3 changes: 3 additions & 0 deletions skills/go-documentation/evals/files/package-overview/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module example.com/go-documentation-evals/kvstore

go 1.23
57 changes: 57 additions & 0 deletions skills/go-documentation/evals/files/package-overview/store.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Package kvstore implements a lightweight, embeddable key-value store.
//
// Check out the project README for the full rundown — install
// instructions, a quick-start walkthrough, and a few benchmarks against
// the alternatives.
package kvstore

import (
"errors"
"sync"
)

// ErrNotFound is returned when a key is missing.
var ErrNotFound = errors.New("key not found")

// A Store is a concurrency-safe in-memory key-value store.
type Store struct {
mu sync.RWMutex
data map[string][]byte
}

// Open makes a Store.
func Open() *Store {
return &Store{data: make(map[string][]byte)}
}

// Get looks up a key.
func (s *Store) Get(key string) ([]byte, error) {
s.mu.RLock()
defer s.mu.RUnlock()
v, ok := s.data[key]
if !ok {
return nil, ErrNotFound
}
return v, nil
}

// Set stores a value.
func (s *Store) Set(key string, value []byte) {
s.mu.Lock()
defer s.mu.Unlock()
s.data[key] = value
}

// Delete removes a key.
func (s *Store) Delete(key string) {
s.mu.Lock()
defer s.mu.Unlock()
delete(s.data, key)
}

// Len returns the number of keys.
func (s *Store) Len() int {
s.mu.RLock()
defer s.mu.RUnlock()
return len(s.data)
}
3 changes: 3 additions & 0 deletions skills/go-documentation/evals/files/testable-examples/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module example.com/go-documentation-evals/slug

go 1.23
61 changes: 61 additions & 0 deletions skills/go-documentation/evals/files/testable-examples/slug.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// Package slug converts arbitrary text into URL-friendly slugs.
//
// Usage:
// ```go
// s := slug.New()
// fmt.Println(s.Make("Hello, World!")) // hello-world
// ```
package slug

import (
"strings"
"unicode"
)

// A Slugifier converts strings to slugs using a configurable separator.
type Slugifier struct {
// Separator is placed between words. Defaults to "-" when empty.
Separator string
}

// New returns a Slugifier that joins words with "-".
func New() *Slugifier {
return &Slugifier{Separator: "-"}
}

// Make converts s into a slug: it lowercases the text, splits it on any run of
// non-alphanumeric characters, and joins the pieces with the separator.
//
// @example
// New().Make("Hello, World!") // => "hello-world"
//
// @example <caption>WithNumbers</caption>
// New().Make("foo_bar 123") // => "foo-bar-123"
func (sl *Slugifier) Make(s string) string {
sep := sl.Separator
if sep == "" {
sep = "-"
}
var words []string
var cur strings.Builder
for _, r := range s {
if unicode.IsLetter(r) || unicode.IsDigit(r) {
cur.WriteRune(unicode.ToLower(r))
} else if cur.Len() > 0 {
words = append(words, cur.String())
cur.Reset()
}
}
if cur.Len() > 0 {
words = append(words, cur.String())
}
return strings.Join(words, sep)
}

// Make is a convenience wrapper that slugifies s with the default separator.
//
// @example
// slug.Make(" Go & TypeScript ") // => "go-typescript"
func Make(s string) string {
return New().Make(s)
}
24 changes: 24 additions & 0 deletions skills/go-documentation/references/comment-syntax.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ The full markup grammar used by Go doc comments, as defined at https://go.dev/do
- [Directives](#directives)
- [How gofmt reformats comments](#how-gofmt-reformats-comments)
- [Common pitfalls](#common-pitfalls)
- [Javadoc-style block comments](#javadoc-style-block-comments)

## Paragraphs

Expand Down Expand Up @@ -89,6 +90,8 @@ Gofmt automatically moves all link definitions to the end of the comment, in up

A URL in prose (e.g., `https://example.com`) is auto-linked in the HTML rendering. There is no Markdown-style `[Text](URL)` form — use the reference style if you need link text.

Symptom: `go doc` or pkg.go.dev shows a literal `[text](url)` in the output. That's leftover Markdown syntax carried over from JSDoc/TSDoc or a README — Go has no such link form, so it's preserved as plain text rather than rendered. Fix by converting to a bare URL or a reference-style link (above).

## Doc links

A doc link is a hyperlink to another Go identifier, written without a URL declaration:
Expand Down Expand Up @@ -358,6 +361,27 @@ Go 1.19+ gofmt applies heuristics that merge unindented lines into adjacent code

If you want a paragraph clearly separated from following non-paragraph content, insert a blank line between them. The blank line is the unambiguous separator and is preserved through every gofmt revision.

## Javadoc-style block comments

A doc comment can be a `/* ... */` block comment as well as a `//` line comment — Go's attachment rule (immediately before the declaration, no blank line) doesn't care which style you use. This matters because a comment ported from Java/TypeScript often looks like:

```go
/**
* The maximum number of tokens a bucket can hold by default.
*/
const DefaultCapacity = 100
```

This attaches as the doc comment for `DefaultCapacity` — it is not silently dropped — but it renders badly. `//` line comments have their leading space stripped per line, but a `/* */` block comment's internal lines are used as written, so every ` * ` prefix becomes part of the text, and the leading space makes each line an indented (code-block) line. `go doc -all` on the example above prints:

```
const DefaultCapacity = 100
*
- The maximum number of tokens a bucket can hold by default.
```

Worse, `gofmt` recognizes the leading-`*` Javadoc pattern and deliberately leaves it alone rather than guessing at a rewrite — unlike an ordinary indented block comment, which gofmt *will* reformat. So this pattern produces broken rendering that no `gofmt -d` diff will ever flag. Fix by converting to `//` on every line before deciding on wrapping or indentation.

## Quick verification

Before committing comment changes that include any of the markup above:
Expand Down
Loading