kythe.io@v0.0.68-0.20240422202219-7225dbc01741/kythe/go/serving/xrefs/assemble/assemble.go (about) 1 /* 2 * Copyright 2015 The Kythe Authors. All rights reserved. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 // Package assemble provides functions to build the various components (nodes, 18 // edges, and decorations) of an xrefs serving table. 19 package assemble // import "kythe.io/kythe/go/serving/xrefs/assemble" 20 21 import ( 22 "context" 23 "errors" 24 "fmt" 25 "sort" 26 "strconv" 27 28 "kythe.io/kythe/go/services/graphstore" 29 "kythe.io/kythe/go/services/xrefs" 30 "kythe.io/kythe/go/storage/stream" 31 "kythe.io/kythe/go/util/compare" 32 "kythe.io/kythe/go/util/encoding/text" 33 "kythe.io/kythe/go/util/kytheuri" 34 "kythe.io/kythe/go/util/log" 35 "kythe.io/kythe/go/util/pager" 36 "kythe.io/kythe/go/util/schema/edges" 37 "kythe.io/kythe/go/util/schema/facts" 38 "kythe.io/kythe/go/util/schema/nodes" 39 "kythe.io/kythe/go/util/schema/tickets" 40 "kythe.io/kythe/go/util/span" 41 cpb "kythe.io/kythe/proto/common_go_proto" 42 ipb "kythe.io/kythe/proto/internal_go_proto" 43 srvpb "kythe.io/kythe/proto/serving_go_proto" 44 spb "kythe.io/kythe/proto/storage_go_proto" 45 ) 46 47 // Node returns the Source as a srvpb.Node. 48 func Node(s *ipb.Source) *srvpb.Node { 49 facts := make([]*cpb.Fact, 0, len(s.Facts)) 50 for name, value := range s.Facts { 51 facts = append(facts, &cpb.Fact{Name: name, Value: value}) 52 } 53 sort.Sort(xrefs.ByName(facts)) 54 return &srvpb.Node{ 55 Ticket: s.Ticket, 56 Fact: facts, 57 } 58 } 59 60 // AppendEntry adds the given Entry to the Source's facts or edges. It is 61 // assumed that src.Ticket == kytheuri.ToString(e.Source). 62 func AppendEntry(src *ipb.Source, e *spb.Entry) { 63 if graphstore.IsEdge(e) { 64 kind, ordinal, _ := edges.ParseOrdinal(e.EdgeKind) 65 group, ok := src.EdgeGroups[kind] 66 if !ok { 67 group = &ipb.Source_EdgeGroup{} 68 src.EdgeGroups[kind] = group 69 } 70 71 ticket := kytheuri.ToString(e.Target) 72 73 ord := int32(ordinal) 74 for _, edge := range group.Edges { 75 if edge.Ticket == ticket && edge.Ordinal == ord { 76 // Don't add duplicate edge 77 return 78 } 79 } 80 81 group.Edges = append(group.Edges, &ipb.Source_Edge{ 82 Ticket: ticket, 83 Ordinal: ord, 84 }) 85 } else { 86 src.Facts[e.FactName] = e.FactValue 87 } 88 } 89 90 // Sources constructs a new Source for every contiguous set of entries sharing 91 // the same Source, calling f for each. 92 func Sources(rd stream.EntryReader, f func(*ipb.Source) error) error { 93 var source *spb.VName 94 var src *ipb.Source 95 if err := rd(func(entry *spb.Entry) error { 96 if src != nil && !compare.VNamesEqual(source, entry.Source) { 97 if err := f(src); err != nil { 98 return err 99 } 100 src = nil 101 } 102 if src == nil { 103 source = entry.Source 104 src = &ipb.Source{ 105 Ticket: kytheuri.ToString(entry.Source), 106 Facts: make(map[string][]byte), 107 EdgeGroups: make(map[string]*ipb.Source_EdgeGroup), 108 } 109 } 110 AppendEntry(src, entry) 111 return nil 112 }); err != nil { 113 return err 114 } 115 if src != nil { 116 return f(src) 117 } 118 return nil 119 } 120 121 // SourceFromEntries returns a new Source from the given a set of entries with 122 // the same source VName. 123 func SourceFromEntries(entries []*spb.Entry) *ipb.Source { 124 if len(entries) == 0 { 125 return nil 126 } 127 128 src := &ipb.Source{ 129 Ticket: kytheuri.ToString(entries[0].Source), 130 Facts: make(map[string][]byte), 131 EdgeGroups: make(map[string]*ipb.Source_EdgeGroup), 132 } 133 134 for _, e := range entries { 135 AppendEntry(src, e) 136 } 137 138 for _, group := range src.EdgeGroups { 139 sort.Sort(byOrdinal(group.Edges)) 140 } 141 142 return src 143 } 144 145 // FactsToMap returns a map from fact name to value. 146 func FactsToMap(facts []*cpb.Fact) map[string][]byte { 147 m := make(map[string][]byte, len(facts)) 148 for _, f := range facts { 149 m[f.Name] = f.Value 150 } 151 return m 152 } 153 154 // GetFact returns the value of the first fact in facts with the given name; otherwise returns nil. 155 func GetFact(facts []*cpb.Fact, name string) []byte { 156 for _, f := range facts { 157 if f.Name == name { 158 return f.Value 159 } 160 } 161 return nil 162 } 163 164 // PartialReverseEdges returns the set of partial reverse edges from the given source. Each 165 // reversed Edge has its Target fully populated and its Source will have no facts. To ensure every 166 // node has at least 1 Edge, the first Edge will be a self-edge without a Kind or Target. To reduce 167 // the size of edge sets, each Target will have any text facts filtered (see FilterTextFacts). 168 func PartialReverseEdges(src *ipb.Source) []*srvpb.Edge { 169 node := Node(src) 170 171 result := []*srvpb.Edge{{ 172 Source: node, // self-edge to ensure every node has at least 1 edge 173 }} 174 175 targetNode := FilterTextFacts(node) 176 177 for kind, group := range src.EdgeGroups { 178 rev := edges.Mirror(kind) 179 for _, target := range group.Edges { 180 result = append(result, &srvpb.Edge{ 181 Source: &srvpb.Node{Ticket: target.Ticket}, 182 Kind: rev, 183 Ordinal: target.Ordinal, 184 Target: targetNode, 185 }) 186 } 187 } 188 189 return result 190 } 191 192 // FilterTextFacts returns a new Node without any text facts. 193 func FilterTextFacts(n *srvpb.Node) *srvpb.Node { 194 res := &srvpb.Node{ 195 Ticket: n.Ticket, 196 Fact: make([]*cpb.Fact, 0, len(n.Fact)), 197 } 198 for _, f := range n.Fact { 199 switch f.Name { 200 case facts.Text, facts.TextEncoding: 201 // Skip large text facts for targets 202 default: 203 res.Fact = append(res.Fact, f) 204 } 205 } 206 return res 207 } 208 209 // DecorationFragmentBuilder builds pieces of FileDecorations given an ordered (see AddEdge) stream 210 // of completed Edges. Each fragment constructed (either by AddEdge or Flush) will be emitted using 211 // the Output function in the builder. There are two types of fragments: file fragments (which have 212 // their SourceText, FileTicket, and Encoding set) and decoration fragments (which have only 213 // Decoration set). 214 type DecorationFragmentBuilder struct { 215 Output func(ctx context.Context, file string, fragment *srvpb.FileDecorations) error 216 217 anchor *srvpb.RawAnchor 218 targets map[string]*srvpb.Node 219 decor []*srvpb.FileDecorations_Decoration 220 parents []string 221 } 222 223 // AddEdge adds the given edge to the current fragment (or emits some fragments and starts a new 224 // fragment with e). AddEdge must be called in GraphStore sorted order of the Edges with the 225 // beginning to every set of edges with the same Source having a signaling Edge with only its Source 226 // set (no Kind or Target). Otherwise, every Edge must have a completed Source, Kind, and Target. 227 // Flush must be called after every call to AddEdge in order to output any remaining fragments. 228 func (b *DecorationFragmentBuilder) AddEdge(ctx context.Context, e *srvpb.Edge) error { 229 if e.Target == nil { 230 // Beginning of a set of edges with a new Source 231 if err := b.Flush(ctx); err != nil { 232 return err 233 } 234 235 srcFacts := FactsToMap(e.Source.Fact) 236 237 switch string(srcFacts[facts.NodeKind]) { 238 case nodes.File: 239 if err := b.Output(ctx, e.Source.Ticket, &srvpb.FileDecorations{ 240 File: &srvpb.File{ 241 Ticket: e.Source.Ticket, 242 Text: srcFacts[facts.Text], 243 Encoding: string(srcFacts[facts.TextEncoding]), 244 }, 245 }); err != nil { 246 return err 247 } 248 case nodes.Anchor: 249 // Implicit anchors don't belong in file decorations. 250 if string(srcFacts[facts.Subkind]) == nodes.Implicit { 251 return nil 252 } 253 anchorStart, err := strconv.Atoi(string(srcFacts[facts.AnchorStart])) 254 if err != nil { 255 log.ErrorContextf(ctx, "parsing anchor start offset %q: %v", 256 string(srcFacts[facts.AnchorStart]), err) 257 return nil 258 } 259 anchorEnd, err := strconv.Atoi(string(srcFacts[facts.AnchorEnd])) 260 if err != nil { 261 log.ErrorContextf(ctx, "parsing anchor end offset %q: %v", 262 string(srcFacts[facts.AnchorEnd]), err) 263 return nil 264 } 265 // Record the parent file for the anchor. 266 parentFile, err := tickets.AnchorFile(e.Source.Ticket) 267 if err != nil { 268 log.ErrorContextf(ctx, "deriving anchor ticket for %q: %v", e.Source.Ticket, err) 269 } else { 270 b.parents = append(b.parents, parentFile) 271 } 272 273 // Ignore errors; offsets will just be zero 274 snippetStart, _ := strconv.Atoi(string(srcFacts[facts.SnippetStart])) 275 snippetEnd, _ := strconv.Atoi(string(srcFacts[facts.SnippetEnd])) 276 277 b.anchor = &srvpb.RawAnchor{ 278 Ticket: e.Source.Ticket, 279 StartOffset: int32(anchorStart), 280 EndOffset: int32(anchorEnd), 281 SnippetStart: int32(snippetStart), 282 SnippetEnd: int32(snippetEnd), 283 } 284 b.targets = make(map[string]*srvpb.Node) 285 } 286 return nil 287 } else if b.anchor == nil { 288 // We don't care about edges for non-anchors 289 return nil 290 } 291 292 if e.Kind != edges.ChildOf { 293 b.decor = append(b.decor, &srvpb.FileDecorations_Decoration{ 294 Anchor: b.anchor, 295 Kind: e.Kind, 296 Target: e.Target.Ticket, 297 }) 298 299 if _, ok := b.targets[e.Target.Ticket]; !ok { 300 b.targets[e.Target.Ticket] = e.Target 301 } 302 303 if len(b.parents) > 0 { 304 fd := &srvpb.FileDecorations{Decoration: b.decor} 305 for _, n := range b.targets { 306 fd.Target = append(fd.Target, n) 307 } 308 sort.Sort(ByTicket(fd.Target)) 309 for _, parent := range b.parents { 310 if err := b.Output(ctx, parent, fd); err != nil { 311 return err 312 } 313 } 314 b.decor = nil 315 b.targets = make(map[string]*srvpb.Node) 316 } 317 } 318 319 return nil 320 } 321 322 // Flush outputs any remaining fragments that are being built. It is safe, but usually unnecessary, 323 // to call Flush in between sets of Edges with the same Source. This also means that 324 // DecorationFragmentBuilder can be used to construct decoration fragments in parallel by 325 // partitioning edges along the same boundaries. 326 func (b *DecorationFragmentBuilder) Flush(ctx context.Context) error { 327 defer func() { 328 b.anchor = nil 329 b.decor = nil 330 b.parents = nil 331 }() 332 333 if len(b.decor) > 0 && len(b.parents) > 0 { 334 fd := &srvpb.FileDecorations{Decoration: b.decor} 335 for _, parent := range b.parents { 336 if err := b.Output(ctx, parent, fd); err != nil { 337 return err 338 } 339 } 340 } 341 return nil 342 } 343 344 // ByOffset sorts file decorations by their byte offsets. 345 type ByOffset []*srvpb.FileDecorations_Decoration 346 347 func (s ByOffset) Len() int { return len(s) } 348 func (s ByOffset) Swap(i, j int) { s[i], s[j] = s[j], s[i] } 349 func (s ByOffset) Less(i, j int) bool { 350 if s[i].Anchor.StartOffset == s[j].Anchor.StartOffset { 351 if s[i].Anchor.EndOffset == s[j].Anchor.EndOffset { 352 if s[i].Kind == s[j].Kind { 353 if s[i].Target == s[j].Target { 354 return s[i].Anchor.Ticket < s[j].Anchor.Ticket 355 } 356 return s[i].Target < s[j].Target 357 } 358 return s[i].Kind < s[j].Kind 359 } 360 return s[i].Anchor.EndOffset < s[j].Anchor.EndOffset 361 } 362 return s[i].Anchor.StartOffset < s[j].Anchor.StartOffset 363 } 364 365 // ByTicket sorts nodes by their ticket. 366 type ByTicket []*srvpb.Node 367 368 func (s ByTicket) Len() int { return len(s) } 369 func (s ByTicket) Swap(i, j int) { s[i], s[j] = s[j], s[i] } 370 func (s ByTicket) Less(i, j int) bool { return s[i].Ticket < s[j].Ticket } 371 372 // ByAnchorTicket sorts anchors by their ticket. 373 type ByAnchorTicket []*srvpb.ExpandedAnchor 374 375 func (s ByAnchorTicket) Len() int { return len(s) } 376 func (s ByAnchorTicket) Swap(i, j int) { s[i], s[j] = s[j], s[i] } 377 func (s ByAnchorTicket) Less(i, j int) bool { return s[i].Ticket < s[j].Ticket } 378 379 // EdgeSetBuilder constructs a set of PagedEdgeSets and EdgePages from a 380 // sequence of Nodes and EdgeSet_Groups. For each set of groups with the same 381 // source, a call to StartEdgeSet must precede. All EdgeSet_Groups for the same 382 // source are then assumed to be given sequentially to AddGroup, secondarily 383 // ordered by the group's edge kind. If given in this order, Output will be 384 // given exactly 1 PagedEdgeSet per source with as few EdgeSet_Group per edge 385 // kind as to satisfy MaxEdgePageSize (MaxEdgePageSize == 0 indicates that there 386 // will be exactly 1 edge group per edge kind). If not given in this order, no 387 // guarantees can be made. Flush must be called after the final call to 388 // AddGroup. 389 type EdgeSetBuilder struct { 390 // MaxEdgePageSize is maximum number of edges that are allowed in the 391 // PagedEdgeSet and any EdgePage. If MaxEdgePageSize <= 0, no paging is 392 // attempted. 393 MaxEdgePageSize int 394 395 // Output is used to emit each PagedEdgeSet constructed. 396 Output func(context.Context, *srvpb.PagedEdgeSet) error 397 // OutputPage is used to emit each EdgePage constructed. 398 OutputPage func(context.Context, *srvpb.EdgePage) error 399 400 pager *pager.SetPager 401 } 402 403 func (b *EdgeSetBuilder) constructPager() *pager.SetPager { 404 // Head: *srvpb.Node 405 // Set: *srvpb.PagedEdgeSet 406 // Group: *srvpb.EdgeGroup 407 return &pager.SetPager{ 408 MaxPageSize: b.MaxEdgePageSize, 409 410 NewSet: func(hd pager.Head) pager.Set { 411 return &srvpb.PagedEdgeSet{ 412 Source: hd.(*srvpb.Node), 413 } 414 }, 415 Combine: func(l, r pager.Group) pager.Group { 416 lg, rg := l.(*srvpb.EdgeGroup), r.(*srvpb.EdgeGroup) 417 if lg.Kind != rg.Kind { 418 return nil 419 } 420 lg.Edge = append(lg.Edge, rg.Edge...) 421 return lg 422 }, 423 Split: func(sz int, g pager.Group) (l, r pager.Group) { 424 eg := g.(*srvpb.EdgeGroup) 425 neg := &srvpb.EdgeGroup{ 426 Kind: eg.Kind, 427 Edge: eg.Edge[:sz], 428 } 429 eg.Edge = eg.Edge[sz:] 430 return neg, eg 431 }, 432 Size: func(g pager.Group) int { return len(g.(*srvpb.EdgeGroup).Edge) }, 433 434 OutputSet: func(ctx context.Context, total int, s pager.Set, grps []pager.Group) error { 435 pes := s.(*srvpb.PagedEdgeSet) 436 437 // pes.Group = []*srvpb.EdgeGroup(grps) 438 pes.Group = make([]*srvpb.EdgeGroup, len(grps)) 439 for i, g := range grps { 440 pes.Group[i] = g.(*srvpb.EdgeGroup) 441 } 442 443 sort.Sort(byEdgeKind(pes.Group)) 444 sort.Sort(byPageKind(pes.PageIndex)) 445 pes.TotalEdges = int32(total) 446 447 return b.Output(ctx, pes) 448 }, 449 OutputPage: func(ctx context.Context, s pager.Set, g pager.Group) error { 450 pes := s.(*srvpb.PagedEdgeSet) 451 eviction := g.(*srvpb.EdgeGroup) 452 453 src := pes.Source.Ticket 454 key := newPageKey(src, len(pes.PageIndex)) 455 456 // Output the EdgePage and add it to the page indices 457 if err := b.OutputPage(ctx, &srvpb.EdgePage{ 458 PageKey: key, 459 SourceTicket: src, 460 EdgesGroup: eviction, 461 }); err != nil { 462 return fmt.Errorf("error emitting EdgePage: %v", err) 463 } 464 pes.PageIndex = append(pes.PageIndex, &srvpb.PageIndex{ 465 PageKey: key, 466 EdgeKind: eviction.Kind, 467 EdgeCount: int32(len(eviction.Edge)), 468 }) 469 return nil 470 }, 471 } 472 } 473 474 // StartEdgeSet begins a new EdgeSet for the given source node, possibly 475 // emitting a PagedEdgeSet for the previous EdgeSet. Each following call to 476 // AddGroup adds the group to this new EdgeSet until another call to 477 // StartEdgeSet is made. 478 func (b *EdgeSetBuilder) StartEdgeSet(ctx context.Context, src *srvpb.Node) error { 479 if b.pager == nil { 480 b.pager = b.constructPager() 481 } 482 return b.pager.StartSet(ctx, src) 483 } 484 485 // AddGroup adds a EdgeSet_Group to current EdgeSet being built, possibly 486 // emitting a new PagedEdgeSet and/or EdgePage. StartEdgeSet must be called 487 // before any calls to this method. See EdgeSetBuilder's documentation for the 488 // assumed order of the groups and this method's relation to StartEdgeSet. 489 func (b *EdgeSetBuilder) AddGroup(ctx context.Context, eg *srvpb.EdgeGroup) error { 490 return b.pager.AddGroup(ctx, eg) 491 } 492 493 // Flush signals the end of the current PagedEdgeSet being built, flushing it, 494 // and its EdgeSet_Groups to the output function. This should be called after 495 // the final call to AddGroup. Manually calling Flush at any other time is 496 // unnecessary. 497 func (b *EdgeSetBuilder) Flush(ctx context.Context) error { return b.pager.Flush(ctx) } 498 499 // CrossReferencesBuilder is a type wrapper around a pager.SetPager that emits 500 // *srvpb.PagedCrossReferences and *srvpb.PagedCrossReferences_Pages. Each 501 // PagedCrossReferences_Group added the builder should be in sorted order so 502 // that groups of the same kind are added sequentially. Before each set of 503 // like-kinded groups, StartSet should be called with the source ticket of the 504 // proceeding groups. See also EdgeSetBuilder. 505 type CrossReferencesBuilder struct { 506 MaxPageSize int 507 508 Output func(context.Context, *srvpb.PagedCrossReferences) error 509 OutputPage func(context.Context, *srvpb.PagedCrossReferences_Page) error 510 511 pager *pager.SetPager 512 } 513 514 func (b *CrossReferencesBuilder) constructPager() *pager.SetPager { 515 // Head: *srvpb.Node 516 // Set: *srvpb.PagedCrossReferences 517 // Group: *srvpb.PagedCrossReferences_Group 518 // Page: *srvpb.PagedCrossReferences_Page 519 return &pager.SetPager{ 520 MaxPageSize: b.MaxPageSize, 521 522 NewSet: func(hd pager.Head) pager.Set { 523 n := hd.(*srvpb.Node) 524 var incomplete bool 525 for _, f := range n.Fact { 526 if f.Name == facts.Complete && string(f.Value) != "definition" { 527 incomplete = true 528 } 529 } 530 return &srvpb.PagedCrossReferences{ 531 SourceTicket: n.Ticket, 532 Incomplete: incomplete, 533 } 534 }, 535 Combine: func(l, r pager.Group) pager.Group { 536 lg, rg := l.(*srvpb.PagedCrossReferences_Group), r.(*srvpb.PagedCrossReferences_Group) 537 if lg.Kind != rg.Kind { 538 return nil 539 } 540 lg.Anchor = append(lg.Anchor, rg.Anchor...) 541 return lg 542 }, 543 Split: func(sz int, g pager.Group) (l, r pager.Group) { 544 og := g.(*srvpb.PagedCrossReferences_Group) 545 ng := &srvpb.PagedCrossReferences_Group{ 546 Kind: og.Kind, 547 Anchor: og.Anchor[:sz], 548 } 549 og.Anchor = og.Anchor[sz:] 550 return ng, og 551 }, 552 Size: func(g pager.Group) int { return len(g.(*srvpb.PagedCrossReferences_Group).Anchor) }, 553 554 OutputSet: func(ctx context.Context, total int, s pager.Set, grps []pager.Group) error { 555 xs := s.(*srvpb.PagedCrossReferences) 556 557 // xs.Group = grps.([]*srvpb.PagedCrossReferences_Group) 558 xs.Group = make([]*srvpb.PagedCrossReferences_Group, len(grps)) 559 for i, g := range grps { 560 xs.Group[i] = g.(*srvpb.PagedCrossReferences_Group) 561 } 562 563 sort.Sort(byRefKind(xs.Group)) 564 sort.Sort(byRefPageKind(xs.PageIndex)) 565 xs.TotalReferences = int32(total) 566 567 return b.Output(ctx, xs) 568 }, 569 OutputPage: func(ctx context.Context, s pager.Set, g pager.Group) error { 570 xs, xg := s.(*srvpb.PagedCrossReferences), g.(*srvpb.PagedCrossReferences_Group) 571 572 key := newPageKey(xs.SourceTicket, len(xs.PageIndex)) 573 574 pg := &srvpb.PagedCrossReferences_Page{ 575 PageKey: key, 576 SourceTicket: xs.SourceTicket, 577 Group: xg, 578 } 579 xs.PageIndex = append(xs.PageIndex, &srvpb.PagedCrossReferences_PageIndex{ 580 PageKey: key, 581 Kind: xg.Kind, 582 Count: int32(len(xg.Anchor)), 583 }) 584 return b.OutputPage(ctx, pg) 585 }, 586 } 587 } 588 589 // StartSet begins a new *srvpb.PagedCrossReferences. As a side-effect, a 590 // previously-built srvpb.PagedCrossReferences may be emitted. 591 func (b *CrossReferencesBuilder) StartSet(ctx context.Context, src *srvpb.Node) error { 592 if b.pager == nil { 593 b.pager = b.constructPager() 594 } 595 return b.pager.StartSet(ctx, src) 596 } 597 598 // AddGroup add the given group of cross-references to the currently being built 599 // *srvpb.PagedCrossReferences. The group should share the same source ticket 600 // as given to the mostly recent invocation to StartSet. 601 func (b *CrossReferencesBuilder) AddGroup(ctx context.Context, g *srvpb.PagedCrossReferences_Group) error { 602 return b.pager.AddGroup(ctx, g) 603 } 604 605 // Flush emits any *srvpb.PagedCrossReferences and 606 // *srvpb.PagedCrossReferences_Page currently being built. 607 func (b *CrossReferencesBuilder) Flush(ctx context.Context) error { return b.pager.Flush(ctx) } 608 609 func newPageKey(src string, n int) string { return fmt.Sprintf("%s.%.10d", src, n) } 610 611 // CrossReference returns a (Referent, TargetAnchor) *ipb.CrossReference 612 // equivalent to the given decoration. The decoration's anchor is expanded 613 // given its parent file and associated Normalizer. 614 func CrossReference(file *srvpb.File, norm *span.Normalizer, d *srvpb.FileDecorations_Decoration, tgt *srvpb.Node) (*ipb.CrossReference, error) { 615 if file == nil || norm == nil { 616 return nil, errors.New("missing decoration's parent file") 617 } 618 619 ea, err := ExpandAnchor(d.Anchor, file, norm, edges.Mirror(d.Kind)) 620 if err != nil { 621 return nil, fmt.Errorf("error expanding anchor {%+v}: %v", d.Anchor, err) 622 } 623 // Throw away most of the referent's facts. They are not needed. 624 var selected []*cpb.Fact 625 if tgt != nil { 626 for _, fact := range tgt.Fact { 627 if fact.Name == facts.Complete { 628 selected = append(selected, fact) 629 } 630 } 631 } 632 return &ipb.CrossReference{ 633 Referent: &srvpb.Node{ 634 Ticket: d.Target, 635 Fact: selected, 636 }, 637 TargetAnchor: ea, 638 }, nil 639 } 640 641 // ExpandAnchor returns the ExpandedAnchor equivalent of the given RawAnchor 642 // where file (and its associated Normalizer) must be the anchor's parent file. 643 func ExpandAnchor(anchor *srvpb.RawAnchor, file *srvpb.File, norm *span.Normalizer, kind string) (*srvpb.ExpandedAnchor, error) { 644 if err := checkSpan(len(file.Text), anchor.StartOffset, anchor.EndOffset); err != nil { 645 return nil, fmt.Errorf("invalid text offsets: %v", err) 646 } 647 648 sp := norm.ByteOffset(anchor.StartOffset) 649 ep := norm.ByteOffset(anchor.EndOffset) 650 txt, err := getText(sp, ep, file) 651 if err != nil { 652 return nil, fmt.Errorf("error getting anchor text: %v", err) 653 } 654 655 var snippet string 656 var ssp, sep *cpb.Point 657 if anchor.SnippetStart != 0 || anchor.SnippetEnd != 0 { 658 if err := checkSpan(len(file.Text), anchor.SnippetStart, anchor.SnippetEnd); err != nil { 659 return nil, fmt.Errorf("invalid snippet offsets: %v", err) 660 } 661 662 ssp = norm.ByteOffset(anchor.SnippetStart) 663 sep = norm.ByteOffset(anchor.SnippetEnd) 664 snippet, err = getText(ssp, sep, file) 665 if err != nil { 666 return nil, fmt.Errorf("error getting text for snippet: %v", err) 667 } 668 } else { 669 // fallback to a line-based snippet if the indexer did not provide its own snippet offsets 670 ssp = &cpb.Point{ 671 ByteOffset: sp.ByteOffset - sp.ColumnOffset, 672 LineNumber: sp.LineNumber, 673 } 674 nextLine := norm.Point(&cpb.Point{LineNumber: sp.LineNumber + 1}) 675 if nextLine.ByteOffset <= ssp.ByteOffset { // double-check ssp != EOF 676 return nil, errors.New("anchor past EOF") 677 } 678 sep = &cpb.Point{ 679 ByteOffset: nextLine.ByteOffset - 1, 680 LineNumber: sp.LineNumber, 681 ColumnOffset: sp.ColumnOffset + (nextLine.ByteOffset - sp.ByteOffset - 1), 682 } 683 snippet, err = getText(ssp, sep, file) 684 if err != nil { 685 return nil, fmt.Errorf("error getting text for line snippet: %v", err) 686 } 687 } 688 689 return &srvpb.ExpandedAnchor{ 690 Ticket: anchor.Ticket, 691 Kind: kind, 692 693 Text: txt, 694 Span: &cpb.Span{ 695 Start: p2p(sp), 696 End: p2p(ep), 697 }, 698 699 Snippet: snippet, 700 SnippetSpan: &cpb.Span{ 701 Start: p2p(ssp), 702 End: p2p(sep), 703 }, 704 705 BuildConfiguration: anchor.BuildConfiguration, 706 }, nil 707 } 708 709 func checkSpan(textLen int, start, end int32) error { 710 if int(end) > textLen { 711 return fmt.Errorf("span past EOF %d: [%d, %d)", textLen, start, end) 712 } else if start < 0 { 713 return fmt.Errorf("negative span: [%d, %d)", start, end) 714 } else if start > end { 715 return fmt.Errorf("crossed span: [%d, %d)", start, end) 716 } 717 return nil 718 } 719 720 func getText(sp, ep *cpb.Point, file *srvpb.File) (string, error) { 721 txt, err := text.ToUTF8(file.Encoding, file.Text[sp.ByteOffset:ep.ByteOffset]) 722 if err != nil { 723 return "", fmt.Errorf("unable to decode file text: %v", err) 724 } 725 return txt, nil 726 } 727 728 func p2p(p *cpb.Point) *cpb.Point { 729 return &cpb.Point{ 730 ByteOffset: p.ByteOffset, 731 LineNumber: p.LineNumber, 732 ColumnOffset: p.ColumnOffset, 733 } 734 } 735 736 var edgeOrdering = []string{ 737 edges.Defines, 738 edges.Documents, 739 edges.Ref, 740 edges.Named, 741 edges.Typed, 742 } 743 744 func edgeKindLess(kind1, kind2 string) bool { 745 // General ordering: 746 // anchor edge kinds before non-anchor edge kinds 747 // forward edges before reverse edges 748 // edgeOrdering[i] (and variants) before edgeOrdering[i+1:] 749 // edge variants after root edge kind (ordered lexicographically) 750 // otherwise, order lexicographically 751 752 if kind1 == kind2 { 753 return false 754 } else if a1, a2 := edges.IsAnchorEdge(kind1), edges.IsAnchorEdge(kind2); a1 != a2 { 755 return a1 756 } else if d1, d2 := edges.IsForward(kind1), edges.IsForward(kind2); d1 != d2 { 757 return d1 758 } 759 kind1, kind2 = edges.Canonical(kind1), edges.Canonical(kind2) 760 for _, kind := range edgeOrdering { 761 if kind1 == kind { 762 return true 763 } else if kind2 == kind { 764 return false 765 } else if v1, v2 := edges.IsVariant(kind1, kind), edges.IsVariant(kind2, kind); v1 != v2 { 766 return v1 767 } else if v1 { 768 return kind1 < kind2 769 } 770 } 771 return kind1 < kind2 772 } 773 774 // byPageKind implements the sort.Interface 775 type byPageKind []*srvpb.PageIndex 776 777 // Implement the sort.Interface using edgeKindLess 778 func (s byPageKind) Len() int { return len(s) } 779 func (s byPageKind) Less(i, j int) bool { return edgeKindLess(s[i].EdgeKind, s[j].EdgeKind) } 780 func (s byPageKind) Swap(i, j int) { s[i], s[j] = s[j], s[i] } 781 782 // byEdgeKind implements the sort.Interface 783 type byEdgeKind []*srvpb.EdgeGroup 784 785 // Implement the sort.Interface using edgeKindLess 786 func (s byEdgeKind) Len() int { return len(s) } 787 func (s byEdgeKind) Less(i, j int) bool { return edgeKindLess(s[i].Kind, s[j].Kind) } 788 func (s byEdgeKind) Swap(i, j int) { s[i], s[j] = s[j], s[i] } 789 790 // byRefPageKind implements the sort.Interface 791 type byRefPageKind []*srvpb.PagedCrossReferences_PageIndex 792 793 // Implement the sort.Interface using edgeKindLess 794 func (s byRefPageKind) Len() int { return len(s) } 795 func (s byRefPageKind) Less(i, j int) bool { return edgeKindLess(s[i].Kind, s[j].Kind) } 796 func (s byRefPageKind) Swap(i, j int) { s[i], s[j] = s[j], s[i] } 797 798 // byRefKind implements the sort.Interface 799 type byRefKind []*srvpb.PagedCrossReferences_Group 800 801 // Implement the sort.Interface using edgeKindLess 802 func (s byRefKind) Len() int { return len(s) } 803 func (s byRefKind) Less(i, j int) bool { return edgeKindLess(s[i].Kind, s[j].Kind) } 804 func (s byRefKind) Swap(i, j int) { s[i], s[j] = s[j], s[i] } 805 806 // byOrdinal sorts edges by their ordinals 807 type byOrdinal []*ipb.Source_Edge 808 809 func (s byOrdinal) Len() int { return len(s) } 810 func (s byOrdinal) Swap(i, j int) { s[i], s[j] = s[j], s[i] } 811 func (s byOrdinal) Less(i, j int) bool { 812 if s[i].Ordinal == s[j].Ordinal { 813 return s[i].Ticket < s[j].Ticket 814 } 815 return s[i].Ordinal < s[j].Ordinal 816 }