Skip to content

Commit

Permalink
feat: suggest near matches (#82)
Browse files Browse the repository at this point in the history
* implement near match suggestions

* update
  • Loading branch information
hay-kot authored Nov 20, 2023
1 parent e93c496 commit e81631e
Show file tree
Hide file tree
Showing 7 changed files with 180 additions and 74 deletions.
3 changes: 3 additions & 0 deletions app/commands/checks.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,21 @@ import (
)

func checkWorkingTree(dir string) bool {
log.Debug().Str("dir", dir).Msg("opening git repository")
repo, err := git.PlainOpen(dir)
if err != nil {
log.Debug().Err(err).Msg("failed to open git repository")
return errors.Is(err, git.ErrRepositoryNotExists)
}

log.Debug().Msg("opening git worktree")
wt, err := repo.Worktree()
if err != nil {
log.Debug().Err(err).Msg("failed to open git worktree")
return false
}

log.Debug().Msg("checking git status")
status, err := wt.Status()
if err != nil {
log.Debug().Err(err).Msg("failed to get git status")
Expand Down
4 changes: 2 additions & 2 deletions app/commands/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,12 @@ func (ctrl *Controller) List(ctx *cli.Context) error {
}
}

our, err := glamour.RenderWithEnvironmentConfig(bldr.String())
out, err := glamour.RenderWithEnvironmentConfig(bldr.String())
if err != nil {
return err
}

fmt.Println(our)
fmt.Println(out)

return nil
}
115 changes: 108 additions & 7 deletions app/commands/new.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,40 +6,141 @@ import (
"strings"

"github.com/charmbracelet/glamour"
"github.com/charmbracelet/lipgloss"
"github.com/hay-kot/scaffold/app/core/rwfs"
"github.com/hay-kot/scaffold/app/scaffold"
"github.com/hay-kot/scaffold/app/scaffold/pkgs"
"github.com/rs/zerolog/log"
"github.com/sahilm/fuzzy"
"github.com/urfave/cli/v2"
)

func (ctrl *Controller) fuzzyFallBack(str string) ([]string, []string, error) {
systemScaffolds, err := pkgs.ListSystem(os.DirFS(ctrl.Flags.Cache))
if err != nil {
return nil, nil, err
}

localScaffolds, err := pkgs.ListLocal(os.DirFS(ctrl.Flags.OutputDir))
if err != nil {
return nil, nil, err
}

systemMatches := fuzzy.Find(str, systemScaffolds)
systemMatchesOutput := make([]string, len(systemMatches))
for i, match := range systemMatches {
systemMatchesOutput[i] = match.Str
}

localMatches := fuzzy.Find(str, localScaffolds)
localMatchesOutput := make([]string, len(localMatches))
for i, match := range localMatches {
localMatchesOutput[i] = match.Str
}

return systemMatchesOutput, localMatchesOutput, nil
}

var (
bold = lipgloss.NewStyle().Bold(true)
colorRed = lipgloss.NewStyle().Foreground(lipgloss.Color("#dc2626"))
)

func didYouMeanPrompt(given, suggestion string) bool {
bldr := strings.Builder{}

// Couldn't find a scaffold named:
// 'foo'
//
// Did you mean:
// 'bar'?
//
// [y/n]:

bldr.WriteString("\n ")
bldr.WriteString(bold.Render(colorRed.Render("could not find a scaffold named")))
bldr.WriteString("\n ")
bldr.WriteString(given)
bldr.WriteString("\n\n")
bldr.WriteString(" ")
bldr.WriteString(bold.Render("did you mean"))
bldr.WriteString("\n ")
bldr.WriteString(suggestion)
bldr.WriteString("?\n\n ")
bldr.WriteString("[y/n]: ")

out := bldr.String()

var resp string

fmt.Print(out)
fmt.Scanln(&resp)

return resp == "y"
}

func (ctrl *Controller) Project(ctx *cli.Context) error {
argPath := ctx.Args().First()
if argPath == "" {
return fmt.Errorf("path is required")
}

// Status() call for go-git is too slow to be used here
// https://github.com/go-git/go-git/issues/181
if !ctrl.Flags.Force {
ok := checkWorkingTree(ctrl.Flags.Cache)
ok := checkWorkingTree(ctrl.Flags.OutputDir)
if !ok {
log.Warn().Msg("working tree is dirty, use --force to apply changes")
return nil
}
}

resolver := pkgs.NewResolver(
ctrl.rc.Shorts,
ctrl.Flags.Cache,
".",
)
resolver := pkgs.NewResolver(ctrl.rc.Shorts, ctrl.Flags.Cache, ".")

if v, ok := ctrl.rc.Aliases[argPath]; ok {
argPath = v
}

path, err := resolver.Resolve(argPath, ctrl.Flags.ScaffoldDirs)
if err != nil {
return fmt.Errorf("failed to resolve path: %w", err)
orgErr := err
systemMatches, localMatches, err := ctrl.fuzzyFallBack(argPath)
if err != nil {
return err
}

var first string
var isSystemMatch bool
if len(systemMatches) > 0 {
first = systemMatches[0]
isSystemMatch = true
}

if len(localMatches) > 0 {
first = localMatches[0]
}

if first != "" {
useMatch := didYouMeanPrompt(argPath, first)

if useMatch {
if isSystemMatch {
// prepend https:// so it resolves to the correct path
first = "https://" + first
}

resolved, err := resolver.Resolve(first, ctrl.Flags.ScaffoldDirs)
if err != nil {
return err
}

path = resolved
}
}

if path == "" {
return fmt.Errorf("failed to resolve path: %w", orgErr)
}
}

rest := ctx.Args().Tail()
Expand Down
1 change: 0 additions & 1 deletion app/scaffold/pkgs/resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,6 @@ func (r *Resolver) Resolve(arg string, checkDirs []string) (path string, err err
}

// Check if path exists
println("ABS PATH: ", absPath)
_, err = os.Stat(absPath)
if err == nil {
path = absPath
Expand Down
37 changes: 20 additions & 17 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@ require (
github.com/bmatcuk/doublestar/v4 v4.6.1
github.com/bradleyjkemp/cupaloy v2.3.0+incompatible
github.com/charmbracelet/glamour v0.6.0
github.com/charmbracelet/lipgloss v0.9.1
github.com/go-git/go-git/v5 v5.10.0
github.com/huandu/xstrings v1.4.0
github.com/psanford/memfs v0.0.0-20230130182539-4dbf7e3e865e
github.com/rs/zerolog v1.31.0
github.com/sahilm/fuzzy v0.1.0
github.com/stretchr/testify v1.8.4
github.com/urfave/cli/v2 v2.25.7
gopkg.in/yaml.v3 v3.0.1
Expand All @@ -22,32 +24,33 @@ require (
github.com/Masterminds/goutils v1.1.1 // indirect
github.com/Masterminds/semver/v3 v3.2.1 // indirect
github.com/Microsoft/go-winio v0.6.1 // indirect
github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 // indirect
github.com/ProtonMail/go-crypto v0.0.0-20230923063757-afb1ddc0824c // indirect
github.com/acomagu/bufpipe v1.0.4 // indirect
github.com/alecthomas/chroma v0.10.0 // indirect
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
github.com/aymerick/douceur v0.2.0 // indirect
github.com/cloudflare/circl v1.3.3 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
github.com/cloudflare/circl v1.3.6 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.3 // indirect
github.com/cyphar/filepath-securejoin v0.2.4 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dlclark/regexp2 v1.10.0 // indirect
github.com/emirpasic/gods v1.18.1 // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
github.com/go-git/go-billy/v5 v5.5.0 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/gorilla/css v1.0.0 // indirect
github.com/google/uuid v1.4.0 // indirect
github.com/gorilla/css v1.0.1 // indirect
github.com/imdario/mergo v0.3.16 // indirect
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
github.com/kevinburke/ssh_config v1.2.0 // indirect
github.com/kylelemons/godebug v1.1.0 // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect
github.com/mattn/go-runewidth v0.0.14 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.15 // indirect
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
github.com/microcosm-cc/bluemonday v1.0.25 // indirect
github.com/microcosm-cc/bluemonday v1.0.26 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/muesli/reflow v0.3.0 // indirect
Expand All @@ -59,18 +62,18 @@ require (
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/sergi/go-diff v1.3.1 // indirect
github.com/shopspring/decimal v1.3.1 // indirect
github.com/skeema/knownhosts v1.2.0 // indirect
github.com/skeema/knownhosts v1.2.1 // indirect
github.com/spf13/cast v1.5.1 // indirect
github.com/xanzy/ssh-agent v0.3.3 // indirect
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
github.com/yuin/goldmark v1.5.4 // indirect
github.com/yuin/goldmark v1.6.0 // indirect
github.com/yuin/goldmark-emoji v1.0.2 // indirect
golang.org/x/crypto v0.14.0 // indirect
golang.org/x/mod v0.12.0 // indirect
golang.org/x/net v0.17.0 // indirect
golang.org/x/sys v0.13.0 // indirect
golang.org/x/term v0.13.0 // indirect
golang.org/x/text v0.13.0 // indirect
golang.org/x/tools v0.13.0 // indirect
golang.org/x/crypto v0.15.0 // indirect
golang.org/x/mod v0.14.0 // indirect
golang.org/x/net v0.18.0 // indirect
golang.org/x/sys v0.14.0 // indirect
golang.org/x/term v0.14.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/tools v0.15.0 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
)
Loading

0 comments on commit e81631e

Please sign in to comment.