func expandAnchor(anchor *srvpb.RawAnchor, file *srvpb.File, norm *xrefs.Normalizer, kind string) (*srvpb.ExpandedAnchor, error) { sp := norm.ByteOffset(anchor.StartOffset) ep := norm.ByteOffset(anchor.EndOffset) txt, err := getText(sp, ep, file) if err != nil { return nil, fmt.Errorf("error getting anchor text: %v", err) } ssp := norm.ByteOffset(anchor.SnippetStart) sep := norm.ByteOffset(anchor.SnippetEnd) snippet, err := getText(ssp, sep, file) if err != nil { return nil, fmt.Errorf("error getting text for snippet: %v", err) } // fallback to a line-based snippet if the indexer did not provide its own snippet offsets if snippet == "" { ssp = &xpb.Location_Point{ ByteOffset: sp.ByteOffset - sp.ColumnOffset, LineNumber: sp.LineNumber, } nextLine := norm.Point(&xpb.Location_Point{LineNumber: sp.LineNumber + 1}) sep = &xpb.Location_Point{ ByteOffset: nextLine.ByteOffset - 1, LineNumber: sp.LineNumber, ColumnOffset: sp.ColumnOffset + (nextLine.ByteOffset - sp.ByteOffset - 1), } snippet, err = getText(ssp, sep, file) if err != nil { return nil, fmt.Errorf("error getting text for line snippet: %v", err) } } return &srvpb.ExpandedAnchor{ Ticket: anchor.Ticket, Kind: kind, Parent: file.Ticket, Text: txt, Span: &cpb.Span{ Start: p2p(sp), End: p2p(ep), }, Snippet: snippet, SnippetSpan: &cpb.Span{ Start: p2p(ssp), End: p2p(sep), }, }, nil }
func (d *DB) scanReferences(fileTicket string, norm *xrefs.Normalizer) ([]*xpb.DecorationsReply_Reference, error) { rs, err := d.Query("SELECT anchor_ticket, kind, target_ticket, start_offset, end_offset FROM Decorations WHERE file_ticket = $1 ORDER BY start_offset, end_offset;", fileTicket) if err != nil { return nil, fmt.Errorf("error retrieving decorations: %v", err) } defer closeRows(rs) var references []*xpb.DecorationsReply_Reference for rs.Next() { r := &xpb.DecorationsReply_Reference{ AnchorStart: &xpb.Location_Point{}, AnchorEnd: &xpb.Location_Point{}, } if err := rs.Scan(&r.SourceTicket, &r.Kind, &r.TargetTicket, &r.AnchorStart.ByteOffset, &r.AnchorEnd.ByteOffset); err != nil { return nil, fmt.Errorf("sql scan error: %v", err) } r.AnchorStart = norm.Point(r.AnchorStart) r.AnchorEnd = norm.Point(r.AnchorEnd) references = append(references, r) } return references, nil }
// ExpandAnchor returns the ExpandedAnchor equivalent of the given RawAnchor // where file (and its associated Normalizer) must be the anchor's parent file. func ExpandAnchor(anchor *srvpb.RawAnchor, file *srvpb.File, norm *xrefs.Normalizer, kind string) (*srvpb.ExpandedAnchor, error) { if err := checkSpan(len(file.Text), anchor.StartOffset, anchor.EndOffset); err != nil { return nil, fmt.Errorf("invalid text offsets: %v", err) } sp := norm.ByteOffset(anchor.StartOffset) ep := norm.ByteOffset(anchor.EndOffset) txt, err := getText(sp, ep, file) if err != nil { return nil, fmt.Errorf("error getting anchor text: %v", err) } var snippet string var ssp, sep *xpb.Location_Point if anchor.SnippetStart != 0 || anchor.SnippetEnd != 0 { if err := checkSpan(len(file.Text), anchor.SnippetStart, anchor.SnippetEnd); err != nil { return nil, fmt.Errorf("invalid snippet offsets: %v", err) } ssp = norm.ByteOffset(anchor.SnippetStart) sep = norm.ByteOffset(anchor.SnippetEnd) snippet, err = getText(ssp, sep, file) if err != nil { return nil, fmt.Errorf("error getting text for snippet: %v", err) } } else { // fallback to a line-based snippet if the indexer did not provide its own snippet offsets ssp = &xpb.Location_Point{ ByteOffset: sp.ByteOffset - sp.ColumnOffset, LineNumber: sp.LineNumber, } nextLine := norm.Point(&xpb.Location_Point{LineNumber: sp.LineNumber + 1}) if nextLine.ByteOffset <= ssp.ByteOffset { // double-check ssp != EOF return nil, errors.New("anchor past EOF") } sep = &xpb.Location_Point{ ByteOffset: nextLine.ByteOffset - 1, LineNumber: sp.LineNumber, ColumnOffset: sp.ColumnOffset + (nextLine.ByteOffset - sp.ByteOffset - 1), } snippet, err = getText(ssp, sep, file) if err != nil { return nil, fmt.Errorf("error getting text for line snippet: %v", err) } } return &srvpb.ExpandedAnchor{ Ticket: anchor.Ticket, Kind: kind, Parent: file.Ticket, Text: txt, Span: &cpb.Span{ Start: p2p(sp), End: p2p(ep), }, Snippet: snippet, SnippetSpan: &cpb.Span{ Start: p2p(ssp), End: p2p(sep), }, }, nil }