Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(api): add resolved path filters for xrefs #5274

Merged
merged 1 commit into from
May 5, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
9 changes: 9 additions & 0 deletions kythe/go/services/cli/commands_xrefs.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ type xrefsCommand struct {
nodeDefinitions bool
anchorText bool

resolvedPathFilters flagutil.StringList

excludeGenerated bool
}

Expand All @@ -66,6 +68,7 @@ func (c *xrefsCommand) SetFlags(flag *flag.FlagSet) {
flag.StringVar(&c.workspaceURI, "workspace_uri", "", "Workspace URI to patch cross-references")
flag.BoolVar(&c.relatedNodes, "related_nodes", true, "Whether to request related nodes")
flag.Var(&c.nodeFilters, "filters", "CSV list of additional fact filters to use when requesting related nodes")
flag.Var(&c.resolvedPathFilters, "resolved_path_filters", "CSV list of additional resolved path filters to use")
flag.Var(&c.buildConfigs, "build_config", "CSV set of build configs with which to filter file decorations")
flag.BoolVar(&c.nodeDefinitions, "node_definitions", false, "Whether to request definition locations for related nodes")
flag.BoolVar(&c.anchorText, "anchor_text", false, "Whether to request text for anchors")
Expand Down Expand Up @@ -96,6 +99,12 @@ func (c xrefsCommand) Run(ctx context.Context, flag *flag.FlagSet, api API) erro
Root: ".+",
})
}
for _, f := range c.resolvedPathFilters {
req.CorpusPathFilters.Filter = append(req.CorpusPathFilters.Filter, &xpb.CorpusPathFilter{
Type: xpb.CorpusPathFilter_INCLUDE_ONLY,
ResolvedPath: f,
})
}
if c.relatedNodes {
req.Filter = []string{facts.NodeKind, facts.Subkind}
if len(c.nodeFilters) > 0 && (len(c.nodeFilters) != 1 || c.nodeFilters[0] != "") {
Expand Down
16 changes: 15 additions & 1 deletion kythe/go/serving/xrefs/xrefs.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import (
"flag"
"fmt"
"log"
"path/filepath"
"regexp"
"strings"

Expand Down Expand Up @@ -228,6 +229,19 @@ type Table struct {

// MakePatcher returns a patching client that targets a Workspace.
MakePatcher func(context.Context, *xpb.Workspace) (MultiFilePatcher, error)

// ResolvePath is used to resolve CorpusPaths for filtering. If unset,
// DefaultResolvePath will be used.
ResolvePath PathResolver
}

// A PathResolver resolves a CorpusPath into a single filepath.
type PathResolver func(*cpb.CorpusPath) string

// DefaultResolvePath returns the default resolved path for the CorpusPath by
// joining its corpus, root, and path into a single filepath.
func DefaultResolvePath(cp *cpb.CorpusPath) string {
return filepath.Join(cp.GetCorpus(), cp.GetRoot(), cp.GetPath())
}

// A MultiFilePatcher provides an interface to patch sets of xref anchors to an
Expand Down Expand Up @@ -611,7 +625,7 @@ func (t *Table) CrossReferences(ctx context.Context, req *xpb.CrossReferencesReq
return nil, err
}

filter, err := compileCorpusPathFilters(req.GetCorpusPathFilters())
filter, err := compileCorpusPathFilters(req.GetCorpusPathFilters(), t.ResolvePath)
if err != nil {
return nil, status.Errorf(codes.InvalidArgument, "invalid corpus_path_filters %s: %v", strings.ReplaceAll(req.GetCorpusPathFilters().String(), "\n", " "), err)
}
Expand Down
23 changes: 18 additions & 5 deletions kythe/go/serving/xrefs/xrefs_filter.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,16 @@ import (
xpb "kythe.io/kythe/proto/xref_go_proto"
)

func compileCorpusPathFilters(fs *xpb.CorpusPathFilters) (*corpusPathFilter, error) {
func compileCorpusPathFilters(fs *xpb.CorpusPathFilters, pr PathResolver) (*corpusPathFilter, error) {
if len(fs.GetFilter()) == 0 {
return nil, nil
}
if pr == nil {
pr = DefaultResolvePath
}
f := &corpusPathFilter{}
for _, filter := range fs.GetFilter() {
p, err := compileCorpusPathFilter(filter)
p, err := compileCorpusPathFilter(filter, pr)
if err != nil {
return nil, err
}
Expand All @@ -41,8 +44,8 @@ func compileCorpusPathFilters(fs *xpb.CorpusPathFilters) (*corpusPathFilter, err
return f, nil
}

func compileCorpusPathFilter(f *xpb.CorpusPathFilter) (*corpusPathPattern, error) {
p := &corpusPathPattern{}
func compileCorpusPathFilter(f *xpb.CorpusPathFilter, pr PathResolver) (*corpusPathPattern, error) {
p := &corpusPathPattern{pathResolver: pr}
if f.GetType() == xpb.CorpusPathFilter_EXCLUDE {
p.inverse = true
}
Expand All @@ -65,19 +68,29 @@ func compileCorpusPathFilter(f *xpb.CorpusPathFilter) (*corpusPathPattern, error
return nil, err
}
}
if resolvedPath := f.GetResolvedPath(); resolvedPath != "" {
p.resolvedPath, err = regexp.Compile(resolvedPath)
if err != nil {
return nil, err
}
}
return p, nil
}

type corpusPathPattern struct {
corpus, root, path *regexp.Regexp

pathResolver PathResolver
resolvedPath *regexp.Regexp

inverse bool
}

func (p *corpusPathPattern) Allow(c *cpb.CorpusPath) bool {
return p.inverse != ((p.corpus == nil || p.corpus.MatchString(c.GetCorpus())) &&
(p.root == nil || p.root.MatchString(c.GetRoot())) &&
(p.path == nil || p.path.MatchString(c.GetPath())))
(p.path == nil || p.path.MatchString(c.GetPath())) &&
(p.resolvedPath == nil || p.resolvedPath.MatchString(p.pathResolver(c))))
}

type corpusPathFilter struct {
Expand Down
5 changes: 4 additions & 1 deletion kythe/go/serving/xrefs/xrefs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2639,11 +2639,14 @@ func TestCorpusPathFilters(t *testing.T) {
{mustParseFilters(`filter: { type: EXCLUDE root: ".+" } filter: { type: INCLUDE_ONLY corpus: "^kythe3"}`),
[]string{cps("kythe3", "", "any/path"), cps("kythe3//branch", "", "some/path")},
[]string{cps("kythe3", "genfiles", "any/path"), cps("kythe3//branch", "bin", "some/path")}},
{mustParseFilters(`filter: { type: INCLUDE_ONLY resolved_path: "^kythe3/branch/genfiles/"}`),
[]string{cps("kythe3//branch", "genfiles", "any/path"), cps("kythe3//branch", "genfiles/more", "any/path")},
[]string{cps("kythe3", "bin", "any/path"), cps("kythe3", "genfiles", "some/path")}},
}

for i, test := range tests {
t.Run(strconv.Itoa(i), func(t *testing.T) {
f, err := compileCorpusPathFilters(test.filters)
f, err := compileCorpusPathFilters(test.filters, nil)
testutil.Fatalf(t, "Error: %v", err)

for _, include := range test.includes {
Expand Down
4 changes: 4 additions & 0 deletions kythe/proto/xref.proto
Original file line number Diff line number Diff line change
Expand Up @@ -457,6 +457,10 @@ message CorpusPathFilter {
string root = 3;
// Path pattern to match against.
string path = 4;

// Fully resolved path pattern to match against. The resolved path is
// determined by the server implementation.
string resolved_path = 5;
}

// TODO(schroederc): eliminate duplicate serving.ExpandedAnchor message
Expand Down