Auto detect line ending on startup

This commit is contained in:
Peter 2021-11-15 15:23:10 +01:00
parent 2cc48cfbb0
commit e4cc33f098
Signed by: prskr
GPG key ID: C1DB5D2E8DB512F9
8 changed files with 168 additions and 40 deletions

View file

@ -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
View file

@ -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=

View file

@ -1,8 +0,0 @@
//go:build !windows
// +build !windows
package rendering
const (
LineEnding string = "\\n"
)

View file

@ -1,5 +0,0 @@
package rendering
const (
LineEnding string = "\\r\\n"
)

View file

@ -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 {

View file

@ -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
}

View 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
}

View 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)
}
})
}
}