// Go support for Protocol Buffers - Google's data interchange format // // Copyright 2017 The Go Authors. All rights reserved. // https://github.com/golang/protobuf // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. /* Package remap handles tracking the locations of Go tokens in a source text across a rewrite by the Go formatter. */ package remap import ( "fmt" "go/scanner" "go/token" ) // A Location represents a span of byte offsets in the source text. type Location struct { Pos, End int // End is exclusive } // A Map represents a mapping between token locations in an input source text // and locations in the correspnding output text. type Map map[Location]Location // Find reports whether the specified span is recorded by m, and if so returns // the new location it was mapped to. If the input span was not found, the // returned location is the same as the input. func (m Map) Find(pos, end int) (Location, bool) { key := Location{ Pos: pos, End: end, } if loc, ok := m[key]; ok { return loc, true } return key, false } func (m Map) add(opos, oend, npos, nend int) { m[Location{Pos: opos, End: oend}] = Location{Pos: npos, End: nend} } // Compute constructs a location mapping from input to output. An error is // reported if any of the tokens of output cannot be mapped. func Compute(input, output []byte) (Map, error) { itok := tokenize(input) otok := tokenize(output) if len(itok) != len(otok) { return nil, fmt.Errorf("wrong number of tokens, %d ≠ %d", len(itok), len(otok)) } m := make(Map) for i, ti := range itok { to := otok[i] if ti.Token != to.Token { return nil, fmt.Errorf("token %d type mismatch: %s ≠ %s", i+1, ti, to) } m.add(ti.pos, ti.end, to.pos, to.end) } return m, nil } // tokinfo records the span and type of a source token. type tokinfo struct { pos, end int token.Token } func tokenize(src []byte) []tokinfo { fs := token.NewFileSet() var s scanner.Scanner s.Init(fs.AddFile("src", fs.Base(), len(src)), src, nil, scanner.ScanComments) var info []tokinfo for { pos, next, lit := s.Scan() switch next { case token.SEMICOLON: continue } info = append(info, tokinfo{ pos: int(pos - 1), end: int(pos + token.Pos(len(lit)) - 1), Token: next, }) if next == token.EOF { break } } return info }