Auto detect line ending on startup
This commit is contained in:
parent
2cc48cfbb0
commit
e4cc33f098
8 changed files with 168 additions and 40 deletions
|
@ -18,8 +18,8 @@
|
||||||
<div class="reveal">
|
<div class="reveal">
|
||||||
<div class="slides">
|
<div class="slides">
|
||||||
<section data-markdown="/content.md"
|
<section data-markdown="/content.md"
|
||||||
data-separator="^{{ .Reveal.LineEnding -}}{{ .Reveal.HorizontalSeparator }}{{- .Reveal.LineEnding }}"
|
data-separator="^{{ .Reveal.LineEnding.Escaped -}}{{ .Reveal.HorizontalSeparator }}{{- .Reveal.LineEnding.Escaped }}"
|
||||||
data-separator-vertical="^{{ .Reveal.LineEnding -}}{{ .Reveal.VerticalSeparator }}{{- .Reveal.LineEnding }}"
|
data-separator-vertical="^{{ .Reveal.LineEnding.Escaped -}}{{ .Reveal.VerticalSeparator }}{{- .Reveal.LineEnding.Escaped }}"
|
||||||
data-separator-notes="^Note:"
|
data-separator-notes="^Note:"
|
||||||
data-charset="iso-8859-15">
|
data-charset="iso-8859-15">
|
||||||
</section>
|
</section>
|
||||||
|
|
2
go.sum
2
go.sum
|
@ -49,8 +49,6 @@ github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJ
|
||||||
github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
|
github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
|
||||||
github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc=
|
github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc=
|
||||||
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
|
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
|
||||||
github.com/Masterminds/sprig/v3 v3.2.1 h1:n6EPaDyLSvCEa3frruQvAiHuNp2dhBlMSmkEr+HuzGc=
|
|
||||||
github.com/Masterminds/sprig/v3 v3.2.1/go.mod h1:UoaO7Yp8KlPnJIYWTFkMaqPUYKTfGFPhxNuwnnxkKlk=
|
|
||||||
github.com/Masterminds/sprig/v3 v3.2.2 h1:17jRggJu518dr3QaafizSXOjKYp94wKfABxUmyxvxX8=
|
github.com/Masterminds/sprig/v3 v3.2.2 h1:17jRggJu518dr3QaafizSXOjKYp94wKfABxUmyxvxX8=
|
||||||
github.com/Masterminds/sprig/v3 v3.2.2/go.mod h1:UoaO7Yp8KlPnJIYWTFkMaqPUYKTfGFPhxNuwnnxkKlk=
|
github.com/Masterminds/sprig/v3 v3.2.2/go.mod h1:UoaO7Yp8KlPnJIYWTFkMaqPUYKTfGFPhxNuwnnxkKlk=
|
||||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||||
|
|
|
@ -1,8 +0,0 @@
|
||||||
//go:build !windows
|
|
||||||
// +build !windows
|
|
||||||
|
|
||||||
package rendering
|
|
||||||
|
|
||||||
const (
|
|
||||||
LineEnding string = "\\n"
|
|
||||||
)
|
|
|
@ -1,5 +0,0 @@
|
||||||
package rendering
|
|
||||||
|
|
||||||
const (
|
|
||||||
LineEnding string = "\\r\\n"
|
|
||||||
)
|
|
|
@ -21,6 +21,8 @@ import (
|
||||||
"github.com/bmatcuk/doublestar/v2"
|
"github.com/bmatcuk/doublestar/v2"
|
||||||
"github.com/imdario/mergo"
|
"github.com/imdario/mergo"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
|
|
||||||
|
"github.com/baez90/goveal/internal/encoding"
|
||||||
)
|
)
|
||||||
|
|
||||||
var defaultParams = RevealParams{
|
var defaultParams = RevealParams{
|
||||||
|
@ -32,24 +34,23 @@ var defaultParams = RevealParams{
|
||||||
VerticalSeparator: "---",
|
VerticalSeparator: "---",
|
||||||
SlideNumberVisibility: "all",
|
SlideNumberVisibility: "all",
|
||||||
SlideNumberFormat: "h.v",
|
SlideNumberFormat: "h.v",
|
||||||
LineEnding: LineEnding,
|
|
||||||
StyleSheets: make([]string, 0),
|
StyleSheets: make([]string, 0),
|
||||||
FilesToMonitor: make([]string, 0),
|
FilesToMonitor: make([]string, 0),
|
||||||
}
|
}
|
||||||
|
|
||||||
type RevealParams struct {
|
type RevealParams struct {
|
||||||
Theme string `mapstructure:"theme"`
|
Theme string `mapstructure:"theme"`
|
||||||
CodeTheme string `mapstructure:"codeTheme"`
|
CodeTheme string `mapstructure:"codeTheme"`
|
||||||
Transition string `mapstructure:"transition"`
|
Transition string `mapstructure:"transition"`
|
||||||
NavigationMode string `mapstructure:"navigationMode"`
|
NavigationMode string `mapstructure:"navigationMode"`
|
||||||
HorizontalSeparator string `mapstructure:"horizontalSeparator"`
|
HorizontalSeparator string `mapstructure:"horizontalSeparator"`
|
||||||
VerticalSeparator string `mapstructure:"verticalSeparator"`
|
VerticalSeparator string `mapstructure:"verticalSeparator"`
|
||||||
SlideNumberVisibility string `mapstructure:"slideNumberVisibility"`
|
SlideNumberVisibility string `mapstructure:"slideNumberVisibility"`
|
||||||
SlideNumberFormat string `mapstructure:"slideNumberFormat"`
|
SlideNumberFormat string `mapstructure:"slideNumberFormat"`
|
||||||
StyleSheets []string `mapstructure:"stylesheets"`
|
StyleSheets []string `mapstructure:"stylesheets"`
|
||||||
FilesToMonitor []string `mapstructure:"filesToMonitor"`
|
FilesToMonitor []string `mapstructure:"filesToMonitor"`
|
||||||
WorkingDirectory string `mapstructure:"working-dir"`
|
WorkingDirectory string `mapstructure:"working-dir"`
|
||||||
LineEnding string `mapstructure:"-"`
|
LineEnding encoding.LineEnding `mapstructure:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (params *RevealParams) Load() error {
|
func (params *RevealParams) Load() error {
|
||||||
|
|
|
@ -5,27 +5,31 @@ import (
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"os"
|
||||||
|
|
||||||
"github.com/baez90/goveal/assets"
|
"github.com/baez90/goveal/assets"
|
||||||
"github.com/baez90/goveal/internal/app/rendering"
|
"github.com/baez90/goveal/internal/app/rendering"
|
||||||
"github.com/baez90/goveal/internal/app/routing"
|
"github.com/baez90/goveal/internal/app/routing"
|
||||||
|
"github.com/baez90/goveal/internal/encoding"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
markdownFilePath = "/content.md"
|
markdownFilePath = "/content.md"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Config struct {
|
type (
|
||||||
Host string
|
Config struct {
|
||||||
Port uint16
|
Host string
|
||||||
MarkdownPath string
|
Port uint16
|
||||||
RevealParams *rendering.RevealParams
|
MarkdownPath string
|
||||||
}
|
RevealParams *rendering.RevealParams
|
||||||
|
}
|
||||||
|
|
||||||
type HTTPServer struct {
|
HTTPServer struct {
|
||||||
listener net.Listener
|
listener net.Listener
|
||||||
handler http.Handler
|
handler http.Handler
|
||||||
}
|
}
|
||||||
|
)
|
||||||
|
|
||||||
func (srv HTTPServer) Serve() error {
|
func (srv HTTPServer) Serve() error {
|
||||||
return http.Serve(srv.listener, srv.handler)
|
return http.Serve(srv.listener, srv.handler)
|
||||||
|
@ -37,6 +41,9 @@ func (srv HTTPServer) ListenAddress() string {
|
||||||
|
|
||||||
func NewHTTPServer(config Config) (srv *HTTPServer, err error) {
|
func NewHTTPServer(config Config) (srv *HTTPServer, err error) {
|
||||||
noCacheFiles := append(config.RevealParams.FilesToMonitor, markdownFilePath)
|
noCacheFiles := append(config.RevealParams.FilesToMonitor, markdownFilePath)
|
||||||
|
if err := detectMarkdownFileEnding(config.MarkdownPath, config.RevealParams); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
router := &routing.RegexpRouter{}
|
router := &routing.RegexpRouter{}
|
||||||
var tmplRenderer rendering.RevealRenderer
|
var tmplRenderer rendering.RevealRenderer
|
||||||
|
@ -92,3 +99,20 @@ func NewHTTPServer(config Config) (srv *HTTPServer, err error) {
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func detectMarkdownFileEnding(filePath string, params *rendering.RevealParams) error {
|
||||||
|
f, err := os.Open(filePath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
_ = f.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
|
if le, err := encoding.Detect(f); err != nil {
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
params.LineEnding = le
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
44
internal/encoding/line_ending.go
Normal file
44
internal/encoding/line_ending.go
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
package encoding
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
LineEndingUnknown LineEnding = ""
|
||||||
|
LineEndingWindows LineEnding = "\r\n"
|
||||||
|
LineEndingUnix LineEnding = "\n"
|
||||||
|
)
|
||||||
|
|
||||||
|
type LineEnding string
|
||||||
|
|
||||||
|
func (e LineEnding) String() string {
|
||||||
|
return string(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e LineEnding) Escaped() string {
|
||||||
|
switch e {
|
||||||
|
case LineEndingUnix:
|
||||||
|
return "\\n"
|
||||||
|
case LineEndingWindows:
|
||||||
|
return "\\r\\n"
|
||||||
|
default:
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Detect(reader io.Reader) (LineEnding, error) {
|
||||||
|
bufferedReader := bufio.NewReader(reader)
|
||||||
|
line, err := bufferedReader.ReadString(byte('\n'))
|
||||||
|
if err != nil {
|
||||||
|
return LineEndingUnknown, err
|
||||||
|
}
|
||||||
|
|
||||||
|
lineLength := len(line)
|
||||||
|
if lineLength <= 1 || line[lineLength-2:] != LineEndingWindows.String() {
|
||||||
|
return LineEndingUnix, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return LineEndingWindows, nil
|
||||||
|
}
|
74
internal/encoding/line_ending_test.go
Normal file
74
internal/encoding/line_ending_test.go
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
package encoding_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/baez90/goveal/internal/encoding"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestDetect(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
reader io.Reader
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
want encoding.LineEnding
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Empty file expect unknown",
|
||||||
|
args: args{
|
||||||
|
reader: strings.NewReader(""),
|
||||||
|
},
|
||||||
|
want: encoding.LineEndingUnknown,
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "File with only Unix line ending",
|
||||||
|
args: args{
|
||||||
|
reader: strings.NewReader("\n"),
|
||||||
|
},
|
||||||
|
want: encoding.LineEndingUnix,
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "File with only Windows line ending",
|
||||||
|
args: args{
|
||||||
|
reader: strings.NewReader("\r\n"),
|
||||||
|
},
|
||||||
|
want: encoding.LineEndingWindows,
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "File with multiple lines - Unix file ending",
|
||||||
|
args: args{
|
||||||
|
reader: strings.NewReader("Hello, World\nThis comes from Unix!\n"),
|
||||||
|
},
|
||||||
|
want: encoding.LineEndingUnix,
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "File with multiple lines - Windows file ending",
|
||||||
|
args: args{
|
||||||
|
reader: strings.NewReader("Hello, World\r\nThis comes from Windows!\r\n"),
|
||||||
|
},
|
||||||
|
want: encoding.LineEndingWindows,
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
got, err := encoding.Detect(tt.args.reader)
|
||||||
|
if (err != nil) != tt.wantErr {
|
||||||
|
t.Errorf("Detect() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if got != tt.want {
|
||||||
|
t.Errorf("Detect() got = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue