Skip to content
Open
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
36 changes: 31 additions & 5 deletions cmd/gomarkdoc/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,18 @@ import (
"hash/fnv"
"html/template"
"io"
"io/ioutil"
"os"
"path/filepath"
"runtime/debug"
"strings"

"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/thediveo/enumflag/v2"

"github.com/princjef/gomarkdoc"
"github.com/princjef/gomarkdoc/format"
"github.com/princjef/gomarkdoc/format/repo_format"
"github.com/princjef/gomarkdoc/lang"
"github.com/princjef/gomarkdoc/logger"
)
Expand Down Expand Up @@ -62,6 +63,11 @@ type commandOptions struct {
version bool
}

var RepoTypeIds = map[lang.RepoType][]string{
lang.GitHubRepoType: {"github"},
lang.BitBucketRepoType: {"bitbucket"},
}

// Flags populated by goreleaser
var version = ""

Expand Down Expand Up @@ -98,6 +104,7 @@ func buildCommand() *cobra.Command {
opts.footerFile = viper.GetString("footerFile")
opts.tags = viper.GetStringSlice("tags")
opts.excludeDirs = viper.GetStringSlice("excludeDirs")
opts.repository.Type = lang.RepoType(viper.GetInt("repository.type"))
opts.repository.Remote = viper.GetString("repository.url")
opts.repository.DefaultBranch = viper.GetString("repository.defaultBranch")
opts.repository.PathFromRoot = viper.GetString("repository.path")
Expand Down Expand Up @@ -217,6 +224,17 @@ func buildCommand() *cobra.Command {
"",
"Manual override for the git repository URL used in place of automatic detection.",
)

repoTypeIds := make([]string, len(RepoTypeIds))
for i, repoTypeId := range RepoTypeIds {
// works because of iota in lang.RepoType consts
repoTypeIds[i] = repoTypeId[0]
}
command.Flags().Var(
enumflag.New(&opts.repository.Type, "mode", RepoTypeIds, enumflag.EnumCaseInsensitive),
"repository.type",
fmt.Sprintf("Specifies the repository management system type: could be one of %v", repoTypeIds))

command.Flags().StringVar(
&opts.repository.DefaultBranch,
"repository.default-branch",
Expand Down Expand Up @@ -250,6 +268,7 @@ func buildCommand() *cobra.Command {
_ = viper.BindPFlag("footerFile", command.Flags().Lookup("footer-file"))
_ = viper.BindPFlag("tags", command.Flags().Lookup("tags"))
_ = viper.BindPFlag("excludeDirs", command.Flags().Lookup("exclude-dirs"))
_ = viper.BindPFlag("repository.type", command.Flags().Lookup("repository.type"))
_ = viper.BindPFlag("repository.url", command.Flags().Lookup("repository.url"))
_ = viper.BindPFlag("repository.defaultBranch", command.Flags().Lookup("repository.default-branch"))
_ = viper.BindPFlag("repository.path", command.Flags().Lookup("repository.path"))
Expand Down Expand Up @@ -356,7 +375,7 @@ func resolveOverrides(opts commandOptions) ([]gomarkdoc.RendererOption, error) {
continue
}

b, err := ioutil.ReadFile(f)
b, err := os.ReadFile(f)
if err != nil {
return nil, fmt.Errorf("gomarkdoc: couldn't resolve template for %s: %w", name, err)
}
Expand All @@ -372,6 +391,13 @@ func resolveOverrides(opts commandOptions) ([]gomarkdoc.RendererOption, error) {
f = &format.AzureDevOpsMarkdown{}
case "plain":
f = &format.PlainMarkdown{}
case "mkdocs":
f = &format.MkDocsMarkdown{
RepoFormats: []repo_format.RepoFormat{
repo_format.GitHubRepoFormat{},
repo_format.BitBucketRepoFormat{},
},
}
default:
return nil, fmt.Errorf("gomarkdoc: invalid format: %s", opts.format)
}
Expand All @@ -387,7 +413,7 @@ func resolveHeader(opts commandOptions) (string, error) {
}

if opts.headerFile != "" {
b, err := ioutil.ReadFile(opts.headerFile)
b, err := os.ReadFile(opts.headerFile)
if err != nil {
return "", fmt.Errorf("gomarkdoc: couldn't resolve header file: %w", err)
}
Expand All @@ -404,7 +430,7 @@ func resolveFooter(opts commandOptions) (string, error) {
}

if opts.footerFile != "" {
b, err := ioutil.ReadFile(opts.footerFile)
b, err := os.ReadFile(opts.footerFile)
if err != nil {
return "", fmt.Errorf("gomarkdoc: couldn't resolve footer file: %w", err)
}
Expand Down Expand Up @@ -531,7 +557,7 @@ func getSpecs(paths ...string) []*PackageSpec {

p := e.Value.(string)

files, err := ioutil.ReadDir(p)
files, err := os.ReadDir(p)
if err != nil {
// If we couldn't read the folder, there are no directories that
// we're going to find beneath it
Expand Down
139 changes: 139 additions & 0 deletions format/mkdocs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
package format

import (
"fmt"
"regexp"
"strings"

"github.com/princjef/gomarkdoc/format/formatcore"
"github.com/princjef/gomarkdoc/format/repo_format"
"github.com/princjef/gomarkdoc/lang"
)

// MkDocsMarkdown provides a Format which is compatible with BitBucket
// Markdown's syntax and semantics
type MkDocsMarkdown struct {
RepoFormats []repo_format.RepoFormat
}

// Bold converts the provided text to bold
func (f *MkDocsMarkdown) Bold(text string) (string, error) {
return formatcore.Bold(text), nil
}

// CodeBlock wraps the provided code as a code block and tags it with the
// provided language (or no language if the empty string is provided).
func (f *MkDocsMarkdown) CodeBlock(language, code string) (string, error) {
return formatcore.GFMCodeBlock(language, code), nil
}

// Anchor produces an anchor for the provided link.
func (f *MkDocsMarkdown) Anchor(anchor string) string {
return formatcore.Anchor(anchor)
}

// AnchorHeader converts the provided text and custom anchor link into a header
// of the provided level. The level is expected to be at least 1.
func (f *MkDocsMarkdown) AnchorHeader(level int, text, anchor string) (string, error) {
return formatcore.AnchorHeader(level, f.Escape(text), anchor)
}

// Header converts the provided text into a header of the provided level. The
// level is expected to be at least 1.
func (f *MkDocsMarkdown) Header(level int, text string) (string, error) {
return formatcore.Header(level, f.Escape(text))
}

// RawAnchorHeader converts the provided text and custom anchor link into a
// header of the provided level without escaping the header text. The level is
// expected to be at least 1.
func (f *MkDocsMarkdown) RawAnchorHeader(level int, text, anchor string) (string, error) {
return formatcore.AnchorHeader(level, text, anchor)
}

// RawHeader converts the provided text into a header of the provided level
// without escaping the header text. The level is expected to be at least 1.
func (f *MkDocsMarkdown) RawHeader(level int, text string) (string, error) {
return formatcore.Header(level, text)
}

var (
mkDocsWhitespaceRegex = regexp.MustCompile(`\s`)
mkDocsRemoveRegex = regexp.MustCompile(`[^\pL-_\d]+`)
)

// LocalHref generates an href for navigating to a header with the given
// headerText located within the same document as the href itself.
func (f *MkDocsMarkdown) LocalHref(headerText string) (string, error) {
result := formatcore.PlainText(headerText)
result = strings.ToLower(result)
result = strings.TrimSpace(result)
result = mkDocsWhitespaceRegex.ReplaceAllString(result, "-")
result = mkDocsRemoveRegex.ReplaceAllString(result, "")

return fmt.Sprintf("#%s", result), nil
}

// RawLocalHref generates an href within the same document but with a direct
// link provided instead of text to slugify.
func (f *MkDocsMarkdown) RawLocalHref(anchor string) string {
return fmt.Sprintf("#%s", anchor)
}

// Link generates a link with the given text and href values.
func (f *MkDocsMarkdown) Link(text, href string) (string, error) {
return formatcore.Link(text, href), nil
}

// CodeHref generates an href to the provided code entry.
func (f *MkDocsMarkdown) CodeHref(loc lang.Location) (string, error) {
// If there's no repo, we can't compute an href
if loc.Repo == nil {
return "", nil
}

for _, repoFormat := range f.RepoFormats {
if repoFormat.Supports(loc.Repo.Type) {
return repoFormat.CodeHref(loc)
}
}

// if there is no any supported repo format
return "", nil
}

// ListEntry generates an unordered list entry with the provided text at the
// provided zero-indexed depth. A depth of 0 is considered the topmost level of
// list.
func (f *MkDocsMarkdown) ListEntry(depth int, text string) (string, error) {
return formatcore.ListEntry(depth, text), nil
}

// Accordion generates a collapsible content. The accordion's visible title
// while collapsed is the provided title and the expanded content is the body.
func (f *MkDocsMarkdown) Accordion(title, body string) (string, error) {
return formatcore.GFMAccordion(title, body), nil
}

// AccordionHeader generates the header visible when an accordion is collapsed.
//
// The AccordionHeader is expected to be used in conjunction with
// AccordionTerminator() when the demands of the body's rendering requires it to
// be generated independently. The result looks conceptually like the following:
//
// accordion := format.AccordionHeader("Accordion Title") + "Accordion Body" + format.AccordionTerminator()
func (f *MkDocsMarkdown) AccordionHeader(title string) (string, error) {
return formatcore.GFMAccordionHeader(title), nil
}

// AccordionTerminator generates the code necessary to terminate an accordion
// after the body. It is expected to be used in conjunction with
// AccordionHeader(). See AccordionHeader for a full description.
func (f *MkDocsMarkdown) AccordionTerminator() (string, error) {
return formatcore.GFMAccordionTerminator(), nil
}

// Escape escapes special markdown characters from the provided text.
func (f *MkDocsMarkdown) Escape(text string) string {
return formatcore.Escape(text)
}
Loading