Merge pull request #351 from charmbracelet/regex-drop-compilation-in-runtime

Raphael Amorim created

perf: avoid regex compilation in runtime

Change summary

internal/llm/provider/anthropic.go        |  6 ++++--
internal/llm/tools/view.go                |  3 ++-
internal/tui/exp/list/filterable.go       | 11 ++++++++---
internal/tui/exp/list/filterable_group.go |  8 +++++---
4 files changed, 19 insertions(+), 9 deletions(-)

Detailed changes

internal/llm/provider/anthropic.go 🔗

@@ -21,6 +21,9 @@ import (
 	"github.com/charmbracelet/crush/internal/message"
 )
 
+// Pre-compiled regex for parsing context limit errors.
+var contextLimitRegex = regexp.MustCompile(`input length and ` + "`max_tokens`" + ` exceed context limit: (\d+) \+ (\d+) > (\d+)`)
+
 type anthropicClient struct {
 	providerOptions   providerClientOptions
 	useBedrock        bool
@@ -512,8 +515,7 @@ func (a *anthropicClient) handleContextLimitError(apiErr *anthropic.Error) (int,
 	// Parse error message like: "input length and max_tokens exceed context limit: 154978 + 50000 > 200000"
 	errorMsg := apiErr.Error()
 
-	re := regexp.MustCompile("input length and `max_tokens` exceed context limit: (\\d+) \\+ (\\d+) > (\\d+)")
-	matches := re.FindStringSubmatch(errorMsg)
+	matches := contextLimitRegex.FindStringSubmatch(errorMsg)
 
 	if len(matches) != 4 {
 		return 0, false

internal/llm/tools/view.go 🔗

@@ -306,7 +306,8 @@ func readTextFile(filePath string, offset, limit int) (string, int, error) {
 		}
 	}
 
-	var lines []string
+	// Pre-allocate slice with expected capacity
+	lines := make([]string, 0, limit)
 	lineCount = offset
 
 	for scanner.Scan() && len(lines) < limit {

internal/tui/exp/list/filterable.go 🔗

@@ -15,6 +15,13 @@ import (
 	"github.com/sahilm/fuzzy"
 )
 
+var (
+	// Pre-compiled regex for checking if a string contains alphabetic characters.
+	alphaRegex = regexp.MustCompile(`[a-zA-Z]`)
+	// Pre-compiled regex for checking if a string is alphanumeric.
+	alphanumericRegex = regexp.MustCompile(`^[a-zA-Z0-9]*$`)
+)
+
 type FilterableItem interface {
 	Item
 	FilterValue() string
@@ -165,8 +172,6 @@ func (f *filterableList[T]) View() string {
 
 // removes bindings that are used for search
 func (f *filterableList[T]) updateKeyMaps() {
-	alphanumeric := regexp.MustCompile("^[a-zA-Z0-9]*$")
-
 	removeLettersAndNumbers := func(bindings []string) []string {
 		var keep []string
 		for _, b := range bindings {
@@ -177,7 +182,7 @@ func (f *filterableList[T]) updateKeyMaps() {
 			if b == " " {
 				continue
 			}
-			m := alphanumeric.MatchString(b)
+			m := alphanumericRegex.MatchString(b)
 			if !m {
 				keep = append(keep, b)
 			}

internal/tui/exp/list/filterable_group.go 🔗

@@ -15,6 +15,10 @@ import (
 	"github.com/sahilm/fuzzy"
 )
 
+// Pre-compiled regex for checking if a string is alphanumeric.
+// Note: This is duplicated from filterable.go to avoid circular dependencies.
+var alphanumericRegexGroup = regexp.MustCompile(`^[a-zA-Z0-9]*$`)
+
 type FilterableGroupList[T FilterableItem] interface {
 	GroupedList[T]
 	Cursor() *tea.Cursor
@@ -114,8 +118,6 @@ func (f *filterableGroupList[T]) View() string {
 
 // removes bindings that are used for search
 func (f *filterableGroupList[T]) updateKeyMaps() {
-	alphanumeric := regexp.MustCompile("^[a-zA-Z0-9]*$")
-
 	removeLettersAndNumbers := func(bindings []string) []string {
 		var keep []string
 		for _, b := range bindings {
@@ -126,7 +128,7 @@ func (f *filterableGroupList[T]) updateKeyMaps() {
 			if b == " " {
 				continue
 			}
-			m := alphanumeric.MatchString(b)
+			m := alphanumericRegexGroup.MatchString(b)
 			if !m {
 				keep = append(keep, b)
 			}