]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blob - vendor/github.com/hashicorp/hcl2/hcl/diagnostic_text.go
Merge pull request #27 from terraform-providers/go-modules-2019-02-22
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / hashicorp / hcl2 / hcl / diagnostic_text.go
1 package hcl
2
3 import (
4 "bufio"
5 "errors"
6 "fmt"
7 "io"
8
9 wordwrap "github.com/mitchellh/go-wordwrap"
10 )
11
12 type diagnosticTextWriter struct {
13 files map[string]*File
14 wr io.Writer
15 width uint
16 color bool
17 }
18
19 // NewDiagnosticTextWriter creates a DiagnosticWriter that writes diagnostics
20 // to the given writer as formatted text.
21 //
22 // It is designed to produce text appropriate to print in a monospaced font
23 // in a terminal of a particular width, or optionally with no width limit.
24 //
25 // The given width may be zero to disable word-wrapping of the detail text
26 // and truncation of source code snippets.
27 //
28 // If color is set to true, the output will include VT100 escape sequences to
29 // color-code the severity indicators. It is suggested to turn this off if
30 // the target writer is not a terminal.
31 func NewDiagnosticTextWriter(wr io.Writer, files map[string]*File, width uint, color bool) DiagnosticWriter {
32 return &diagnosticTextWriter{
33 files: files,
34 wr: wr,
35 width: width,
36 color: color,
37 }
38 }
39
40 func (w *diagnosticTextWriter) WriteDiagnostic(diag *Diagnostic) error {
41 if diag == nil {
42 return errors.New("nil diagnostic")
43 }
44
45 var colorCode, highlightCode, resetCode string
46 if w.color {
47 switch diag.Severity {
48 case DiagError:
49 colorCode = "\x1b[31m"
50 case DiagWarning:
51 colorCode = "\x1b[33m"
52 }
53 resetCode = "\x1b[0m"
54 highlightCode = "\x1b[1;4m"
55 }
56
57 var severityStr string
58 switch diag.Severity {
59 case DiagError:
60 severityStr = "Error"
61 case DiagWarning:
62 severityStr = "Warning"
63 default:
64 // should never happen
65 severityStr = "???????"
66 }
67
68 fmt.Fprintf(w.wr, "%s%s%s: %s\n\n", colorCode, severityStr, resetCode, diag.Summary)
69
70 if diag.Subject != nil {
71 snipRange := *diag.Subject
72 highlightRange := snipRange
73 if diag.Context != nil {
74 // Show enough of the source code to include both the subject
75 // and context ranges, which overlap in all reasonable
76 // situations.
77 snipRange = RangeOver(snipRange, *diag.Context)
78 }
79 // We can't illustrate an empty range, so we'll turn such ranges into
80 // single-character ranges, which might not be totally valid (may point
81 // off the end of a line, or off the end of the file) but are good
82 // enough for the bounds checks we do below.
83 if snipRange.Empty() {
84 snipRange.End.Byte++
85 snipRange.End.Column++
86 }
87 if highlightRange.Empty() {
88 highlightRange.End.Byte++
89 highlightRange.End.Column++
90 }
91
92 file := w.files[diag.Subject.Filename]
93 if file == nil || file.Bytes == nil {
94 fmt.Fprintf(w.wr, " on %s line %d:\n (source code not available)\n\n", diag.Subject.Filename, diag.Subject.Start.Line)
95 } else {
96
97 var contextLine string
98 if diag.Subject != nil {
99 contextLine = contextString(file, diag.Subject.Start.Byte)
100 if contextLine != "" {
101 contextLine = ", in " + contextLine
102 }
103 }
104
105 fmt.Fprintf(w.wr, " on %s line %d%s:\n", diag.Subject.Filename, diag.Subject.Start.Line, contextLine)
106
107 src := file.Bytes
108 sc := NewRangeScanner(src, diag.Subject.Filename, bufio.ScanLines)
109
110 for sc.Scan() {
111 lineRange := sc.Range()
112 if !lineRange.Overlaps(snipRange) {
113 continue
114 }
115
116 beforeRange, highlightedRange, afterRange := lineRange.PartitionAround(highlightRange)
117 if highlightedRange.Empty() {
118 fmt.Fprintf(w.wr, "%4d: %s\n", lineRange.Start.Line, sc.Bytes())
119 } else {
120 before := beforeRange.SliceBytes(src)
121 highlighted := highlightedRange.SliceBytes(src)
122 after := afterRange.SliceBytes(src)
123 fmt.Fprintf(
124 w.wr, "%4d: %s%s%s%s%s\n",
125 lineRange.Start.Line,
126 before,
127 highlightCode, highlighted, resetCode,
128 after,
129 )
130 }
131
132 }
133
134 w.wr.Write([]byte{'\n'})
135 }
136 }
137
138 if diag.Detail != "" {
139 detail := diag.Detail
140 if w.width != 0 {
141 detail = wordwrap.WrapString(detail, w.width)
142 }
143 fmt.Fprintf(w.wr, "%s\n\n", detail)
144 }
145
146 return nil
147 }
148
149 func (w *diagnosticTextWriter) WriteDiagnostics(diags Diagnostics) error {
150 for _, diag := range diags {
151 err := w.WriteDiagnostic(diag)
152 if err != nil {
153 return err
154 }
155 }
156 return nil
157 }
158
159 func contextString(file *File, offset int) string {
160 type contextStringer interface {
161 ContextString(offset int) string
162 }
163
164 if cser, ok := file.Nav.(contextStringer); ok {
165 return cser.ContextString(offset)
166 }
167 return ""
168 }