github.com/linchen2chris/hugo@v0.0.0-20230307053224-cec209389705/common/text/position.go (about) 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 returns the current position. 28 // Useful in error logging, e.g. {{ errorf "error in code block: %s" .Position }}. 29 Position() Position 30 } 31 32 // Position holds a source position in a text file or stream. 33 type Position struct { 34 Filename string // filename, if any 35 Offset int // byte offset, starting at 0. It's set to -1 if not provided. 36 LineNumber int // line number, starting at 1 37 ColumnNumber int // column number, starting at 1 (character count per line) 38 } 39 40 func (pos Position) String() string { 41 if pos.Filename == "" { 42 pos.Filename = "<stream>" 43 } 44 return positionStringFormatfunc(pos) 45 } 46 47 // IsValid returns true if line number is > 0. 48 func (pos Position) IsValid() bool { 49 return pos.LineNumber > 0 50 } 51 52 var positionStringFormatfunc func(p Position) string 53 54 func createPositionStringFormatter(formatStr string) func(p Position) string { 55 if formatStr == "" { 56 formatStr = "\":file::line::col\"" 57 } 58 59 identifiers := []string{":file", ":line", ":col"} 60 var identifiersFound []string 61 62 for i := range formatStr { 63 for _, id := range identifiers { 64 if strings.HasPrefix(formatStr[i:], id) { 65 identifiersFound = append(identifiersFound, id) 66 } 67 } 68 } 69 70 replacer := strings.NewReplacer(":file", "%s", ":line", "%d", ":col", "%d") 71 format := replacer.Replace(formatStr) 72 73 f := func(pos Position) string { 74 args := make([]any, len(identifiersFound)) 75 for i, id := range identifiersFound { 76 switch id { 77 case ":file": 78 args[i] = pos.Filename 79 case ":line": 80 args[i] = pos.LineNumber 81 case ":col": 82 args[i] = pos.ColumnNumber 83 } 84 } 85 86 msg := fmt.Sprintf(format, args...) 87 88 if terminal.PrintANSIColors(os.Stdout) { 89 return terminal.Notice(msg) 90 } 91 92 return msg 93 } 94 95 return f 96 } 97 98 func init() { 99 positionStringFormatfunc = createPositionStringFormatter(os.Getenv("HUGO_FILE_LOG_FORMAT")) 100 }