// addFile adds a file to the index if possible and returns the file set file // and the file's AST if it was successfully parsed as a Go file. If addFile // failed (that is, if the file was not added), it returns file == nil. func (x *Indexer) addFile(filename string, goFile bool) (file *token.File, ast *ast.File) { // open file f, err := fs.Open(filename) if err != nil { return } defer f.Close() // The file set's base offset and x.sources size must be in lock-step; // this permits the direct mapping of suffix array lookup results to // to corresponding Pos values. // // When a file is added to the file set, its offset base increases by // the size of the file + 1; and the initial base offset is 1. Add an // extra byte to the sources here. x.sources.WriteByte(0) // If the sources length doesn't match the file set base at this point // the file set implementation changed or we have another error. base := x.fset.Base() if x.sources.Len() != base { panic("internal error: file base incorrect") } // append file contents (src) to x.sources if _, err := x.sources.ReadFrom(f); err == nil { src := x.sources.Bytes()[base:] if goFile { // parse the file and in the process add it to the file set if ast, err = parser.ParseFile(x.fset, filename, src, parser.ParseComments); err == nil { file = x.fset.File(ast.Pos()) // ast.Pos() is inside the file return } // file has parse errors, and the AST may be incorrect - // set lines information explicitly and index as ordinary // text file (cannot fall through to the text case below // because the file has already been added to the file set // by the parser) file = x.fset.File(token.Pos(base)) // token.Pos(base) is inside the file file.SetLinesForContent(src) ast = nil return } if isText(src) { // only add the file to the file set (for the full text index) file = x.fset.AddFile(filename, x.fset.Base(), len(src)) file.SetLinesForContent(src) return } } // discard possibly added data x.sources.Truncate(base - 1) // -1 to remove added byte 0 since no file was added return }
func (s *BranchStmt) End() token.Pos { if s.Label != nil { return s.Label.End() } return token.Pos(int(s.TokPos) + len(s.Tok.String())) }
func (c *Comment) End() token.Pos { return token.Pos(int(c.Slash) + len(c.Text)) }
func (x *BasicLit) End() token.Pos { return token.Pos(int(x.ValuePos) + len(x.Value)) }
func (x *Ident) End() token.Pos { return token.Pos(int(x.NamePos) + len(x.Name)) }
// LookupRegexp returns the number of matches and the matches where a regular // expression r is found in the full text index. At most n matches are // returned (thus found <= n). // func (x *Index) LookupRegexp(r *regexp.Regexp, n int) (found int, result []FileLines) { if x.suffixes == nil || n <= 0 { return } // n > 0 var list positionList // FindAllIndex may returns matches that span across file boundaries. // Such matches are unlikely, buf after eliminating them we may end up // with fewer than n matches. If we don't have enough at the end, redo // the search with an increased value n1, but only if FindAllIndex // returned all the requested matches in the first place (if it // returned fewer than that there cannot be more). for n1 := n; found < n; n1 += n - found { found = 0 matches := x.suffixes.FindAllIndex(r, n1) // compute files, exclude matches that span file boundaries, // and map offsets to file-local offsets list = make(positionList, len(matches)) for _, m := range matches { // by construction, an offset corresponds to the Pos value // for the file set - use it to get the file and line p := token.Pos(m[0]) if file := x.fset.File(p); file != nil { if base := file.Base(); base <= m[1] && m[1] <= base+file.Size() { // match [m[0], m[1]) is within the file boundaries list[found].filename = file.Name() list[found].line = file.Line(p) found++ } } } if found == n || len(matches) < n1 { // found all matches or there's no chance to find more break } } list = list[0:found] sort.Sort(list) // sort by filename // collect matches belonging to the same file var last string var lines []int addLines := func() { if len(lines) > 0 { // remove duplicate lines result = append(result, FileLines{last, unique(lines)}) lines = nil } } for _, m := range list { if m.filename != last { addLines() last = m.filename } lines = append(lines, m.line) } addLines() return }