in internal/parsers/markdown.go [91:199]
func ExtractCodeBlocksFromAst(
node ast.Node,
source []byte,
languagesToExtract []string,
) []CodeBlock {
var lastHeader string
var commands []CodeBlock
var nextBlockIsExpectedOutput bool
var lastExpectedSimilarityScore float64
var lastExpectedRegex *regexp.Regexp
var lastNode ast.Node
ast.Walk(node, func(node ast.Node, entering bool) (ast.WalkStatus, error) {
if entering {
switch n := node.(type) {
// Set the last header when we encounter a heading.
case *ast.Heading:
lastHeader = string(extractTextFromMarkdown(&n.BaseBlock, source))
lastNode = node
case *ast.Paragraph:
lastNode = node
// Extract the code block if it matches the language.
case *ast.HTMLBlock:
content := extractTextFromMarkdown(&n.BaseBlock, source)
matches := expectedSimilarityRegex.FindStringSubmatch(content)
if len(matches) < 3 {
break
}
match := matches[1]
if match != "" {
score, err := strconv.ParseFloat(match, 64)
logging.GlobalLogger.Debugf("Simalrity score of %f found", score)
if err != nil {
return ast.WalkStop, err
}
lastExpectedSimilarityScore = score
} else {
match = matches[2]
logging.GlobalLogger.Debugf("Regex %q found", match)
if match == "" {
return ast.WalkStop, errors.New("No regex found")
}
re, err := regexp.Compile(match)
if err != nil {
return ast.WalkStop, fmt.Errorf("Cannot compile the following regex: %q", match)
}
lastExpectedRegex = re
}
nextBlockIsExpectedOutput = true
case *ast.FencedCodeBlock:
language := string(n.Language((source)))
content := extractTextFromMarkdown(&n.BaseBlock, source)
description := ""
if lastNode != nil {
switch n := lastNode.(type) {
case *ast.Paragraph:
description = string(extractTextFromMarkdown(&n.BaseBlock, source))
default:
logging.GlobalLogger.Warnf("The node before the codeblock `%s` is not a paragraph, it is a %s", content, n.Kind())
}
} else {
logging.GlobalLogger.Warnf("There are no markdown elements before the last codeblock `%s`", content)
}
lastNode = node
for _, desiredLanguage := range languagesToExtract {
if language == desiredLanguage {
command := CodeBlock{
Language: language,
Content: content,
Header: lastHeader,
Description: description,
}
commands = append(commands, command)
break
} else if nextBlockIsExpectedOutput {
// Map the expected output to the last command. If there
// are no commands, then we ignore the expected output.
if len(commands) > 0 {
expectedOutputBlock := ExpectedOutputBlock{
Language: language,
Content: extractTextFromMarkdown(&n.BaseBlock, source),
ExpectedSimilarity: lastExpectedSimilarityScore,
ExpectedRegex: lastExpectedRegex,
}
commands[len(commands)-1].ExpectedOutput = expectedOutputBlock
// Reset the expected output state.
nextBlockIsExpectedOutput = false
lastExpectedSimilarityScore = 0
lastExpectedRegex = nil
}
break
}
}
}
}
return ast.WalkContinue, nil
})
return commands
}