diff --git a/docs/content/overview/configuration.md b/docs/content/overview/configuration.md index e351c651a..9d721d4a0 100644 --- a/docs/content/overview/configuration.md +++ b/docs/content/overview/configuration.md @@ -289,6 +289,38 @@ Its behavior can be modified with the latexDashes flag listed below Extensions in this option won't be loaded.
Example: Add "autoHeaderIds" to disable EXTENSION_AUTO_HEADER_IDS. + + +sourceRelativeLinksEval +false +none + + +Purpose: +Source file based relative linking (a la Github).
+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. + + + + +sourceRelativeLinksProjectFolder +"/docs/content" +none + + +Purpose: +Source file based relative linking Hugo Project sub-folder.
+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. + + + diff --git a/helpers/content.go b/helpers/content.go index c1353ae29..f60349055 100644 --- a/helpers/content.go +++ b/helpers/content.go @@ -43,29 +43,31 @@ var SummaryDivider = []byte("") // Blackfriday holds configuration values for Blackfriday rendering. type Blackfriday struct { - Smartypants bool - AngledQuotes bool - Fractions bool - HrefTargetBlank bool - SmartDashes bool - LatexDashes bool - PlainIDAnchors bool - SourceRelativeLinksEval bool - Extensions []string - ExtensionsMask []string + Smartypants bool + AngledQuotes bool + Fractions bool + HrefTargetBlank bool + SmartDashes bool + LatexDashes bool + PlainIDAnchors bool + SourceRelativeLinksEval bool + SourceRelativeLinksProjectFolder string + Extensions []string + ExtensionsMask []string } // NewBlackfriday creates a new Blackfriday filled with site config or some sane defaults. func NewBlackfriday() *Blackfriday { combinedParam := map[string]interface{}{ - "smartypants": true, - "angledQuotes": false, - "fractions": true, - "hrefTargetBlank": false, - "smartDashes": true, - "latexDashes": true, - "plainIDAnchors": false, - "sourceRelativeLinks": false, + "smartypants": true, + "angledQuotes": false, + "fractions": true, + "hrefTargetBlank": false, + "smartDashes": true, + "latexDashes": true, + "plainIDAnchors": false, + "sourceRelativeLinks": false, + "sourceRelativeLinksProjectFolder": "/docs/content", } siteParam := viper.GetStringMap("blackfriday") diff --git a/helpers/content_renderer.go b/helpers/content_renderer.go index aedf0e1d0..18df9ebef 100644 --- a/helpers/content_renderer.go +++ b/helpers/content_renderer.go @@ -49,6 +49,7 @@ func (renderer *HugoHTMLRenderer) Link(out *bytes.Buffer, link []byte, title []b // Use the blackfriday built in Link handler renderer.Renderer.Link(out, link, title, content) } else { + // set by SourceRelativeLinksEval newLink, err := renderer.LinkResolver(string(link)) if err != nil { newLink = string(link) @@ -62,6 +63,7 @@ func (renderer *HugoHTMLRenderer) Image(out *bytes.Buffer, link []byte, title [] // Use the blackfriday built in Image handler renderer.Renderer.Image(out, link, title, alt) } else { + // set by SourceRelativeLinksEval newLink, err := renderer.FileResolver(string(link)) if err != nil { newLink = string(link) diff --git a/hugolib/page.go b/hugolib/page.go index 3d1b48738..2342e368f 100644 --- a/hugolib/page.go +++ b/hugolib/page.go @@ -257,10 +257,10 @@ func (p *Page) renderBytes(content []byte) []byte { var fileFn helpers.FileResolverFunc if p.getRenderingConfig().SourceRelativeLinksEval { 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) { - return p.Node.Site.GitHubFileLink(ref, p) + return p.Node.Site.SourceRelativeLinkFile(ref, p) } } return helpers.RenderBytes( @@ -273,10 +273,10 @@ func (p *Page) renderContent(content []byte) []byte { var fileFn helpers.FileResolverFunc if p.getRenderingConfig().SourceRelativeLinksEval { 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) { - return p.Node.Site.GitHubFileLink(ref, p) + return p.Node.Site.SourceRelativeLinkFile(ref, p) } } return helpers.RenderBytesWithTOC(&helpers.RenderingContext{Content: content, PageFmt: p.guessMarkupType(), diff --git a/hugolib/site.go b/hugolib/site.go index ea9297b3d..0f488f7cb 100644 --- a/hugolib/site.go +++ b/hugolib/site.go @@ -225,28 +225,18 @@ func (s *SiteInfo) RelRef(ref string, page *Page) (string, error) { return s.refLink(ref, page, true) } -// TODO(sven): Document -func (s *SiteInfo) GitHub(ref string, page *Page) (string, error) { - return s.githubLink(ref, page, true) -} - -func (s *SiteInfo) githubLink(ref string, currentPage *Page, relative bool) (string, error) { +// SourceRelativeLink attempts to convert any source page relative links (like [../another.md]) into absolute links +func (s *SiteInfo) SourceRelativeLink(ref string, currentPage *Page) (string, error) { var refURL *url.URL var err error - // TODO can I make this a param to `hugo --use-github-links=/docs`? - // 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)) + refURL, err = url.Parse(strings.TrimPrefix(ref, currentPage.getRenderingConfig().SourceRelativeLinksProjectFolder)) if err != nil { return "", err } if refURL.Scheme != "" { - // TODO: consider looking for http(s?)://github.com/user/project/prefix and replacing it - tho this may be intentional, so idk - //return "", fmt.Errorf("Not a plain filepath link (%s)", ref) - // Treat this as not an error, as the link is used as-is + // Not a relative source level path 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()) } - // SVEN: look at filepath.Rel() it might help, got the rel/non-rel url's (dangerous tho) - if relative { - link, err = target.RelPermalink() - } else { - link, err = target.Permalink() - } + link, err = target.RelPermalink() if err != nil { return "", err } } - // SVEN: add tests for github style relative fragments if refURL.Fragment != "" { link = link + "#" + refURL.Fragment @@ -317,29 +301,18 @@ func (s *SiteInfo) githubLink(ref string, currentPage *Page, relative bool) (str return link, nil } -// TODO(sven): Document -func (s *SiteInfo) GitHubFileLink(ref string, page *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) { +// SourceRelativeLinkFile attempts to convert any non-md source relative links (like [../another.gif]) into absolute links +func (s *SiteInfo) SourceRelativeLinkFile(ref string, currentPage *Page) (string, error) { var refURL *url.URL var err error - // TODO can I make this a param to `hugo --use-github-links=/docs`? - // 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)) + refURL, err = url.Parse(strings.TrimPrefix(ref, currentPage.getRenderingConfig().SourceRelativeLinksProjectFolder)) if err != nil { return "", err } if refURL.Scheme != "" { - // TODO: consider looking for http(s?)://github.com/user/project/prefix and replacing it - tho this may be intentional, so idk - //return "", fmt.Errorf("Not a plain filepath link (%s)", ref) - // Treat this as not an error, as the link is used as-is + // Not a relative source level path return ref, nil } @@ -369,13 +342,7 @@ func (s *SiteInfo) githubFileLink(ref string, currentPage *Page, relative bool) } link = target.Path() - // SVEN: look at filepath.Rel() it might help, got the rel/non-rel url's (dangerous tho) - // 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 "/" + filepath.ToSlash(link), nil } return "", fmt.Errorf("failed to find a file to match \"%s\" on page \"%s\"", ref, currentPage.Source.Path()) diff --git a/hugolib/site_test.go b/hugolib/site_test.go index ae3021624..dd52b6d26 100644 --- a/hugolib/site_test.go +++ b/hugolib/site_test.go @@ -1023,8 +1023,6 @@ func TestWeightedTaxonomies(t *testing.T) { } 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)) //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("")}, } + 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{ Source: &source.InMemorySource{ByteSource: sources}, } @@ -1072,12 +1079,6 @@ func setupLinkingMockSite(t *testing.T) *Site { 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 } @@ -1228,7 +1229,7 @@ func TestSourceRelativeLinksing(t *testing.T) { t.Fatalf("failed to find current page in site") } 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) } else { //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() defer viper.Reset() site := setupLinkingMockSite(t) @@ -1278,15 +1279,11 @@ func TestGitHubFileLinking(t *testing.T) { t.Fatalf("failed to find current page in site") } 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) } else { //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) - }