mirror of
https://github.com/gohugoio/hugo.git
synced 2025-08-28 22:19:59 +02:00
all: Refactor to nonglobal Viper, i18n etc.
This is a final rewrite that removes all the global state in Hugo, which also enables the use if `t.Parallel` in tests. Updates #2701 Fixes #3016
This commit is contained in:
@@ -1,63 +0,0 @@
|
||||
// Copyright 2016-present The Hugo Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package helpers implements general utility functions that work with
|
||||
// and on content. The helper functions defined here lay down the
|
||||
// foundation of how Hugo works with files and filepaths, and perform
|
||||
// string operations on content.
|
||||
package helpers
|
||||
|
||||
import (
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
// A cached version of the current ConfigProvider (language) and relatives. These globals
|
||||
// are unfortunate, but we still have some places that needs this that does
|
||||
// not have access to the site configuration.
|
||||
// These values will be set on initialization when rendering a new language.
|
||||
//
|
||||
// TODO(bep) Get rid of these.
|
||||
var (
|
||||
currentConfigProvider ConfigProvider
|
||||
)
|
||||
|
||||
// ConfigProvider provides the configuration settings for Hugo.
|
||||
type ConfigProvider interface {
|
||||
GetString(key string) string
|
||||
GetInt(key string) int
|
||||
GetBool(key string) bool
|
||||
GetStringMap(key string) map[string]interface{}
|
||||
GetStringMapString(key string) map[string]string
|
||||
Get(key string) interface{}
|
||||
}
|
||||
|
||||
// Config returns the currently active Hugo config. This will be set
|
||||
// per site (language) rendered.
|
||||
func Config() ConfigProvider {
|
||||
if currentConfigProvider != nil {
|
||||
return currentConfigProvider
|
||||
}
|
||||
// Some tests rely on this. We will fix that, eventually.
|
||||
return viper.Get("currentContentLanguage").(ConfigProvider)
|
||||
}
|
||||
|
||||
// InitConfigProviderForCurrentContentLanguage does what it says.
|
||||
func InitConfigProviderForCurrentContentLanguage() {
|
||||
currentConfigProvider = viper.Get("CurrentContentLanguage").(ConfigProvider)
|
||||
}
|
||||
|
||||
// ResetConfigProvider is used in tests.
|
||||
func ResetConfigProvider() {
|
||||
currentConfigProvider = nil
|
||||
|
||||
}
|
@@ -24,12 +24,13 @@ import (
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/spf13/hugo/config"
|
||||
|
||||
"github.com/miekg/mmark"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"github.com/russross/blackfriday"
|
||||
bp "github.com/spf13/hugo/bufferpool"
|
||||
jww "github.com/spf13/jwalterweatherman"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
"strings"
|
||||
"sync"
|
||||
@@ -41,6 +42,14 @@ var SummaryLength = 70
|
||||
// SummaryDivider denotes where content summarization should end. The default is "<!--more-->".
|
||||
var SummaryDivider = []byte("<!--more-->")
|
||||
|
||||
type ContentSpec struct {
|
||||
cfg config.Provider
|
||||
}
|
||||
|
||||
func NewContentSpec(cfg config.Provider) *ContentSpec {
|
||||
return &ContentSpec{cfg}
|
||||
}
|
||||
|
||||
// Blackfriday holds configuration values for Blackfriday rendering.
|
||||
type Blackfriday struct {
|
||||
Smartypants bool
|
||||
@@ -58,7 +67,7 @@ type Blackfriday struct {
|
||||
}
|
||||
|
||||
// NewBlackfriday creates a new Blackfriday filled with site config or some sane defaults.
|
||||
func NewBlackfriday(c ConfigProvider) *Blackfriday {
|
||||
func (c ContentSpec) NewBlackfriday() *Blackfriday {
|
||||
|
||||
defaultParam := map[string]interface{}{
|
||||
"smartypants": true,
|
||||
@@ -75,7 +84,7 @@ func NewBlackfriday(c ConfigProvider) *Blackfriday {
|
||||
|
||||
ToLowerMap(defaultParam)
|
||||
|
||||
siteParam := c.GetStringMap("blackfriday")
|
||||
siteParam := c.cfg.GetStringMap("blackfriday")
|
||||
|
||||
siteConfig := make(map[string]interface{})
|
||||
|
||||
@@ -187,10 +196,10 @@ func BytesToHTML(b []byte) template.HTML {
|
||||
}
|
||||
|
||||
// getHTMLRenderer creates a new Blackfriday HTML Renderer with the given configuration.
|
||||
func getHTMLRenderer(defaultFlags int, ctx *RenderingContext) blackfriday.Renderer {
|
||||
func (c ContentSpec) getHTMLRenderer(defaultFlags int, ctx *RenderingContext) blackfriday.Renderer {
|
||||
renderParameters := blackfriday.HtmlRendererParameters{
|
||||
FootnoteAnchorPrefix: viper.GetString("footnoteAnchorPrefix"),
|
||||
FootnoteReturnLinkContents: viper.GetString("footnoteReturnLinkContents"),
|
||||
FootnoteAnchorPrefix: c.cfg.GetString("footnoteAnchorPrefix"),
|
||||
FootnoteReturnLinkContents: c.cfg.GetString("footnoteReturnLinkContents"),
|
||||
}
|
||||
|
||||
b := len(ctx.DocumentID) != 0
|
||||
@@ -265,21 +274,21 @@ func getMarkdownExtensions(ctx *RenderingContext) int {
|
||||
return flags
|
||||
}
|
||||
|
||||
func markdownRender(ctx *RenderingContext) []byte {
|
||||
func (c ContentSpec) markdownRender(ctx *RenderingContext) []byte {
|
||||
if ctx.RenderTOC {
|
||||
return blackfriday.Markdown(ctx.Content,
|
||||
getHTMLRenderer(blackfriday.HTML_TOC, ctx),
|
||||
c.getHTMLRenderer(blackfriday.HTML_TOC, ctx),
|
||||
getMarkdownExtensions(ctx))
|
||||
}
|
||||
return blackfriday.Markdown(ctx.Content, getHTMLRenderer(0, ctx),
|
||||
return blackfriday.Markdown(ctx.Content, c.getHTMLRenderer(0, ctx),
|
||||
getMarkdownExtensions(ctx))
|
||||
}
|
||||
|
||||
// getMmarkHTMLRenderer creates a new mmark HTML Renderer with the given configuration.
|
||||
func getMmarkHTMLRenderer(defaultFlags int, ctx *RenderingContext) mmark.Renderer {
|
||||
func (c ContentSpec) getMmarkHTMLRenderer(defaultFlags int, ctx *RenderingContext) mmark.Renderer {
|
||||
renderParameters := mmark.HtmlRendererParameters{
|
||||
FootnoteAnchorPrefix: viper.GetString("footnoteAnchorPrefix"),
|
||||
FootnoteReturnLinkContents: viper.GetString("footnoteReturnLinkContents"),
|
||||
FootnoteAnchorPrefix: c.cfg.GetString("footnoteAnchorPrefix"),
|
||||
FootnoteReturnLinkContents: c.cfg.GetString("footnoteReturnLinkContents"),
|
||||
}
|
||||
|
||||
b := len(ctx.DocumentID) != 0
|
||||
@@ -294,6 +303,7 @@ func getMmarkHTMLRenderer(defaultFlags int, ctx *RenderingContext) mmark.Rendere
|
||||
|
||||
return &HugoMmarkHTMLRenderer{
|
||||
mmark.HtmlRendererWithParameters(htmlFlags, "", "", renderParameters),
|
||||
c.cfg,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -321,8 +331,8 @@ func getMmarkExtensions(ctx *RenderingContext) int {
|
||||
return flags
|
||||
}
|
||||
|
||||
func mmarkRender(ctx *RenderingContext) []byte {
|
||||
return mmark.Parse(ctx.Content, getMmarkHTMLRenderer(0, ctx),
|
||||
func (c ContentSpec) mmarkRender(ctx *RenderingContext) []byte {
|
||||
return mmark.Parse(ctx.Content, c.getMmarkHTMLRenderer(0, ctx),
|
||||
getMmarkExtensions(ctx)).Bytes()
|
||||
}
|
||||
|
||||
@@ -365,42 +375,44 @@ func ExtractTOC(content []byte) (newcontent []byte, toc []byte) {
|
||||
// RenderingContext holds contextual information, like content and configuration,
|
||||
// for a given content rendering.
|
||||
type RenderingContext struct {
|
||||
Content []byte
|
||||
PageFmt string
|
||||
DocumentID string
|
||||
DocumentName string
|
||||
Config *Blackfriday
|
||||
RenderTOC bool
|
||||
FileResolver FileResolverFunc
|
||||
LinkResolver LinkResolverFunc
|
||||
ConfigProvider ConfigProvider
|
||||
configInit sync.Once
|
||||
Content []byte
|
||||
PageFmt string
|
||||
DocumentID string
|
||||
DocumentName string
|
||||
Config *Blackfriday
|
||||
RenderTOC bool
|
||||
FileResolver FileResolverFunc
|
||||
LinkResolver LinkResolverFunc
|
||||
Cfg config.Provider
|
||||
configInit sync.Once
|
||||
}
|
||||
|
||||
func newViperProvidedRenderingContext() *RenderingContext {
|
||||
return &RenderingContext{ConfigProvider: viper.GetViper()}
|
||||
func newRenderingContext(cfg config.Provider) *RenderingContext {
|
||||
return &RenderingContext{Cfg: cfg}
|
||||
}
|
||||
|
||||
func (c *RenderingContext) getConfig() *Blackfriday {
|
||||
// TODO(bep) get rid of this
|
||||
c.configInit.Do(func() {
|
||||
if c.Config == nil {
|
||||
c.Config = NewBlackfriday(c.ConfigProvider)
|
||||
cs := NewContentSpec(c.Cfg)
|
||||
c.Config = cs.NewBlackfriday()
|
||||
}
|
||||
})
|
||||
return c.Config
|
||||
}
|
||||
|
||||
// RenderBytes renders a []byte.
|
||||
func RenderBytes(ctx *RenderingContext) []byte {
|
||||
func (c ContentSpec) RenderBytes(ctx *RenderingContext) []byte {
|
||||
switch ctx.PageFmt {
|
||||
default:
|
||||
return markdownRender(ctx)
|
||||
return c.markdownRender(ctx)
|
||||
case "markdown":
|
||||
return markdownRender(ctx)
|
||||
return c.markdownRender(ctx)
|
||||
case "asciidoc":
|
||||
return getAsciidocContent(ctx)
|
||||
case "mmark":
|
||||
return mmarkRender(ctx)
|
||||
return c.mmarkRender(ctx)
|
||||
case "rst":
|
||||
return getRstContent(ctx)
|
||||
}
|
||||
|
@@ -19,8 +19,8 @@ import (
|
||||
|
||||
"github.com/miekg/mmark"
|
||||
"github.com/russross/blackfriday"
|
||||
"github.com/spf13/hugo/config"
|
||||
jww "github.com/spf13/jwalterweatherman"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
type LinkResolverFunc func(ref string) (string, error)
|
||||
@@ -33,49 +33,49 @@ type HugoHTMLRenderer struct {
|
||||
blackfriday.Renderer
|
||||
}
|
||||
|
||||
func (renderer *HugoHTMLRenderer) BlockCode(out *bytes.Buffer, text []byte, lang string) {
|
||||
if viper.GetBool("pygmentsCodeFences") && (lang != "" || viper.GetBool("pygmentsCodeFencesGuessSyntax")) {
|
||||
opts := viper.GetString("pygmentsOptions")
|
||||
func (r *HugoHTMLRenderer) BlockCode(out *bytes.Buffer, text []byte, lang string) {
|
||||
if r.Cfg.GetBool("pygmentsCodeFences") && (lang != "" || r.Cfg.GetBool("pygmentsCodeFencesGuessSyntax")) {
|
||||
opts := r.Cfg.GetString("pygmentsOptions")
|
||||
str := html.UnescapeString(string(text))
|
||||
out.WriteString(Highlight(str, lang, opts))
|
||||
out.WriteString(Highlight(r.RenderingContext.Cfg, str, lang, opts))
|
||||
} else {
|
||||
renderer.Renderer.BlockCode(out, text, lang)
|
||||
r.Renderer.BlockCode(out, text, lang)
|
||||
}
|
||||
}
|
||||
|
||||
func (renderer *HugoHTMLRenderer) Link(out *bytes.Buffer, link []byte, title []byte, content []byte) {
|
||||
if renderer.LinkResolver == nil || bytes.HasPrefix(link, []byte("HAHAHUGOSHORTCODE")) {
|
||||
func (r *HugoHTMLRenderer) Link(out *bytes.Buffer, link []byte, title []byte, content []byte) {
|
||||
if r.LinkResolver == nil || bytes.HasPrefix(link, []byte("HAHAHUGOSHORTCODE")) {
|
||||
// Use the blackfriday built in Link handler
|
||||
renderer.Renderer.Link(out, link, title, content)
|
||||
r.Renderer.Link(out, link, title, content)
|
||||
} else {
|
||||
// set by SourceRelativeLinksEval
|
||||
newLink, err := renderer.LinkResolver(string(link))
|
||||
newLink, err := r.LinkResolver(string(link))
|
||||
if err != nil {
|
||||
newLink = string(link)
|
||||
jww.ERROR.Printf("LinkResolver: %s", err)
|
||||
}
|
||||
renderer.Renderer.Link(out, []byte(newLink), title, content)
|
||||
r.Renderer.Link(out, []byte(newLink), title, content)
|
||||
}
|
||||
}
|
||||
func (renderer *HugoHTMLRenderer) Image(out *bytes.Buffer, link []byte, title []byte, alt []byte) {
|
||||
if renderer.FileResolver == nil || bytes.HasPrefix(link, []byte("HAHAHUGOSHORTCODE")) {
|
||||
func (r *HugoHTMLRenderer) Image(out *bytes.Buffer, link []byte, title []byte, alt []byte) {
|
||||
if r.FileResolver == nil || bytes.HasPrefix(link, []byte("HAHAHUGOSHORTCODE")) {
|
||||
// Use the blackfriday built in Image handler
|
||||
renderer.Renderer.Image(out, link, title, alt)
|
||||
r.Renderer.Image(out, link, title, alt)
|
||||
} else {
|
||||
// set by SourceRelativeLinksEval
|
||||
newLink, err := renderer.FileResolver(string(link))
|
||||
newLink, err := r.FileResolver(string(link))
|
||||
if err != nil {
|
||||
newLink = string(link)
|
||||
jww.ERROR.Printf("FileResolver: %s", err)
|
||||
}
|
||||
renderer.Renderer.Image(out, []byte(newLink), title, alt)
|
||||
r.Renderer.Image(out, []byte(newLink), title, alt)
|
||||
}
|
||||
}
|
||||
|
||||
// ListItem adds task list support to the Blackfriday renderer.
|
||||
func (renderer *HugoHTMLRenderer) ListItem(out *bytes.Buffer, text []byte, flags int) {
|
||||
if !renderer.Config.TaskLists {
|
||||
renderer.Renderer.ListItem(out, text, flags)
|
||||
func (r *HugoHTMLRenderer) ListItem(out *bytes.Buffer, text []byte, flags int) {
|
||||
if !r.Config.TaskLists {
|
||||
r.Renderer.ListItem(out, text, flags)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -87,17 +87,17 @@ func (renderer *HugoHTMLRenderer) ListItem(out *bytes.Buffer, text []byte, flags
|
||||
text = append([]byte(`<input type="checkbox" checked disabled class="task-list-item">`), text[3:]...)
|
||||
}
|
||||
|
||||
renderer.Renderer.ListItem(out, text, flags)
|
||||
r.Renderer.ListItem(out, text, flags)
|
||||
}
|
||||
|
||||
// List adds task list support to the Blackfriday renderer.
|
||||
func (renderer *HugoHTMLRenderer) List(out *bytes.Buffer, text func() bool, flags int) {
|
||||
if !renderer.Config.TaskLists {
|
||||
renderer.Renderer.List(out, text, flags)
|
||||
func (r *HugoHTMLRenderer) List(out *bytes.Buffer, text func() bool, flags int) {
|
||||
if !r.Config.TaskLists {
|
||||
r.Renderer.List(out, text, flags)
|
||||
return
|
||||
}
|
||||
marker := out.Len()
|
||||
renderer.Renderer.List(out, text, flags)
|
||||
r.Renderer.List(out, text, flags)
|
||||
if out.Len() > marker {
|
||||
list := out.Bytes()[marker:]
|
||||
if bytes.Contains(list, []byte("task-list-item")) {
|
||||
@@ -114,13 +114,14 @@ func (renderer *HugoHTMLRenderer) List(out *bytes.Buffer, text func() bool, flag
|
||||
// Enabling Hugo to customise the rendering experience
|
||||
type HugoMmarkHTMLRenderer struct {
|
||||
mmark.Renderer
|
||||
Cfg config.Provider
|
||||
}
|
||||
|
||||
func (renderer *HugoMmarkHTMLRenderer) BlockCode(out *bytes.Buffer, text []byte, lang string, caption []byte, subfigure bool, callouts bool) {
|
||||
if viper.GetBool("pygmentsCodeFences") && (lang != "" || viper.GetBool("pygmentsCodeFencesGuessSyntax")) {
|
||||
func (r *HugoMmarkHTMLRenderer) BlockCode(out *bytes.Buffer, text []byte, lang string, caption []byte, subfigure bool, callouts bool) {
|
||||
if r.Cfg.GetBool("pygmentsCodeFences") && (lang != "" || r.Cfg.GetBool("pygmentsCodeFencesGuessSyntax")) {
|
||||
str := html.UnescapeString(string(text))
|
||||
out.WriteString(Highlight(str, lang, ""))
|
||||
out.WriteString(Highlight(r.Cfg, str, lang, ""))
|
||||
} else {
|
||||
renderer.Renderer.BlockCode(out, text, lang, caption, subfigure, callouts)
|
||||
r.Renderer.BlockCode(out, text, lang, caption, subfigure, callouts)
|
||||
}
|
||||
}
|
||||
|
@@ -22,9 +22,9 @@ import (
|
||||
)
|
||||
|
||||
// Renders a codeblock using Blackfriday
|
||||
func render(input string) string {
|
||||
ctx := newViperProvidedRenderingContext()
|
||||
render := getHTMLRenderer(0, ctx)
|
||||
func (c ContentSpec) render(input string) string {
|
||||
ctx := newRenderingContext(c.cfg)
|
||||
render := c.getHTMLRenderer(0, ctx)
|
||||
|
||||
buf := &bytes.Buffer{}
|
||||
render.BlockCode(buf, []byte(input), "html")
|
||||
@@ -32,9 +32,9 @@ func render(input string) string {
|
||||
}
|
||||
|
||||
// Renders a codeblock using Mmark
|
||||
func renderWithMmark(input string) string {
|
||||
ctx := newViperProvidedRenderingContext()
|
||||
render := getMmarkHTMLRenderer(0, ctx)
|
||||
func (c ContentSpec) renderWithMmark(input string) string {
|
||||
ctx := newRenderingContext(c.cfg)
|
||||
render := c.getMmarkHTMLRenderer(0, ctx)
|
||||
|
||||
buf := &bytes.Buffer{}
|
||||
render.BlockCode(buf, []byte(input), "html", []byte(""), false, false)
|
||||
@@ -59,16 +59,16 @@ func TestCodeFence(t *testing.T) {
|
||||
{false, "<html></html>", `(?s)^<pre><code class="language-html">.*?</code></pre>\n$`},
|
||||
}
|
||||
|
||||
viper.Reset()
|
||||
defer viper.Reset()
|
||||
|
||||
viper.Set("pygmentsStyle", "monokai")
|
||||
viper.Set("pygmentsUseClasses", true)
|
||||
|
||||
for i, d := range data {
|
||||
viper.Set("pygmentsCodeFences", d.enabled)
|
||||
v := viper.New()
|
||||
|
||||
result := render(d.input)
|
||||
v.Set("pygmentsStyle", "monokai")
|
||||
v.Set("pygmentsUseClasses", true)
|
||||
v.Set("pygmentsCodeFences", d.enabled)
|
||||
|
||||
c := NewContentSpec(v)
|
||||
|
||||
result := c.render(d.input)
|
||||
|
||||
expectedRe, err := regexp.Compile(d.expected)
|
||||
|
||||
@@ -81,7 +81,7 @@ func TestCodeFence(t *testing.T) {
|
||||
t.Errorf("Test %d failed. BlackFriday enabled:%t, Expected:\n%q got:\n%q", i, d.enabled, d.expected, result)
|
||||
}
|
||||
|
||||
result = renderWithMmark(d.input)
|
||||
result = c.renderWithMmark(d.input)
|
||||
matched = expectedRe.MatchString(result)
|
||||
if !matched {
|
||||
t.Errorf("Test %d failed. Mmark enabled:%t, Expected:\n%q got:\n%q", i, d.enabled, d.expected, result)
|
||||
@@ -90,6 +90,8 @@ func TestCodeFence(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestBlackfridayTaskList(t *testing.T) {
|
||||
c := newTestContentSpec()
|
||||
|
||||
for i, this := range []struct {
|
||||
markdown string
|
||||
taskListEnabled bool
|
||||
@@ -118,11 +120,11 @@ END
|
||||
</ul>
|
||||
`},
|
||||
} {
|
||||
blackFridayConfig := NewBlackfriday(viper.GetViper())
|
||||
blackFridayConfig := c.NewBlackfriday()
|
||||
blackFridayConfig.TaskLists = this.taskListEnabled
|
||||
ctx := &RenderingContext{Content: []byte(this.markdown), PageFmt: "markdown", Config: blackFridayConfig}
|
||||
|
||||
result := string(RenderBytes(ctx))
|
||||
result := string(c.RenderBytes(ctx))
|
||||
|
||||
if result != this.expect {
|
||||
t.Errorf("[%d] got \n%v but expected \n%v", i, result, this.expect)
|
||||
|
@@ -21,7 +21,6 @@ import (
|
||||
|
||||
"github.com/miekg/mmark"
|
||||
"github.com/russross/blackfriday"
|
||||
"github.com/spf13/viper"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
@@ -152,8 +151,9 @@ func TestTruncateWordsByRune(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetHTMLRendererFlags(t *testing.T) {
|
||||
ctx := newViperProvidedRenderingContext()
|
||||
renderer := getHTMLRenderer(blackfriday.HTML_USE_XHTML, ctx)
|
||||
c := newTestContentSpec()
|
||||
ctx := newRenderingContext(c.cfg)
|
||||
renderer := c.getHTMLRenderer(blackfriday.HTML_USE_XHTML, ctx)
|
||||
flags := renderer.GetFlags()
|
||||
if flags&blackfriday.HTML_USE_XHTML != blackfriday.HTML_USE_XHTML {
|
||||
t.Errorf("Test flag: %d was not found amongs set flags:%d; Result: %d", blackfriday.HTML_USE_XHTML, flags, flags&blackfriday.HTML_USE_XHTML)
|
||||
@@ -161,6 +161,8 @@ func TestGetHTMLRendererFlags(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetHTMLRendererAllFlags(t *testing.T) {
|
||||
c := newTestContentSpec()
|
||||
|
||||
type data struct {
|
||||
testFlag int
|
||||
}
|
||||
@@ -176,7 +178,7 @@ func TestGetHTMLRendererAllFlags(t *testing.T) {
|
||||
{blackfriday.HTML_SMARTYPANTS_LATEX_DASHES},
|
||||
}
|
||||
defaultFlags := blackfriday.HTML_USE_XHTML
|
||||
ctx := newViperProvidedRenderingContext()
|
||||
ctx := newRenderingContext(c.cfg)
|
||||
ctx.Config = ctx.getConfig()
|
||||
ctx.Config.AngledQuotes = true
|
||||
ctx.Config.Fractions = true
|
||||
@@ -186,7 +188,7 @@ func TestGetHTMLRendererAllFlags(t *testing.T) {
|
||||
ctx.Config.SmartDashes = true
|
||||
ctx.Config.Smartypants = true
|
||||
ctx.Config.SourceRelativeLinksEval = true
|
||||
renderer := getHTMLRenderer(defaultFlags, ctx)
|
||||
renderer := c.getHTMLRenderer(defaultFlags, ctx)
|
||||
actualFlags := renderer.GetFlags()
|
||||
var expectedFlags int
|
||||
//OR-ing flags together...
|
||||
@@ -199,12 +201,13 @@ func TestGetHTMLRendererAllFlags(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetHTMLRendererAnchors(t *testing.T) {
|
||||
ctx := newViperProvidedRenderingContext()
|
||||
c := newTestContentSpec()
|
||||
ctx := newRenderingContext(c.cfg)
|
||||
ctx.DocumentID = "testid"
|
||||
ctx.Config = ctx.getConfig()
|
||||
ctx.Config.PlainIDAnchors = false
|
||||
|
||||
actualRenderer := getHTMLRenderer(0, ctx)
|
||||
actualRenderer := c.getHTMLRenderer(0, ctx)
|
||||
headerBuffer := &bytes.Buffer{}
|
||||
footnoteBuffer := &bytes.Buffer{}
|
||||
expectedFootnoteHref := []byte("href=\"#fn:testid:href\"")
|
||||
@@ -223,11 +226,12 @@ func TestGetHTMLRendererAnchors(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetMmarkHTMLRenderer(t *testing.T) {
|
||||
ctx := newViperProvidedRenderingContext()
|
||||
c := newTestContentSpec()
|
||||
ctx := newRenderingContext(c.cfg)
|
||||
ctx.DocumentID = "testid"
|
||||
ctx.Config = ctx.getConfig()
|
||||
ctx.Config.PlainIDAnchors = false
|
||||
actualRenderer := getMmarkHTMLRenderer(0, ctx)
|
||||
actualRenderer := c.getMmarkHTMLRenderer(0, ctx)
|
||||
|
||||
headerBuffer := &bytes.Buffer{}
|
||||
footnoteBuffer := &bytes.Buffer{}
|
||||
@@ -247,7 +251,8 @@ func TestGetMmarkHTMLRenderer(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetMarkdownExtensionsMasksAreRemovedFromExtensions(t *testing.T) {
|
||||
ctx := newViperProvidedRenderingContext()
|
||||
c := newTestContentSpec()
|
||||
ctx := newRenderingContext(c.cfg)
|
||||
ctx.Config = ctx.getConfig()
|
||||
ctx.Config.Extensions = []string{"headerId"}
|
||||
ctx.Config.ExtensionsMask = []string{"noIntraEmphasis"}
|
||||
@@ -262,7 +267,8 @@ func TestGetMarkdownExtensionsByDefaultAllExtensionsAreEnabled(t *testing.T) {
|
||||
type data struct {
|
||||
testFlag int
|
||||
}
|
||||
ctx := newViperProvidedRenderingContext()
|
||||
c := newTestContentSpec()
|
||||
ctx := newRenderingContext(c.cfg)
|
||||
ctx.Config = ctx.getConfig()
|
||||
ctx.Config.Extensions = []string{""}
|
||||
ctx.Config.ExtensionsMask = []string{""}
|
||||
@@ -294,7 +300,8 @@ func TestGetMarkdownExtensionsByDefaultAllExtensionsAreEnabled(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetMarkdownExtensionsAddingFlagsThroughRenderingContext(t *testing.T) {
|
||||
ctx := newViperProvidedRenderingContext()
|
||||
c := newTestContentSpec()
|
||||
ctx := newRenderingContext(c.cfg)
|
||||
ctx.Config = ctx.getConfig()
|
||||
ctx.Config.Extensions = []string{"definitionLists"}
|
||||
ctx.Config.ExtensionsMask = []string{""}
|
||||
@@ -306,10 +313,11 @@ func TestGetMarkdownExtensionsAddingFlagsThroughRenderingContext(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetMarkdownRenderer(t *testing.T) {
|
||||
ctx := newViperProvidedRenderingContext()
|
||||
c := newTestContentSpec()
|
||||
ctx := newRenderingContext(c.cfg)
|
||||
ctx.Content = []byte("testContent")
|
||||
ctx.Config = ctx.getConfig()
|
||||
actualRenderedMarkdown := markdownRender(ctx)
|
||||
actualRenderedMarkdown := c.markdownRender(ctx)
|
||||
expectedRenderedMarkdown := []byte("<p>testContent</p>\n")
|
||||
if !bytes.Equal(actualRenderedMarkdown, expectedRenderedMarkdown) {
|
||||
t.Errorf("Actual rendered Markdown (%s) did not match expected markdown (%s)", actualRenderedMarkdown, expectedRenderedMarkdown)
|
||||
@@ -317,10 +325,11 @@ func TestGetMarkdownRenderer(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetMarkdownRendererWithTOC(t *testing.T) {
|
||||
ctx := &RenderingContext{RenderTOC: true, ConfigProvider: viper.GetViper()}
|
||||
c := newTestContentSpec()
|
||||
ctx := &RenderingContext{RenderTOC: true, Cfg: c.cfg}
|
||||
ctx.Content = []byte("testContent")
|
||||
ctx.Config = ctx.getConfig()
|
||||
actualRenderedMarkdown := markdownRender(ctx)
|
||||
actualRenderedMarkdown := c.markdownRender(ctx)
|
||||
expectedRenderedMarkdown := []byte("<nav>\n</nav>\n\n<p>testContent</p>\n")
|
||||
if !bytes.Equal(actualRenderedMarkdown, expectedRenderedMarkdown) {
|
||||
t.Errorf("Actual rendered Markdown (%s) did not match expected markdown (%s)", actualRenderedMarkdown, expectedRenderedMarkdown)
|
||||
@@ -332,7 +341,8 @@ func TestGetMmarkExtensions(t *testing.T) {
|
||||
type data struct {
|
||||
testFlag int
|
||||
}
|
||||
ctx := newViperProvidedRenderingContext()
|
||||
c := newTestContentSpec()
|
||||
ctx := newRenderingContext(c.cfg)
|
||||
ctx.Config = ctx.getConfig()
|
||||
ctx.Config.Extensions = []string{"tables"}
|
||||
ctx.Config.ExtensionsMask = []string{""}
|
||||
@@ -361,10 +371,11 @@ func TestGetMmarkExtensions(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestMmarkRender(t *testing.T) {
|
||||
ctx := newViperProvidedRenderingContext()
|
||||
c := newTestContentSpec()
|
||||
ctx := newRenderingContext(c.cfg)
|
||||
ctx.Content = []byte("testContent")
|
||||
ctx.Config = ctx.getConfig()
|
||||
actualRenderedMarkdown := mmarkRender(ctx)
|
||||
actualRenderedMarkdown := c.mmarkRender(ctx)
|
||||
expectedRenderedMarkdown := []byte("<p>testContent</p>\n")
|
||||
if !bytes.Equal(actualRenderedMarkdown, expectedRenderedMarkdown) {
|
||||
t.Errorf("Actual rendered Markdown (%s) did not match expected markdown (%s)", actualRenderedMarkdown, expectedRenderedMarkdown)
|
||||
|
@@ -32,7 +32,6 @@ import (
|
||||
bp "github.com/spf13/hugo/bufferpool"
|
||||
jww "github.com/spf13/jwalterweatherman"
|
||||
"github.com/spf13/pflag"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
// FilePathSeparator as defined by os.Separator.
|
||||
@@ -196,8 +195,8 @@ func ReaderContains(r io.Reader, subslice []byte) bool {
|
||||
}
|
||||
|
||||
// ThemeSet checks whether a theme is in use or not.
|
||||
func ThemeSet() bool {
|
||||
return viper.GetString("theme") != ""
|
||||
func (p *PathSpec) ThemeSet() bool {
|
||||
return p.theme != ""
|
||||
}
|
||||
|
||||
type logPrinter interface {
|
||||
|
@@ -19,8 +19,7 @@ import (
|
||||
"sync"
|
||||
|
||||
"github.com/spf13/cast"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
"github.com/spf13/hugo/config"
|
||||
)
|
||||
|
||||
// These are the settings that should only be looked up in the global Viper
|
||||
@@ -41,26 +40,28 @@ type Language struct {
|
||||
LanguageName string
|
||||
Title string
|
||||
Weight int
|
||||
params map[string]interface{}
|
||||
paramsInit sync.Once
|
||||
|
||||
Cfg config.Provider
|
||||
params map[string]interface{}
|
||||
paramsInit sync.Once
|
||||
}
|
||||
|
||||
func (l *Language) String() string {
|
||||
return l.Lang
|
||||
}
|
||||
|
||||
func NewLanguage(lang string) *Language {
|
||||
return &Language{Lang: lang, params: make(map[string]interface{})}
|
||||
func NewLanguage(lang string, cfg config.Provider) *Language {
|
||||
return &Language{Lang: lang, Cfg: cfg, params: make(map[string]interface{})}
|
||||
}
|
||||
|
||||
func NewDefaultLanguage() *Language {
|
||||
defaultLang := viper.GetString("defaultContentLanguage")
|
||||
func NewDefaultLanguage(cfg config.Provider) *Language {
|
||||
defaultLang := cfg.GetString("defaultContentLanguage")
|
||||
|
||||
if defaultLang == "" {
|
||||
defaultLang = "en"
|
||||
}
|
||||
|
||||
return NewLanguage(defaultLang)
|
||||
return NewLanguage(defaultLang, cfg)
|
||||
}
|
||||
|
||||
type Languages []*Language
|
||||
@@ -83,7 +84,7 @@ func (l *Language) Params() map[string]interface{} {
|
||||
// Merge with global config.
|
||||
// TODO(bep) consider making this part of a constructor func.
|
||||
|
||||
globalParams := viper.GetStringMap("params")
|
||||
globalParams := l.Cfg.GetStringMap("params")
|
||||
for k, v := range globalParams {
|
||||
if _, ok := l.params[k]; !ok {
|
||||
l.params[k] = v
|
||||
@@ -132,5 +133,28 @@ func (l *Language) Get(key string) interface{} {
|
||||
return v
|
||||
}
|
||||
}
|
||||
return viper.Get(key)
|
||||
return l.Cfg.Get(key)
|
||||
}
|
||||
|
||||
// Set sets the value for the key in the language's params.
|
||||
func (l *Language) Set(key string, value interface{}) {
|
||||
if l == nil {
|
||||
panic("language not set")
|
||||
}
|
||||
key = strings.ToLower(key)
|
||||
l.params[key] = value
|
||||
}
|
||||
|
||||
// IsSet checks whether the key is set in the language or the related config store.
|
||||
func (l *Language) IsSet(key string) bool {
|
||||
key = strings.ToLower(key)
|
||||
|
||||
key = strings.ToLower(key)
|
||||
if !globalOnlySettings[key] {
|
||||
if _, ok := l.params[key]; ok {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return l.Cfg.IsSet(key)
|
||||
|
||||
}
|
||||
|
@@ -21,11 +21,12 @@ import (
|
||||
)
|
||||
|
||||
func TestGetGlobalOnlySetting(t *testing.T) {
|
||||
lang := NewDefaultLanguage()
|
||||
v := viper.New()
|
||||
lang := NewDefaultLanguage(v)
|
||||
lang.SetParam("defaultContentLanguageInSubdir", false)
|
||||
lang.SetParam("paginatePath", "side")
|
||||
viper.Set("defaultContentLanguageInSubdir", true)
|
||||
viper.Set("paginatePath", "page")
|
||||
v.Set("defaultContentLanguageInSubdir", true)
|
||||
v.Set("paginatePath", "page")
|
||||
|
||||
require.True(t, lang.GetBool("defaultContentLanguageInSubdir"))
|
||||
require.Equal(t, "side", lang.GetString("paginatePath"))
|
||||
|
@@ -24,7 +24,6 @@ import (
|
||||
"unicode"
|
||||
|
||||
"github.com/spf13/afero"
|
||||
"github.com/spf13/viper"
|
||||
"golang.org/x/text/transform"
|
||||
"golang.org/x/text/unicode/norm"
|
||||
)
|
||||
@@ -153,41 +152,41 @@ func ReplaceExtension(path string, newExt string) string {
|
||||
|
||||
// AbsPathify creates an absolute path if given a relative path. If already
|
||||
// absolute, the path is just cleaned.
|
||||
func AbsPathify(inPath string) string {
|
||||
func (p *PathSpec) AbsPathify(inPath string) string {
|
||||
if filepath.IsAbs(inPath) {
|
||||
return filepath.Clean(inPath)
|
||||
}
|
||||
|
||||
// TODO(bep): Consider moving workingDir to argument list
|
||||
return filepath.Clean(filepath.Join(viper.GetString("workingDir"), inPath))
|
||||
return filepath.Clean(filepath.Join(p.workingDir, inPath))
|
||||
}
|
||||
|
||||
// GetLayoutDirPath returns the absolute path to the layout file dir
|
||||
// for the current Hugo project.
|
||||
func GetLayoutDirPath() string {
|
||||
return AbsPathify(viper.GetString("layoutDir"))
|
||||
func (p *PathSpec) GetLayoutDirPath() string {
|
||||
return p.AbsPathify(p.layoutDir)
|
||||
}
|
||||
|
||||
// GetStaticDirPath returns the absolute path to the static file dir
|
||||
// for the current Hugo project.
|
||||
func GetStaticDirPath() string {
|
||||
return AbsPathify(viper.GetString("staticDir"))
|
||||
func (p *PathSpec) GetStaticDirPath() string {
|
||||
return p.AbsPathify(p.staticDir)
|
||||
}
|
||||
|
||||
// GetThemeDir gets the root directory of the current theme, if there is one.
|
||||
// If there is no theme, returns the empty string.
|
||||
func GetThemeDir() string {
|
||||
if ThemeSet() {
|
||||
return AbsPathify(filepath.Join(viper.GetString("themesDir"), viper.GetString("theme")))
|
||||
func (p *PathSpec) GetThemeDir() string {
|
||||
if p.ThemeSet() {
|
||||
return p.AbsPathify(filepath.Join(p.themesDir, p.theme))
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// GetRelativeThemeDir gets the relative root directory of the current theme, if there is one.
|
||||
// If there is no theme, returns the empty string.
|
||||
func GetRelativeThemeDir() string {
|
||||
if ThemeSet() {
|
||||
return strings.TrimPrefix(filepath.Join(viper.GetString("themesDir"), viper.GetString("theme")), FilePathSeparator)
|
||||
func (p *PathSpec) GetRelativeThemeDir() string {
|
||||
if p.ThemeSet() {
|
||||
return strings.TrimPrefix(filepath.Join(p.themesDir, p.theme), FilePathSeparator)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
@@ -211,13 +210,13 @@ func (p *PathSpec) GetThemeI18nDirPath() (string, error) {
|
||||
}
|
||||
|
||||
func (p *PathSpec) getThemeDirPath(path string) (string, error) {
|
||||
if !ThemeSet() {
|
||||
if !p.ThemeSet() {
|
||||
return "", ErrThemeUndefined
|
||||
}
|
||||
|
||||
themeDir := filepath.Join(GetThemeDir(), path)
|
||||
themeDir := filepath.Join(p.GetThemeDir(), path)
|
||||
if _, err := p.fs.Source.Stat(themeDir); os.IsNotExist(err) {
|
||||
return "", fmt.Errorf("Unable to find %s directory for theme %s in %s", path, viper.GetString("theme"), themeDir)
|
||||
return "", fmt.Errorf("Unable to find %s directory for theme %s in %s", path, p.theme, themeDir)
|
||||
}
|
||||
|
||||
return themeDir, nil
|
||||
@@ -235,7 +234,7 @@ func (p *PathSpec) GetThemesDirPath() string {
|
||||
// It does so by taking either the project's static path or the theme's static
|
||||
// path into consideration.
|
||||
func (p *PathSpec) MakeStaticPathRelative(inPath string) (string, error) {
|
||||
staticDir := GetStaticDirPath()
|
||||
staticDir := p.GetStaticDirPath()
|
||||
themeStaticDir := p.GetThemesDirPath()
|
||||
|
||||
return makePathRelative(inPath, staticDir, themeStaticDir)
|
||||
@@ -360,20 +359,20 @@ func GetRelativePath(path, base string) (final string, err error) {
|
||||
}
|
||||
|
||||
// PaginateAliasPath creates a path used to access the aliases in the paginator.
|
||||
func PaginateAliasPath(base string, page int) string {
|
||||
paginatePath := Config().GetString("paginatePath")
|
||||
uglify := viper.GetBool("uglyURLs")
|
||||
var p string
|
||||
func (p *PathSpec) PaginateAliasPath(base string, page int) string {
|
||||
paginatePath := p.paginatePath
|
||||
uglify := p.uglyURLs
|
||||
var pth string
|
||||
if base != "" {
|
||||
p = filepath.FromSlash(fmt.Sprintf("/%s/%s/%d", base, paginatePath, page))
|
||||
pth = filepath.FromSlash(fmt.Sprintf("/%s/%s/%d", base, paginatePath, page))
|
||||
} else {
|
||||
p = filepath.FromSlash(fmt.Sprintf("/%s/%d", paginatePath, page))
|
||||
pth = filepath.FromSlash(fmt.Sprintf("/%s/%d", paginatePath, page))
|
||||
}
|
||||
if uglify {
|
||||
p += ".html"
|
||||
pth += ".html"
|
||||
}
|
||||
|
||||
return p
|
||||
return pth
|
||||
}
|
||||
|
||||
// GuessSection returns the section given a source path.
|
||||
|
@@ -34,15 +34,7 @@ import (
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
func initCommonTestConfig() {
|
||||
viper.Set("currentContentLanguage", NewLanguage("en"))
|
||||
}
|
||||
|
||||
func TestMakePath(t *testing.T) {
|
||||
viper.Reset()
|
||||
defer viper.Reset()
|
||||
initCommonTestConfig()
|
||||
|
||||
tests := []struct {
|
||||
input string
|
||||
expected string
|
||||
@@ -64,8 +56,10 @@ func TestMakePath(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
viper.Set("removePathAccents", test.removeAccents)
|
||||
p := NewPathSpec(hugofs.NewMem(), viper.GetViper())
|
||||
v := viper.New()
|
||||
l := NewDefaultLanguage(v)
|
||||
v.Set("removePathAccents", test.removeAccents)
|
||||
p := NewPathSpec(hugofs.NewMem(v), l)
|
||||
|
||||
output := p.MakePath(test.input)
|
||||
if output != test.expected {
|
||||
@@ -75,11 +69,9 @@ func TestMakePath(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestMakePathSanitized(t *testing.T) {
|
||||
viper.Reset()
|
||||
defer viper.Reset()
|
||||
initCommonTestConfig()
|
||||
|
||||
p := NewPathSpec(hugofs.NewMem(), viper.GetViper())
|
||||
v := viper.New()
|
||||
l := NewDefaultLanguage(v)
|
||||
p := NewPathSpec(hugofs.NewMem(v), l)
|
||||
|
||||
tests := []struct {
|
||||
input string
|
||||
@@ -102,12 +94,12 @@ func TestMakePathSanitized(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestMakePathSanitizedDisablePathToLower(t *testing.T) {
|
||||
viper.Reset()
|
||||
defer viper.Reset()
|
||||
v := viper.New()
|
||||
|
||||
initCommonTestConfig()
|
||||
viper.Set("disablePathToLower", true)
|
||||
p := NewPathSpec(hugofs.NewMem(), viper.GetViper())
|
||||
v.Set("disablePathToLower", true)
|
||||
|
||||
l := NewDefaultLanguage(v)
|
||||
p := NewPathSpec(hugofs.NewMem(v), l)
|
||||
|
||||
tests := []struct {
|
||||
input string
|
||||
@@ -553,9 +545,9 @@ func TestAbsPathify(t *testing.T) {
|
||||
for i, d := range data {
|
||||
viper.Reset()
|
||||
// todo see comment in AbsPathify
|
||||
viper.Set("workingDir", d.workingDir)
|
||||
ps := newTestDefaultPathSpec("workingDir", d.workingDir)
|
||||
|
||||
expected := AbsPathify(d.inPath)
|
||||
expected := ps.AbsPathify(d.inPath)
|
||||
if d.expected != expected {
|
||||
t.Errorf("Test %d failed. Expected %q but got %q", i, d.expected, expected)
|
||||
}
|
||||
@@ -563,18 +555,18 @@ func TestAbsPathify(t *testing.T) {
|
||||
t.Logf("Running platform specific path tests for %s", runtime.GOOS)
|
||||
if runtime.GOOS == "windows" {
|
||||
for i, d := range windowsData {
|
||||
viper.Set("workingDir", d.workingDir)
|
||||
ps := newTestDefaultPathSpec("workingDir", d.workingDir)
|
||||
|
||||
expected := AbsPathify(d.inPath)
|
||||
expected := ps.AbsPathify(d.inPath)
|
||||
if d.expected != expected {
|
||||
t.Errorf("Test %d failed. Expected %q but got %q", i, d.expected, expected)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for i, d := range unixData {
|
||||
viper.Set("workingDir", d.workingDir)
|
||||
ps := newTestDefaultPathSpec("workingDir", d.workingDir)
|
||||
|
||||
expected := AbsPathify(d.inPath)
|
||||
expected := ps.AbsPathify(d.inPath)
|
||||
if d.expected != expected {
|
||||
t.Errorf("Test %d failed. Expected %q but got %q", i, d.expected, expected)
|
||||
}
|
||||
|
@@ -16,6 +16,7 @@ package helpers
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/hugo/config"
|
||||
"github.com/spf13/hugo/hugofs"
|
||||
)
|
||||
|
||||
@@ -26,11 +27,20 @@ type PathSpec struct {
|
||||
uglyURLs bool
|
||||
canonifyURLs bool
|
||||
|
||||
currentContentLanguage *Language
|
||||
language *Language
|
||||
|
||||
// pagination path handling
|
||||
paginatePath string
|
||||
|
||||
baseURL string
|
||||
theme string
|
||||
|
||||
// Directories
|
||||
themesDir string
|
||||
layoutDir string
|
||||
workingDir string
|
||||
staticDir string
|
||||
|
||||
// The PathSpec looks up its config settings in both the current language
|
||||
// and then in the global Viper config.
|
||||
// Some settings, the settings listed below, does not make sense to be set
|
||||
@@ -45,31 +55,35 @@ type PathSpec struct {
|
||||
}
|
||||
|
||||
func (p PathSpec) String() string {
|
||||
return fmt.Sprintf("PathSpec, language %q, prefix %q, multilingual: %T", p.currentContentLanguage.Lang, p.getLanguagePrefix(), p.multilingual)
|
||||
return fmt.Sprintf("PathSpec, language %q, prefix %q, multilingual: %T", p.language.Lang, p.getLanguagePrefix(), p.multilingual)
|
||||
}
|
||||
|
||||
// NewPathSpec creats a new PathSpec from the given filesystems and ConfigProvider.
|
||||
func NewPathSpec(fs *hugofs.Fs, config ConfigProvider) *PathSpec {
|
||||
// NewPathSpec creats a new PathSpec from the given filesystems and Language.
|
||||
func NewPathSpec(fs *hugofs.Fs, cfg config.Provider) *PathSpec {
|
||||
|
||||
currCl, ok := config.Get("currentContentLanguage").(*Language)
|
||||
|
||||
if !ok {
|
||||
// TODO(bep) globals
|
||||
currCl = NewLanguage("en")
|
||||
}
|
||||
|
||||
return &PathSpec{
|
||||
ps := &PathSpec{
|
||||
fs: fs,
|
||||
disablePathToLower: config.GetBool("disablePathToLower"),
|
||||
removePathAccents: config.GetBool("removePathAccents"),
|
||||
uglyURLs: config.GetBool("uglyURLs"),
|
||||
canonifyURLs: config.GetBool("canonifyURLs"),
|
||||
multilingual: config.GetBool("multilingual"),
|
||||
defaultContentLanguageInSubdir: config.GetBool("defaultContentLanguageInSubdir"),
|
||||
defaultContentLanguage: config.GetString("defaultContentLanguage"),
|
||||
currentContentLanguage: currCl,
|
||||
paginatePath: config.GetString("paginatePath"),
|
||||
disablePathToLower: cfg.GetBool("disablePathToLower"),
|
||||
removePathAccents: cfg.GetBool("removePathAccents"),
|
||||
uglyURLs: cfg.GetBool("uglyURLs"),
|
||||
canonifyURLs: cfg.GetBool("canonifyURLs"),
|
||||
multilingual: cfg.GetBool("multilingual"),
|
||||
defaultContentLanguageInSubdir: cfg.GetBool("defaultContentLanguageInSubdir"),
|
||||
defaultContentLanguage: cfg.GetString("defaultContentLanguage"),
|
||||
paginatePath: cfg.GetString("paginatePath"),
|
||||
baseURL: cfg.GetString("baseURL"),
|
||||
themesDir: cfg.GetString("themesDir"),
|
||||
layoutDir: cfg.GetString("layoutDir"),
|
||||
workingDir: cfg.GetString("workingDir"),
|
||||
staticDir: cfg.GetString("staticDir"),
|
||||
theme: cfg.GetString("theme"),
|
||||
}
|
||||
|
||||
if language, ok := cfg.(*Language); ok {
|
||||
ps.language = language
|
||||
}
|
||||
|
||||
return ps
|
||||
}
|
||||
|
||||
// PaginatePath returns the configured root path used for paginator pages.
|
||||
|
@@ -23,17 +23,24 @@ import (
|
||||
)
|
||||
|
||||
func TestNewPathSpecFromConfig(t *testing.T) {
|
||||
viper.Set("disablePathToLower", true)
|
||||
viper.Set("removePathAccents", true)
|
||||
viper.Set("uglyURLs", true)
|
||||
viper.Set("multilingual", true)
|
||||
viper.Set("defaultContentLanguageInSubdir", true)
|
||||
viper.Set("defaultContentLanguage", "no")
|
||||
viper.Set("currentContentLanguage", NewLanguage("no"))
|
||||
viper.Set("canonifyURLs", true)
|
||||
viper.Set("paginatePath", "side")
|
||||
v := viper.New()
|
||||
l := NewLanguage("no", v)
|
||||
v.Set("disablePathToLower", true)
|
||||
v.Set("removePathAccents", true)
|
||||
v.Set("uglyURLs", true)
|
||||
v.Set("multilingual", true)
|
||||
v.Set("defaultContentLanguageInSubdir", true)
|
||||
v.Set("defaultContentLanguage", "no")
|
||||
v.Set("canonifyURLs", true)
|
||||
v.Set("paginatePath", "side")
|
||||
v.Set("baseURL", "http://base.com")
|
||||
v.Set("themesDir", "thethemes")
|
||||
v.Set("layoutDir", "thelayouts")
|
||||
v.Set("workingDir", "thework")
|
||||
v.Set("staticDir", "thestatic")
|
||||
v.Set("theme", "thetheme")
|
||||
|
||||
p := NewPathSpec(hugofs.NewMem(), viper.GetViper())
|
||||
p := NewPathSpec(hugofs.NewMem(v), l)
|
||||
|
||||
require.True(t, p.canonifyURLs)
|
||||
require.True(t, p.defaultContentLanguageInSubdir)
|
||||
@@ -42,6 +49,13 @@ func TestNewPathSpecFromConfig(t *testing.T) {
|
||||
require.True(t, p.removePathAccents)
|
||||
require.True(t, p.uglyURLs)
|
||||
require.Equal(t, "no", p.defaultContentLanguage)
|
||||
require.Equal(t, "no", p.currentContentLanguage.Lang)
|
||||
require.Equal(t, "no", p.language.Lang)
|
||||
require.Equal(t, "side", p.paginatePath)
|
||||
|
||||
require.Equal(t, "http://base.com", p.baseURL)
|
||||
require.Equal(t, "thethemes", p.themesDir)
|
||||
require.Equal(t, "thelayouts", p.layoutDir)
|
||||
require.Equal(t, "thework", p.workingDir)
|
||||
require.Equal(t, "thestatic", p.staticDir)
|
||||
require.Equal(t, "thetheme", p.theme)
|
||||
}
|
||||
|
@@ -24,9 +24,9 @@ import (
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/hugo/config"
|
||||
"github.com/spf13/hugo/hugofs"
|
||||
jww "github.com/spf13/jwalterweatherman"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
const pygmentsBin = "pygmentize"
|
||||
@@ -41,13 +41,13 @@ func HasPygments() bool {
|
||||
}
|
||||
|
||||
// Highlight takes some code and returns highlighted code.
|
||||
func Highlight(code, lang, optsStr string) string {
|
||||
func Highlight(cfg config.Provider, code, lang, optsStr string) string {
|
||||
if !HasPygments() {
|
||||
jww.WARN.Println("Highlighting requires Pygments to be installed and in the path")
|
||||
return code
|
||||
}
|
||||
|
||||
options, err := parsePygmentsOpts(optsStr)
|
||||
options, err := parsePygmentsOpts(cfg, optsStr)
|
||||
|
||||
if err != nil {
|
||||
jww.ERROR.Print(err.Error())
|
||||
@@ -62,8 +62,8 @@ func Highlight(code, lang, optsStr string) string {
|
||||
|
||||
fs := hugofs.Os
|
||||
|
||||
ignoreCache := viper.GetBool("ignoreCache")
|
||||
cacheDir := viper.GetString("cacheDir")
|
||||
ignoreCache := cfg.GetBool("ignoreCache")
|
||||
cacheDir := cfg.GetString("cacheDir")
|
||||
var cachefile string
|
||||
|
||||
if !ignoreCache && cacheDir != "" {
|
||||
@@ -195,19 +195,19 @@ func createOptionsString(options map[string]string) string {
|
||||
return optionsStr
|
||||
}
|
||||
|
||||
func parseDefaultPygmentsOpts() (map[string]string, error) {
|
||||
func parseDefaultPygmentsOpts(cfg config.Provider) (map[string]string, error) {
|
||||
options := make(map[string]string)
|
||||
err := parseOptions(options, viper.GetString("pygmentsOptions"))
|
||||
err := parseOptions(options, cfg.GetString("pygmentsOptions"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if viper.IsSet("pygmentsStyle") {
|
||||
options["style"] = viper.GetString("pygmentsStyle")
|
||||
if cfg.IsSet("pygmentsStyle") {
|
||||
options["style"] = cfg.GetString("pygmentsStyle")
|
||||
}
|
||||
|
||||
if viper.IsSet("pygmentsUseClasses") {
|
||||
if viper.GetBool("pygmentsUseClasses") {
|
||||
if cfg.IsSet("pygmentsUseClasses") {
|
||||
if cfg.GetBool("pygmentsUseClasses") {
|
||||
options["noclasses"] = "false"
|
||||
} else {
|
||||
options["noclasses"] = "true"
|
||||
@@ -222,8 +222,8 @@ func parseDefaultPygmentsOpts() (map[string]string, error) {
|
||||
return options, nil
|
||||
}
|
||||
|
||||
func parsePygmentsOpts(in string) (string, error) {
|
||||
options, err := parseDefaultPygmentsOpts()
|
||||
func parsePygmentsOpts(cfg config.Provider, in string) (string, error) {
|
||||
options, err := parseDefaultPygmentsOpts(cfg)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
@@ -34,11 +34,12 @@ func TestParsePygmentsArgs(t *testing.T) {
|
||||
{"boo=invalid", "foo", false, false},
|
||||
{"style", "foo", false, false},
|
||||
} {
|
||||
viper.Reset()
|
||||
viper.Set("pygmentsStyle", this.pygmentsStyle)
|
||||
viper.Set("pygmentsUseClasses", this.pygmentsUseClasses)
|
||||
|
||||
result1, err := parsePygmentsOpts(this.in)
|
||||
v := viper.New()
|
||||
v.Set("pygmentsStyle", this.pygmentsStyle)
|
||||
v.Set("pygmentsUseClasses", this.pygmentsUseClasses)
|
||||
|
||||
result1, err := parsePygmentsOpts(v, this.in)
|
||||
if b, ok := this.expect1.(bool); ok && !b {
|
||||
if err == nil {
|
||||
t.Errorf("[%d] parsePygmentArgs didn't return an expected error", i)
|
||||
@@ -70,19 +71,19 @@ func TestParseDefaultPygmentsArgs(t *testing.T) {
|
||||
{"style=foo,noclasses=false", nil, nil, "style=override,noclasses=override"},
|
||||
{"style=foo,noclasses=false", "override", false, "style=override,noclasses=override"},
|
||||
} {
|
||||
viper.Reset()
|
||||
v := viper.New()
|
||||
|
||||
viper.Set("pygmentsOptions", this.pygmentsOptions)
|
||||
v.Set("pygmentsOptions", this.pygmentsOptions)
|
||||
|
||||
if s, ok := this.pygmentsStyle.(string); ok {
|
||||
viper.Set("pygmentsStyle", s)
|
||||
v.Set("pygmentsStyle", s)
|
||||
}
|
||||
|
||||
if b, ok := this.pygmentsUseClasses.(bool); ok {
|
||||
viper.Set("pygmentsUseClasses", b)
|
||||
v.Set("pygmentsUseClasses", b)
|
||||
}
|
||||
|
||||
result, err := parsePygmentsOpts(this.in)
|
||||
result, err := parsePygmentsOpts(v, this.in)
|
||||
if err != nil {
|
||||
t.Errorf("[%d] parsePygmentArgs failed: %s", i, err)
|
||||
continue
|
||||
|
37
helpers/testhelpers_test.go
Normal file
37
helpers/testhelpers_test.go
Normal file
@@ -0,0 +1,37 @@
|
||||
package helpers
|
||||
|
||||
import (
|
||||
"github.com/spf13/viper"
|
||||
|
||||
"github.com/spf13/hugo/hugofs"
|
||||
)
|
||||
|
||||
func newTestPathSpec(fs *hugofs.Fs, v *viper.Viper) *PathSpec {
|
||||
l := NewDefaultLanguage(v)
|
||||
return NewPathSpec(fs, l)
|
||||
}
|
||||
|
||||
func newTestDefaultPathSpec(configKeyValues ...interface{}) *PathSpec {
|
||||
v := viper.New()
|
||||
fs := hugofs.NewMem(v)
|
||||
cfg := newTestCfg(fs)
|
||||
|
||||
for i := 0; i < len(configKeyValues); i += 2 {
|
||||
cfg.Set(configKeyValues[i].(string), configKeyValues[i+1])
|
||||
}
|
||||
return newTestPathSpec(fs, cfg)
|
||||
}
|
||||
|
||||
func newTestCfg(fs *hugofs.Fs) *viper.Viper {
|
||||
v := viper.New()
|
||||
|
||||
v.SetFs(fs.Source)
|
||||
|
||||
return v
|
||||
|
||||
}
|
||||
|
||||
func newTestContentSpec() *ContentSpec {
|
||||
v := viper.New()
|
||||
return NewContentSpec(v)
|
||||
}
|
@@ -21,7 +21,6 @@ import (
|
||||
|
||||
"github.com/PuerkitoBio/purell"
|
||||
jww "github.com/spf13/jwalterweatherman"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
type pathBridge struct {
|
||||
@@ -158,7 +157,7 @@ func (p *PathSpec) AbsURL(in string, addLanguage bool) string {
|
||||
return in
|
||||
}
|
||||
|
||||
baseURL := viper.GetString("baseURL")
|
||||
baseURL := p.baseURL
|
||||
if strings.HasPrefix(in, "/") {
|
||||
p, err := url.Parse(baseURL)
|
||||
if err != nil {
|
||||
@@ -200,7 +199,7 @@ func (p *PathSpec) getLanguagePrefix() string {
|
||||
defaultLang := p.defaultContentLanguage
|
||||
defaultInSubDir := p.defaultContentLanguageInSubdir
|
||||
|
||||
currentLang := p.currentContentLanguage.Lang
|
||||
currentLang := p.language.Lang
|
||||
if currentLang == "" || (currentLang == defaultLang && !defaultInSubDir) {
|
||||
return ""
|
||||
}
|
||||
@@ -220,7 +219,7 @@ func IsAbsURL(path string) bool {
|
||||
// RelURL creates a URL relative to the BaseURL root.
|
||||
// Note: The result URL will not include the context root if canonifyURLs is enabled.
|
||||
func (p *PathSpec) RelURL(in string, addLanguage bool) string {
|
||||
baseURL := viper.GetString("baseURL")
|
||||
baseURL := p.baseURL
|
||||
canonifyURLs := p.canonifyURLs
|
||||
if (!strings.HasPrefix(in, baseURL) && strings.HasPrefix(in, "http")) || strings.HasPrefix(in, "//") {
|
||||
return in
|
||||
|
@@ -25,9 +25,10 @@ import (
|
||||
)
|
||||
|
||||
func TestURLize(t *testing.T) {
|
||||
initCommonTestConfig()
|
||||
|
||||
p := NewPathSpec(hugofs.NewMem(), viper.GetViper())
|
||||
v := viper.New()
|
||||
l := NewDefaultLanguage(v)
|
||||
p := NewPathSpec(hugofs.NewMem(v), l)
|
||||
|
||||
tests := []struct {
|
||||
input string
|
||||
@@ -62,11 +63,10 @@ func TestAbsURL(t *testing.T) {
|
||||
}
|
||||
|
||||
func doTestAbsURL(t *testing.T, defaultInSubDir, addLanguage, multilingual bool, lang string) {
|
||||
viper.Reset()
|
||||
viper.Set("multilingual", multilingual)
|
||||
viper.Set("currentContentLanguage", NewLanguage(lang))
|
||||
viper.Set("defaultContentLanguage", "en")
|
||||
viper.Set("defaultContentLanguageInSubdir", defaultInSubDir)
|
||||
v := viper.New()
|
||||
v.Set("multilingual", multilingual)
|
||||
v.Set("defaultContentLanguage", "en")
|
||||
v.Set("defaultContentLanguageInSubdir", defaultInSubDir)
|
||||
|
||||
tests := []struct {
|
||||
input string
|
||||
@@ -86,10 +86,10 @@ func doTestAbsURL(t *testing.T, defaultInSubDir, addLanguage, multilingual bool,
|
||||
{"http//foo", "http://base/path", "http://base/path/MULTIhttp/foo"},
|
||||
}
|
||||
|
||||
p := NewPathSpec(hugofs.NewMem(), viper.GetViper())
|
||||
|
||||
for _, test := range tests {
|
||||
viper.Set("baseURL", test.baseURL)
|
||||
v.Set("baseURL", test.baseURL)
|
||||
l := NewLanguage(lang, v)
|
||||
p := NewPathSpec(hugofs.NewMem(v), l)
|
||||
|
||||
output := p.AbsURL(test.input, addLanguage)
|
||||
expected := test.expected
|
||||
@@ -138,11 +138,10 @@ func TestRelURL(t *testing.T) {
|
||||
}
|
||||
|
||||
func doTestRelURL(t *testing.T, defaultInSubDir, addLanguage, multilingual bool, lang string) {
|
||||
viper.Reset()
|
||||
viper.Set("multilingual", multilingual)
|
||||
viper.Set("currentContentLanguage", NewLanguage(lang))
|
||||
viper.Set("defaultContentLanguage", "en")
|
||||
viper.Set("defaultContentLanguageInSubdir", defaultInSubDir)
|
||||
v := viper.New()
|
||||
v.Set("multilingual", multilingual)
|
||||
v.Set("defaultContentLanguage", "en")
|
||||
v.Set("defaultContentLanguageInSubdir", defaultInSubDir)
|
||||
|
||||
tests := []struct {
|
||||
input string
|
||||
@@ -165,9 +164,10 @@ func doTestRelURL(t *testing.T, defaultInSubDir, addLanguage, multilingual bool,
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
viper.Set("baseURL", test.baseURL)
|
||||
viper.Set("canonifyURLs", test.canonify)
|
||||
p := NewPathSpec(hugofs.NewMem(), viper.GetViper())
|
||||
v.Set("baseURL", test.baseURL)
|
||||
v.Set("canonifyURLs", test.canonify)
|
||||
l := NewLanguage(lang, v)
|
||||
p := NewPathSpec(hugofs.NewMem(v), l)
|
||||
|
||||
output := p.RelURL(test.input, addLanguage)
|
||||
|
||||
@@ -252,8 +252,10 @@ func TestURLPrep(t *testing.T) {
|
||||
}
|
||||
|
||||
for i, d := range data {
|
||||
viper.Set("uglyURLs", d.ugly)
|
||||
p := NewPathSpec(hugofs.NewMem(), viper.GetViper())
|
||||
v := viper.New()
|
||||
v.Set("uglyURLs", d.ugly)
|
||||
l := NewDefaultLanguage(v)
|
||||
p := NewPathSpec(hugofs.NewMem(v), l)
|
||||
|
||||
output := p.URLPrep(d.input)
|
||||
if d.output != output {
|
||||
|
Reference in New Issue
Block a user