Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

LRU cache #64

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 2 additions & 1 deletion cmd/editorconfig/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"gopkg.in/ini.v1"

"github.com/editorconfig/editorconfig-core-go/v2"
"github.com/editorconfig/editorconfig-core-go/v2/parser"
)

const (
Expand Down Expand Up @@ -47,7 +48,7 @@ func main() {
}

if len(rest) > 1 {
config.Parser = editorconfig.NewCachedParser()
config.Parser = parser.NewCached()
}

for _, file := range rest {
Expand Down
10 changes: 5 additions & 5 deletions editorconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,8 @@ type Editorconfig struct {
config *Config
}

// newEditorconfig builds the configuration from an INI file.
func newEditorconfig(iniFile *ini.File) (*Editorconfig, error) {
// NewEditorconfig builds the configuration from an INI file.
func NewEditorconfig(iniFile *ini.File) (*Editorconfig, error) {
editorConfig := &Editorconfig{}

// Consider mixed-case values for true and false.
Expand Down Expand Up @@ -206,7 +206,7 @@ func Parse(r io.Reader) (*Editorconfig, error) {
return nil, err
}

return newEditorconfig(iniFile)
return NewEditorconfig(iniFile)
}

// ParseBytes parses from a slice of bytes.
Expand All @@ -218,7 +218,7 @@ func ParseBytes(data []byte) (*Editorconfig, error) {
return nil, err
}

return newEditorconfig(iniFile)
return NewEditorconfig(iniFile)
}

// ParseFile parses from a file.
Expand All @@ -230,7 +230,7 @@ func ParseFile(path string) (*Editorconfig, error) {
return nil, err
}

return newEditorconfig(iniFile)
return NewEditorconfig(iniFile)
}

// GetDefinitionForFilename given a filename, searches
Expand Down
9 changes: 6 additions & 3 deletions fnmatch.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ var (

// FnmatchCase tests whether the name matches the given pattern case included.
func FnmatchCase(pattern, name string) (bool, error) {
p := translate(pattern)
p := Translate(pattern)

r, err := regexp.Compile(fmt.Sprintf("^%s$", p))
if err != nil {
Expand All @@ -28,7 +28,10 @@ func FnmatchCase(pattern, name string) (bool, error) {
return r.MatchString(name), nil
}

func translate(pattern string) string { // nolint: gocyclo
// Translate converts a glob-like pattern into a regexp.
//
// Known limitation: it's pretty bad for ranges such as {1..n}
func Translate(pattern string) string { // nolint: gocyclo
index := 0
pat := []rune(pattern)
length := len(pat)
Expand Down Expand Up @@ -143,7 +146,7 @@ func translate(pattern string) string { // nolint: gocyclo
result.WriteString(strconv.Itoa(to))
result.WriteRune(')')
} else {
r := translate(inner)
r := Translate(inner)

result.WriteString(fmt.Sprintf("\\{%s\\}", r))
}
Expand Down
8 changes: 5 additions & 3 deletions fnmatch_test.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package editorconfig
package editorconfig_test

import (
"fmt"
"testing"

"github.com/editorconfig/editorconfig-core-go/v2"
)

func TestTranslate(t *testing.T) {
Expand All @@ -27,8 +29,8 @@ func TestTranslate(t *testing.T) {
title := fmt.Sprintf("%d) %s => %s", i, test[0], test[1])
t.Run(title, func(t *testing.T) {
t.Parallel()
result := translate(test[0]) // nolint: scopelint
if result != test[1] { // nolint: scopelint
result := editorconfig.Translate(test[0]) // nolint: scopelint
if result != test[1] { // nolint: scopelint
t.Errorf("%s != %s", test[1], result) // nolint: scopelint
}
})
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ go 1.12

require (
github.com/google/go-cmp v0.4.0
github.com/hashicorp/golang-lru v0.5.4
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a // indirect
golang.org/x/mod v0.2.0
gopkg.in/ini.v1 v1.55.0
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
Expand Down
25 changes: 13 additions & 12 deletions cached_parser.go → parser/cached.go
Original file line number Diff line number Diff line change
@@ -1,30 +1,31 @@
package editorconfig
package parser

import (
"fmt"
"os"
"regexp"

"github.com/editorconfig/editorconfig-core-go/v2"
"gopkg.in/ini.v1"
)

// CachedParser implements the Parser interface but caches the definition and
// Cached implements the interface but caches the definition and
// the regular expressions.
type CachedParser struct {
editorconfigs map[string]*Editorconfig
type Cached struct {
editorconfigs map[string]*editorconfig.Editorconfig
regexps map[string]*regexp.Regexp
}

// NewCachedParser initializes the CachedParser.
func NewCachedParser() *CachedParser {
return &CachedParser{
editorconfigs: make(map[string]*Editorconfig),
// NewCached initializes the Cached.
func NewCached() *Cached {
return &Cached{
editorconfigs: make(map[string]*editorconfig.Editorconfig),
regexps: make(map[string]*regexp.Regexp),
}
}

// ParseIni parses the given filename to a Definition and caches the result.
func (parser *CachedParser) ParseIni(filename string) (*Editorconfig, error) {
func (parser *Cached) ParseIni(filename string) (*editorconfig.Editorconfig, error) {
ec, ok := parser.editorconfigs[filename]
if !ok {
fp, err := os.Open(filename)
Expand All @@ -39,7 +40,7 @@ func (parser *CachedParser) ParseIni(filename string) (*Editorconfig, error) {
return nil, err
}

ec, err = newEditorconfig(iniFile)
ec, err = editorconfig.NewEditorconfig(iniFile)
if err != nil {
return nil, err
}
Expand All @@ -51,10 +52,10 @@ func (parser *CachedParser) ParseIni(filename string) (*Editorconfig, error) {
}

// FnmatchCase calls the module's FnmatchCase and caches the parsed selector.
func (parser *CachedParser) FnmatchCase(selector string, filename string) (bool, error) {
func (parser *Cached) FnmatchCase(selector string, filename string) (bool, error) {
r, ok := parser.regexps[selector]
if !ok {
p := translate(selector)
p := editorconfig.Translate(selector)

var err error

Expand Down
77 changes: 77 additions & 0 deletions parser/lru.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package parser

import (
"fmt"
"os"
"regexp"

"github.com/editorconfig/editorconfig-core-go/v2"
"github.com/hashicorp/golang-lru"
"gopkg.in/ini.v1"
)

// LRU implements the interface but caches the definition and
// the regular expressions using Hashicorp's LRU cache.
//
// https://github.com/hashicorp/golang-lru
type LRU struct {
editorconfigs *lru.Cache
regexps *lru.Cache
}

// NewLRU initializes the LRU.
func NewLRU() *LRU {
c, _ := lru.New(64)
r, _ := lru.New(256)

return &LRU{
editorconfigs: c,
regexps: r,
}
}

// ParseIni parses the given filename to a Definition and caches the result.
func (parser *LRU) ParseIni(filename string) (*editorconfig.Editorconfig, error) {
ec, ok := parser.editorconfigs.Get(filename)
if !ok {
fp, err := os.Open(filename)
if err != nil {
return nil, err
}

defer fp.Close()

iniFile, err := ini.Load(fp)
if err != nil {
return nil, err
}

ec, err = editorconfig.NewEditorconfig(iniFile)
if err != nil {
return nil, err
}

parser.editorconfigs.Add(filename, ec)
}

return ec.(*editorconfig.Editorconfig), nil
}

// FnmatchCase calls the module's FnmatchCase and caches the parsed selector.
func (parser *LRU) FnmatchCase(selector string, filename string) (bool, error) {
r, ok := parser.regexps.Get(selector)
if !ok {
p := editorconfig.Translate(selector)

var err error

r, err = regexp.Compile(fmt.Sprintf("^%s$", p))
if err != nil {
return false, err
}

parser.regexps.Add(selector, r)
}

return r.(*regexp.Regexp).MatchString(filename), nil
}
4 changes: 2 additions & 2 deletions simple_parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (
"gopkg.in/ini.v1"
)

// SimpleParser implements the Parser interface but without doing any caching.
// SimpleParser implements the interface but without doing any caching.
type SimpleParser struct{}

// ParseIni calls go-ini's Load on the file.
Expand All @@ -23,7 +23,7 @@ func (parser *SimpleParser) ParseIni(filename string) (*Editorconfig, error) {
return nil, err
}

return newEditorconfig(iniFile)
return NewEditorconfig(iniFile)
}

// FnmatchCase calls the module's FnmatchCase.
Expand Down