Skip to content

Commit

Permalink
Add expansion functionality to api/cli
Browse files Browse the repository at this point in the history
  • Loading branch information
everestmz committed Dec 1, 2024
1 parent e3b4ca2 commit f29120c
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 17 deletions.
9 changes: 7 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -244,9 +244,14 @@ Display an entire directory:
llmcat .
```

Produce a map of a directory or file:
Display a map of a repo or file:
```bash
llmcat --outline llmcat.go
llmcat --outline .
```

Display a map of the repo, but with the `RenderDirectory` function expanded:
```bash
llmcat --outline --expand "llmcat.go RenderDirectory" .
```

### Navigation
Expand Down
17 changes: 16 additions & 1 deletion cmd/llmcat/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"strings"

"github.com/everestmz/llmcat"
"github.com/everestmz/llmcat/ctxspec"
"github.com/rs/zerolog"
"github.com/spf13/cobra"
)
Expand All @@ -21,6 +22,18 @@ func main() {
RunE: func(cmd *cobra.Command, args []string) error {
path := args[0]

contextSpecLines, err := cmd.Flags().GetStringSlice("expand")
if err != nil {
return err
}

contextSpec, err := ctxspec.ParseContextSpec(strings.Join(contextSpecLines, "\n"))
if err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
os.Exit(1)
}
dirOptions.ContextSpec = contextSpec

dirOptions.FileOptions = &options

if strings.HasSuffix(path, ".git") {
Expand Down Expand Up @@ -73,6 +86,7 @@ func main() {
flags.BoolVarP(&options.ShowLineNumbers, "line-numbers", "n", true, "show line numbers")
flags.StringVarP(&options.GutterSeparator, "separator", "s", "|", "gutter separator character")
flags.BoolVar(&options.Outline, "outline", false, "produce an outline for supported source files using tree-sitter")
flags.StringArrayVar(&options.ExpandSymbols, "symbols", nil, "specify symbols to expand when showing an outline")

// Pagination flags
flags.IntVarP(&options.PageSize, "page-size", "p", 10000, "number of lines to show (0 = show all)")
Expand All @@ -84,7 +98,8 @@ func main() {
flags.StringSliceVar(&dirOptions.IncludeGlobs, "include", nil, "glob patterns to include")
flags.StringSliceVar(&dirOptions.ExcludeExtensions, "exclude-ext", nil, "comma-separated list of file extensions to exclude")
flags.StringSliceVar(&dirOptions.IncludeExtensions, "ext", nil, "comma-separated list of file extensions to include")
// flags.BoolVarP(&dirOptions.ShowTree, "tree", "t", false, "show directory tree")

flags.StringSliceP("expand", "e", nil, "symbols or files to expand when showing an outline, in ctxspec format")

if err := rootCmd.Execute(); err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
Expand Down
59 changes: 45 additions & 14 deletions llmcat.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,30 @@ import (
"path/filepath"
"strings"

"github.com/everestmz/llmcat/ctxspec"
"github.com/everestmz/llmcat/treesym"
"github.com/everestmz/llmcat/treesym/language"
"github.com/gobwas/glob"
"github.com/rs/zerolog/log"
)

type RenderFileOptions struct {
Outline bool `json:"outline"`
OutputMarkdown bool `json:"output_markdown"`
ShowLineNumbers bool `json:"hide_line_numbers"`
GutterSeparator string `json:"gutter_separator"`
PageSize int `json:"page_size"`
StartLine int `json:"start_line"`
ShowPageInfo bool `json:"show_page_info"`
Outline bool `json:"outline"`
OutputMarkdown bool `json:"output_markdown"`
ShowLineNumbers bool `json:"hide_line_numbers"`
GutterSeparator string `json:"gutter_separator"`
PageSize int `json:"page_size"`
StartLine int `json:"start_line"`
ShowPageInfo bool `json:"show_page_info"`
ExpandSymbols []string `json:"expand_symbols"`
}

// TODO: split this up so we produce another type which contains
// ExpandSymbols - they're not a generic input, they're specific to a file
func (ro *RenderFileOptions) Copy() *RenderFileOptions {
new := *ro

return &new
}

func (ro *RenderFileOptions) SetDefaults() {
Expand Down Expand Up @@ -94,6 +104,16 @@ func RenderFile(filename, text string, options *RenderFileOptions) (string, erro
return line
}

shouldExpandChunk := func(chunk *treesym.OutlineChunk) bool {
for _, symbolName := range options.ExpandSymbols {
if chunk.Name == symbolName {
return true
}
}

return false
}

chunks, err := treesym.GetSymbols(context.TODO(), &treesym.SourceFile{
Path: filename,
Text: text,
Expand Down Expand Up @@ -122,7 +142,7 @@ func RenderFile(filename, text string, options *RenderFileOptions) (string, erro

// This chunk is at least partially in the range

if options.Outline && chunk.ShouldOmit {
if options.Outline && chunk.ShouldOmit && !shouldExpandChunk(chunk) {
// Specify how many lines have been omitted (it may not be the size of the chunk,
// if some of it is on the next or previous page!)
var headLinesAlreadyOmitted, tailLinesAlreadyOmitted int
Expand Down Expand Up @@ -169,11 +189,12 @@ func RenderFile(filename, text string, options *RenderFileOptions) (string, erro

// We should probably allow for glob-based ignores, extension-based ignores, and some other dir-based filters
type RenderDirectoryOptions struct {
FileOptions *RenderFileOptions `json:"file_options"`
IgnoreGlobs []string `json:"ignore_globs"`
IncludeGlobs []string `json:"include_globs"`
IncludeExtensions []string `json:"include_extensions"`
ExcludeExtensions []string `json:"exclude_extensions"`
FileOptions *RenderFileOptions `json:"file_options"`
IgnoreGlobs []string `json:"ignore_globs"`
IncludeGlobs []string `json:"include_globs"`
IncludeExtensions []string `json:"include_extensions"`
ExcludeExtensions []string `json:"exclude_extensions"`
ContextSpec ctxspec.ContextSpec `json:"context_spec"`

compiledIgnoreGlobs []glob.Glob
compiledIncludeGlobs []glob.Glob
Expand Down Expand Up @@ -301,7 +322,17 @@ func RenderDirectory(dirName string, options *RenderDirectoryOptions) (string, e
return err
}

rendered, err := RenderFile(relPath, string(text), options.FileOptions)
fileOpts := options.FileOptions
if spec, ok := options.ContextSpec[relPath]; ok {
fileOpts = fileOpts.Copy()
if len(spec.Symbols) > 0 {
fileOpts.ExpandSymbols = spec.Symbols
} else {
// Just show everything
fileOpts.Outline = false
}
}
rendered, err := RenderFile(relPath, string(text), fileOpts)
if err != nil {
return err
}
Expand Down
3 changes: 3 additions & 0 deletions treesym/treesym.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ type Symbols struct {
}

type OutlineChunk struct {
// Name only set if item is omitted, since otherwise chunk could be bigger than a single symbol
Name string
Content string
ShouldOmit bool
// 0-indexed, like tree-sitter rows are
Expand Down Expand Up @@ -85,6 +87,7 @@ func (psf *ProcessedSourceFile) GetOutline() []*OutlineChunk {
ShouldOmit: true,
StartRow: startLine,
EndRow: endLine,
Name: def.Name,
}

for currentLine := startLine; currentLine <= endLine; currentLine++ {
Expand Down

0 comments on commit f29120c

Please sign in to comment.