mirror of
https://github.com/cheat/cheat.git
synced 2025-09-04 02:58:29 +02:00
Compare commits
5 Commits
Author | SHA1 | Date | |
---|---|---|---|
e08a4f3cec | |||
d6ebe0799d | |||
51aaaf3423 | |||
197ff58796 | |||
b8f512aae8 |
@ -13,7 +13,7 @@ import (
|
|||||||
"github.com/cheat/cheat/internal/config"
|
"github.com/cheat/cheat/internal/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
const version = "3.0.4"
|
const version = "3.0.5"
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
||||||
@ -39,7 +39,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// initialize the configs
|
// initialize the configs
|
||||||
conf, err := config.New(opts, confpath)
|
conf, err := config.New(opts, confpath, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "failed to load config: %v\n", err)
|
fmt.Fprintf(os.Stderr, "failed to load config: %v\n", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
|
3
go.mod
3
go.mod
@ -9,7 +9,6 @@ require (
|
|||||||
github.com/mattn/go-isatty v0.0.10
|
github.com/mattn/go-isatty v0.0.10
|
||||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b
|
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b
|
||||||
github.com/mitchellh/go-homedir v1.1.0
|
github.com/mitchellh/go-homedir v1.1.0
|
||||||
github.com/tj/front v0.0.0-20170212063142-739be213b0a1
|
gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0
|
||||||
gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0 // indirect
|
|
||||||
gopkg.in/yaml.v2 v2.2.4
|
gopkg.in/yaml.v2 v2.2.4
|
||||||
)
|
)
|
||||||
|
2
go.sum
2
go.sum
@ -50,8 +50,6 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
|
|||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/tj/front v0.0.0-20170212063142-739be213b0a1 h1:lA+aPRvltlx2fwv/BnxyYSDQo3pIeqzHgMO5GvK0T9E=
|
|
||||||
github.com/tj/front v0.0.0-20170212063142-739be213b0a1/go.mod h1:deJrtusCTptAW4EUn5vBLpl3dhNqPqUwEjWJz5UNxpQ=
|
|
||||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||||
github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
|
github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
|
||||||
golang.org/x/sys v0.0.0-20181128092732-4ed8d59d0b35 h1:YAFjXN64LMvktoUZH9zgY4lGc/msGN7HQfoSuKCgaDU=
|
golang.org/x/sys v0.0.0-20181128092732-4ed8d59d0b35 h1:YAFjXN64LMvktoUZH9zgY4lGc/msGN7HQfoSuKCgaDU=
|
||||||
|
@ -22,7 +22,7 @@ type Config struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// New returns a new Config struct
|
// New returns a new Config struct
|
||||||
func New(opts map[string]interface{}, confPath string) (Config, error) {
|
func New(opts map[string]interface{}, confPath string, resolve bool) (Config, error) {
|
||||||
|
|
||||||
// read the config file
|
// read the config file
|
||||||
buf, err := ioutil.ReadFile(confPath)
|
buf, err := ioutil.ReadFile(confPath)
|
||||||
@ -49,13 +49,22 @@ func New(opts map[string]interface{}, confPath string) (Config, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// follow symlinks
|
// follow symlinks
|
||||||
expanded, err = filepath.EvalSymlinks(expanded)
|
//
|
||||||
if err != nil {
|
// NB: `resolve` is an ugly kludge that exists for the sake of unit-tests.
|
||||||
return Config{}, fmt.Errorf(
|
// It's necessary because `EvalSymlinks` will error if the symlink points
|
||||||
"failed to resolve symlink: %s, %v",
|
// to a non-existent location on the filesystem. When unit-testing,
|
||||||
expanded,
|
// however, we don't want to have dependencies on the filesystem. As such,
|
||||||
err,
|
// `resolve` is a switch that allows us to turn off symlink resolution when
|
||||||
)
|
// running the config tests.
|
||||||
|
if resolve {
|
||||||
|
expanded, err = filepath.EvalSymlinks(expanded)
|
||||||
|
if err != nil {
|
||||||
|
return Config{}, fmt.Errorf(
|
||||||
|
"failed to resolve symlink: %s, %v",
|
||||||
|
expanded,
|
||||||
|
err,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
conf.Cheatpaths[i].Path = expanded
|
conf.Cheatpaths[i].Path = expanded
|
||||||
|
@ -17,7 +17,7 @@ import (
|
|||||||
func TestConfigSuccessful(t *testing.T) {
|
func TestConfigSuccessful(t *testing.T) {
|
||||||
|
|
||||||
// initialize a config
|
// initialize a config
|
||||||
conf, err := New(map[string]interface{}{}, mock.Path("conf/conf.yml"))
|
conf, err := New(map[string]interface{}{}, mock.Path("conf/conf.yml"), false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("failed to parse config file: %v", err)
|
t.Errorf("failed to parse config file: %v", err)
|
||||||
}
|
}
|
||||||
@ -69,7 +69,7 @@ func TestConfigSuccessful(t *testing.T) {
|
|||||||
func TestConfigFailure(t *testing.T) {
|
func TestConfigFailure(t *testing.T) {
|
||||||
|
|
||||||
// attempt to read a non-existent config file
|
// attempt to read a non-existent config file
|
||||||
_, err := New(map[string]interface{}{}, "/does-not-exit")
|
_, err := New(map[string]interface{}{}, "/does-not-exit", false)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Errorf("failed to error on unreadable config")
|
t.Errorf("failed to error on unreadable config")
|
||||||
}
|
}
|
||||||
@ -84,14 +84,14 @@ func TestEmptyEditor(t *testing.T) {
|
|||||||
os.Setenv("EDITOR", "")
|
os.Setenv("EDITOR", "")
|
||||||
|
|
||||||
// initialize a config
|
// initialize a config
|
||||||
conf, err := New(map[string]interface{}{}, mock.Path("conf/empty.yml"))
|
conf, err := New(map[string]interface{}{}, mock.Path("conf/empty.yml"), false)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Errorf("failed to return an error on empty editor")
|
t.Errorf("failed to return an error on empty editor")
|
||||||
}
|
}
|
||||||
|
|
||||||
// set editor, and assert that it is respected
|
// set editor, and assert that it is respected
|
||||||
os.Setenv("EDITOR", "foo")
|
os.Setenv("EDITOR", "foo")
|
||||||
conf, err = New(map[string]interface{}{}, mock.Path("conf/empty.yml"))
|
conf, err = New(map[string]interface{}{}, mock.Path("conf/empty.yml"), false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("failed to init configs: %v", err)
|
t.Errorf("failed to init configs: %v", err)
|
||||||
}
|
}
|
||||||
@ -101,7 +101,7 @@ func TestEmptyEditor(t *testing.T) {
|
|||||||
|
|
||||||
// set visual, and assert that it overrides editor
|
// set visual, and assert that it overrides editor
|
||||||
os.Setenv("VISUAL", "bar")
|
os.Setenv("VISUAL", "bar")
|
||||||
conf, err = New(map[string]interface{}{}, mock.Path("conf/empty.yml"))
|
conf, err = New(map[string]interface{}{}, mock.Path("conf/empty.yml"), false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("failed to init configs: %v", err)
|
t.Errorf("failed to init configs: %v", err)
|
||||||
}
|
}
|
||||||
|
34
internal/frontmatter/frontmatter.go
Normal file
34
internal/frontmatter/frontmatter.go
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
package frontmatter
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"gopkg.in/yaml.v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Frontmatter encapsulates cheatsheet frontmatter data
|
||||||
|
type Frontmatter struct {
|
||||||
|
Tags []string
|
||||||
|
Syntax string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse parses cheatsheet frontmatter
|
||||||
|
func Parse(markdown string) (string, Frontmatter, error) {
|
||||||
|
|
||||||
|
// specify the frontmatter delimiter
|
||||||
|
delim := "---"
|
||||||
|
|
||||||
|
// initialize a frontmatter struct
|
||||||
|
var fm Frontmatter
|
||||||
|
|
||||||
|
// if the markdown does not contain frontmatter, pass it through unmodified
|
||||||
|
if !strings.HasPrefix(markdown, delim) {
|
||||||
|
return strings.TrimSpace(markdown), fm, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// otherwise, split the frontmatter and cheatsheet text
|
||||||
|
parts := strings.SplitN(markdown, delim, 3)
|
||||||
|
err := yaml.Unmarshal([]byte(parts[1]), &fm)
|
||||||
|
|
||||||
|
return strings.TrimSpace(parts[2]), fm, err
|
||||||
|
}
|
71
internal/frontmatter/frontmatter_test.go
Normal file
71
internal/frontmatter/frontmatter_test.go
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
package frontmatter
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestHasFrontmatter asserts that markdown is properly parsed when it contains
|
||||||
|
// frontmatter
|
||||||
|
func TestHasFrontmatter(t *testing.T) {
|
||||||
|
|
||||||
|
// stub our cheatsheet content
|
||||||
|
markdown := `---
|
||||||
|
syntax: go
|
||||||
|
tags: [ test ]
|
||||||
|
---
|
||||||
|
To foo the bar: baz`
|
||||||
|
|
||||||
|
// parse the frontmatter
|
||||||
|
text, fm, err := Parse(markdown)
|
||||||
|
|
||||||
|
// assert expectations
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("failed to parse markdown: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
want := "To foo the bar: baz"
|
||||||
|
if text != want {
|
||||||
|
t.Errorf("failed to parse text: want: %s, got: %s", want, text)
|
||||||
|
}
|
||||||
|
|
||||||
|
want = "go"
|
||||||
|
if fm.Syntax != want {
|
||||||
|
t.Errorf("failed to parse syntax: want: %s, got: %s", want, fm.Syntax)
|
||||||
|
}
|
||||||
|
|
||||||
|
want = "test"
|
||||||
|
if fm.Tags[0] != want {
|
||||||
|
t.Errorf("failed to parse tags: want: %s, got: %s", want, fm.Tags[0])
|
||||||
|
}
|
||||||
|
if len(fm.Tags) != 1 {
|
||||||
|
t.Errorf("failed to parse tags: want: len 0, got: len %d", len(fm.Tags))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestHasFrontmatter asserts that markdown is properly parsed when it does not
|
||||||
|
// contain frontmatter
|
||||||
|
func TestHasNoFrontmatter(t *testing.T) {
|
||||||
|
|
||||||
|
// stub our cheatsheet content
|
||||||
|
markdown := "To foo the bar: baz"
|
||||||
|
|
||||||
|
// parse the frontmatter
|
||||||
|
text, fm, err := Parse(markdown)
|
||||||
|
|
||||||
|
// assert expectations
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("failed to parse markdown: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if text != markdown {
|
||||||
|
t.Errorf("failed to parse text: want: %s, got: %s", markdown, text)
|
||||||
|
}
|
||||||
|
|
||||||
|
if fm.Syntax != "" {
|
||||||
|
t.Errorf("failed to parse syntax: want: '', got: %s", fm.Syntax)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(fm.Tags) != 0 {
|
||||||
|
t.Errorf("failed to parse tags: want: len 0, got: len %d", len(fm.Tags))
|
||||||
|
}
|
||||||
|
}
|
@ -4,17 +4,10 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/tj/front"
|
"github.com/cheat/cheat/internal/frontmatter"
|
||||||
)
|
)
|
||||||
|
|
||||||
// frontmatter is an un-exported helper struct used in parsing cheatsheets
|
|
||||||
type frontmatter struct {
|
|
||||||
Tags []string
|
|
||||||
Syntax string
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sheet encapsulates sheet information
|
// Sheet encapsulates sheet information
|
||||||
type Sheet struct {
|
type Sheet struct {
|
||||||
Title string
|
Title string
|
||||||
@ -39,9 +32,8 @@ func New(
|
|||||||
return Sheet{}, fmt.Errorf("failed to read file: %s, %v", path, err)
|
return Sheet{}, fmt.Errorf("failed to read file: %s, %v", path, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// parse the front-matter
|
// parse the cheatsheet frontmatter
|
||||||
var fm frontmatter
|
text, fm, err := frontmatter.Parse(string(markdown))
|
||||||
text, err := front.Unmarshal(markdown, &fm)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Sheet{}, fmt.Errorf("failed to parse front-matter: %v", err)
|
return Sheet{}, fmt.Errorf("failed to parse front-matter: %v", err)
|
||||||
}
|
}
|
||||||
@ -56,7 +48,7 @@ func New(
|
|||||||
return Sheet{
|
return Sheet{
|
||||||
Title: title,
|
Title: title,
|
||||||
Path: path,
|
Path: path,
|
||||||
Text: strings.TrimSpace(string(text)) + "\n",
|
Text: text + "\n",
|
||||||
Tags: tags,
|
Tags: tags,
|
||||||
Syntax: fm.Syntax,
|
Syntax: fm.Syntax,
|
||||||
ReadOnly: readOnly,
|
ReadOnly: readOnly,
|
||||||
|
16
vendor/github.com/tj/front/Readme.md
generated
vendored
16
vendor/github.com/tj/front/Readme.md
generated
vendored
@ -1,16 +0,0 @@
|
|||||||
# Front
|
|
||||||
|
|
||||||
Frontmatter unmarshaller, couldn't find one without a weird API.
|
|
||||||
|
|
||||||
## Badges
|
|
||||||
|
|
||||||
[](https://godoc.org/github.com/tj/front)
|
|
||||||

|
|
||||||

|
|
||||||
[](https://apex.sh/ping/)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
> [tjholowaychuk.com](http://tjholowaychuk.com) ·
|
|
||||||
> GitHub [@tj](https://github.com/tj) ·
|
|
||||||
> Twitter [@tjholowaychuk](https://twitter.com/tjholowaychuk)
|
|
24
vendor/github.com/tj/front/front.go
generated
vendored
24
vendor/github.com/tj/front/front.go
generated
vendored
@ -1,24 +0,0 @@
|
|||||||
// Package front provides YAML frontmatter unmarshalling.
|
|
||||||
package front
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
|
|
||||||
"gopkg.in/yaml.v1"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Delimiter.
|
|
||||||
var delim = []byte("---")
|
|
||||||
|
|
||||||
// Unmarshal parses YAML frontmatter and returns the content. When no
|
|
||||||
// frontmatter delimiters are present the original content is returned.
|
|
||||||
func Unmarshal(b []byte, v interface{}) (content []byte, err error) {
|
|
||||||
if !bytes.HasPrefix(b, delim) {
|
|
||||||
return b, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
parts := bytes.SplitN(b, delim, 3)
|
|
||||||
content = parts[2]
|
|
||||||
err = yaml.Unmarshal(parts[1], v)
|
|
||||||
return
|
|
||||||
}
|
|
Reference in New Issue
Block a user