testmark-go

Subslice hunk bodies out as we parse. Split Hunk and DocHunk; will probably use former in more APIs.

commit 6515cef47bd70b628a82b9cab999e9ab34a10882 · parent 65a30e9259 · browse this tree
Eric Myhre, 2021-08-17 18:17:12 UTC

diff --git a/read.go b/read.go
index 58d6af5..a489ec5 100644
--- a/read.go
+++ b/read.go
@@ -34,7 +34,7 @@ var (
 func Parse(data []byte) (*Document, error) {
 	doc := Document{
 		Original:    data,
-		HunksByName: make(map[string]Hunk),
+		HunksByName: make(map[string]DocHunk),
 	}
 
 	// Markdown can be effectively parsed line by line.
@@ -44,9 +44,11 @@ func Parse(data []byte) (*Document, error) {
 	// Code blocks are the only feature of markdown that meaningfully changes what mode you're in at the start of a line.
 	// After that, we look for our magic prefix (on any lines that *aren't* already in a code block).
 	// Then, the rest is... pretty straightforward.
+	var offset int
 	var inCodeBlock bool
 	var expectCodeBlock bool
-	hunkInProgress := Hunk{Line: -1}
+	var codeBlockOffset int
+	hunkInProgress := DocHunk{Line: -1}
 	for i, line := range doc.OriginalLines {
 		// Check for transition in or out of codeblock.
 		if bytes.HasPrefix(line, sigilCodeBlock) {
@@ -54,22 +56,24 @@ func Parse(data []byte) (*Document, error) {
 			case false: // starting a block
 				if expectCodeBlock {
 					hunkInProgress.BlockTag = string(line[len(sigilCodeBlock):])
+					codeBlockOffset = offset + len(line) + 1
 				}
 				expectCodeBlock = false
 			case true: // ending a block
 				if hunkInProgress.Line > -1 {
 					hunkInProgress.EndLine = i
+					hunkInProgress.Body = doc.Original[codeBlockOffset:offset]
 					doc.DataHunks = append(doc.DataHunks, hunkInProgress)
 					doc.HunksByName[hunkInProgress.Name] = hunkInProgress
-					hunkInProgress = Hunk{Line: -1}
+					hunkInProgress = DocHunk{Line: -1}
 				}
 			}
 			inCodeBlock = !inCodeBlock
-			continue
+			goto next
 		}
 		// If we're in a code block, just fly by.
 		if inCodeBlock {
-			continue
+			goto next
 		}
 		// If we were expecting a code block just now, we didn't get it.
 		if expectCodeBlock {
@@ -96,6 +100,10 @@ func Parse(data []byte) (*Document, error) {
 			hunkInProgress.Name = name
 		}
 		// Any other text?  It's prose.  No action.
+	next:
+		// Track total offset, so we can use it to subslice out document hunks.
+		// Mind: this is going to be off by one at the very end of the file... but that turns out never to matter to us.
+		offset += len(line) + 1
 	}
 	return &doc, nil
 }
diff --git a/read_test.go b/read_test.go
index 0b88b2c..47e9138 100644
--- a/read_test.go
+++ b/read_test.go
@@ -12,5 +12,7 @@ func TestRead(t *testing.T) {
 		panic(err)
 	}
 	doc, err := ReadFile(filepath.Join(testdata, "example.md"))
-	fmt.Printf("%v -- %v\n", doc.HunksByName, err)
+	for _, hunk := range doc.DataHunks {
+		fmt.Printf("hunk %q is on lines %d:%d, has body %q\n", hunk.Name, hunk.Line+1, hunk.EndLine+1, string(hunk.Body))
+	}
 }
diff --git a/testmark.go b/testmark.go
index 2cefe63..11f9d43 100644
--- a/testmark.go
+++ b/testmark.go
@@ -12,13 +12,14 @@ type Document struct {
 
 	// Each data hunk.
 	// Contains just offset information, and the parsed name header.
-	DataHunks []Hunk
+	DataHunks []DocHunk
 
 	// Like it says on the tin.
-	HunksByName map[string]Hunk
+	HunksByName map[string]DocHunk
 }
 
-type Hunk struct {
+// DocHunk is the Document's internal idea of where hunks are.
+type DocHunk struct {
 	// Index into Document.OriginalLines where the comment block is found.
 	// The code block indicator is necessarily is the following line,
 	// and the code block body one line after that.
@@ -28,6 +29,12 @@ type Hunk struct {
 	// Index into Document.OriginalLines that contains the closing code block indicator.
 	EndLine int
 
+	Hunk
+}
+
+// Hunk is a simple tuple of hunk name string and body bytes.
+// Optionally, it may also have a BlockTag (which is whatever markdown has in the code block; usually, in practice, this is used to state a syntax for highlighting, which does not have much to do with testmark.)
+type Hunk struct {
 	// The hunk name (e.g. whatever comes after `[testmark]:# ` and before any more whitespace).
 	// Cannot be empty.
 	Name string
@@ -35,4 +42,8 @@ type Hunk struct {
 	// The code block syntax hint (or more literally: anything that comes after the triple-tick that starts the code block).
 	// Usually we don't encourage use of this much in testmark, but it's here.  Can be empty.
 	BlockTag string
+
+	// The full body of the hunk, as bytes.
+	// (This is *still* a subslice of Document.Original, if this hunk was created by Parse, but probably a unique slice otherwise.)
+	Body []byte
 }