Document and clean SourceRelativeLinksEval code

This commit is contained in:
Sven Dowideit 2016-03-15 16:00:36 +10:00 committed by Bjørn Erik Pedersen
parent 819271cb78
commit 1648e327c0
6 changed files with 80 additions and 80 deletions

View file

@ -289,6 +289,38 @@ Its behavior can be modified with the <code>latexDashes</code> flag listed below
<td class="purpose-description" colspan="2">Extensions in this option won't be loaded.<br> <td class="purpose-description" colspan="2">Extensions in this option won't be loaded.<br>
<small><strong>Example:</strong>&nbsp;Add <code>"autoHeaderIds"</code> to disable <code>EXTENSION_AUTO_HEADER_IDS</code>.</small></td> <small><strong>Example:</strong>&nbsp;Add <code>"autoHeaderIds"</code> to disable <code>EXTENSION_AUTO_HEADER_IDS</code>.</small></td>
</tr> </tr>
<tr>
<td><code><strong>sourceRelativeLinksEval</strong></code></td>
<td><code>false</code></td>
<td><code>none</code></td>
</tr>
<tr>
<td class="purpose-title">Purpose:</td>
<td class="purpose-description" colspan="2">Source file based relative linking (a la Github).<br>
Relative links to markdown and static files within a page will be evaluated relative to the
location of that page, and then converted to html links during rendering. For example,
`[example](../other/page.md)` in `content/total/overview.md` will be linked to
`content/other/overview.md`, and then rendered to `/other/overview/` in the HTML output.
</td>
</tr>
<tr>
<td><code><strong>sourceRelativeLinksProjectFolder</strong></code></td>
<td><code>"/docs/content"</code></td>
<td><code>none</code></td>
</tr>
<tr>
<td class="purpose-title">Purpose:</td>
<td class="purpose-description" colspan="2">Source file based relative linking Hugo Project sub-folder.<br>
When `sourceRelativeLinksEval` is enabled, source level paths may contain an absolute respository path to the
markdown or static file which needs to be removed before trying to match it with the intended link.
For example, if your documentation is in `/docs/content`, then
`[example](/docs/content/other/page.md)` in `/docs/content/total/overview.md` will be linked to
`/docs/content/other/overview.md`, and then rendered to `/other/overview/` in the HTML output.
</td>
</tr>
</tbody> </tbody>
</table> </table>

View file

@ -43,29 +43,31 @@ var SummaryDivider = []byte("<!--more-->")
// Blackfriday holds configuration values for Blackfriday rendering. // Blackfriday holds configuration values for Blackfriday rendering.
type Blackfriday struct { type Blackfriday struct {
Smartypants bool Smartypants bool
AngledQuotes bool AngledQuotes bool
Fractions bool Fractions bool
HrefTargetBlank bool HrefTargetBlank bool
SmartDashes bool SmartDashes bool
LatexDashes bool LatexDashes bool
PlainIDAnchors bool PlainIDAnchors bool
SourceRelativeLinksEval bool SourceRelativeLinksEval bool
Extensions []string SourceRelativeLinksProjectFolder string
ExtensionsMask []string Extensions []string
ExtensionsMask []string
} }
// NewBlackfriday creates a new Blackfriday filled with site config or some sane defaults. // NewBlackfriday creates a new Blackfriday filled with site config or some sane defaults.
func NewBlackfriday() *Blackfriday { func NewBlackfriday() *Blackfriday {
combinedParam := map[string]interface{}{ combinedParam := map[string]interface{}{
"smartypants": true, "smartypants": true,
"angledQuotes": false, "angledQuotes": false,
"fractions": true, "fractions": true,
"hrefTargetBlank": false, "hrefTargetBlank": false,
"smartDashes": true, "smartDashes": true,
"latexDashes": true, "latexDashes": true,
"plainIDAnchors": false, "plainIDAnchors": false,
"sourceRelativeLinks": false, "sourceRelativeLinks": false,
"sourceRelativeLinksProjectFolder": "/docs/content",
} }
siteParam := viper.GetStringMap("blackfriday") siteParam := viper.GetStringMap("blackfriday")

View file

@ -49,6 +49,7 @@ func (renderer *HugoHTMLRenderer) Link(out *bytes.Buffer, link []byte, title []b
// Use the blackfriday built in Link handler // Use the blackfriday built in Link handler
renderer.Renderer.Link(out, link, title, content) renderer.Renderer.Link(out, link, title, content)
} else { } else {
// set by SourceRelativeLinksEval
newLink, err := renderer.LinkResolver(string(link)) newLink, err := renderer.LinkResolver(string(link))
if err != nil { if err != nil {
newLink = string(link) newLink = string(link)
@ -62,6 +63,7 @@ func (renderer *HugoHTMLRenderer) Image(out *bytes.Buffer, link []byte, title []
// Use the blackfriday built in Image handler // Use the blackfriday built in Image handler
renderer.Renderer.Image(out, link, title, alt) renderer.Renderer.Image(out, link, title, alt)
} else { } else {
// set by SourceRelativeLinksEval
newLink, err := renderer.FileResolver(string(link)) newLink, err := renderer.FileResolver(string(link))
if err != nil { if err != nil {
newLink = string(link) newLink = string(link)

View file

@ -257,10 +257,10 @@ func (p *Page) renderBytes(content []byte) []byte {
var fileFn helpers.FileResolverFunc var fileFn helpers.FileResolverFunc
if p.getRenderingConfig().SourceRelativeLinksEval { if p.getRenderingConfig().SourceRelativeLinksEval {
fn = func(ref string) (string, error) { fn = func(ref string) (string, error) {
return p.Node.Site.GitHub(ref, p) return p.Node.Site.SourceRelativeLink(ref, p)
} }
fileFn = func(ref string) (string, error) { fileFn = func(ref string) (string, error) {
return p.Node.Site.GitHubFileLink(ref, p) return p.Node.Site.SourceRelativeLinkFile(ref, p)
} }
} }
return helpers.RenderBytes( return helpers.RenderBytes(
@ -273,10 +273,10 @@ func (p *Page) renderContent(content []byte) []byte {
var fileFn helpers.FileResolverFunc var fileFn helpers.FileResolverFunc
if p.getRenderingConfig().SourceRelativeLinksEval { if p.getRenderingConfig().SourceRelativeLinksEval {
fn = func(ref string) (string, error) { fn = func(ref string) (string, error) {
return p.Node.Site.GitHub(ref, p) return p.Node.Site.SourceRelativeLink(ref, p)
} }
fileFn = func(ref string) (string, error) { fileFn = func(ref string) (string, error) {
return p.Node.Site.GitHubFileLink(ref, p) return p.Node.Site.SourceRelativeLinkFile(ref, p)
} }
} }
return helpers.RenderBytesWithTOC(&helpers.RenderingContext{Content: content, PageFmt: p.guessMarkupType(), return helpers.RenderBytesWithTOC(&helpers.RenderingContext{Content: content, PageFmt: p.guessMarkupType(),

View file

@ -225,28 +225,18 @@ func (s *SiteInfo) RelRef(ref string, page *Page) (string, error) {
return s.refLink(ref, page, true) return s.refLink(ref, page, true)
} }
// TODO(sven): Document // SourceRelativeLink attempts to convert any source page relative links (like [../another.md]) into absolute links
func (s *SiteInfo) GitHub(ref string, page *Page) (string, error) { func (s *SiteInfo) SourceRelativeLink(ref string, currentPage *Page) (string, error) {
return s.githubLink(ref, page, true)
}
func (s *SiteInfo) githubLink(ref string, currentPage *Page, relative bool) (string, error) {
var refURL *url.URL var refURL *url.URL
var err error var err error
// TODO can I make this a param to `hugo --use-github-links=/docs`? refURL, err = url.Parse(strings.TrimPrefix(ref, currentPage.getRenderingConfig().SourceRelativeLinksProjectFolder))
// SVEN: add more tests - the prefix might be a real dir inside tho - add some pages that have it as a legitimate path
repositoryPathPrefix := "/docs"
refURL, err = url.Parse(strings.TrimPrefix(ref, repositoryPathPrefix))
if err != nil { if err != nil {
return "", err return "", err
} }
if refURL.Scheme != "" { if refURL.Scheme != "" {
// TODO: consider looking for http(s?)://github.com/user/project/prefix and replacing it - tho this may be intentional, so idk // Not a relative source level path
//return "", fmt.Errorf("Not a plain filepath link (%s)", ref)
// Treat this as not an error, as the link is used as-is
return ref, nil return ref, nil
} }
@ -291,19 +281,13 @@ func (s *SiteInfo) githubLink(ref string, currentPage *Page, relative bool) (str
return "", fmt.Errorf("No page found for \"%s\" on page \"%s\".\n", ref, currentPage.Source.Path()) return "", fmt.Errorf("No page found for \"%s\" on page \"%s\".\n", ref, currentPage.Source.Path())
} }
// SVEN: look at filepath.Rel() it might help, got the rel/non-rel url's (dangerous tho) link, err = target.RelPermalink()
if relative {
link, err = target.RelPermalink()
} else {
link, err = target.Permalink()
}
if err != nil { if err != nil {
return "", err return "", err
} }
} }
// SVEN: add tests for github style relative fragments
if refURL.Fragment != "" { if refURL.Fragment != "" {
link = link + "#" + refURL.Fragment link = link + "#" + refURL.Fragment
@ -317,29 +301,18 @@ func (s *SiteInfo) githubLink(ref string, currentPage *Page, relative bool) (str
return link, nil return link, nil
} }
// TODO(sven): Document // SourceRelativeLinkFile attempts to convert any non-md source relative links (like [../another.gif]) into absolute links
func (s *SiteInfo) GitHubFileLink(ref string, page *Page) (string, error) { func (s *SiteInfo) SourceRelativeLinkFile(ref string, currentPage *Page) (string, error) {
return s.githubFileLink(ref, page, false)
}
// for non-pages in the site tree
func (s *SiteInfo) githubFileLink(ref string, currentPage *Page, relative bool) (string, error) {
var refURL *url.URL var refURL *url.URL
var err error var err error
// TODO can I make this a param to `hugo --use-github-links=/docs`? refURL, err = url.Parse(strings.TrimPrefix(ref, currentPage.getRenderingConfig().SourceRelativeLinksProjectFolder))
// SVEN: add more tests - the prefix might be a real dir inside tho - add some pages that have it as a legitimate path
repositoryPathPrefix := "/docs"
refURL, err = url.Parse(strings.TrimPrefix(ref, repositoryPathPrefix))
if err != nil { if err != nil {
return "", err return "", err
} }
if refURL.Scheme != "" { if refURL.Scheme != "" {
// TODO: consider looking for http(s?)://github.com/user/project/prefix and replacing it - tho this may be intentional, so idk // Not a relative source level path
//return "", fmt.Errorf("Not a plain filepath link (%s)", ref)
// Treat this as not an error, as the link is used as-is
return ref, nil return ref, nil
} }
@ -369,13 +342,7 @@ func (s *SiteInfo) githubFileLink(ref string, currentPage *Page, relative bool)
} }
link = target.Path() link = target.Path()
// SVEN: look at filepath.Rel() it might help, got the rel/non-rel url's (dangerous tho) return "/" + filepath.ToSlash(link), nil
// SVEN: reconsider the fact I hardcoded the `relative` bool in both github resolvers
if relative {
return "./" + filepath.ToSlash(link), nil
} else {
return "/" + filepath.ToSlash(link), nil
}
} }
return "", fmt.Errorf("failed to find a file to match \"%s\" on page \"%s\"", ref, currentPage.Source.Path()) return "", fmt.Errorf("failed to find a file to match \"%s\" on page \"%s\"", ref, currentPage.Source.Path())

View file

@ -1023,8 +1023,6 @@ func TestWeightedTaxonomies(t *testing.T) {
} }
func findPage(site *Site, f string) *Page { func findPage(site *Site, f string) *Page {
// TODO: it seems that filepath.FromSlash results in page.Source.Path() returning windows backslash - which means refLinking's string compare is totally busted.
// TODO: Not used for non-fragment linking (SVEN thinks this is a bug)
currentPath := source.NewFile(filepath.FromSlash(f)) currentPath := source.NewFile(filepath.FromSlash(f))
//t.Logf("looking for currentPath: %s", currentPath.Path()) //t.Logf("looking for currentPath: %s", currentPath.Path())
@ -1062,6 +1060,15 @@ func setupLinkingMockSite(t *testing.T) *Site {
{filepath.FromSlash("level2/level3/common.png"), []byte("")}, {filepath.FromSlash("level2/level3/common.png"), []byte("")},
} }
viper.Set("baseurl", "http://auth/")
viper.Set("DefaultExtension", "html")
viper.Set("UglyURLs", false)
viper.Set("PluralizeListTitles", false)
viper.Set("CanonifyURLs", false)
viper.Set("blackfriday",
map[string]interface{}{
"sourceRelativeLinksProjectFolder": "/docs"})
site := &Site{ site := &Site{
Source: &source.InMemorySource{ByteSource: sources}, Source: &source.InMemorySource{ByteSource: sources},
} }
@ -1072,12 +1079,6 @@ func setupLinkingMockSite(t *testing.T) *Site {
t.Fatalf("Unable to create pages: %s", err) t.Fatalf("Unable to create pages: %s", err)
} }
viper.Set("baseurl", "http://auth/bub")
viper.Set("DefaultExtension", "html")
viper.Set("UglyURLs", false)
viper.Set("PluralizeListTitles", false)
viper.Set("CanonifyURLs", false)
return site return site
} }
@ -1228,7 +1229,7 @@ func TestSourceRelativeLinksing(t *testing.T) {
t.Fatalf("failed to find current page in site") t.Fatalf("failed to find current page in site")
} }
for link, url := range results { for link, url := range results {
if out, err := site.Info.githubLink(link, currentPage, true); err != nil || out != url { if out, err := site.Info.SourceRelativeLink(link, currentPage); err != nil || out != url {
t.Errorf("Expected %s to resolve to (%s), got (%s) - error: %s", link, url, out, err) t.Errorf("Expected %s to resolve to (%s), got (%s) - error: %s", link, url, out, err)
} else { } else {
//t.Logf("tested ok %s maps to %s", link, out) //t.Logf("tested ok %s maps to %s", link, out)
@ -1241,7 +1242,7 @@ func TestSourceRelativeLinksing(t *testing.T) {
} }
func TestGitHubFileLinking(t *testing.T) { func TestSourceRelativeLinkFileing(t *testing.T) {
viper.Reset() viper.Reset()
defer viper.Reset() defer viper.Reset()
site := setupLinkingMockSite(t) site := setupLinkingMockSite(t)
@ -1278,15 +1279,11 @@ func TestGitHubFileLinking(t *testing.T) {
t.Fatalf("failed to find current page in site") t.Fatalf("failed to find current page in site")
} }
for link, url := range results { for link, url := range results {
if out, err := site.Info.githubFileLink(link, currentPage, false); err != nil || out != url { if out, err := site.Info.SourceRelativeLinkFile(link, currentPage); err != nil || out != url {
t.Errorf("Expected %s to resolve to (%s), got (%s) - error: %s", link, url, out, err) t.Errorf("Expected %s to resolve to (%s), got (%s) - error: %s", link, url, out, err)
} else { } else {
//t.Logf("tested ok %s maps to %s", link, out) //t.Logf("tested ok %s maps to %s", link, out)
} }
} }
} }
// TODO: and then the failure cases.
// "https://docker.com": "",
// site_test.go:1094: Expected https://docker.com to resolve to (), got () - error: Not a plain filepath link (https://docker.com)
} }