Example #1
0
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
}
Example #2
0
// 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
}
Example #3
0
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),
	}}
}
Example #4
0
File: xrefs.go Project: bzz/kythe
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
}
Example #5
0
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
}