github.com/elek/golangci-lint@v1.42.2-0.20211208090441-c05b7fcb3a9a/pkg/fsutils/linecache.go (about)

     1  package fsutils
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"sync"
     7  
     8  	"github.com/pkg/errors"
     9  )
    10  
    11  type fileLinesCache [][]byte
    12  
    13  type LineCache struct {
    14  	files     sync.Map
    15  	fileCache *FileCache
    16  }
    17  
    18  func NewLineCache(fc *FileCache) *LineCache {
    19  	return &LineCache{
    20  		fileCache: fc,
    21  	}
    22  }
    23  
    24  // GetLine returns a index1-th (1-based index) line from the file on filePath
    25  func (lc *LineCache) GetLine(filePath string, index1 int) (string, error) {
    26  	if index1 == 0 { // some linters, e.g. gosec can do it: it really means first line
    27  		index1 = 1
    28  	}
    29  
    30  	const index1To0Offset = -1
    31  	rawLine, err := lc.getRawLine(filePath, index1+index1To0Offset)
    32  	if err != nil {
    33  		return "", err
    34  	}
    35  
    36  	return string(bytes.Trim(rawLine, "\r")), nil
    37  }
    38  
    39  func (lc *LineCache) getRawLine(filePath string, index0 int) ([]byte, error) {
    40  	fc, err := lc.getFileCache(filePath)
    41  	if err != nil {
    42  		return nil, errors.Wrapf(err, "failed to get file %s lines cache", filePath)
    43  	}
    44  
    45  	if index0 < 0 {
    46  		return nil, fmt.Errorf("invalid file line index0 < 0: %d", index0)
    47  	}
    48  
    49  	if index0 >= len(fc) {
    50  		return nil, fmt.Errorf("invalid file line index0 (%d) >= len(fc) (%d)", index0, len(fc))
    51  	}
    52  
    53  	return fc[index0], nil
    54  }
    55  
    56  func (lc *LineCache) getFileCache(filePath string) (fileLinesCache, error) {
    57  	loadedFc, ok := lc.files.Load(filePath)
    58  	if ok {
    59  		return loadedFc.(fileLinesCache), nil
    60  	}
    61  
    62  	fileBytes, err := lc.fileCache.GetFileBytes(filePath)
    63  	if err != nil {
    64  		return nil, errors.Wrapf(err, "can't get file %s bytes from cache", filePath)
    65  	}
    66  
    67  	fc := bytes.Split(fileBytes, []byte("\n"))
    68  	lc.files.Store(filePath, fileLinesCache(fc))
    69  	return fc, nil
    70  }