Skip to content

Commit f212bc5

Browse files
committed
fix(gmail): show ordinary message bodies in full
1 parent c17d12b commit f212bc5

9 files changed

Lines changed: 31 additions & 20 deletions

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
## 0.27.2 - Unreleased
44

5+
### Changed
6+
7+
- Gmail: show ordinary message bodies in full by default in text output, retain a generous cap for unusually large messages, and point truncated output to `--full` or `--json`. (#807) — thanks @privatenumber.
8+
59
## 0.27.1 - 2026-06-15
610

711
### Fixed

docs/commands/gog-gmail-messages-search.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ gog gmail (mail,email) messages (message,msg,msgs) search (find,query,ls,list) <
3434
| `--gmail-no-send` | `bool` | false | Block Gmail send operations (agent safety) |
3535
| `-h`<br>`--help` | `kong.helpFlag` | | Show context-sensitive help. |
3636
| `--home` | `string` | | Override gogcli config/data/state/cache root (equivalent to GOG_HOME) |
37-
| `--include-body` | `bool` | | Include decoded message body (JSON is full; text output is truncated) |
37+
| `--include-body` | `bool` | | Include decoded message body (JSON is full; text output truncates only unusually large bodies) |
3838
| `-j`<br>`--json`<br>`--machine` | `bool` | false | Output JSON to stdout (best for scripting) |
3939
| `--local` | `bool` | | Use local timezone (default behavior, useful to override --timezone) |
4040
| `--max`<br>`--limit` | `int64` | 10 | Max results |

internal/cmd/execute_gmail_messages_search_text_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ func TestExecute_GmailMessagesSearch_JSON_IncludeBody(t *testing.T) {
200200
}
201201

202202
func TestExecute_GmailMessagesSearch_Text_IncludeBodyTruncationDiscoverable(t *testing.T) {
203-
longBody := strings.Repeat("b", 240)
203+
longBody := strings.Repeat("b", gmailDefaultTextBodyLimit+40)
204204
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
205205
path := r.URL.Path
206206
switch {
@@ -256,7 +256,7 @@ func TestExecute_GmailMessagesSearch_Text_IncludeBodyTruncationDiscoverable(t *t
256256
if defaultResult.err != nil {
257257
t.Fatalf("Execute: %v\nstderr=%q", defaultResult.err, defaultResult.stderr)
258258
}
259-
if !strings.Contains(defaultResult.stdout, strings.Repeat("b", 200)+gmailTextTruncationMarker) {
259+
if !strings.Contains(defaultResult.stdout, strings.Repeat("b", gmailDefaultTextBodyLimit)+gmailTextTruncationMarker) {
260260
t.Fatalf("expected actionable truncation marker, got: %q", defaultResult.stdout)
261261
}
262262
if strings.Contains(defaultResult.stdout, longBody) {

internal/cmd/execute_gmail_text_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ func TestExecute_GmailThread_Text_Download(t *testing.T) {
8989
func TestExecute_GmailThread_Text_FullFlag(t *testing.T) {
9090
t.Setenv("XDG_CONFIG_HOME", t.TempDir())
9191

92-
longBody := strings.Repeat("a", 600)
92+
longBody := strings.Repeat("a", gmailDefaultTextBodyLimit+100)
9393
bodyEncoded := base64.RawURLEncoding.EncodeToString([]byte(longBody))
9494

9595
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {

internal/cmd/gmail_messages.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import (
1717
const (
1818
gmailMessageBodyFormatText = "text"
1919
gmailMessageBodyFormatHTML = "html"
20+
gmailDefaultTextBodyLimit = 20_000
2021
gmailTextTruncationMarker = "... [truncated; use --full or --json]"
2122
)
2223

@@ -33,7 +34,7 @@ type GmailMessagesSearchCmd struct {
3334
FailEmpty bool `name:"fail-empty" aliases:"non-empty,require-results" help:"Exit with code 3 if no results"`
3435
Timezone string `name:"timezone" short:"z" help:"Output timezone (IANA name, e.g. America/New_York, UTC). Default: GOG_TIMEZONE, config, then local"`
3536
Local bool `name:"local" help:"Use local timezone (default behavior, useful to override --timezone)"`
36-
IncludeBody bool `name:"include-body" help:"Include decoded message body (JSON is full; text output is truncated)"`
37+
IncludeBody bool `name:"include-body" help:"Include decoded message body (JSON is full; text output truncates only unusually large bodies)"`
3738
BodyFormat string `name:"body-format" help:"Body format preference when --include-body is set: text or html" default:"text" enum:"text,html"`
3839
Full bool `name:"full" help:"Show full message bodies without truncation (implies --include-body)"`
3940
}
@@ -326,7 +327,7 @@ func sanitizeMessageBody(body string, full bool) string {
326327
if full {
327328
return body
328329
}
329-
return truncateRunes(body, 200)
330+
return truncateRunes(body, gmailDefaultTextBodyLimit)
330331
}
331332

332333
func truncateRunes(s string, maxLen int) string {

internal/cmd/gmail_messages_test.go

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,25 +14,33 @@ import (
1414
)
1515

1616
func TestSanitizeMessageBody_TruncateUTF8(t *testing.T) {
17-
long := strings.Repeat("€", 210)
17+
long := strings.Repeat("€", gmailDefaultTextBodyLimit+10)
1818
got := sanitizeMessageBody(long, false)
1919
if !strings.HasSuffix(got, gmailTextTruncationMarker) {
2020
t.Fatalf("expected truncation suffix, got %q", got)
2121
}
2222
preview := strings.TrimSuffix(got, gmailTextTruncationMarker)
23-
if len([]rune(preview)) != 200 {
24-
t.Fatalf("expected 200 preview runes, got %d", len([]rune(preview)))
23+
if len([]rune(preview)) != gmailDefaultTextBodyLimit {
24+
t.Fatalf("expected %d preview runes, got %d", gmailDefaultTextBodyLimit, len([]rune(preview)))
2525
}
2626
}
2727

2828
func TestSanitizeMessageBody_FullSkipsTruncation(t *testing.T) {
29-
long := strings.Repeat("€", 210)
29+
long := strings.Repeat("€", gmailDefaultTextBodyLimit+10)
3030
got := sanitizeMessageBody(long, true)
3131
if strings.Contains(got, "[truncated") {
3232
t.Fatalf("expected no truncation with full=true, got %q", got)
3333
}
34-
if len([]rune(got)) != 210 {
35-
t.Fatalf("expected 210 runes, got %d", len([]rune(got)))
34+
if len([]rune(got)) != gmailDefaultTextBodyLimit+10 {
35+
t.Fatalf("expected %d runes, got %d", gmailDefaultTextBodyLimit+10, len([]rune(got)))
36+
}
37+
}
38+
39+
func TestSanitizeMessageBody_DefaultShowsTypicalBody(t *testing.T) {
40+
body := strings.Repeat("ordinary message body ", 500)
41+
got := sanitizeMessageBody(body, false)
42+
if got != strings.TrimSpace(body) {
43+
t.Fatalf("expected ordinary body in full, got %q", got)
3644
}
3745
}
3846

internal/cmd/gmail_presentation_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ func TestGmailPresentationSchemas(t *testing.T) {
5050

5151
t.Run("messages with body", func(t *testing.T) {
5252
t.Parallel()
53-
body := strings.Repeat("x", 201) + "\nmore"
53+
body := strings.Repeat("x", gmailDefaultTextBodyLimit+1) + "\nmore"
5454
got := renderPlainTable(t, []messageItem{{
5555
ID: "m1",
5656
Body: body,
@@ -59,7 +59,7 @@ func TestGmailPresentationSchemas(t *testing.T) {
5959
t,
6060
got,
6161
"ID\tTHREAD\tDATE\tFROM\tSUBJECT\tLABELS\tBODY\n"+
62-
"m1\t\t\t\t\t\t"+strings.Repeat("x", 200)+gmailTextTruncationMarker+"\n",
62+
"m1\t\t\t\t\t\t"+strings.Repeat("x", gmailDefaultTextBodyLimit)+gmailTextTruncationMarker+"\n",
6363
)
6464
})
6565

internal/cmd/gmail_thread.go

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -126,11 +126,9 @@ func (c *GmailThreadGetCmd) Run(ctx context.Context, flags *RootFlags) error {
126126
} else if isHTML {
127127
cleanBody = gmailcontent.StripHTMLTags(body)
128128
}
129-
// Limit body preview to avoid overwhelming output
130-
// Use runes to avoid breaking multi-byte UTF-8 characters
131-
runes := []rune(cleanBody)
132-
if len(runes) > 500 && !c.Full {
133-
cleanBody = string(runes[:500]) + gmailTextTruncationMarker
129+
// Keep unusually large bodies bounded without turning normal messages into previews.
130+
if !c.Full {
131+
cleanBody = truncateRunes(cleanBody, gmailDefaultTextBodyLimit)
134132
}
135133
u.Out().Println(cleanBody)
136134
u.Out().Println("")

internal/cmd/schema_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ func TestExecute_Schema_GmailTruncationHelp(t *testing.T) {
128128
t.Fatalf("messages search --full help = %q", messagesFull.Help)
129129
}
130130
includeBody := schemaFlagByName(t, messagesDoc.Command, "include-body")
131-
if includeBody.Help != "Include decoded message body (JSON is full; text output is truncated)" {
131+
if includeBody.Help != "Include decoded message body (JSON is full; text output truncates only unusually large bodies)" {
132132
t.Fatalf("messages search --include-body help = %q", includeBody.Help)
133133
}
134134
}

0 commit comments

Comments
 (0)