func edgeKindLess(kind1, kind2 string) bool { // General ordering: // anchor edge kinds before non-anchor edge kinds // forward edges before reverse edges // edgeOrdering[i] (and variants) before edgeOrdering[i+1:] // edge variants after root edge kind (ordered lexicographically) // otherwise, order lexicographically if kind1 == kind2 { return false } else if a1, a2 := schema.IsAnchorEdge(kind1), schema.IsAnchorEdge(kind2); a1 != a2 { return a1 } else if d1, d2 := schema.EdgeDirection(kind1), schema.EdgeDirection(kind2); d1 != d2 { return d1 == schema.Forward } kind1, kind2 = schema.Canonicalize(kind1), schema.Canonicalize(kind2) for _, kind := range edgeOrdering { if kind1 == kind { return true } else if kind2 == kind { return false } else if v1, v2 := schema.IsEdgeVariant(kind1, kind), schema.IsEdgeVariant(kind2, kind); v1 != v2 { return v1 } else if v1 { return kind1 < kind2 } } return kind1 < kind2 }
// expandEdgeKind prefixes unrooted (not starting with "/") edge kinds with the // standard Kythe edge prefix ("/kythe/edge/"). func expandEdgeKind(kind string) string { ck := schema.Canonicalize(kind) if strings.HasPrefix(ck, "/") { return kind } expansion := schema.EdgePrefix + ck if schema.EdgeDirection(kind) == schema.Reverse { return schema.MirrorEdge(expansion) } return expansion }
func a2a(a *srvpb.ExpandedAnchor, anchorText bool) *xpb.CrossReferencesReply_RelatedAnchor { var text string if anchorText { text = a.Text } return &xpb.CrossReferencesReply_RelatedAnchor{Anchor: &xpb.Anchor{ Ticket: a.Ticket, Kind: schema.Canonicalize(a.Kind), Parent: a.Parent, Text: text, Start: p2p(a.Span.Start), End: p2p(a.Span.End), Snippet: a.Snippet, SnippetStart: p2p(a.SnippetSpan.Start), SnippetEnd: p2p(a.SnippetSpan.End), }} }
func completeAnchors(ctx context.Context, xs xrefs.NodesEdgesService, retrieveText bool, files map[string]*fileNode, edgeKind string, anchors []string) ([]*xpb.CrossReferencesReply_RelatedAnchor, error) { edgeKind = schema.Canonicalize(edgeKind) // AllEdges is relatively safe because each anchor will have very few parents (almost always 1) reply, err := xrefs.AllEdges(ctx, xs, &xpb.EdgesRequest{ Ticket: anchors, Kind: []string{schema.ChildOfEdge}, Filter: []string{ schema.NodeKindFact, schema.AnchorLocFilter, schema.SnippetLocFilter, }, }) if err != nil { return nil, err } nodes := xrefs.NodesMap(reply.Nodes) var result []*xpb.CrossReferencesReply_RelatedAnchor for ticket, es := range reply.EdgeSets { if nodeKind := string(nodes[ticket][schema.NodeKindFact]); nodeKind != schema.AnchorKind { log.Printf("Found non-anchor target to %q edge: %q (kind: %q)", edgeKind, ticket, nodeKind) continue } // Parse anchor location start/end facts so, eo, err := getSpan(nodes[ticket], schema.AnchorStartFact, schema.AnchorEndFact) if err != nil { log.Printf("Invalid anchor span for %q: %v", ticket, err) continue } // For each file parent to the anchor, add an Anchor to the result. for kind, g := range es.Groups { if kind != schema.ChildOfEdge { continue } for _, edge := range g.Edge { parent := edge.TargetTicket if parentKind := string(nodes[parent][schema.NodeKindFact]); parentKind != schema.FileKind { log.Printf("Found non-file parent to anchor: %q (kind: %q)", parent, parentKind) continue } a := &xpb.Anchor{ Ticket: ticket, Kind: edgeKind, Parent: parent, } file, ok := files[a.Parent] if !ok { nReply, err := xs.Nodes(ctx, &xpb.NodesRequest{Ticket: []string{a.Parent}}) if err != nil { return nil, fmt.Errorf("error getting file contents for %q: %v", a.Parent, err) } nMap := xrefs.NodesMap(nReply.Nodes) text := nMap[a.Parent][schema.TextFact] file = &fileNode{ text: text, encoding: string(nMap[a.Parent][schema.TextEncodingFact]), norm: xrefs.NewNormalizer(text), } files[a.Parent] = file } a.Start, a.End, err = normalizeSpan(file.norm, int32(so), int32(eo)) if err != nil { log.Printf("Invalid anchor span %q in file %q: %v", ticket, a.Parent, err) continue } if retrieveText && a.Start.ByteOffset < a.End.ByteOffset { a.Text, err = text.ToUTF8(file.encoding, file.text[a.Start.ByteOffset:a.End.ByteOffset]) if err != nil { log.Printf("Error decoding anchor text: %v", err) } } if snippetStart, snippetEnd, err := getSpan(nodes[ticket], schema.SnippetStartFact, schema.SnippetEndFact); err == nil { startPoint, endPoint, err := normalizeSpan(file.norm, int32(snippetStart), int32(snippetEnd)) if err != nil { log.Printf("Invalid snippet span %q in file %q: %v", ticket, a.Parent, err) } else { a.Snippet, err = text.ToUTF8(file.encoding, file.text[startPoint.ByteOffset:endPoint.ByteOffset]) if err != nil { log.Printf("Error decoding snippet text: %v", err) } a.SnippetStart = startPoint a.SnippetEnd = endPoint } } // fallback to a line-based snippet if the indexer did not provide its own snippet offsets if a.Snippet == "" { a.SnippetStart = &xpb.Location_Point{ ByteOffset: a.Start.ByteOffset - a.Start.ColumnOffset, LineNumber: a.Start.LineNumber, } nextLine := file.norm.Point(&xpb.Location_Point{LineNumber: a.Start.LineNumber + 1}) a.SnippetEnd = &xpb.Location_Point{ ByteOffset: nextLine.ByteOffset - 1, LineNumber: a.Start.LineNumber, ColumnOffset: a.Start.ColumnOffset + (nextLine.ByteOffset - a.Start.ByteOffset - 1), } a.Snippet, err = text.ToUTF8(file.encoding, file.text[a.SnippetStart.ByteOffset:a.SnippetEnd.ByteOffset]) if err != nil { log.Printf("Error decoding snippet text: %v", err) } } result = append(result, &xpb.CrossReferencesReply_RelatedAnchor{Anchor: a}) } break // we've handled the only /kythe/edge/childof group } } return result, nil }
func completeAnchors(ctx context.Context, xs NodesEdgesService, retrieveText bool, normalizers map[string]*Normalizer, edgeKind string, anchors []string) ([]*xpb.Anchor, error) { edgeKind = schema.Canonicalize(edgeKind) // AllEdges is relatively safe because each anchor will have very few parents (almost always 1) reply, err := AllEdges(ctx, xs, &xpb.EdgesRequest{ Ticket: anchors, Kind: []string{schema.ChildOfEdge}, Filter: []string{ schema.NodeKindFact, schema.TextFact, schema.AnchorLocFilter, schema.SnippetLocFilter, }, }) if err != nil { return nil, err } nodes := NodesMap(reply.Node) var result []*xpb.Anchor for _, es := range reply.EdgeSet { ticket := es.SourceTicket if nodeKind := string(nodes[ticket][schema.NodeKindFact]); nodeKind != schema.AnchorKind { log.Printf("Found non-anchor target to %q edge: %q (kind: %q)", edgeKind, ticket, nodeKind) continue } // Parse anchor location start/end facts so, eo, err := getSpan(nodes[ticket], schema.AnchorStartFact, schema.AnchorEndFact) if err != nil { log.Printf("Invalid anchor span for %q: %v", ticket, err) continue } // For each file parent to the anchor, add an Anchor to the result. for _, g := range es.Group { if g.Kind != schema.ChildOfEdge { continue } for _, parent := range g.TargetTicket { if parentKind := string(nodes[parent][schema.NodeKindFact]); parentKind != schema.FileKind { log.Printf("Found non-file parent to anchor: %q (kind: %q)", parent, parentKind) continue } a := &xpb.Anchor{ Ticket: ticket, Kind: edgeKind, Parent: parent, } text := nodes[a.Parent][schema.TextFact] norm, ok := normalizers[a.Parent] if !ok { norm = NewNormalizer(text) normalizers[a.Parent] = norm } a.Start, a.End, err = normalizeSpan(norm, int32(so), int32(eo)) if err != nil { log.Printf("Invalid anchor span %q in file %q: %v", ticket, parent, err) continue } if retrieveText && a.Start.ByteOffset < a.End.ByteOffset { // TODO(schroederc): handle non-UTF8 encodings a.Text = string(text[a.Start.ByteOffset:a.End.ByteOffset]) } if snippetStart, snippetEnd, err := getSpan(nodes[ticket], schema.SnippetStartFact, schema.SnippetEndFact); err == nil { startPoint, endPoint, err := normalizeSpan(norm, int32(snippetStart), int32(snippetEnd)) if err != nil { log.Printf("Invalid snippet span %q in file %q: %v", ticket, parent, err) } else { a.Snippet = string(text[startPoint.ByteOffset:endPoint.ByteOffset]) } } // fallback to a line-based snippet if the indexer did not provide its own snippet offsets if a.Snippet == "" { nextLine := norm.Point(&xpb.Location_Point{LineNumber: a.Start.LineNumber + 1}) a.Snippet = string(text[a.Start.ByteOffset-a.Start.ColumnOffset : nextLine.ByteOffset-1]) } result = append(result, a) } break // we've handled the only /kythe/edge/childof group } } return result, nil }