Repository: gokcehan/lf
Pull request #1650 (2024-03-19 23:36:01)

Added globfilter command
fixes: https://github.com/gokcehan/lf/issues/1649, Added a new `globfilter` option which is verry similar to `globsearch` but only apply to the `filter` command.

I only added documentation in `doc.md` because I think the rest of the files will be auto-generated on the next release (or the one after the next one)? Or should I run `gen/doc-with-docker.sh` myself?  Please let me know.

joelim-work: Regarding the documentation, if you have Docker setup then you can just run `gen/doc-with-docker.sh`. It is not mandatory, we will run it anyway just before a new release is made just in case, but it does make things easier as the diffs will be smaller if updated incrementally rather than once in bulk.

Also please add Fixes #1649 to the PR description to link it properly, so it will close the corresponding issue when the PR is merged.

Patch:
diff --git a/client.go b/client.go
index db60df4..559102d 100644
--- a/client.go
+++ b/client.go
@@ -36,7 +36,6 @@ func run() {
 	if gOpts.mouse {
 		screen.EnableMouse()
 	}
-	screen.EnableFocus()
 
 	if gLogPath != "" {
 		f, err := os.OpenFile(gLogPath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
diff --git a/colors.go b/colors.go
index 9520480..8558b3f 100644
--- a/colors.go
+++ b/colors.go
@@ -10,10 +10,16 @@ import (
 	"github.com/gdamore/tcell/v2"
 )
 
-type styleMap map[string]tcell.Style
+type styleMap struct {
+	styles        map[string]tcell.Style
+	useLinkTarget bool
+}
 
 func parseStyles() styleMap {
-	sm := make(styleMap)
+	sm := styleMap{
+		styles:        make(map[string]tcell.Style),
+		useLinkTarget: false,
+	}
 
 	// Default values from dircolors
 	//
@@ -191,20 +197,12 @@ func (sm styleMap) parseFile(path string) {
 	}
 
 	for _, pair := range pairs {
-		key, val := pair[0], pair[1]
-
-		key = replaceTilde(key)
-
-		if filepath.IsAbs(key) {
-			key = filepath.Clean(key)
-		}
-
-		sm[key] = applyAnsiCodes(val, tcell.StyleDefault)
+		sm.parsePair(pair)
 	}
 }
 
 // This function parses $LS_COLORS environment variable.
-func (sm styleMap) parseGNU(env string) {
+func (sm *styleMap) parseGNU(env string) {
 	for _, entry := range strings.Split(env, ":") {
 		if entry == "" {
 			continue
@@ -217,16 +215,24 @@ func (sm styleMap) parseGNU(env string) {
 			return
 		}
 
-		key, val := pair[0], pair[1]
+		sm.parsePair(pair)
+	}
+}
 
-		key = replaceTilde(key)
+func (sm *styleMap) parsePair(pair []string) {
+	key, val := pair[0], pair[1]
 
-		if filepath.IsAbs(key) {
-			key = filepath.Clean(key)
-		}
+	key = replaceTilde(key)
 
-		sm[key] = applyAnsiCodes(val, tcell.StyleDefault)
+	if filepath.IsAbs(key) {
+		key = filepath.Clean(key)
 	}
+
+	if key == "ln" && val == "target" {
+		sm.useLinkTarget = true
+	}
+
+	sm.styles[key] = applyAnsiCodes(val, tcell.StyleDefault)
 }
 
 // This function parses $LSCOLORS environment variable.
@@ -267,17 +273,17 @@ func (sm styleMap) parseBSD(env string) {
 	}
 
 	for i, key := range colorNames {
-		sm[key] = getStyle(env[i*2], env[i*2+1])
+		sm.styles[key] = getStyle(env[i*2], env[i*2+1])
 	}
 }
 
 func (sm styleMap) get(f *file) tcell.Style {
-	if val, ok := sm[f.path]; ok {
+	if val, ok := sm.styles[f.path]; ok {
 		return val
 	}
 
 	if f.IsDir() {
-		if val, ok := sm[f.Name()+"/"]; ok {
+		if val, ok := sm.styles[f.Name()+"/"]; ok {
 			return val
 		}
 	}
@@ -285,7 +291,7 @@ func (sm styleMap) get(f *file) tcell.Style {
 	var key string
 
 	switch {
-	case f.linkState == working:
+	case f.linkState == working && !sm.useLinkTarget:
 		key = "ln"
 	case f.linkState == broken:
 		key = "or"
@@ -313,27 +319,27 @@ func (sm styleMap) get(f *file) tcell.Style {
 		key = "ex"
 	}
 
-	if val, ok := sm[key]; ok {
+	if val, ok := sm.styles[key]; ok {
 		return val
 	}
 
-	if val, ok := sm[f.Name()+"*"]; ok {
+	if val, ok := sm.styles[f.Name()+"*"]; ok {
 		return val
 	}
 
-	if val, ok := sm["*"+f.Name()]; ok {
+	if val, ok := sm.styles["*"+f.Name()]; ok {
 		return val
 	}
 
-	if val, ok := sm[filepath.Base(f.Name())+".*"]; ok {
+	if val, ok := sm.styles[filepath.Base(f.Name())+".*"]; ok {
 		return val
 	}
 
-	if val, ok := sm["*"+strings.ToLower(f.ext)]; ok {
+	if val, ok := sm.styles["*"+strings.ToLower(f.ext)]; ok {
 		return val
 	}
 
-	if val, ok := sm["fi"]; ok {
+	if val, ok := sm.styles["fi"]; ok {
 		return val
 	}
 
diff --git a/doc.md b/doc.md
index 92dee46..e608e71 100644
--- a/doc.md
+++ b/doc.md
@@ -155,6 +155,7 @@ The following options can be used to customize the behavior of lf:
 	errorfmt            string    (default "\033[7;31;47m")
 	filesep             string    (default "\n")
 	findlen             int       (default 1)
+	globfilter          bool      (default false)
 	globsearch          bool      (default false)
 	hidden              bool      (default false)
 	hiddenfiles         []string  (default '.*')
@@ -749,6 +750,10 @@ File separator used in environment variables `fs` and `fx`.
 Number of characters prompted for the find command.
 When this value is set to 0, find command prompts until there is only a single match left.
 
+## globfilter (bool) (default false)
+
+Patterns are treated as globs for the filter command, see `globsearch` for more details.
+
 ## globsearch (bool) (default false)
 
 When this option is enabled, search command patterns are considered as globs, otherwise, they are literals.
@@ -1790,6 +1795,7 @@ You may instead divide it into multiple lines in between double quotes by escapi
 	ex=01;32:\
 	"
 
+The `ln` entry supports the special value `target`, which will use the link target to select a style. File name rules will still apply based on the link's name -- this mirrors GNU's `ls` and `dircolors` behavior.
 Having such a long variable definition in a shell configuration file might be undesirable.
 You may instead use the colors file (refer to the [CONFIGURATION section](https://github.com/gokcehan/lf/blob/master/doc.md#configuration)) for configuration.
 A sample colors file can be found at
@@ -1802,6 +1808,7 @@ https://en.wikipedia.org/wiki/ANSI_escape_code
 Icons are configured using `LF_ICONS` environment variable or an icons file (refer to the [CONFIGURATION section](https://github.com/gokcehan/lf/blob/master/doc.md#configuration)).
 The variable uses the same syntax as `LS_COLORS/LF_COLORS`.
 Instead of colors, you should put a single characters as values of entries.
+The `ln` entry supports the special value `target`, which will use the link target to select a icon. File name rules will still apply based on the link's name -- this mirrors GNU's `ls` and `dircolors` behavior.
 The icons file (refer to the [CONFIGURATION section](https://github.com/gokcehan/lf/blob/master/doc.md#configuration)) should consist of whitespace-separated pairs with a `#` character to start comments until the end of the line.
 Do not forget to add `set icons true` to your `lfrc` to see the icons.
 Default values are as follows given with their matching order in lf:
diff --git a/doc.txt b/doc.txt
index f932ec1..fd90003 100644
--- a/doc.txt
+++ b/doc.txt
@@ -145,6 +145,7 @@ The following options can be used to customize the behavior of lf:
     errorfmt            string    (default "\033[7;31;47m")
     filesep             string    (default "\n")
     findlen             int       (default 1)
+    globfilter          bool      (default false)
     globsearch          bool      (default false)
     hidden              bool      (default false)
     hiddenfiles         []string  (default '.*')
@@ -797,6 +798,11 @@ findlen (int) (default 1)
 Number of characters prompted for the find command. When this value is
 set to 0, find command prompts until there is only a single match left.
 
+globfilter (bool) (default false)
+
+Patterns are treated as globs for the filter command, see globsearch for
+more details.
+
 globsearch (bool) (default false)
 
 When this option is enabled, search command patterns are considered as
@@ -2026,8 +2032,11 @@ escaping newlines with backslashes as follows:
     ex=01;32:\
     "
 
-Having such a long variable definition in a shell configuration file
-might be undesirable. You may instead use the colors file (refer to the
+The ln entry supports the special value target, which will use the link
+target to select a style. File name rules will still apply based on the
+link's name -- this mirrors GNU's ls and dircolors behavior. Having such
+a long variable definition in a shell configuration file might be
+undesirable. You may instead use the colors file (refer to the
 CONFIGURATION section) for configuration. A sample colors file can be
 found at https://github.com/gokcehan/lf/blob/master/etc/colors.example
 You may also see the wiki page for ANSI escape codes
@@ -2038,7 +2047,10 @@ ICONS
 Icons are configured using LF_ICONS environment variable or an icons
 file (refer to the CONFIGURATION section). The variable uses the same
 syntax as LS_COLORS/LF_COLORS. Instead of colors, you should put a
-single characters as values of entries. The icons file (refer to the
+single characters as values of entries. The ln entry supports the
+special value target, which will use the link target to select a icon.
+File name rules will still apply based on the link's name -- this
+mirrors GNU's ls and dircolors behavior. The icons file (refer to the
 CONFIGURATION section) should consist of whitespace-separated pairs with
 a # character to start comments until the end of the line. Do not forget
 to add set icons true to your lfrc to see the icons. Default values are
diff --git a/eval.go b/eval.go
index bdb7ff8..c3282b3 100644
--- a/eval.go
+++ b/eval.go
@@ -96,14 +96,16 @@ func (e *setExpr) eval(app *app, args []string) {
 			}
 			app.ui.loadFile(app, true)
 		}
-	case "globsearch", "noglobsearch", "globsearch!":
-		err = applyBoolOpt(&gOpts.globsearch, e)
+	case "globfilter", "noglobfilter", "globfilter!":
+		err = applyBoolOpt(&gOpts.globfilter, e)
 		if err == nil {
 			app.nav.sort()
 			app.nav.position()
 			app.ui.sort()
 			app.ui.loadFile(app, true)
 		}
+	case "globsearch", "noglobsearch", "globsearch!":
+		err = applyBoolOpt(&gOpts.globsearch, e)
 	case "hidden", "nohidden", "hidden!":
 		err = applyBoolOpt(&gOpts.hidden, e)
 		if err == nil {
@@ -114,6 +116,13 @@ func (e *setExpr) eval(app *app, args []string) {
 		}
 	case "hidecursorinactive", "nohidecursorinactive", "hidecursorinactive!":
 		err = applyBoolOpt(&gOpts.hidecursorinactive, e)
+		if err == nil {
+			if gOpts.hidecursorinactive {
+				app.ui.screen.EnableFocus()
+			} else {
+				app.ui.screen.DisableFocus()
+			}
+		}
 	case "history", "nohistory", "history!":
 		err = applyBoolOpt(&gOpts.history, e)
 	case "icons", "noicons", "icons!":
diff --git a/go.mod b/go.mod
index 10886b4..eb3cdc4 100644
--- a/go.mod
+++ b/go.mod
@@ -6,8 +6,8 @@ require (
 	github.com/djherbis/times v1.6.0
 	github.com/gdamore/tcell/v2 v2.7.4
 	github.com/mattn/go-runewidth v0.0.15
-	golang.org/x/sys v0.18.0
-	golang.org/x/term v0.18.0
+	golang.org/x/sys v0.19.0
+	golang.org/x/term v0.19.0
 )
 
 require (
diff --git a/go.sum b/go.sum
index b10dc49..1548183 100644
--- a/go.sum
+++ b/go.sum
@@ -31,14 +31,14 @@ golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBc
 golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
-golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
-golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
+golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
 golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
-golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8=
-golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
+golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q=
+golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
diff --git a/icons.go b/icons.go
index ac4230f..5583864 100644
--- a/icons.go
+++ b/icons.go
@@ -7,10 +7,16 @@ import (
 	"strings"
 )
 
-type iconMap map[string]string
+type iconMap struct {
+	icons         map[string]string
+	useLinkTarget bool
+}
 
 func parseIcons() iconMap {
-	im := make(iconMap)
+	im := iconMap{
+		icons:         make(map[string]string),
+		useLinkTarget: false,
+	}
 
 	defaultIcons := []string{
 		"ln=l",
@@ -44,7 +50,7 @@ func parseIcons() iconMap {
 	return im
 }
 
-func (im iconMap) parseFile(path string) {
+func (im *iconMap) parseFile(path string) {
 	log.Printf("reading file: %s", path)
 
 	f, err := os.Open(path)
@@ -61,19 +67,11 @@ func (im iconMap) parseFile(path string) {
 	}
 
 	for _, pair := range pairs {
-		key, val := pair[0], pair[1]
-
-		key = replaceTilde(key)
-
-		if filepath.IsAbs(key) {
-			key = filepath.Clean(key)
-		}
-
-		im[key] = val
+		im.parsePair(pair)
 	}
 }
 
-func (im iconMap) parseEnv(env string) {
+func (im *iconMap) parseEnv(env string) {
 	for _, entry := range strings.Split(env, ":") {
 		if entry == "" {
 			continue
@@ -86,25 +84,33 @@ func (im iconMap) parseEnv(env string) {
 			return
 		}
 
-		key, val := pair[0], pair[1]
+		im.parsePair(pair)
+	}
+}
 
-		key = replaceTilde(key)
+func (im *iconMap) parsePair(pair []string) {
+	key, val := pair[0], pair[1]
 
-		if filepath.IsAbs(key) {
-			key = filepath.Clean(key)
-		}
+	key = replaceTilde(key)
 
-		im[key] = val
+	if filepath.IsAbs(key) {
+		key = filepath.Clean(key)
 	}
+
+	if key == "ln" && val == "target" {
+		im.useLinkTarget = true
+	}
+
+	im.icons[key] = val
 }
 
 func (im iconMap) get(f *file) string {
-	if val, ok := im[f.path]; ok {
+	if val, ok := im.icons[f.path]; ok {
 		return val
 	}
 
 	if f.IsDir() {
-		if val, ok := im[f.Name()+"/"]; ok {
+		if val, ok := im.icons[f.Name()+"/"]; ok {
 			return val
 		}
 	}
@@ -112,7 +118,7 @@ func (im iconMap) get(f *file) string {
 	var key string
 
 	switch {
-	case f.linkState == working:
+	case f.linkState == working && !im.useLinkTarget:
 		key = "ln"
 	case f.linkState == broken:
 		key = "or"
@@ -140,27 +146,27 @@ func (im iconMap) get(f *file) string {
 		key = "ex"
 	}
 
-	if val, ok := im[key]; ok {
+	if val, ok := im.icons[key]; ok {
 		return val
 	}
 
-	if val, ok := im[f.Name()+"*"]; ok {
+	if val, ok := im.icons[f.Name()+"*"]; ok {
 		return val
 	}
 
-	if val, ok := im["*"+f.Name()]; ok {
+	if val, ok := im.icons["*"+f.Name()]; ok {
 		return val
 	}
 
-	if val, ok := im[filepath.Base(f.Name())+".*"]; ok {
+	if val, ok := im.icons[filepath.Base(f.Name())+".*"]; ok {
 		return val
 	}
 
-	if val, ok := im["*"+strings.ToLower(f.ext)]; ok {
+	if val, ok := im.icons["*"+strings.ToLower(f.ext)]; ok {
 		return val
 	}
 
-	if val, ok := im["fi"]; ok {
+	if val, ok := im.icons["fi"]; ok {
 		return val
 	}
 
diff --git a/lf.1 b/lf.1
index 1d0c45e..41f5f9c 100644
--- a/lf.1
+++ b/lf.1
@@ -163,6 +163,7 @@ dupfilefmt          string    (default \[aq]%f.\[ti]%n\[ti]\[aq])
 errorfmt            string    (default \[dq]\[rs]033[7;31;47m\[dq])
 filesep             string    (default \[dq]\[rs]n\[dq])
 findlen             int       (default 1)
+globfilter          bool      (default false)
 globsearch          bool      (default false)
 hidden              bool      (default false)
 hiddenfiles         []string  (default \[aq].*\[aq])
@@ -815,6 +816,10 @@ File separator used in environment variables \f[C]fs\f[R] and
 Number of characters prompted for the find command.
 When this value is set to 0, find command prompts until there is only a
 single match left.
+.SS globfilter (bool) (default false)
+.PP
+Patterns are treated as globs for the filter command, see
+\f[C]globsearch\f[R] for more details.
 .SS globsearch (bool) (default false)
 .PP
 When this option is enabled, search command patterns are considered as
@@ -2363,6 +2368,10 @@ ex=01;32:\[rs]
 \f[R]
 .fi
 .PP
+The \f[C]ln\f[R] entry supports the special value \f[C]target\f[R],
+which will use the link target to select a style.
+File name rules will still apply based on the link\[aq]s name -- this
+mirrors GNU\[aq]s \f[C]ls\f[R] and \f[C]dircolors\f[R] behavior.
 Having such a long variable definition in a shell configuration file
 might be undesirable.
 You may instead use the colors file (refer to the CONFIGURATION
@@ -2380,6 +2389,10 @@ section (https://github.com/gokcehan/lf/blob/master/doc.md#configuration)).
 The variable uses the same syntax as \f[C]LS_COLORS/LF_COLORS\f[R].
 Instead of colors, you should put a single characters as values of
 entries.
+The \f[C]ln\f[R] entry supports the special value \f[C]target\f[R],
+which will use the link target to select a icon.
+File name rules will still apply based on the link\[aq]s name -- this
+mirrors GNU\[aq]s \f[C]ls\f[R] and \f[C]dircolors\f[R] behavior.
 The icons file (refer to the CONFIGURATION
 section (https://github.com/gokcehan/lf/blob/master/doc.md#configuration))
 should consist of whitespace-separated pairs with a \f[C]#\f[R]
diff --git a/nav.go b/nav.go
index f620222..acd2c1d 100644
--- a/nav.go
+++ b/nav.go
@@ -1756,7 +1756,7 @@ func (nav *nav) findPrev() (bool, bool) {
 	return false, false
 }
 
-func searchMatch(name, pattern string) (matched bool, err error) {
+func searchMatch(name, pattern string, glob bool) (matched bool, err error) {
 	if gOpts.ignorecase {
 		lpattern := strings.ToLower(pattern)
 		if !gOpts.smartcase || lpattern == pattern {
@@ -1771,7 +1771,7 @@ func searchMatch(name, pattern string) (matched bool, err error) {
 			name = removeDiacritics(name)
 		}
 	}
-	if gOpts.globsearch {
+	if glob {
 		return filepath.Match(pattern, name)
 	}
 	return strings.Contains(name, pattern), nil
@@ -1780,7 +1780,7 @@ func searchMatch(name, pattern string) (matched bool, err error) {
 func (nav *nav) searchNext() (bool, error) {
 	dir := nav.currDir()
 	for i := dir.ind + 1; i < len(dir.files); i++ {
-		if matched, err := searchMatch(dir.files[i].Name(), nav.search); err != nil {
+		if matched, err := searchMatch(dir.files[i].Name(), nav.search, gOpts.globsearch); err != nil {
 			return false, err
 		} else if matched {
 			return nav.down(i - dir.ind), nil
@@ -1788,7 +1788,7 @@ func (nav *nav) searchNext() (bool, error) {
 	}
 	if gOpts.wrapscan {
 		for i := 0; i < dir.ind; i++ {
-			if matched, err := searchMatch(dir.files[i].Name(), nav.search); err != nil {
+			if matched, err := searchMatch(dir.files[i].Name(), nav.search, gOpts.globsearch); err != nil {
 				return false, err
 			} else if matched {
 				return nav.up(dir.ind - i), nil
@@ -1801,7 +1801,7 @@ func (nav *nav) searchNext() (bool, error) {
 func (nav *nav) searchPrev() (bool, error) {
 	dir := nav.currDir()
 	for i := dir.ind - 1; i >= 0; i-- {
-		if matched, err := searchMatch(dir.files[i].Name(), nav.search); err != nil {
+		if matched, err := searchMatch(dir.files[i].Name(), nav.search, gOpts.globsearch); err != nil {
 			return false, err
 		} else if matched {
 			return nav.up(dir.ind - i), nil
@@ -1809,7 +1809,7 @@ func (nav *nav) searchPrev() (bool, error) {
 	}
 	if gOpts.wrapscan {
 		for i := len(dir.files) - 1; i > dir.ind; i-- {
-			if matched, err := searchMatch(dir.files[i].Name(), nav.search); err != nil {
+			if matched, err := searchMatch(dir.files[i].Name(), nav.search, gOpts.globsearch); err != nil {
 				return false, err
 			} else if matched {
 				return nav.down(i - dir.ind), nil
@@ -1821,7 +1821,7 @@ func (nav *nav) searchPrev() (bool, error) {
 
 func isFiltered(f os.FileInfo, filter []string) bool {
 	for _, pattern := range filter {
-		matched, err := searchMatch(f.Name(), strings.TrimPrefix(pattern, "!"))
+		matched, err := searchMatch(f.Name(), strings.TrimPrefix(pattern, "!"), gOpts.globfilter)
 		if err != nil {
 			log.Printf("Filter Error: %s", err)
 			return false
diff --git a/opts.go b/opts.go
index fc211e0..681673b 100644
--- a/opts.go
+++ b/opts.go
@@ -46,6 +46,7 @@ var gOpts struct {
 	dirpreviews        bool
 	drawbox            bool
 	dupfilefmt         string
+	globfilter         bool
 	globsearch         bool
 	hidden             bool
 	icons              bool
@@ -191,6 +192,7 @@ func init() {
 	gOpts.cursorpreviewfmt = "\033[4m"
 	gOpts.cutfmt = "\033[7;31m"
 	gOpts.hidecursorinactive = false
+	gOpts.globfilter = false
 	gOpts.globsearch = false
 	gOpts.hidden = false
 	gOpts.icons = false
diff --git a/os_windows.go b/os_windows.go
index 4eefe83..a07821f 100644
--- a/os_windows.go
+++ b/os_windows.go
@@ -8,6 +8,7 @@ import (
 	"os/user"
 	"path/filepath"
 	"strings"
+	"syscall"
 
 	"golang.org/x/sys/windows"
 )
@@ -24,8 +25,8 @@ var envPathExt = os.Getenv("PATHEXT")
 var (
 	gDefaultShell      = "cmd"
 	gDefaultShellFlag  = "/c"
-	gDefaultSocketProt = "tcp"
-	gDefaultSocketPath = "127.0.0.1:12345"
+	gDefaultSocketProt = "unix"
+	gDefaultSocketPath string
 )
 
 var (
@@ -98,6 +99,16 @@ func init() {
 	gMarksPath = filepath.Join(data, "lf", "marks")
 	gTagsPath = filepath.Join(data, "lf", "tags")
 	gHistoryPath = filepath.Join(data, "lf", "history")
+
+	socket, err := syscall.Socket(syscall.AF_UNIX, syscall.SOCK_STREAM, 0)
+	if err != nil {
+		gDefaultSocketProt = "tcp"
+		gDefaultSocketPath = "127.0.0.1:12345"
+	} else {
+		runtime := os.TempDir()
+		gDefaultSocketPath = filepath.Join(runtime, fmt.Sprintf("lf.%s.sock", gUser.Username))
+		syscall.Close(socket)
+	}
 }
 
 func detachedCommand(name string, arg ...string) *exec.Cmd {


Test patch:
diff --git a/diacritics_test.go b/diacritics_test.go
index 3e29cf2..1d71eea 100644
--- a/diacritics_test.go
+++ b/diacritics_test.go
@@ -61,7 +61,7 @@ func runSearch(t *testing.T, ignorecase, smartcase, ignorediacritics, smartdiacr
 	gOpts.smartcase = smartcase
 	gOpts.ignoredia = ignorediacritics
 	gOpts.smartdia = smartdiacritics
-	matched, _ := searchMatch(base, pattern)
+	matched, _ := searchMatch(base, pattern, false)
 	if matched != expected {
 		t.Errorf("False search for" +
 			" ignorecase = " + strconv.FormatBool(gOpts.ignorecase) + ", " +