blob: d963897390291806ec76a770ade820d731c3c3c2 [file]
// Copyright 2025 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package xeddata
import (
"bufio"
"bytes"
"fmt"
"io"
"iter"
"path/filepath"
"strings"
)
type lineInfo struct {
Pos
data []byte
}
type Pos struct {
Path string
Line int
}
func (p Pos) String() string {
if p.Line == 0 {
if p.Path == "" {
return "?:?"
}
return p.Path
} else if p.Path == "" {
return fmt.Sprintf("?:%d", p.Line)
}
return fmt.Sprintf("%s:%d", p.Path, p.Line)
}
func (p Pos) ShortString() string {
p2 := p
p2.Path = filepath.Base(p.Path)
return p2.String()
}
// readLines yields lines from r, with continuation lines folded, comments and
// trailing whitespace removed, and blank lines omitted.
//
// The returned lineInfo.data buffer may be reused between yields.
//
// If r has a Name() string method, this is used to populate lineInfo.Path.
func readLines(r io.Reader) iter.Seq2[lineInfo, error] {
type Named interface {
Name() string // Matches os.File
}
path := ""
if f, ok := r.(Named); ok {
path = f.Name()
}
s := bufio.NewScanner(r)
return func(yield func(lineInfo, error) bool) {
var info lineInfo
info.Path = path
var lineBuf []byte
for s.Scan() {
info.Line++
lineBuf = append(lineBuf, s.Bytes()...)
if len(lineBuf) > 0 && lineBuf[len(lineBuf)-1] == '\\' {
// Continuation line. Drop the \ and keep reading.
lineBuf = lineBuf[:len(lineBuf)-1]
continue
}
// Remove comments and trailing whitespace
if i := strings.IndexByte(string(lineBuf), '#'); i >= 0 {
lineBuf = lineBuf[:i]
}
lineBuf = bytes.TrimRight(lineBuf, " \t")
// Don't yield blank lines
if len(lineBuf) == 0 {
continue
}
info.data = lineBuf
if !yield(info, nil) {
return
}
lineBuf = lineBuf[:0]
}
if err := s.Err(); err != nil {
yield(lineInfo{}, err)
return
}
if len(lineBuf) > 0 {
yield(lineInfo{}, fmt.Errorf("continuation line at EOF"))
}
}
}