kythe.io@v0.0.68-0.20240422202219-7225dbc01741/kythe/go/services/cli/command_source.go (about) 1 /* 2 * Copyright 2017 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 cli 18 19 import ( 20 "context" 21 "errors" 22 "flag" 23 "fmt" 24 "regexp" 25 "strconv" 26 "strings" 27 28 cpb "kythe.io/kythe/proto/common_go_proto" 29 xpb "kythe.io/kythe/proto/xref_go_proto" 30 ) 31 32 var ( 33 byteOffsetPointRE = regexp.MustCompile(`^b(\d+)$`) 34 lineNumberPointRE = regexp.MustCompile(`^(\d+)(:(\d+))?$`) 35 ) 36 37 func parsePoint(p string) (*cpb.Point, error) { 38 if m := byteOffsetPointRE.FindStringSubmatch(p); m != nil { 39 offset, err := strconv.Atoi(m[1]) 40 if err != nil { 41 return nil, fmt.Errorf("invalid byte-offset: %v", err) 42 } 43 return &cpb.Point{ByteOffset: int32(offset)}, nil 44 } else if m := lineNumberPointRE.FindStringSubmatch(p); m != nil { 45 line, err := strconv.Atoi(m[1]) 46 if err != nil { 47 return nil, fmt.Errorf("invalid line number: %v", err) 48 } 49 np := &cpb.Point{LineNumber: int32(line)} 50 if m[3] != "" { 51 col, err := strconv.Atoi(m[3]) 52 if err != nil { 53 return nil, fmt.Errorf("invalid column offset: %v", err) 54 } 55 np.ColumnOffset = int32(col) 56 } 57 return np, nil 58 } 59 return nil, fmt.Errorf("unknown format %q", p) 60 } 61 62 func parseLine(p string) (*cpb.Span, error) { 63 m := lineNumberPointRE.FindStringSubmatch(p) 64 if m == nil { 65 return nil, errors.New("unknown format") 66 } 67 line, err := strconv.Atoi(m[1]) 68 if err != nil { 69 return nil, fmt.Errorf("invalid line number: %v", err) 70 } 71 start := &cpb.Point{LineNumber: int32(line)} 72 if m[3] != "" { 73 col, err := strconv.Atoi(m[3]) 74 if err != nil { 75 return nil, fmt.Errorf("invalid column offset: %v", err) 76 } 77 start.ColumnOffset = int32(col) 78 } 79 return &cpb.Span{ 80 Start: start, 81 End: &cpb.Point{LineNumber: start.LineNumber + 1}, 82 }, nil 83 } 84 85 func parseSpan(span string) (*cpb.Span, error) { 86 parts := strings.Split(span, "-") 87 if len(parts) == 1 { 88 return parseLine(parts[0]) 89 } else if len(parts) != 2 { 90 return nil, errors.New("unknown format") 91 } 92 start, err := parsePoint(parts[0]) 93 if err != nil { 94 return nil, fmt.Errorf("invalid start: %v", err) 95 } 96 end, err := parsePoint(parts[1]) 97 if err != nil { 98 return nil, fmt.Errorf("invalid end: %v", err) 99 } 100 return &cpb.Span{ 101 Start: start, 102 End: end, 103 }, nil 104 } 105 106 type sourceCommand struct { 107 baseDecorCommand 108 } 109 110 func (sourceCommand) Name() string { return "source" } 111 func (sourceCommand) Synopsis() string { return "retrieve a file's source text" } 112 func (sourceCommand) Usage() string { return "" } 113 func (c *sourceCommand) SetFlags(flag *flag.FlagSet) { 114 c.baseDecorCommand.SetFlags(flag) 115 } 116 func (c sourceCommand) Run(ctx context.Context, flag *flag.FlagSet, api API) error { 117 req, err := c.baseRequest(flag) 118 if err != nil { 119 return err 120 } 121 req.SourceText = true 122 123 LogRequest(req) 124 reply, err := api.XRefService.Decorations(ctx, req) 125 if err != nil { 126 return err 127 } 128 return displaySource(reply) 129 } 130 131 func displaySource(decor *xpb.DecorationsReply) error { 132 if DisplayJSON { 133 return PrintJSONMessage(decor) 134 } 135 136 _, err := out.Write(decor.SourceText) 137 return err 138 }