Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions EMBEDDING.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,16 @@ Alternatively, you can specify a fragment using `start` and `end` patterns:
Patterns match the first and last lines of the desired fragment.
If a pattern is omitted, the fragment will start at the beginning or end at the end of the file, respectively.

To embed a single line, use `line` with the same pattern syntax:

````markdown
<embed-code file="java/lang/String.java" line="public String()"></embed-code>
```java
```
````

The `line` attribute cannot be combined with `start`, `end`, or `fragment`.

### Pattern syntax

The tool supports an extended glob syntax for matching lines:
Expand Down
40 changes: 34 additions & 6 deletions embedding/parsing/instruction.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ import (
// EndPattern — an optional glob-like pattern. If specified, lines after the matching one
// are excluded.
//
// LinePattern — an optional glob-like pattern. If specified, only the matching line is embedded.
//
// CommentMode — specifies which comments are retained in the embedded code.
//
// DocumentationFile — a documentation file containing the instruction.
Expand All @@ -55,6 +57,7 @@ type Instruction struct {
Fragment string
StartPattern *Pattern
EndPattern *Pattern
LinePattern *Pattern
CommentMode commentfilter.Mode
DocumentationFile string
DocumentationLine int
Expand Down Expand Up @@ -93,6 +96,7 @@ func (e PatternNotFoundError) Error() string {
// - start — an optional glob-like pattern. If specified, lines before the matching one
// are excluded;
// - end — an optional glob-like pattern. If specified, lines after the matching one are excluded.
// - line — an optional glob-like pattern. If specified, only the matching line is embedded.
// - comments — an optional comment filtering mode. If omitted, all comments are retained.
//
// config — a Configuration with all embed-code settings.
Expand All @@ -104,16 +108,22 @@ func NewInstruction(
fragment := attributes["fragment"]
startValue := attributes["start"]
endValue := attributes["end"]
lineValue := attributes["line"]
commentMode, err := commentfilter.ParseMode(attributes["comments"])
if err != nil {
return Instruction{}, err
}

if fragment != "" && (startValue != "" || endValue != "") {
if fragment != "" && (startValue != "" || endValue != "" || lineValue != "") {
return Instruction{},
fmt.Errorf("<embed-code> must NOT specify both a fragment name and start/end/line patterns")
}
if lineValue != "" && (startValue != "" || endValue != "") {
return Instruction{},
fmt.Errorf("<embed-code> must NOT specify both a fragment name and start/end patterns")
fmt.Errorf("<embed-code> must NOT specify both a line pattern and start/end patterns")
}
var end *Pattern
var line *Pattern
var start *Pattern

if startValue != "" {
Expand All @@ -124,12 +134,17 @@ func NewInstruction(
endPattern := NewPattern(endValue)
end = &endPattern
}
if lineValue != "" {
linePattern := NewPattern(lineValue)
line = &linePattern
}

return Instruction{
CodeFile: codeFile,
Fragment: fragment,
StartPattern: start,
EndPattern: end,
LinePattern: line,
CommentMode: commentMode,
Configuration: config,
}, nil
Expand All @@ -143,7 +158,7 @@ func (e Instruction) Content() ([]string, error) {
if err != nil {
return nil, err
}
if e.StartPattern != nil || e.EndPattern != nil {
if e.StartPattern != nil || e.EndPattern != nil || e.LinePattern != nil {
codeFileReference, err := fragmentation.ResolveCodeFileReference(e.CodeFile, e.Configuration)
if err != nil {
return nil, err
Expand All @@ -166,15 +181,28 @@ func (e Instruction) Content() ([]string, error) {
// Returns string representation of Instruction.
func (e Instruction) String() string {
return fmt.Sprintf(
"EmbeddingInstruction[file=`%s`, fragment=`%s`, start=`%s`, end=`%s`, comments=`%s`]",
e.CodeFile, e.Fragment, e.StartPattern, e.EndPattern, e.CommentMode,
"EmbeddingInstruction[file=`%s`, fragment=`%s`, start=`%s`, end=`%s`, line=`%s`, comments=`%s`]",
e.CodeFile, e.Fragment, e.StartPattern, e.EndPattern, e.LinePattern, e.CommentMode,
)
}

// Filters and returns a subset of input lines based on start and end patterns.
// Filters and returns a subset of input lines based on start, end, or line patterns.
//
// lines — a list of strings representing the input lines.
func (e Instruction) matchingLines(lines []string, codeFileReference string) ([]string, error) {
if e.LinePattern != nil {
linePosition, err := e.matchGlob(
e.LinePattern, lines, 0, "line", codeFileReference,
)
if err != nil {
return nil, err
}
requiredLines := []string{lines[linePosition]}
indentation := indent.MaxCommonIndentation(requiredLines)

return indent.CutIndent(requiredLines, indentation), nil
}

startPosition := 0
if e.StartPattern != nil {
var err error
Expand Down
50 changes: 49 additions & 1 deletion embedding/parsing/instruction_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ type TestInstructionParams struct {
fragment string
startGlob string
endGlob string
lineGlob string
comments string
closeTag bool
}
Expand Down Expand Up @@ -205,6 +206,36 @@ var _ = Describe("Instruction", func() {
Expect(parsing.FromXML(xmlString, config)).Error().Should(HaveOccurred())
})

It("should have an error when parsing fragment with line glob", func() {
instructionParams := TestInstructionParams{
fragment: "fragment",
lineGlob: "public void hello()",
}
xmlString := buildInstruction("org/example/Hello.java", instructionParams)

Expect(parsing.FromXML(xmlString, config)).Error().Should(HaveOccurred())
})

It("should have an error when parsing line glob with start glob", func() {
instructionParams := TestInstructionParams{
startGlob: "public class*",
lineGlob: "public void hello()",
}
xmlString := buildInstruction("org/example/Hello.java", instructionParams)

Expect(parsing.FromXML(xmlString, config)).Error().Should(HaveOccurred())
})

It("should have an error when parsing line glob with end glob", func() {
instructionParams := TestInstructionParams{
endGlob: "*System.out*",
lineGlob: "public void hello()",
}
xmlString := buildInstruction("org/example/Hello.java", instructionParams)

Expect(parsing.FromXML(xmlString, config)).Error().Should(HaveOccurred())
})

It("should successfully parse XML from start to end glob", func() {
instructionParams := TestInstructionParams{
startGlob: "public class*",
Expand Down Expand Up @@ -257,6 +288,19 @@ var _ = Describe("Instruction", func() {
Expect(actualLines[0]).Should(Equal(expectedFirstLine))
})

It("should embed only the matching line when line glob is specified", func() {
instructionParams := TestInstructionParams{
lineGlob: "*class*",
}

actualLines := getXMLExtractionContent(
"org/example/Hello.java", instructionParams, config)

Expect(actualLines).Should(Equal([]string{
"public class Hello {",
}))
})

It("should successfully parse XML by only end glob", func() {
instructionParams := TestInstructionParams{
endGlob: "package*",
Expand Down Expand Up @@ -308,7 +352,7 @@ var _ = Describe("Instruction", func() {
Expect(actualLines[1]).Should(MatchRegexp(expectedLastLinePattern))
})

It("should embed one line when the start and end globs match the same line", func() {
It("should embed one line when the start and end globs match the same line", func() {
instructionParams := TestInstructionParams{
startGlob: "*spine.enableJava()*",
endGlob: "*.server()",
Expand Down Expand Up @@ -423,6 +467,10 @@ func buildInstruction(fileName string, params TestInstructionParams) string {
endAttr := xmlAttribute("end", params.endGlob)
instructionLine += " " + endAttr
}
if len(params.lineGlob) > 0 {
lineAttr := xmlAttribute("line", params.lineGlob)
instructionLine += " " + lineAttr
}
if len(params.comments) > 0 {
commentsAttr := xmlAttribute("comments", params.comments)
instructionLine += " " + commentsAttr
Expand Down
1 change: 1 addition & 0 deletions embedding/parsing/xml_parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ type Item struct {
// - start — an optional glob-like pattern. If specified, lines before the matching one
// are excluded;
// - end — an optional glob-like pattern. If specified, lines after the matching one are excluded.
// - line — an optional glob-like pattern. If specified, only the matching line is embedded.
// - comments — an optional comment filtering mode. If omitted, all comments are retained.
//
// config — a Configuration with all embed-code settings.
Expand Down
Loading