github.com/phsym/gomarkdoc@v0.5.4/format/github.go (about)

     1  package format
     2  
     3  import (
     4  	"fmt"
     5  	"path/filepath"
     6  	"regexp"
     7  	"strings"
     8  
     9  	"github.com/phsym/gomarkdoc/format/formatcore"
    10  	"github.com/phsym/gomarkdoc/lang"
    11  )
    12  
    13  // GitHubFlavoredMarkdown provides a Format which is compatible with GitHub
    14  // Flavored Markdown's syntax and semantics. See GitHub's documentation for
    15  // more details about their markdown format:
    16  // https://guides.github.com/features/mastering-markdown/
    17  type GitHubFlavoredMarkdown struct{}
    18  
    19  // Bold converts the provided text to bold
    20  func (f *GitHubFlavoredMarkdown) Bold(text string) (string, error) {
    21  	return formatcore.Bold(text), nil
    22  }
    23  
    24  // CodeBlock wraps the provided code as a code block and tags it with the
    25  // provided language (or no language if the empty string is provided).
    26  func (f *GitHubFlavoredMarkdown) CodeBlock(language, code string) (string, error) {
    27  	return formatcore.GFMCodeBlock(language, code), nil
    28  }
    29  
    30  // Header converts the provided text into a header of the provided level. The
    31  // level is expected to be at least 1.
    32  func (f *GitHubFlavoredMarkdown) Header(level int, text string) (string, error) {
    33  	return formatcore.Header(level, formatcore.Escape(text))
    34  }
    35  
    36  // RawHeader converts the provided text into a header of the provided level
    37  // without escaping the header text. The level is expected to be at least 1.
    38  func (f *GitHubFlavoredMarkdown) RawHeader(level int, text string) (string, error) {
    39  	return formatcore.Header(level, text)
    40  }
    41  
    42  var (
    43  	gfmWhitespaceRegex = regexp.MustCompile(`\s`)
    44  	gfmRemoveRegex     = regexp.MustCompile(`[^\pL-_\d]+`)
    45  )
    46  
    47  // LocalHref generates an href for navigating to a header with the given
    48  // headerText located within the same document as the href itself.
    49  func (f *GitHubFlavoredMarkdown) LocalHref(headerText string) (string, error) {
    50  	result := formatcore.PlainText(headerText)
    51  	result = strings.ToLower(result)
    52  	result = strings.TrimSpace(result)
    53  	result = gfmWhitespaceRegex.ReplaceAllString(result, "-")
    54  	result = gfmRemoveRegex.ReplaceAllString(result, "")
    55  
    56  	return fmt.Sprintf("#%s", result), nil
    57  }
    58  
    59  // Link generates a link with the given text and href values.
    60  func (f *GitHubFlavoredMarkdown) Link(text, href string) (string, error) {
    61  	return formatcore.Link(text, href), nil
    62  }
    63  
    64  // CodeHref generates an href to the provided code entry.
    65  func (f *GitHubFlavoredMarkdown) CodeHref(loc lang.Location) (string, error) {
    66  	// If there's no repo, we can't compute an href
    67  	if loc.Repo == nil {
    68  		return "", nil
    69  	}
    70  
    71  	var (
    72  		relative string
    73  		err      error
    74  	)
    75  	if filepath.IsAbs(loc.Filepath) {
    76  		relative, err = filepath.Rel(loc.WorkDir, loc.Filepath)
    77  		if err != nil {
    78  			return "", err
    79  		}
    80  	} else {
    81  		relative = loc.Filepath
    82  	}
    83  
    84  	full := filepath.Join(loc.Repo.PathFromRoot, relative)
    85  	p, err := filepath.Rel(string(filepath.Separator), full)
    86  	if err != nil {
    87  		return "", err
    88  	}
    89  
    90  	var locStr string
    91  	if loc.Start.Line == loc.End.Line {
    92  		locStr = fmt.Sprintf("L%d", loc.Start.Line)
    93  	} else {
    94  		locStr = fmt.Sprintf("L%d-L%d", loc.Start.Line, loc.End.Line)
    95  	}
    96  
    97  	return fmt.Sprintf(
    98  		"%s/blob/%s/%s#%s",
    99  		loc.Repo.Remote,
   100  		loc.Repo.DefaultBranch,
   101  		filepath.ToSlash(p),
   102  		locStr,
   103  	), nil
   104  }
   105  
   106  // ListEntry generates an unordered list entry with the provided text at the
   107  // provided zero-indexed depth. A depth of 0 is considered the topmost level of
   108  // list.
   109  func (f *GitHubFlavoredMarkdown) ListEntry(depth int, text string) (string, error) {
   110  	return formatcore.ListEntry(depth, text), nil
   111  }
   112  
   113  // Accordion generates a collapsible content. The accordion's visible title
   114  // while collapsed is the provided title and the expanded content is the body.
   115  func (f *GitHubFlavoredMarkdown) Accordion(title, body string) (string, error) {
   116  	return formatcore.GFMAccordion(title, body), nil
   117  }
   118  
   119  // AccordionHeader generates the header visible when an accordion is collapsed.
   120  //
   121  // The AccordionHeader is expected to be used in conjunction with
   122  // AccordionTerminator() when the demands of the body's rendering requires it to
   123  // be generated independently. The result looks conceptually like the following:
   124  //
   125  //	accordion := format.AccordionHeader("Accordion Title") + "Accordion Body" + format.AccordionTerminator()
   126  func (f *GitHubFlavoredMarkdown) AccordionHeader(title string) (string, error) {
   127  	return formatcore.GFMAccordionHeader(title), nil
   128  }
   129  
   130  // AccordionTerminator generates the code necessary to terminate an accordion
   131  // after the body. It is expected to be used in conjunction with
   132  // AccordionHeader(). See AccordionHeader for a full description.
   133  func (f *GitHubFlavoredMarkdown) AccordionTerminator() (string, error) {
   134  	return formatcore.GFMAccordionTerminator(), nil
   135  }
   136  
   137  // Paragraph formats a paragraph with the provided text as the contents.
   138  func (f *GitHubFlavoredMarkdown) Paragraph(text string) (string, error) {
   139  	return formatcore.Paragraph(text), nil
   140  }
   141  
   142  // Escape escapes special markdown characters from the provided text.
   143  func (f *GitHubFlavoredMarkdown) Escape(text string) string {
   144  	return formatcore.Escape(text)
   145  }