Skip to content

Commit

Permalink
loc from stdin
Browse files Browse the repository at this point in the history
  • Loading branch information
Itay Donanhirsh committed Feb 13, 2021
1 parent 5d94a6f commit 4d35050
Show file tree
Hide file tree
Showing 12 changed files with 284 additions and 136 deletions.
15 changes: 0 additions & 15 deletions cmd/clutter/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,18 +94,3 @@ var (
},
}
)

func indexPaths(c *cli.Context) []string {
if c.IsSet(indexFlag.Name) {
// specifically use name specified.
return []string{opts.indexPath}
}

if cfg.UseIndex {
// fallback on no index.
return []string{opts.indexPath, ""}
}

// no index
return []string{""}
}
100 changes: 87 additions & 13 deletions cmd/clutter/cmd_resolve.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ var (
resolveOpts = struct {
content, loc string
prev, next, cyclic bool
locFromStdin bool
}{}

resolveCommand = cli.Command{
Expand Down Expand Up @@ -48,6 +49,11 @@ var (
Required: true,
Usage: "tag position as path:line.col",
},
&cli.BoolFlag{
Name: "loc-from-stdin",
Destination: &resolveOpts.locFromStdin,
Usage: "read file at loc from stdin",
},
},
Action: func(c *cli.Context) error {
if resolveOpts.next && resolveOpts.prev {
Expand All @@ -59,26 +65,94 @@ var (
return fmt.Errorf("loc: %w", err)
}

idx, err := readIndex(c)
var (
skipFullIdx bool // should scan full tree
idxAtLoc *index.Index // index built for loc only
what *index.Entry // located tag
)

filter, err := scanner.NewFilter(z, cfg.Scanner)
if err != nil {
return fmt.Errorf("read index: %w", err)
return fmt.Errorf("new filter: %w", err)
}

var what *index.Entry
if ok, _ := filter(loc.Path, nil); ok && (resolveOpts.locFromStdin || !hasIndex(c)) {
locPath := loc.Path

idx, err = index.Filter(
idx,
func(ent *index.Entry) (bool, error) {
if ent.Loc.Path == loc.Path && ent.Loc.Line == loc.Line && loc.StartColumn >= ent.Loc.StartColumn && loc.EndColumn <= ent.Loc.EndColumn {
what = ent
if resolveOpts.locFromStdin {
locPath = ""
}

z.Debugw("preindexing", "path", locPath)

idxAtLoc, err = indexFile(locPath, loc.Path)
if err != nil {
return fmt.Errorf("index loc: %w", err)
}

z.Debugw("preindex", "idx", idxAtLoc.Slice())

if idxAtLoc != nil {
founds, _ := index.Filter(idxAtLoc, func(ent *index.Entry) (bool, error) {
return ent.Loc.Contains(*loc), nil
})

if founds.Size() == 0 {
return fmt.Errorf("no tag found at loc")
}

return true, nil
},
)
if founds.Size() > 1 {
z.Panicw("found more than single tag at loc", "found", founds)
}

if err != nil {
return fmt.Errorf("index: %w", err)
what = founds.Slice()[0]

if what.Attrs["scope"] == loc.Path {
// optimization: in this case we can skip the full index since we
// got all data that we need in the file at loc.
skipFullIdx = true
z.Debug("loc with matching file scope found at loc, skipping rest of index")
} else {
z.Debugw("full index building is required", "tag", what)
}
}
}

idx := idxAtLoc

if !skipFullIdx {
idx1, err := readIndex(c)
if err != nil {
return fmt.Errorf("read index: %w", err)
}

if idxAtLoc == nil {
idx = idx1
} else { // already got data regarding file at loc.

idx1, _ = index.Filter(idx1, func(ent *index.Entry) (bool, error) {
// eliminate entries from loc, as we already have them in idxAtLoc.
return ent.Loc.Path != loc.Path, nil
})

idx.Add(idx1.Slice())
}
}

if what == nil {
_, _ = index.Filter(idx, func(ent *index.Entry) (bool, error) {
z.Debugw("considering", "loc", ent.Loc)

if ent.Loc.Contains(*loc) {
z.Debugw("located", "loc", ent.Loc)

what = ent

return false, index.ErrStop
}

return false, nil
})
}

if what == nil {
Expand Down
56 changes: 55 additions & 1 deletion cmd/clutter/index.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,35 @@ import (
"github.com/cluttercode/clutter/internal/pkg/index"
)

func hasIndex(c *cli.Context) bool {
if c.IsSet(indexFlag.Name) {
return opts.indexPath != ""
}

if cfg.UseIndex && opts.indexPath != "" {
_, err := os.Stat(opts.indexPath)

return err == nil
}

return false
}

func indexPaths(c *cli.Context) []string {
if c.IsSet(indexFlag.Name) {
// specifically use name specified.
return []string{opts.indexPath}
}

if cfg.UseIndex {
// fallback on no index.
return []string{opts.indexPath, ""}
}

// no index
return []string{""}
}

func readIndex(c *cli.Context) (*index.Index, error) {
paths := indexPaths(c)

Expand All @@ -34,7 +63,7 @@ func readIndex(c *cli.Context) (*index.Index, error) {
return nil, fmt.Errorf("%s: %w", path, err)
}

z.Info("index read")
z.Infow("index read", "n", idx.Size())

return idx, nil
}
Expand Down Expand Up @@ -68,3 +97,28 @@ func readAdHocIndex() (*index.Index, error) {

return index.NewIndex(ents), nil
}

func indexFile(inputPath, actualPath string) (*index.Index, error) {
elems := make([]*scanner.RawElement, 0, 10)

if err := scanner.ScanFile(
nil,
z.Named("scan1"),
cfg.Scanner.Bracket,
inputPath,
func(elem *scanner.RawElement) error {
elem.Loc.Path = actualPath
elems = append(elems, elem)
return nil
},
); err != nil {
return nil, err // do not wrap
}

ents, err := parser.ParseElements(elems)
if err != nil {
return nil, fmt.Errorf("parser: %w", err)
}

return index.NewIndex(ents), nil
}
2 changes: 1 addition & 1 deletion internal/pkg/index/filter.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ func ReadFile(path string) (*Index, error) {
err error
)

if path == "stdin" || path == "-" {
if path == "stdin" || path == "-" || path == "" {
f = os.Stdin
} else if f, err = os.Open(path); err != nil {
return nil, err // don't wrap here - checking for IsNotExist in caller.
Expand Down
2 changes: 2 additions & 0 deletions internal/pkg/index/index.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ func (i *Index) Add(ents []*Entry) *Index {

func (i *Index) Size() int { return len(i.entries) }

func (i *Index) Slice() []*Entry { return i.entries[:] }

func WriteEntries(w io.Writer, index *Index) error {
for _, i := range index.entries {
text := i.marshal() + "\n"
Expand Down
66 changes: 0 additions & 66 deletions internal/pkg/scanner/element.go
Original file line number Diff line number Diff line change
@@ -1,72 +1,6 @@
package scanner

import (
"fmt"
"regexp"
"strconv"
)

type Loc struct {
Path string
Line int
StartColumn int
EndColumn int
}

type RawElement struct {
Text string
Loc Loc
}

func (e *Loc) Less(other Loc) bool {
if x, y := e.Path, other.Path; x != y {
return e.Path < other.Path
}

if x, y := e.Line, other.Line; x != y {
return x < y
}

return e.StartColumn < other.StartColumn

}

func (e Loc) String() string {
return fmt.Sprintf("%s:%d.%d-%d", e.Path, e.Line, e.StartColumn, e.EndColumn)
}

var locRegexp = regexp.MustCompile(`^(.+):([0-9]+)\.([0-9]+)(-[0-9]+)?$`)

func ParseLocString(text string) (*Loc, error) {
ms := locRegexp.FindAllStringSubmatch(text, -1)
if len(ms) != 1 || len(ms[0]) < 5 {
return nil, fmt.Errorf("invalid")
}

var (
loc = Loc{Path: ms[0][1]}
err error
)

if loc.Line, err = strconv.Atoi(ms[0][2]); err != nil {
return nil, fmt.Errorf("invalid line number")
}

if loc.StartColumn, err = strconv.Atoi(ms[0][3]); err != nil {
return nil, fmt.Errorf("invalid start column")
}

if rest := ms[0][4]; rest != "" {
if loc.EndColumn, err = strconv.Atoi(rest[1:]); err != nil {
return nil, fmt.Errorf("invalid end column")
}

if loc.EndColumn <= loc.StartColumn {
return nil, fmt.Errorf("invalid end column")
}
} else {
loc.EndColumn = loc.StartColumn + 1
}

return &loc, nil
}
2 changes: 1 addition & 1 deletion internal/pkg/scanner/filter.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ func NewFilter(z *zap.SugaredLogger, cfg Config) (func(string, os.FileInfo) (boo
exclude := gitignore.NewMatcher(ignores).Match

return func(path string, fi os.FileInfo) (bool, error) {
isDir := fi.IsDir()
isDir := fi != nil && fi.IsDir()

split := strings.Split(path, string(filepath.Separator))

Expand Down
74 changes: 74 additions & 0 deletions internal/pkg/scanner/loc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package scanner

import (
"fmt"
"regexp"
"strconv"
)

type Loc struct {
Path string
Line int
StartColumn int
EndColumn int
}

func (l *Loc) Less(other Loc) bool {
if x, y := l.Path, other.Path; x != y {
return l.Path < other.Path
}

if x, y := l.Line, other.Line; x != y {
return x < y
}

return l.StartColumn < other.StartColumn

}

func (l Loc) Contains(other Loc) bool {
return l.Path == other.Path &&
l.Line == other.Line &&
other.StartColumn >= l.StartColumn &&
other.EndColumn <= l.EndColumn
}

func (l Loc) String() string {
return fmt.Sprintf("%s:%d.%d-%d", l.Path, l.Line, l.StartColumn, l.EndColumn)
}

var locRegexp = regexp.MustCompile(`^(.+):([0-9]+)\.([0-9]+)(-[0-9]+)?$`)

func ParseLocString(text string) (*Loc, error) {
ms := locRegexp.FindAllStringSubmatch(text, -1)
if len(ms) != 1 || len(ms[0]) < 5 {
return nil, fmt.Errorf("invalid")
}

var (
loc = Loc{Path: ms[0][1]}
err error
)

if loc.Line, err = strconv.Atoi(ms[0][2]); err != nil {
return nil, fmt.Errorf("invalid line number")
}

if loc.StartColumn, err = strconv.Atoi(ms[0][3]); err != nil {
return nil, fmt.Errorf("invalid start column")
}

if rest := ms[0][4]; rest != "" {
if loc.EndColumn, err = strconv.Atoi(rest[1:]); err != nil {
return nil, fmt.Errorf("invalid end column")
}

if loc.EndColumn <= loc.StartColumn {
return nil, fmt.Errorf("invalid end column")
}
} else {
loc.EndColumn = loc.StartColumn + 1
}

return &loc, nil
}
Loading

0 comments on commit 4d35050

Please sign in to comment.