Skip to content

Commit 656dfb0

Browse files
authored
Merge pull request #34 from andev0x/optimization/func
Optimization/func
2 parents 41e4eb1 + 9bad32f commit 656dfb0

6 files changed

Lines changed: 206 additions & 19 deletions

File tree

assets/prompts/system_prompt.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ Metadata Context:
1818
- Dependency Changes: {{.DependencyAlert}}
1919
- Added/Deleted Line Ratio: {{printf "%.2f" .DiffSummary.Ratio}}
2020

21+
Recent Commit History (for style reference):
22+
{{range .RecentCommits}}- {{.}}
23+
{{end}}
24+
2125
Summarized Git Diff:
2226
{{.DiffContent}}
2327

internal/ai/ai_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ func TestRenderPrompt(t *testing.T) {
2929
"internal/auth/login.go",
3030
"[func] Login",
3131
"Added/Deleted Line Ratio: 0.83",
32+
"Recent Commit History",
3233
}
3334

3435
for _, part := range expectedParts {

internal/ai/prompt.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88

99
"github.com/andev0x/gitmit/assets"
1010
"github.com/andev0x/gitmit/internal/analyzer"
11+
"github.com/andev0x/gitmit/internal/history"
1112
)
1213

1314
// PromptContext represents the data structure passed to the prompt template
@@ -20,6 +21,7 @@ type PromptContext struct {
2021
DependencyAlert string
2122
DiffSummary DiffSummary
2223
DiffContent string
24+
RecentCommits []string
2325
}
2426

2527
// DiffSummary contains ratio of changes
@@ -61,6 +63,9 @@ func RenderPrompt(msg *analyzer.CommitMessage, projectType, branchName string) (
6163
ratio = float64(msg.TotalAdded) / float64(total)
6264
}
6365

66+
// Fetch recent commits for style reference
67+
recentCommits, _ := history.GetRecentCommits(5)
68+
6469
ctx := PromptContext{
6570
ProjectType: projectType,
6671
CurrentBranch: branchName,
@@ -71,7 +76,8 @@ func RenderPrompt(msg *analyzer.CommitMessage, projectType, branchName string) (
7176
DiffSummary: DiffSummary{
7277
Ratio: ratio,
7378
},
74-
DiffContent: msg.FullDiff,
79+
DiffContent: msg.FullDiff,
80+
RecentCommits: recentCommits,
7581
}
7682

7783
var buf bytes.Buffer

internal/analyzer/analyzer.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1290,11 +1290,16 @@ func (a *Analyzer) summarizeDiff(diff string) string {
12901290
var summary strings.Builder
12911291
scanner := bufio.NewScanner(strings.NewReader(diff))
12921292
lineCount := 0
1293-
maxLines := 20 // Limit lines per file to avoid context bloat
1293+
maxLines := 25 // Limit lines per file to avoid context bloat
12941294

12951295
for scanner.Scan() {
12961296
line := scanner.Text()
12971297
// Only include added/removed lines and hunk headers
1298+
// Skip binary files or extremely long lines
1299+
if len(line) > 500 {
1300+
continue
1301+
}
1302+
12981303
if strings.HasPrefix(line, "+") || strings.HasPrefix(line, "-") || strings.HasPrefix(line, "@@") {
12991304
if strings.HasPrefix(line, "+++") || strings.HasPrefix(line, "---") {
13001305
continue
@@ -1304,7 +1309,7 @@ func (a *Analyzer) summarizeDiff(diff string) string {
13041309
lineCount++
13051310
}
13061311
if lineCount >= maxLines {
1307-
summary.WriteString("... (truncated)\n")
1312+
summary.WriteString("... (rest of file truncated)\n")
13081313
break
13091314
}
13101315
}

internal/formatter/formatter.go

Lines changed: 159 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package formatter
22

33
import (
44
"fmt"
5+
"regexp"
56
"strings"
67
)
78

@@ -30,7 +31,8 @@ func (f *Formatter) FormatMessage(msg string, isMajor bool) string {
3031
subject := strings.TrimSpace(parts[0])
3132
body := ""
3233
if len(parts) > 1 {
33-
body = strings.TrimSpace(parts[1])
34+
body = strings.TrimLeft(parts[1], "\n\r")
35+
body = strings.TrimRight(body, "\n\r\t ")
3436
}
3537

3638
// Remove redundant phrases from subject
@@ -49,6 +51,7 @@ func (f *Formatter) FormatMessage(msg string, isMajor bool) string {
4951
subjectParts := strings.SplitN(wrapped, "\n", 2)
5052
subject = subjectParts[0]
5153
if len(subjectParts) > 1 {
54+
// Subject overflow becomes the start of the body
5255
if body != "" {
5356
body = subjectParts[1] + "\n\n" + body
5457
} else {
@@ -68,7 +71,7 @@ func (f *Formatter) FormatMessage(msg string, isMajor bool) string {
6871
return subject
6972
}
7073

71-
// wrapString wraps a string at the specified limit, preserving paragraphs
74+
// wrapString wraps a string at the specified limit, preserving paragraphs and structures
7275
func (f *Formatter) wrapString(s string, limit int) string {
7376
if limit <= 0 {
7477
return s
@@ -82,27 +85,167 @@ func (f *Formatter) wrapString(s string, limit int) string {
8285
result.WriteString("\n\n")
8386
}
8487

85-
words := strings.Fields(p)
86-
if len(words) == 0 {
87-
continue
88-
}
88+
lines := strings.Split(p, "\n")
89+
var currentParagraph strings.Builder
8990

90-
currentLineLength := 0
91-
for j, word := range words {
92-
if j > 0 {
93-
if currentLineLength+1+len(word) > limit {
91+
for _, line := range lines {
92+
if f.isStructural(line) {
93+
// Flush any pending paragraph text
94+
if currentParagraph.Len() > 0 {
95+
result.WriteString(f.reflow(currentParagraph.String(), limit))
9496
result.WriteString("\n")
95-
currentLineLength = 0
96-
} else {
97-
result.WriteString(" ")
98-
currentLineLength++
97+
currentParagraph.Reset()
98+
}
99+
// Wrap the structural line itself (preserving its prefix if possible)
100+
result.WriteString(f.wrapLine(line, limit))
101+
result.WriteString("\n")
102+
} else {
103+
trimmed := strings.TrimSpace(line)
104+
if trimmed == "" {
105+
continue
106+
}
107+
if currentParagraph.Len() > 0 {
108+
currentParagraph.WriteString(" ")
99109
}
110+
currentParagraph.WriteString(trimmed)
100111
}
112+
}
113+
114+
if currentParagraph.Len() > 0 {
115+
result.WriteString(f.reflow(currentParagraph.String(), limit))
116+
}
101117

102-
result.WriteString(word)
103-
currentLineLength += len(word)
118+
// Cleanup trailing newline from structural lines at the end of a paragraph
119+
resStr := result.String()
120+
if strings.HasSuffix(resStr, "\n") {
121+
result.Reset()
122+
result.WriteString(strings.TrimSuffix(resStr, "\n"))
104123
}
105124
}
106125

107126
return result.String()
108127
}
128+
129+
// isStructural identifies lines that should not be reflowed into paragraphs
130+
func (f *Formatter) isStructural(line string) bool {
131+
trimmed := strings.TrimSpace(line)
132+
if trimmed == "" {
133+
return false
134+
}
135+
136+
// List markers at the start of the trimmed line
137+
markers := []string{"- ", "* ", "+ "}
138+
for _, m := range markers {
139+
if strings.HasPrefix(trimmed, m) {
140+
return true
141+
}
142+
}
143+
144+
// Numeric list markers (e.g., "1. ")
145+
numericRegex := regexp.MustCompile(`^\d+\.\s`)
146+
if numericRegex.MatchString(trimmed) {
147+
return true
148+
}
149+
150+
// Significant indentation (at least 2 spaces or a tab)
151+
if strings.HasPrefix(line, " ") || strings.HasPrefix(line, "\t") {
152+
return true
153+
}
154+
155+
return false
156+
}
157+
158+
// reflow joins words and wraps them at the limit
159+
func (f *Formatter) reflow(s string, limit int) string {
160+
words := strings.Fields(s)
161+
if len(words) == 0 {
162+
return ""
163+
}
164+
165+
var res strings.Builder
166+
currLen := 0
167+
for i, w := range words {
168+
if i > 0 {
169+
if currLen+1+len(w) > limit {
170+
res.WriteString("\n")
171+
currLen = 0
172+
} else {
173+
res.WriteString(" ")
174+
currLen++
175+
}
176+
}
177+
res.WriteString(w)
178+
currLen += len(w)
179+
}
180+
return res.String()
181+
}
182+
183+
// wrapLine wraps a single structural line, attempting to preserve indentation
184+
func (f *Formatter) wrapLine(line string, limit int) string {
185+
if len(line) <= limit {
186+
return line
187+
}
188+
189+
// Find indentation and prefix
190+
indent := ""
191+
for _, char := range line {
192+
if char == ' ' || char == '\t' {
193+
indent += string(char)
194+
} else {
195+
break
196+
}
197+
}
198+
199+
// Also check for list markers
200+
content := line[len(indent):]
201+
prefix := ""
202+
markers := []string{"- ", "* ", "+ "}
203+
for _, m := range markers {
204+
if strings.HasPrefix(content, m) {
205+
prefix = m
206+
content = content[len(m):]
207+
break
208+
}
209+
}
210+
211+
// Handle numeric markers
212+
numericRegex := regexp.MustCompile(`^\d+\.\s`)
213+
if loc := numericRegex.FindStringIndex(content); loc != nil && loc[0] == 0 {
214+
prefix = content[loc[0]:loc[1]]
215+
content = content[loc[1]:]
216+
}
217+
218+
words := strings.Fields(content)
219+
if len(words) == 0 {
220+
return line
221+
}
222+
223+
var res strings.Builder
224+
res.WriteString(indent)
225+
res.WriteString(prefix)
226+
currLen := len(indent) + len(prefix)
227+
228+
for i, w := range words {
229+
if i > 0 {
230+
if currLen+1+len(w) > limit {
231+
res.WriteString("\n")
232+
res.WriteString(indent)
233+
// Extra indentation for wrapped list items
234+
if prefix != "" {
235+
res.WriteString(" ")
236+
}
237+
currLen = len(indent)
238+
if prefix != "" {
239+
currLen += 2
240+
}
241+
} else {
242+
res.WriteString(" ")
243+
currLen++
244+
}
245+
}
246+
res.WriteString(w)
247+
currLen += len(w)
248+
}
249+
250+
return res.String()
251+
}

internal/formatter/formatter_test.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,34 @@ func TestFormatMessage(t *testing.T) {
4040
maxBody: 10,
4141
expected: "feat: add\n\nfeature\n\nThis is a\nbody\nmessage\nthat is\nvery long.",
4242
},
43+
{
44+
name: "preserve list structure",
45+
msg: "feat: add feature\n\n- Item 1\n- Item 2 which is quite long",
46+
maxSubject: 50,
47+
maxBody: 15,
48+
expected: "feat: add feature\n\n- Item 1\n- Item 2 which\n is quite long",
49+
},
50+
{
51+
name: "preserve indentation",
52+
msg: "feat: add feature\n\n Indented text that should not be joined",
53+
maxSubject: 50,
54+
maxBody: 20,
55+
expected: "feat: add feature\n\n Indented text\n that should not\n be joined",
56+
},
57+
{
58+
name: "multi-paragraph reflow",
59+
msg: "feat: add feature\n\nParagraph 1 line 1\nline 2\n\nParagraph 2",
60+
maxSubject: 50,
61+
maxBody: 50,
62+
expected: "feat: add feature\n\nParagraph 1 line 1 line 2\n\nParagraph 2",
63+
},
64+
{
65+
name: "subject overflow to body with blank line",
66+
msg: "feat: this is a very long subject line that will overflow",
67+
maxSubject: 20,
68+
maxBody: 72,
69+
expected: "feat: this is a very\n\nlong subject line that will overflow",
70+
},
4371
{
4472
name: "redundant phrases",
4573
msg: "feat feat: add add new feature",

0 commit comments

Comments
 (0)