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 }