hcl: Simplify the text DiagnosticWriter using new Range functions

Now that we have helper methods for computing relationships between
ranges, we can eliminate all of the tricky line-counting and byte-counting
code here and instead use the higher-level operations.

The result is a single loop using the RangeScanner.
This commit is contained in:
Martin Atkins 2018-01-14 12:07:33 -08:00
parent 368a3f81c0
commit 14cfe59a52
2 changed files with 20 additions and 38 deletions

View File

@ -2,7 +2,6 @@ package hcl
import ( import (
"bufio" "bufio"
"bytes"
"errors" "errors"
"fmt" "fmt"
"io" "io"
@ -70,21 +69,15 @@ func (w *diagnosticTextWriter) WriteDiagnostic(diag *Diagnostic) error {
if diag.Subject != nil { if diag.Subject != nil {
file := w.files[diag.Subject.Filename] file := w.files[diag.Subject.Filename]
if file == nil || file.Bytes == nil { if file == nil || file.Bytes == nil || diag.Subject.Empty() {
fmt.Fprintf(w.wr, " on %s line %d:\n (source code not available)\n\n", diag.Subject.Filename, diag.Subject.Start.Line) fmt.Fprintf(w.wr, " on %s line %d:\n (source code not available)\n\n", diag.Subject.Filename, diag.Subject.Start.Line)
} else { } else {
src := file.Bytes snipRange := *diag.Subject
r := bytes.NewReader(src)
sc := bufio.NewScanner(r)
sc.Split(bufio.ScanLines)
var startLine, endLine int
if diag.Context != nil { if diag.Context != nil {
startLine = diag.Context.Start.Line // Show enough of the source code to include both the subject
endLine = diag.Context.End.Line // and context ranges, which overlap in all reasonable
} else { // situations.
startLine = diag.Subject.Start.Line snipRange = RangeOver(snipRange, *diag.Context)
endLine = diag.Subject.End.Line
} }
var contextLine string var contextLine string
@ -95,35 +88,18 @@ func (w *diagnosticTextWriter) WriteDiagnostic(diag *Diagnostic) error {
} }
} }
li := 1
var ls string
for sc.Scan() {
ls = sc.Text()
if li == startLine {
break
}
li++
}
fmt.Fprintf(w.wr, " on %s line %d%s:\n", diag.Subject.Filename, diag.Subject.Start.Line, contextLine) fmt.Fprintf(w.wr, " on %s line %d%s:\n", diag.Subject.Filename, diag.Subject.Start.Line, contextLine)
// TODO: Generate markers for the specific characters that are in the Context and Subject ranges. src := file.Bytes
// For now, we just print out the lines. sc := NewRangeScanner(src, diag.Subject.Filename, bufio.ScanLines)
fmt.Fprintf(w.wr, "%4d: %s\n", li, ls) for sc.Scan() {
lineRange := sc.Range()
if endLine > li { if !lineRange.Overlaps(snipRange) {
for sc.Scan() { continue
ls = sc.Text()
li++
fmt.Fprintf(w.wr, "%4d: %s\n", li, ls)
if li == endLine {
break
}
} }
fmt.Fprintf(w.wr, "%4d: %s\n", lineRange.Start.Line, sc.Bytes())
} }
w.wr.Write([]byte{'\n'}) w.wr.Write([]byte{'\n'})

View File

@ -45,10 +45,12 @@ All splines must be pre-reticulated.
Detail: `"baz" is not a supported top-level attribute. Did you mean "bam"?`, Detail: `"baz" is not a supported top-level attribute. Did you mean "bam"?`,
Subject: &Range{ Subject: &Range{
Start: Pos{ Start: Pos{
Byte: 16,
Column: 1, Column: 1,
Line: 3, Line: 3,
}, },
End: Pos{ End: Pos{
Byte: 19,
Column: 4, Column: 4,
Line: 3, Line: 3,
}, },
@ -71,10 +73,12 @@ attribute. Did you mean "bam"?
Detail: `"pizza" is not a supported attribute. Did you mean "pizzetta"?`, Detail: `"pizza" is not a supported attribute. Did you mean "pizzetta"?`,
Subject: &Range{ Subject: &Range{
Start: Pos{ Start: Pos{
Byte: 42,
Column: 3, Column: 3,
Line: 5, Line: 5,
}, },
End: Pos{ End: Pos{
Byte: 47,
Column: 8, Column: 8,
Line: 5, Line: 5,
}, },
@ -83,10 +87,12 @@ attribute. Did you mean "bam"?
// whether we're able to show a multi-line context when needed. // whether we're able to show a multi-line context when needed.
Context: &Range{ Context: &Range{
Start: Pos{ Start: Pos{
Byte: 24,
Column: 1, Column: 1,
Line: 4, Line: 4,
}, },
End: Pos{ End: Pos{
Byte: 60,
Column: 2, Column: 2,
Line: 6, Line: 6,
}, },