Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for quote and verbatim #77

Merged
merged 6 commits into from
Oct 22, 2021
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
5 changes: 5 additions & 0 deletions internal/cli/generate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ func TestGenerateStdout(t *testing.T) {
keepMarkers: true,
wantFile: "../../testdata/markdown/import-with-exporter-updated.md",
},
"markdown with verbatim YAML ": {
inputFile: "../../testdata/markdown/verbatim-yaml-before.md",
keepMarkers: true,
wantFile: "../../testdata/markdown/verbatim-yaml-updated.md",
},
"yaml with exporter": {
inputFile: "../../testdata/yaml/demo-before.yaml",
keepMarkers: true,
Expand Down
87 changes: 79 additions & 8 deletions internal/marker/marker.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,11 @@ type Marker struct {
ImportTargetFile
ImportLogic

Indentation *Indentation
// Below are optional

// TODO: Add insert style such as code verbatim, details, quotes, etc.
Indentation *Indentation
ImportStyle *ImportStyle
Wrap *Wrap
}

type ImportTargetFileType int
Expand Down Expand Up @@ -82,31 +84,60 @@ type Indentation struct {
MarkerIndentation int
}

type StyleMode int

const (
// Reserve 0 value as invalid
Quote StyleMode = iota + 1

// TODO: implement this if there is a use case
// UnorderedList
// OrderedList
)

type ImportStyle struct {
Mode StyleMode
}

type Wrap struct {
LanguageType string
}

func NewMarker(raw *RawMarker) (*Marker, error) {
err := raw.Validate()
if err != nil {
return nil, err
}

result := &Marker{
marker := &Marker{
Name: raw.Name,
LineToInsertAt: raw.LineToInsertAt,
}

err = processFileOption(result, raw)
err = marker.processFileOption(raw)
if err != nil {
return nil, err
}

err = marker.processIndentOption(raw)
if err != nil {
return nil, err
}

err = processIndentOption(result, raw)
err = marker.processStyle(raw)
if err != nil {
return nil, err
}

return result, nil
err = marker.processWrap(raw)
if err != nil {
return nil, err
}

return marker, nil
}

func processFileOption(marker *Marker, match *RawMarker) error {
func (marker *Marker) processFileOption(match *RawMarker) error {
matches, err := regexpplus.MapWithNamedSubgroups(match.Options, OptionFilePathIndicator)
if err != nil {
return fmt.Errorf("%w for '%s', import target option is missing", ErrInvalidSyntax, match.Name)
Expand All @@ -126,7 +157,7 @@ func processFileOption(marker *Marker, match *RawMarker) error {
return nil
}

func processIndentOption(marker *Marker, match *RawMarker) error {
func (marker *Marker) processIndentOption(match *RawMarker) error {
matches, err := regexpplus.MapWithNamedSubgroups(match.Options, OptionIndentMode)
if err != nil {
return nil // Indent options are not required, and thus simply ignore if no match
Expand Down Expand Up @@ -171,6 +202,46 @@ func processIndentOption(marker *Marker, match *RawMarker) error {
return nil
}

func (m *Marker) processStyle(match *RawMarker) error {
matches, err := regexpplus.MapWithNamedSubgroups(match.Options, OptionStyleAndWrap)
if err != nil {
return nil // Indent options are not required, and thus simply ignore if no match
}

if styleMode, found := matches["importer_style"]; found {
switch styleMode {
case "quote":
m.ImportStyle = &ImportStyle{Mode: Quote}
case "verbatim":
lang, found := matches["importer_style_lang"]
if !found {
m.Wrap = &Wrap{} // default verbatim, without language syntax
}
m.Wrap = &Wrap{LanguageType: lang}
default:
return errors.New("unsupported style") // This shouldn't happen with the underlying regex
}
}

return nil
}

func (m *Marker) processWrap(match *RawMarker) error {
matches, err := regexpplus.MapWithNamedSubgroups(match.Options, OptionWrap)
if err != nil {
return nil // Indent options are not required, and thus simply ignore if no match
}

w := &Wrap{}
if lang, found := matches["importer_wrap_lang"]; found {
w.LanguageType = lang
}

m.Wrap = w

return nil
}

// processTargetPath processes string input of import target path.
//
// Target path can be 2 forms.
Expand Down
3 changes: 3 additions & 0 deletions internal/marker/marker_regex.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ var (
// OptionIndentMode is the pattern used for specifying indentation mode.
OptionIndentMode = `indent: (?P<importer_indent_mode>absolute|extra|align|keep)\s?(?P<importer_indent_length>\d*)`

OptionStyleAndWrap = `style: (?P<importer_style>quote|q|verbatim|v)\s?(?P<importer_style_lang>\S*)`
OptionWrap = `wrap: (?P<importer_wrap_lang>\S*)`

ImporterSkipProcessingMarkdown = `<!-- == importer-skip-update == -->`
ImporterSkipProcessingYAML = `# == importer-skip-update ==`
)
Expand Down
52 changes: 52 additions & 0 deletions internal/marker/marker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,58 @@ func TestNewMarker(t *testing.T) {
},
},
},
"Quote": {
input: &marker.RawMarker{
Name: "simple-marker",
IsBeginFound: true,
IsEndFound: true,
LineToInsertAt: 3,
Options: "from: ./abc.md#3~5 style: quote abc", // "abc" is used as language, but quote simply ignores this
},
want: &marker.Marker{
Name: "simple-marker",
LineToInsertAt: 3,
ImportTargetFile: marker.ImportTargetFile{
Type: marker.PathBased,
File: "./abc.md",
},
ImportLogic: marker.ImportLogic{
Type: marker.LineRange,
LineFrom: 3,
LineTo: 5,
},
Indentation: nil,
ImportStyle: &marker.ImportStyle{
Mode: marker.Quote,
},
},
},
"Verbatim ": {
input: &marker.RawMarker{
Name: "simple-marker",
IsBeginFound: true,
IsEndFound: true,
LineToInsertAt: 3,
Options: "from: ./abc.md#3~5 style: verbatim some-lang",
},
want: &marker.Marker{
Name: "simple-marker",
LineToInsertAt: 3,
ImportTargetFile: marker.ImportTargetFile{
Type: marker.PathBased,
File: "./abc.md",
},
ImportLogic: marker.ImportLogic{
Type: marker.LineRange,
LineFrom: 3,
LineTo: 5,
},
Indentation: nil,
Wrap: &marker.Wrap{
LanguageType: "some-lang",
},
},
},
}

for name, tc := range cases {
Expand Down
38 changes: 31 additions & 7 deletions internal/marker/process.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,12 +74,33 @@ func (m *Marker) processSingleMarkerMarkdown(file io.Reader) ([]byte, error) {
withinExportMarker := false
currentLine := 0

if m.Wrap != nil {
result = append(result, []byte("```"+m.Wrap.LanguageType)...)
result = append(result, br)
}

scanner := bufio.NewScanner(file)
for scanner.Scan() {
currentLine++

dataToWrite := append(scanner.Bytes(), br)
if m.ImportStyle != nil && m.ImportStyle.Mode == Quote {
dataToWrite = append([]byte("> "), dataToWrite...)
}

// Find Exporter Marker
matches, err := regexpplus.MapWithNamedSubgroups(scanner.Text(), ExporterMarkerMarkdown)
var exporter string
targetFileType := filepath.Ext(m.ImportTargetFile.File)
switch targetFileType {
case ".md":
exporter = ExporterMarkerMarkdown
case ".yaml", ".yml":
exporter = ExporterMarkerYAML
default:
exporter = ExporterMarkerMarkdown
}

matches, err := regexpplus.MapWithNamedSubgroups(scanner.Text(), exporter)
if err != nil && !errors.Is(err, regexpplus.ErrNoMatch) {
panic(err) // Unknown error, should not happen
}
Expand All @@ -98,26 +119,29 @@ func (m *Marker) processSingleMarkerMarkdown(file io.Reader) ([]byte, error) {

// Handle Exporter Marker imports
if withinExportMarker {
result = append(result, scanner.Bytes()...)
result = append(result, br)
result = append(result, dataToWrite...)
continue
}

// Handle line number imports
if currentLine >= m.ImportLogic.LineFrom &&
currentLine <= m.ImportLogic.LineTo {
result = append(result, scanner.Bytes()...)
result = append(result, br)
result = append(result, dataToWrite...)
continue
}
for _, l := range m.ImportLogic.Lines {
if currentLine == l {
result = append(result, scanner.Bytes()...)
result = append(result, br)
result = append(result, dataToWrite...)
continue
}
}
}

if m.Wrap != nil {
result = append(result, []byte("```")...)
result = append(result, br)
}

return result, nil
}

Expand Down
56 changes: 56 additions & 0 deletions internal/marker/process_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,62 @@ func TestProcessSingleMarker(t *testing.T) {
✨✨✨✨✨✨✨✨
✨✨✨✨✨✨✨✨

`),
},
"markdown: exporter marker with quote": {
callerFile: "./some_file.md",
marker: &Marker{
LineToInsertAt: 1,
ImportTargetFile: ImportTargetFile{
Type: PathBased,
File: "../../testdata/markdown/snippet-with-exporter.md",
},
ImportLogic: ImportLogic{
Type: ExporterMarker,
ExporterMarker: "test_exporter",
},
ImportStyle: &ImportStyle{
Mode: Quote,
},
},
want: []byte(`>
> ✨✨✨✨✨✨✨✨
> ✨✨✨✨✨✨✨✨
> ✨✨✨✨✨✨✨✨
> 🚀🚀🚀🚀🚀🚀🚀🚀
> ✨✨✨✨✨✨✨✨
> ✨✨✨✨✨✨✨✨
> ✨✨✨✨✨✨✨✨
>
`),
},
"markdown: exporter marker with verbatim": {
callerFile: "./some_file.md",
marker: &Marker{
LineToInsertAt: 1,
ImportTargetFile: ImportTargetFile{
Type: PathBased,
File: "../../testdata/markdown/snippet-with-exporter.md",
},
ImportLogic: ImportLogic{
Type: ExporterMarker,
ExporterMarker: "test_exporter",
},
Wrap: &Wrap{
LanguageType: "some-lang",
},
},
want: []byte("```" + `some-lang

✨✨✨✨✨✨✨✨
✨✨✨✨✨✨✨✨
✨✨✨✨✨✨✨✨
🚀🚀🚀🚀🚀🚀🚀🚀
✨✨✨✨✨✨✨✨
✨✨✨✨✨✨✨✨
✨✨✨✨✨✨✨✨

` + "```" + `
`),
},
"yaml: line range": {
Expand Down
15 changes: 15 additions & 0 deletions testdata/markdown/quote-import-before.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Quote

<!-- == imptr: quoted-note / begin from: ../other/note.txt#1~ style: quote == -->

Any content here will be removed by Importer.

<!-- == imptr: quoted-note / end == -->

Content after marker is left untouched.

<!-- == imptr: quoted-exporter / begin from: ./snippet-description.md#[for-demo] style: quote == -->

Any content here will be removed by Importer.

<!-- == imptr: quoted-exporter / end == -->
9 changes: 9 additions & 0 deletions testdata/markdown/quote-import-purged.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Quote

<!-- == imptr: quoted-note / begin from: ../other/note.txt#1~ style: quote == -->
<!-- == imptr: quoted-note / end == -->

Content after marker is left untouched.

<!-- == imptr: quoted-exporter / begin from: ./snippet-description.md#[for-demo] style: quote == -->
<!-- == imptr: quoted-exporter / end == -->
16 changes: 16 additions & 0 deletions testdata/markdown/quote-import-updated.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Quote

<!-- == imptr: quoted-note / begin from: ../other/note.txt#1~ style: quote == -->
> This is test data.
> 他言語サポートのためのテスト文章。
> 🍸 Emojis 🍷 Supported 🍺
<!-- == imptr: quoted-note / end == -->

Content after marker is left untouched.

<!-- == imptr: quoted-exporter / begin from: ./snippet-description.md#[for-demo] style: quote == -->
> This demonstrates how a markdown can import other file content.
>
> Importer is a CLI tool to read and process Importer and Exporter markers.
> This can be easily integrated into CI/CD and automation setup.
<!-- == imptr: quoted-exporter / end == -->
Loading