position.go (2562B)
1 // Copyright 2018 The Hugo Authors. All rights reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 // http://www.apache.org/licenses/LICENSE-2.0
7 //
8 // Unless required by applicable law or agreed to in writing, software
9 // distributed under the License is distributed on an "AS IS" BASIS,
10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 // See the License for the specific language governing permissions and
12 // limitations under the License.
13
14 package text
15
16 import (
17 "fmt"
18 "os"
19 "strings"
20
21 "github.com/gohugoio/hugo/common/terminal"
22 )
23
24 // Positioner represents a thing that knows its position in a text file or stream,
25 // typically an error.
26 type Positioner interface {
27 Position() Position
28 }
29
30 // Position holds a source position in a text file or stream.
31 type Position struct {
32 Filename string // filename, if any
33 Offset int // byte offset, starting at 0. It's set to -1 if not provided.
34 LineNumber int // line number, starting at 1
35 ColumnNumber int // column number, starting at 1 (character count per line)
36 }
37
38 func (pos Position) String() string {
39 if pos.Filename == "" {
40 pos.Filename = "<stream>"
41 }
42 return positionStringFormatfunc(pos)
43 }
44
45 // IsValid returns true if line number is > 0.
46 func (pos Position) IsValid() bool {
47 return pos.LineNumber > 0
48 }
49
50 var positionStringFormatfunc func(p Position) string
51
52 func createPositionStringFormatter(formatStr string) func(p Position) string {
53 if formatStr == "" {
54 formatStr = "\":file::line::col\""
55 }
56
57 identifiers := []string{":file", ":line", ":col"}
58 var identifiersFound []string
59
60 for i := range formatStr {
61 for _, id := range identifiers {
62 if strings.HasPrefix(formatStr[i:], id) {
63 identifiersFound = append(identifiersFound, id)
64 }
65 }
66 }
67
68 replacer := strings.NewReplacer(":file", "%s", ":line", "%d", ":col", "%d")
69 format := replacer.Replace(formatStr)
70
71 f := func(pos Position) string {
72 args := make([]any, len(identifiersFound))
73 for i, id := range identifiersFound {
74 switch id {
75 case ":file":
76 args[i] = pos.Filename
77 case ":line":
78 args[i] = pos.LineNumber
79 case ":col":
80 args[i] = pos.ColumnNumber
81 }
82 }
83
84 msg := fmt.Sprintf(format, args...)
85
86 if terminal.PrintANSIColors(os.Stdout) {
87 return terminal.Notice(msg)
88 }
89
90 return msg
91 }
92
93 return f
94 }
95
96 func init() {
97 positionStringFormatfunc = createPositionStringFormatter(os.Getenv("HUGO_FILE_LOG_FORMAT"))
98 }