Пример #1
0
func sortReferences(d *xpb.DecorationsReply) {
	refs := d.Reference
	spans := make([]struct{ start, end int }, len(refs))
	nodes := xrefs.NodesMap(d.Node)
	for i, r := range refs {
		if n, ok := nodes[r.SourceTicket]; ok {
			// Ignore errors; 0 works fine for sorting purposes
			start, _ := strconv.Atoi(string(n[schema.AnchorStartFact]))
			end, _ := strconv.Atoi(string(n[schema.AnchorEndFact]))
			spans[i] = struct{ start, end int }{start, end}
		}
	}
	sort.Sort(bySpan{refs, spans})
}
Пример #2
0
func main() {
	flag.Parse()
	if len(flag.Args()) == 0 {
		flagutil.UsageError("not given any files")
	}

	xs := xrefs.WebClient(*remoteAPI)

	for _, file := range flag.Args() {
		ticket := (&kytheuri.URI{Corpus: *corpus, Path: file}).String()
		decor, err := xs.Decorations(ctx, &xpb.DecorationsRequest{
			Location:   &xpb.Location{Ticket: ticket},
			SourceText: true,
			References: true,
		})
		if err != nil {
			log.Fatalf("Failed to get decorations for file %q", file)
		}

		nodes := xrefs.NodesMap(decor.Nodes)
		emitted := stringset.New()

		for _, r := range decor.Reference {
			if r.Kind != schema.DefinesBindingEdge || emitted.Contains(r.TargetTicket) {
				continue
			}

			ident := string(nodes[r.TargetTicket][identifierFact])
			if ident == "" {
				continue
			}

			offset, err := strconv.Atoi(string(nodes[r.SourceTicket][schema.AnchorStartFact]))
			if err != nil {
				log.Printf("Invalid start offset for anchor %q", r.SourceTicket)
				continue
			}

			fields, err := getTagFields(xs, r.TargetTicket)
			if err != nil {
				log.Printf("Failed to get tagfields for %q: %v", r.TargetTicket, err)
			}

			fmt.Printf("%s\t%s\t%d;\"\t%s\n",
				ident, file, offsetLine(decor.SourceText, offset), strings.Join(fields, "\t"))
			emitted.Add(r.TargetTicket)
		}
	}
}
Пример #3
0
func displayReferences(decor *xpb.DecorationsReply) error {
	if *displayJSON {
		return json.NewEncoder(out).Encode(decor)
	}

	nodes := xrefs.NodesMap(decor.Node)

	// TODO(schroederc): return anchor locations from server
	norm := xrefs.NewNormalizer(decor.SourceText)

	for _, ref := range decor.Reference {
		nodeKind := factValue(nodes, ref.TargetTicket, schema.NodeKindFact, "UNKNOWN")
		subkind := factValue(nodes, ref.TargetTicket, schema.SubkindFact, "")
		startOffset := factValue(nodes, ref.SourceTicket, schema.AnchorStartFact, "_")
		endOffset := factValue(nodes, ref.SourceTicket, schema.AnchorEndFact, "_")

		// ignore errors (locations will be 0)
		s, _ := strconv.Atoi(startOffset)
		e, _ := strconv.Atoi(endOffset)

		loc, err := norm.Location(&xpb.Location{
			Kind:  xpb.Location_SPAN,
			Start: &xpb.Location_Point{ByteOffset: int32(s)},
			End:   &xpb.Location_Point{ByteOffset: int32(e)},
		})
		if err != nil {
			return fmt.Errorf("error normalizing reference location for anchor %q: %v", ref.SourceTicket, err)
		}

		r := strings.NewReplacer(
			"@source@", ref.SourceTicket,
			"@target@", ref.TargetTicket,
			"@edgeKind@", ref.Kind,
			"@nodeKind@", nodeKind,
			"@subkind@", subkind,
			"@^offset@", startOffset,
			"@^line@", strconv.Itoa(int(loc.Start.LineNumber)),
			"@^col@", strconv.Itoa(int(loc.Start.ColumnOffset)),
			"@$offset@", endOffset,
			"@$line@", strconv.Itoa(int(loc.End.LineNumber)),
			"@$col@", strconv.Itoa(int(loc.End.ColumnOffset)),
		)
		if _, err := r.WriteString(out, refFormat+"\n"); err != nil {
			return err
		}
	}

	return nil
}
Пример #4
0
func getTagFields(xs xrefs.Service, ticket string) ([]string, error) {
	reply, err := xs.Edges(ctx, &xpb.EdgesRequest{
		Ticket: []string{ticket},
		Kind:   []string{schema.ChildOfEdge, schema.ParamEdge},
		Filter: []string{schema.NodeKindFact, schema.SubkindFact, identifierFact},
	})
	if err != nil || len(reply.EdgeSet) == 0 {
		return nil, err
	}

	var fields []string

	nodes := xrefs.NodesMap(reply.Node)
	edges := xrefs.EdgesMap(reply.EdgeSet)

	switch string(nodes[ticket][schema.NodeKindFact]) + "|" + string(nodes[ticket][schema.SubkindFact]) {
	case schema.FunctionKind + "|":
		fields = append(fields, "f")
		fields = append(fields, "arity:"+strconv.Itoa(len(edges[ticket][schema.ParamEdge])))
	case schema.EnumKind + "|" + schema.EnumClassSubkind:
		fields = append(fields, "g")
	case schema.PackageKind + "|":
		fields = append(fields, "p")
	case schema.RecordKind + "|" + schema.ClassSubkind:
		fields = append(fields, "c")
	case schema.VariableKind + "|":
		fields = append(fields, "v")
	}

	for _, parent := range edges[ticket][schema.ChildOfEdge] {
		parentIdent := string(nodes[parent][identifierFact])
		if parentIdent == "" {
			continue
		}
		switch string(nodes[parent][schema.NodeKindFact]) + "|" + string(nodes[parent][schema.SubkindFact]) {
		case schema.FunctionKind + "|":
			fields = append(fields, "function:"+parentIdent)
		case schema.RecordKind + "|" + schema.ClassSubkind:
			fields = append(fields, "class:"+parentIdent)
		case schema.EnumKind + "|" + schema.EnumClassSubkind:
			fields = append(fields, "enum:"+parentIdent)
		}
	}

	return fields, nil
}
Пример #5
0
func getTagFields(xs xrefs.Service, ticket string) ([]string, error) {
	reply, err := xs.Edges(ctx, &gpb.EdgesRequest{
		Ticket: []string{ticket},
		Kind:   []string{edges.ChildOf, edges.Param},
		Filter: []string{facts.NodeKind, facts.Subkind, identifierFact},
	})
	if err != nil || len(reply.EdgeSets) == 0 {
		return nil, err
	}

	var fields []string

	nmap := xrefs.NodesMap(reply.Nodes)
	emap := xrefs.EdgesMap(reply.EdgeSets)

	switch string(nmap[ticket][facts.NodeKind]) + "|" + string(nmap[ticket][facts.Subkind]) {
	case nodes.Function + "|":
		fields = append(fields, "f")
		fields = append(fields, "arity:"+strconv.Itoa(len(emap[ticket][edges.Param])))
	case nodes.Enum + "|" + nodes.EnumClass:
		fields = append(fields, "g")
	case nodes.Package + "|":
		fields = append(fields, "p")
	case nodes.Record + "|" + nodes.Class:
		fields = append(fields, "c")
	case nodes.Variable + "|":
		fields = append(fields, "v")
	}

	for parent := range emap[ticket][edges.ChildOf] {
		parentIdent := string(nmap[parent][identifierFact])
		if parentIdent == "" {
			continue
		}
		switch string(nmap[parent][facts.NodeKind]) + "|" + string(nmap[parent][facts.Subkind]) {
		case nodes.Function + "|":
			fields = append(fields, "function:"+parentIdent)
		case nodes.Record + "|" + nodes.Class:
			fields = append(fields, "class:"+parentIdent)
		case nodes.Enum + "|" + nodes.EnumClass:
			fields = append(fields, "enum:"+parentIdent)
		}
	}

	return fields, nil
}
Пример #6
0
func displayDecorations(decor *xpb.DecorationsReply) error {
	if *displayJSON {
		return jsonMarshaler.Marshal(out, decor)
	}

	nodes := xrefs.NodesMap(decor.Nodes)

	for _, ref := range decor.Reference {
		nodeKind := factValue(nodes, ref.TargetTicket, facts.NodeKind, "UNKNOWN")
		subkind := factValue(nodes, ref.TargetTicket, facts.Subkind, "")

		loc := &xpb.Location{
			Kind:  xpb.Location_SPAN,
			Start: ref.AnchorStart,
			End:   ref.AnchorEnd,
		}

		var targetDef string
		if ref.TargetDefinition != "" {
			targetDef = ref.TargetDefinition
			// TODO(schroederc): fields from decor.DefinitionLocations
			// TODO(zarko): fields from decor.ExtendsOverrides
		}

		r := strings.NewReplacer(
			"@source@", ref.SourceTicket,
			"@target@", ref.TargetTicket,
			"@edgeKind@", ref.Kind,
			"@nodeKind@", nodeKind,
			"@subkind@", subkind,
			"@^offset@", itoa(loc.Start.ByteOffset),
			"@^line@", itoa(loc.Start.LineNumber),
			"@^col@", itoa(loc.Start.ColumnOffset),
			"@$offset@", itoa(loc.End.ByteOffset),
			"@$line@", itoa(loc.End.LineNumber),
			"@$col@", itoa(loc.End.ColumnOffset),
			"@targetDef@", targetDef,
		)
		if _, err := r.WriteString(out, refFormat+"\n"); err != nil {
			return err
		}
	}

	return nil
}
Пример #7
0
func displayDecorations(decor *xpb.DecorationsReply) error {
	if *displayJSON {
		return jsonMarshaler.Marshal(out, decor)
	}

	nodes := xrefs.NodesMap(decor.Node)

	for _, ref := range decor.Reference {
		nodeKind := factValue(nodes, ref.TargetTicket, schema.NodeKindFact, "UNKNOWN")
		subkind := factValue(nodes, ref.TargetTicket, schema.SubkindFact, "")

		loc := &xpb.Location{
			Kind:  xpb.Location_SPAN,
			Start: ref.AnchorStart,
			End:   ref.AnchorEnd,
		}

		r := strings.NewReplacer(
			"@source@", ref.SourceTicket,
			"@target@", ref.TargetTicket,
			"@edgeKind@", ref.Kind,
			"@nodeKind@", nodeKind,
			"@subkind@", subkind,
			"@^offset@", itoa(loc.Start.ByteOffset),
			"@^line@", itoa(loc.Start.LineNumber),
			"@^col@", itoa(loc.Start.ColumnOffset),
			"@$offset@", itoa(loc.End.ByteOffset),
			"@$line@", itoa(loc.End.LineNumber),
			"@$col@", itoa(loc.End.ColumnOffset),
		)
		if _, err := r.WriteString(out, refFormat+"\n"); err != nil {
			return err
		}
	}

	return nil
}
Пример #8
0
func completeDefinition(definesAnchor string) (*definition, error) {
	parentReply, err := xs.Edges(ctx, &xpb.EdgesRequest{
		Ticket: []string{definesAnchor},
		Kind:   []string{schema.ChildOfEdge},
		Filter: []string{schema.NodeKindFact, schema.AnchorLocFilter},
	})
	if err != nil {
		return nil, err
	}

	parentNodes := xrefs.NodesMap(parentReply.Node)
	var files []string
	for _, parent := range xrefs.EdgesMap(parentReply.EdgeSet)[definesAnchor][schema.ChildOfEdge] {
		if string(parentNodes[parent][schema.NodeKindFact]) == schema.FileKind {
			files = append(files, parent)
		}
	}

	if len(files) == 0 {
		return nil, nil
	} else if len(files) > 1 {
		return nil, fmt.Errorf("anchor has multiple file parents %q: %v", definesAnchor, files)
	}

	vName, err := kytheuri.Parse(files[0])
	if err != nil {
		return nil, err
	}
	start, end := parseAnchorSpan(parentNodes[definesAnchor])

	return &definition{
		File:  vName.VName(),
		Start: start,
		End:   end,
	}, nil
}
Пример #9
0
func main() {
	flag.Parse()
	if *offset < 0 {
		flagutil.UsageError("non-negative --offset required")
	} else if *signature == "" && *path == "" {
		flagutil.UsageError("must provide at least --path or --signature")
	}

	if strings.HasPrefix(*remoteAPI, "http://") || strings.HasPrefix(*remoteAPI, "https://") {
		xs = xrefs.WebClient(*remoteAPI)
		idx = search.WebClient(*remoteAPI)
	} else {
		conn, err := grpc.Dial(*remoteAPI)
		if err != nil {
			log.Fatalf("Error connecting to remote API %q: %v", *remoteAPI, err)
		}
		defer conn.Close()
		xs = xrefs.GRPC(xpb.NewXRefServiceClient(conn))
		idx = search.GRPC(spb.NewSearchServiceClient(conn))
	}

	relPath := *path
	if !*ignoreLocalRepo {
		if _, err := os.Stat(relPath); err == nil {
			absPath, err := filepath.Abs(relPath)
			if err != nil {
				log.Fatal(err)
			}
			kytheRoot := findKytheRoot(filepath.Dir(absPath))
			if kytheRoot != "" {
				relPath, err = filepath.Rel(filepath.Join(kytheRoot, *root), absPath)
				if err != nil {
					log.Fatal(err)
				}
			}
		}
	}
	partialFile := &spb.VName{
		Signature: *signature,
		Corpus:    *corpus,
		Root:      *root,
		Path:      relPath,
		Language:  *language,
	}
	reply, err := idx.Search(ctx, &spb.SearchRequest{
		Partial: partialFile,
		Fact:    fileFacts,
	})
	if err != nil {
		log.Fatalf("Error locating file {%v}: %v", partialFile, err)
	}
	if len(reply.Ticket) == 0 {
		log.Fatalf("Could not locate file {%v}", partialFile)
	} else if len(reply.Ticket) > 1 {
		log.Fatalf("Ambiguous file {%v}; multiple results: %v", partialFile, reply.Ticket)
	}

	fileTicket := reply.Ticket[0]
	decor, err := xs.Decorations(ctx, &xpb.DecorationsRequest{
		// TODO(schroederc): limit Location to a SPAN around *offset
		Location:    &xpb.Location{Ticket: fileTicket},
		References:  true,
		SourceText:  true,
		DirtyBuffer: readDirtyBuffer(ctx),
	})
	if err != nil {
		log.Fatal(err)
	}
	nodes := xrefs.NodesMap(decor.Node)

	en := json.NewEncoder(os.Stdout)
	for _, ref := range decor.Reference {
		start, end := parseAnchorSpan(nodes[ref.SourceTicket])

		if start <= *offset && *offset < end {
			var r reference
			r.Span.Start = start
			r.Span.End = end
			r.Span.Text = string(decor.SourceText[start:end])
			r.Kind = strings.TrimPrefix(ref.Kind, schema.EdgePrefix)
			r.Node.Ticket = ref.TargetTicket

			node := nodes[ref.TargetTicket]
			r.Node.Kind = string(node[schema.NodeKindFact])
			r.Node.Subkind = string(node[schema.SubkindFact])

			if eReply, err := xs.Edges(ctx, &xpb.EdgesRequest{
				Ticket: []string{ref.TargetTicket},
				Kind:   []string{schema.NamedEdge, definedAtEdge},
			}); err != nil {
				log.Printf("WARNING: error getting edges for %q: %v", ref.TargetTicket, err)
			} else {
				edges := xrefs.EdgesMap(eReply.EdgeSet)[ref.TargetTicket]
				for _, name := range edges[schema.NamedEdge] {
					if uri, err := kytheuri.Parse(name); err != nil {
						log.Printf("WARNING: named node ticket (%q) could not be parsed: %v", name, err)
					} else {
						r.Node.Names = append(r.Node.Names, uri.Signature)
					}
				}

				if !*skipDefinitions {
					for _, defAnchor := range edges[definedAtEdge] {
						def, err := completeDefinition(defAnchor)
						if err != nil {
							log.Printf("WARNING: failed to complete definition for %q: %v", defAnchor, err)
						} else {
							r.Node.Definitions = append(r.Node.Definitions, def)
						}
					}
				}
			}

			if err := en.Encode(r); err != nil {
				log.Fatal(err)
			}
		}
	}
}
Пример #10
0
func main() {
	flag.Parse()
	if len(flag.Args()) == 0 {
		flagutil.UsageError("not given any files")
	}

	xs := xrefs.WebClient(*remoteAPI)
	idx := search.WebClient(*remoteAPI)

	for _, file := range flag.Args() {
		results, err := idx.Search(ctx, &spb.SearchRequest{
			Partial: &spb.VName{Path: file},
			Fact: []*spb.SearchRequest_Fact{{
				Name:  schema.NodeKindFact,
				Value: []byte(schema.FileKind),
			}},
		})
		if err != nil {
			log.Fatalf("Error searching for ticket of file %q", file)
		} else if len(results.Ticket) == 0 {
			log.Printf("Could not find ticket for file %q", file)
			continue
		} else if len(results.Ticket) != 1 {
			log.Printf("Multiple tickets found for file %q; choosing first from %v", file, results.Ticket)
		}

		ticket := results.Ticket[0]
		decor, err := xs.Decorations(ctx, &xpb.DecorationsRequest{
			Location:   &xpb.Location{Ticket: ticket},
			SourceText: true,
			References: true,
		})
		if err != nil {
			log.Fatalf("Failed to get decorations for file %q", file)
		}

		nodes := xrefs.NodesMap(decor.Node)
		emitted := stringset.New()

		for _, r := range decor.Reference {
			if r.Kind != schema.DefinesBindingEdge || emitted.Contains(r.TargetTicket) {
				continue
			}

			ident := string(nodes[r.TargetTicket][identifierFact])
			if ident == "" {
				continue
			}

			offset, err := strconv.Atoi(string(nodes[r.SourceTicket][schema.AnchorStartFact]))
			if err != nil {
				log.Printf("Invalid start offset for anchor %q", r.SourceTicket)
				continue
			}

			fields, err := getTagFields(xs, r.TargetTicket)
			if err != nil {
				log.Printf("Failed to get tagfields for %q: %v", r.TargetTicket, err)
			}

			fmt.Printf("%s\t%s\t%d;\"\t%s\n",
				ident, file, offsetLine(decor.SourceText, offset), strings.Join(fields, "\t"))
			emitted.Add(r.TargetTicket)
		}
	}
}
Пример #11
0
Файл: xrefs.go Проект: 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
}
Пример #12
0
// Decorations implements part of the xrefs.Service interface.
func (db *DB) Decorations(req *xpb.DecorationsRequest) (*xpb.DecorationsReply, error) {
	if req.GetLocation() == nil {
		return nil, errors.New("missing location")
	} else if req.Location.Kind != xpb.Location_FILE {
		return nil, fmt.Errorf("%s location kind unimplemented", req.Location.Kind)
	} else if len(req.DirtyBuffer) != 0 {
		return nil, errors.New("dirty buffers unimplemented")
	}

	fileTicket := req.Location.Ticket

	edgesReply, err := db.Edges(&xpb.EdgesRequest{
		Ticket: []string{fileTicket},
		Kind:   []string{revChildOfEdge},
		Filter: []string{schema.NodeKindFact, schema.TextFact, schema.TextEncodingFact},
	})
	if err != nil {
		return nil, err
	}

	reply := &xpb.DecorationsReply{
		Location: &xpb.Location{
			Ticket: fileTicket,
		},
	}

	nodes := xrefs.NodesMap(edgesReply.Node)

	if req.SourceText {
		if nodes[fileTicket] == nil {
			nodesReply, err := db.Nodes(&xpb.NodesRequest{Ticket: []string{fileTicket}})
			if err != nil {
				return nil, err
			}
			nodes = xrefs.NodesMap(nodesReply.Node)
		}
		reply.SourceText = nodes[fileTicket][schema.TextFact]
		reply.Encoding = string(nodes[fileTicket][schema.TextEncodingFact])
	}

	nodeTickets := stringset.New()

	if req.References {
		// Traverse the following chain of edges:
		//   file --%/kythe/edge/childof-> []anchor --forwardEdgeKind-> []target
		edges := xrefs.EdgesMap(edgesReply.EdgeSet)

		for _, anchor := range edges[fileTicket][revChildOfEdge] {
			if string(nodes[anchor][schema.NodeKindFact]) != schema.AnchorKind {
				continue
			}

			uri, err := kytheuri.Parse(anchor)
			if err != nil {
				return nil, fmt.Errorf("invalid anchor ticket found %q: %v", anchor, err)
			}
			rows, err := db.anchorEdgesStmt.Query(schema.ChildOfEdge, uri.Signature, uri.Corpus, uri.Root, uri.Path, uri.Language)
			if err != nil {
				return nil, fmt.Errorf("anchor %q edge query error: %v", anchor, err)
			}

			for rows.Next() {
				var target kytheuri.URI
				var kind string
				if err := rows.Scan(&target.Signature, &target.Corpus, &target.Root, &target.Path, &target.Language, &kind); err != nil {
					return nil, fmt.Errorf("anchor %q edge scan error: %v", anchor, err)
				}
				ticket := target.String()
				reply.Reference = append(reply.Reference, &xpb.DecorationsReply_Reference{
					SourceTicket: anchor,
					Kind:         kind,
					TargetTicket: ticket,
				})
				nodeTickets.Add(anchor, ticket)
			}
		}
	}

	nodesReply, err := db.Nodes(&xpb.NodesRequest{Ticket: nodeTickets.Slice()})
	if err != nil {
		return nil, err
	}
	reply.Node = nodesReply.Node

	return reply, nil
}
Пример #13
0
Файл: xrefs.go Проект: bzz/kythe
// Decorations implements part of the Service interface.
func (g *GraphStoreService) Decorations(ctx context.Context, req *xpb.DecorationsRequest) (*xpb.DecorationsReply, error) {
	if len(req.DirtyBuffer) > 0 {
		return nil, errors.New("UNIMPLEMENTED: dirty buffers")
	} else if req.GetLocation() == nil {
		// TODO(schroederc): allow empty location when given dirty buffer
		return nil, errors.New("missing location")
	}

	fileVName, err := kytheuri.ToVName(req.Location.Ticket)
	if err != nil {
		return nil, fmt.Errorf("invalid file ticket %q: %v", req.Location.Ticket, err)
	}

	text, encoding, err := getSourceText(ctx, g.gs, fileVName)
	if err != nil {
		return nil, fmt.Errorf("failed to retrieve file text: %v", err)
	}
	norm := xrefs.NewNormalizer(text)

	loc, err := norm.Location(req.GetLocation())
	if err != nil {
		return nil, err
	}

	reply := &xpb.DecorationsReply{
		Location: loc,
		Nodes:    make(map[string]*xpb.NodeInfo),
	}

	// Handle DecorationsRequest.SourceText switch
	if req.SourceText {
		if loc.Kind == xpb.Location_FILE {
			reply.SourceText = text
		} else {
			reply.SourceText = text[loc.Start.ByteOffset:loc.End.ByteOffset]
		}
		reply.Encoding = encoding
	}

	// Handle DecorationsRequest.References switch
	if req.References {
		// Traverse the following chain of edges:
		//   file --%/kythe/edge/childof-> []anchor --forwardEdgeKind-> []target
		//
		// Add []anchor and []target nodes to reply.Nodes
		// Add all {anchor, forwardEdgeKind, target} tuples to reply.Reference

		patterns := xrefs.ConvertFilters(req.Filter)

		children, err := getEdges(ctx, g.gs, fileVName, func(e *spb.Entry) bool {
			return e.EdgeKind == revChildOfEdgeKind
		})
		if err != nil {
			return nil, fmt.Errorf("failed to retrieve file children: %v", err)
		}

		targetSet := stringset.New()
		for _, edge := range children {
			anchor := edge.Target
			ticket := kytheuri.ToString(anchor)
			anchorNodeReply, err := g.Nodes(ctx, &xpb.NodesRequest{
				Ticket: []string{ticket},
			})
			if err != nil {
				return nil, fmt.Errorf("failure getting reference source node: %v", err)
			} else if len(anchorNodeReply.Nodes) != 1 {
				return nil, fmt.Errorf("found %d nodes for {%+v}", len(anchorNodeReply.Nodes), anchor)
			}

			node, ok := xrefs.NodesMap(anchorNodeReply.Nodes)[ticket]
			if !ok {
				return nil, fmt.Errorf("failed to find info for node %q", ticket)
			} else if string(node[schema.NodeKindFact]) != schema.AnchorKind {
				// Skip child if it isn't an anchor node
				continue
			}

			anchorStart, err := strconv.Atoi(string(node[schema.AnchorStartFact]))
			if err != nil {
				log.Printf("Invalid anchor start offset %q for node %q: %v", node[schema.AnchorStartFact], ticket, err)
				continue
			}
			anchorEnd, err := strconv.Atoi(string(node[schema.AnchorEndFact]))
			if err != nil {
				log.Printf("Invalid anchor end offset %q for node %q: %v", node[schema.AnchorEndFact], ticket, err)
				continue
			}

			if loc.Kind == xpb.Location_SPAN {
				// Check if anchor fits within/around requested source text window
				if !xrefs.InSpanBounds(req.SpanKind, int32(anchorStart), int32(anchorEnd), loc.Start.ByteOffset, loc.End.ByteOffset) {
					continue
				} else if anchorStart > anchorEnd {
					log.Printf("Invalid anchor offset span %d:%d", anchorStart, anchorEnd)
					continue
				}
			}

			targets, err := getEdges(ctx, g.gs, anchor, func(e *spb.Entry) bool {
				return schema.EdgeDirection(e.EdgeKind) == schema.Forward && e.EdgeKind != schema.ChildOfEdge
			})
			if err != nil {
				return nil, fmt.Errorf("failed to retrieve targets of anchor %v: %v", anchor, err)
			}
			if len(targets) == 0 {
				log.Printf("Anchor missing forward edges: {%+v}", anchor)
				continue
			}

			if node := filterNode(patterns, anchorNodeReply.Nodes[ticket]); node != nil {
				reply.Nodes[ticket] = node
			}
			for _, edge := range targets {
				targetTicket := kytheuri.ToString(edge.Target)
				targetSet.Add(targetTicket)
				reply.Reference = append(reply.Reference, &xpb.DecorationsReply_Reference{
					SourceTicket: ticket,
					Kind:         edge.Kind,
					TargetTicket: targetTicket,
					AnchorStart:  norm.ByteOffset(int32(anchorStart)),
					AnchorEnd:    norm.ByteOffset(int32(anchorEnd)),
				})
			}
		}
		sort.Sort(bySpan(reply.Reference))

		// Only request Nodes when there are fact filters given.
		if len(req.Filter) > 0 {
			// Ensure returned nodes are not duplicated.
			for ticket := range reply.Nodes {
				targetSet.Remove(ticket)
			}

			// Batch request all Reference target nodes
			nodesReply, err := g.Nodes(ctx, &xpb.NodesRequest{
				Ticket: targetSet.Slice(),
				Filter: req.Filter,
			})
			if err != nil {
				return nil, fmt.Errorf("failure getting reference target nodes: %v", err)
			}
			for ticket, node := range nodesReply.Nodes {
				reply.Nodes[ticket] = node
			}
		}
	}

	return reply, nil
}
Пример #14
0
func displayEdgeGraph(reply *xpb.EdgesReply) error {
	nodes := xrefs.NodesMap(reply.Nodes)
	edges := make(map[string]map[string]stringset.Set)

	for source, es := range reply.EdgeSets {
		for gKind, g := range es.Groups {
			for _, edge := range g.Edge {
				tgt := edge.TargetTicket
				src, kind := source, gKind
				if schema.EdgeDirection(kind) == schema.Reverse {
					src, kind, tgt = tgt, schema.MirrorEdge(kind), src
				}
				groups, ok := edges[src]
				if !ok {
					groups = make(map[string]stringset.Set)
					edges[src] = groups
				}
				targets, ok := groups[kind]
				if !ok {
					targets = stringset.New()
					groups[kind] = targets
				}
				targets.Add(tgt)
			}
		}
	}
	if _, err := fmt.Println("digraph kythe {"); err != nil {
		return err
	}
	for ticket, node := range nodes {
		if _, err := fmt.Printf(`	%q [label=<<table><tr><td colspan="2">%s</td></tr>`, ticket, html.EscapeString(ticket)); err != nil {
			return err
		}
		var facts []string
		for fact := range node {
			facts = append(facts, fact)
		}
		sort.Strings(facts)
		for _, fact := range facts {
			if _, err := fmt.Printf("<tr><td>%s</td><td>%s</td></tr>", html.EscapeString(fact), html.EscapeString(string(node[fact]))); err != nil {
				return err
			}
		}
		if _, err := fmt.Println("</table>> shape=plaintext];"); err != nil {
			return err
		}
	}
	if _, err := fmt.Println(); err != nil {
		return err
	}

	for src, groups := range edges {
		for kind, targets := range groups {
			for tgt := range targets {
				if _, err := fmt.Printf("\t%q -> %q [label=%q];\n", src, tgt, kind); err != nil {
					return err
				}
			}
		}
	}
	if _, err := fmt.Println("}"); err != nil {
		return err
	}
	return nil
}
Пример #15
0
func main() {
	flag.Parse()
	if flag.NArg() > 0 {
		flagutil.UsageErrorf("unknown non-flag argument(s): %v", flag.Args())
	} else if *offset < 0 && (*lineNumber < 0 || *columnOffset < 0) {
		flagutil.UsageError("non-negative --offset (or --line and --column) required")
	} else if *signature == "" && *path == "" {
		flagutil.UsageError("must provide at least --path or --signature")
	}

	defer (*apiFlag).Close()
	xs, idx = *apiFlag, *apiFlag

	relPath := *path
	if *localRepoRoot != "NONE" {
		if _, err := os.Stat(relPath); err == nil {
			absPath, err := filepath.Abs(relPath)
			if err != nil {
				log.Fatal(err)
			}
			if *dirtyBuffer == "" {
				*dirtyBuffer = absPath
			}

			kytheRoot := *localRepoRoot
			if kytheRoot == "" {
				kytheRoot = findKytheRoot(filepath.Dir(absPath))
			}
			if kytheRoot != "" {
				relPath, err = filepath.Rel(filepath.Join(kytheRoot, *root), absPath)
				if err != nil {
					log.Fatal(err)
				}
			}
		}
	}
	partialFile := &spb.VName{
		Signature: *signature,
		Corpus:    *corpus,
		Root:      *root,
		Path:      relPath,
		Language:  *language,
	}
	reply, err := idx.Search(ctx, &spb.SearchRequest{
		Partial: partialFile,
		Fact:    fileFacts,
	})
	if err != nil {
		log.Fatalf("Error locating file {%v}: %v", partialFile, err)
	}
	if len(reply.Ticket) == 0 {
		log.Fatalf("Could not locate file {%v}", partialFile)
	} else if len(reply.Ticket) > 1 {
		log.Fatalf("Ambiguous file {%v}; multiple results: %v", partialFile, reply.Ticket)
	}

	fileTicket := reply.Ticket[0]
	text := readDirtyBuffer(ctx)
	decor, err := xs.Decorations(ctx, &xpb.DecorationsRequest{
		// TODO(schroederc): limit Location to a SPAN around *offset
		Location:    &xpb.Location{Ticket: fileTicket},
		References:  true,
		SourceText:  true,
		DirtyBuffer: text,
		Filter: []string{
			schema.NodeKindFact,
			schema.SubkindFact,
			schema.AnchorLocFilter, // TODO(schroederc): remove once backwards-compatibility fix below is removed
		},
	})
	if err != nil {
		log.Fatal(err)
	}
	if text == nil {
		text = decor.SourceText
	}
	nodes := xrefs.NodesMap(decor.Node)

	// Normalize point within source text
	point := normalizedPoint(text)

	en := json.NewEncoder(os.Stdout)
	for _, ref := range decor.Reference {
		var start, end int
		if ref.AnchorStart == nil || ref.AnchorEnd == nil {
			// TODO(schroederc): remove this backwards-compatibility fix
			start, end = parseAnchorSpan(nodes[ref.SourceTicket])
		} else {
			start, end = int(ref.AnchorStart.ByteOffset), int(ref.AnchorEnd.ByteOffset)
		}

		if start <= point && point < end {
			var r reference
			r.Span.Start = start
			r.Span.End = end
			r.Span.Text = string(text[start:end])
			r.Kind = strings.TrimPrefix(ref.Kind, schema.EdgePrefix)
			r.Node.Ticket = ref.TargetTicket

			node := nodes[ref.TargetTicket]
			r.Node.Kind = string(node[schema.NodeKindFact])
			r.Node.Subkind = string(node[schema.SubkindFact])

			if eReply, err := xrefs.AllEdges(ctx, xs, &xpb.EdgesRequest{
				Ticket: []string{ref.TargetTicket},
				Kind:   []string{schema.NamedEdge, schema.TypedEdge, definedAtEdge, definedBindingAtEdge},
			}); err != nil {
				log.Printf("WARNING: error getting edges for %q: %v", ref.TargetTicket, err)
			} else {
				edges := xrefs.EdgesMap(eReply.EdgeSet)[ref.TargetTicket]
				for _, name := range edges[schema.NamedEdge] {
					if uri, err := kytheuri.Parse(name); err != nil {
						log.Printf("WARNING: named node ticket (%q) could not be parsed: %v", name, err)
					} else {
						r.Node.Names = append(r.Node.Names, uri.Signature)
					}
				}

				if typed := edges[schema.TypedEdge]; len(typed) > 0 {
					r.Node.Typed = typed[0]
				}

				if !*skipDefinitions {
					defs := edges[definedAtEdge]
					if len(defs) == 0 {
						defs = edges[definedBindingAtEdge]
					}
					for _, defAnchor := range defs {
						def, err := completeDefinition(defAnchor)
						if err != nil {
							log.Printf("WARNING: failed to complete definition for %q: %v", defAnchor, err)
						} else {
							r.Node.Definitions = append(r.Node.Definitions, def)
						}
					}
				}
			}

			if err := en.Encode(r); err != nil {
				log.Fatal(err)
			}
		}
	}
}
Пример #16
0
func main() {
	flag.Parse()
	if flag.NArg() > 0 {
		flagutil.UsageErrorf("unknown non-flag argument(s): %v", flag.Args())
	} else if *offset < 0 && (*lineNumber < 0 || *columnOffset < 0) {
		flagutil.UsageError("non-negative --offset (or --line and --column) required")
	} else if *path == "" {
		flagutil.UsageError("must provide --path")
	}

	defer (*apiFlag).Close()
	xs = *apiFlag

	relPath := *path
	if *localRepoRoot != "NONE" {
		if _, err := os.Stat(relPath); err == nil {
			absPath, err := filepath.Abs(relPath)
			if err != nil {
				log.Fatal(err)
			}
			if *dirtyBuffer == "" {
				*dirtyBuffer = absPath
			}

			kytheRoot := *localRepoRoot
			if kytheRoot == "" {
				kytheRoot = findKytheRoot(filepath.Dir(absPath))
			}
			if kytheRoot != "" {
				relPath, err = filepath.Rel(filepath.Join(kytheRoot, *root), absPath)
				if err != nil {
					log.Fatal(err)
				}
			}
		}
	}

	fileTicket := (&kytheuri.URI{Corpus: *corpus, Root: *root, Path: relPath}).String()
	point := &xpb.Location_Point{
		ByteOffset:   int32(*offset),
		LineNumber:   int32(*lineNumber),
		ColumnOffset: int32(*columnOffset),
	}
	dirtyBuffer := readDirtyBuffer(ctx)
	decor, err := xs.Decorations(ctx, &xpb.DecorationsRequest{
		Location: &xpb.Location{
			Ticket: fileTicket,
			Kind:   xpb.Location_SPAN,
			Start:  point,
			End:    point,
		},
		SpanKind:    xpb.DecorationsRequest_AROUND_SPAN,
		References:  true,
		SourceText:  true,
		DirtyBuffer: dirtyBuffer,
		Filter: []string{
			facts.NodeKind,
			facts.Subkind,
		},
	})
	if err != nil {
		log.Fatal(err)
	}
	nodes := xrefs.NodesMap(decor.Nodes)

	en := json.NewEncoder(os.Stdout)
	for _, ref := range decor.Reference {
		start, end := int(ref.AnchorStart.ByteOffset), int(ref.AnchorEnd.ByteOffset)

		var r reference
		r.Span.Start = start
		r.Span.End = end
		if len(dirtyBuffer) > 0 {
			r.Span.Text = string(dirtyBuffer[start:end])
		} // TODO(schroederc): add option to get anchor text from DecorationsReply
		r.Kind = strings.TrimPrefix(ref.Kind, edges.Prefix)
		r.Node.Ticket = ref.TargetTicket

		node := nodes[ref.TargetTicket]
		r.Node.Kind = string(node[facts.NodeKind])
		r.Node.Subkind = string(node[facts.Subkind])

		// TODO(schroederc): use CrossReferences method
		if eReply, err := xrefs.AllEdges(ctx, xs, &gpb.EdgesRequest{
			Ticket: []string{ref.TargetTicket},
			Kind:   []string{edges.Named, edges.Typed, definedAtEdge, definedBindingAtEdge},
		}); err != nil {
			log.Printf("WARNING: error getting edges for %q: %v", ref.TargetTicket, err)
		} else {
			matching := xrefs.EdgesMap(eReply.EdgeSets)[ref.TargetTicket]
			for name := range matching[edges.Named] {
				if uri, err := kytheuri.Parse(name); err != nil {
					log.Printf("WARNING: named node ticket (%q) could not be parsed: %v", name, err)
				} else {
					r.Node.Names = append(r.Node.Names, uri.Signature)
				}
			}

			for typed := range matching[edges.Typed] {
				r.Node.Typed = typed
				break
			}

			if !*skipDefinitions {
				defs := matching[definedAtEdge]
				if len(defs) == 0 {
					defs = matching[definedBindingAtEdge]
				}
				for defAnchor := range defs {
					def, err := completeDefinition(defAnchor)
					if err != nil {
						log.Printf("WARNING: failed to complete definition for %q: %v", defAnchor, err)
					} else {
						r.Node.Definitions = append(r.Node.Definitions, def)
					}
				}
			}
		}

		if err := en.Encode(r); err != nil {
			log.Fatal(err)
		}
	}
}
Пример #17
0
func displayDecorations(text []byte, decor *xpb.DecorationsReply) error {
	if *displayJSON {
		return json.NewEncoder(out).Encode(decor)
	}

	nodes := xrefs.NodesMap(decor.Node)

	if text == nil {
		text = decor.SourceText
	}
	norm := xrefs.NewNormalizer(text)

	for _, ref := range decor.Reference {
		nodeKind := factValue(nodes, ref.TargetTicket, schema.NodeKindFact, "UNKNOWN")
		subkind := factValue(nodes, ref.TargetTicket, schema.SubkindFact, "")

		var loc *xpb.Location
		if ref.AnchorStart != nil && ref.AnchorEnd != nil {
			loc = &xpb.Location{
				Kind:  xpb.Location_SPAN,
				Start: ref.AnchorStart,
				End:   ref.AnchorEnd,
			}
		} else {
			// TODO(schroederc): remove this backwards-compatibility branch

			startOffset := factValue(nodes, ref.SourceTicket, schema.AnchorStartFact, "_")
			endOffset := factValue(nodes, ref.SourceTicket, schema.AnchorEndFact, "_")

			// ignore errors (locations will be 0)
			s, _ := strconv.Atoi(startOffset)
			e, _ := strconv.Atoi(endOffset)

			var err error
			loc, err = norm.Location(&xpb.Location{
				Kind:  xpb.Location_SPAN,
				Start: &xpb.Location_Point{ByteOffset: int32(s)},
				End:   &xpb.Location_Point{ByteOffset: int32(e)},
			})
			if err != nil {
				return fmt.Errorf("error normalizing reference location for anchor %q: %v", ref.SourceTicket, err)
			}
		}

		r := strings.NewReplacer(
			"@source@", ref.SourceTicket,
			"@target@", ref.TargetTicket,
			"@edgeKind@", ref.Kind,
			"@nodeKind@", nodeKind,
			"@subkind@", subkind,
			"@^offset@", itoa(loc.Start.ByteOffset),
			"@^line@", itoa(loc.Start.LineNumber),
			"@^col@", itoa(loc.Start.ColumnOffset),
			"@$offset@", itoa(loc.End.ByteOffset),
			"@$line@", itoa(loc.End.LineNumber),
			"@$col@", itoa(loc.End.ColumnOffset),
		)
		if _, err := r.WriteString(out, refFormat+"\n"); err != nil {
			return err
		}
	}

	return nil
}