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 }