feat: flexible matching for include/exclude folders

This commit is contained in:
Aaron Sherber 2021-09-18 13:39:40 -04:00 committed by Jan De Dobbeleer
parent b2515650c1
commit 7e4865779a
3 changed files with 75 additions and 13 deletions

View file

@ -293,8 +293,9 @@ will not be rendered when in one of the excluded locations.
]
```
You can also specify a [regular expression][regex] to create folder wildcards.
In the sample below, any folders inside the `/Users/posh/Projects` path will be matched.
The strings specified in these properties are evaluated as [regular expressions][regex]. You
can use any valid regular expression construct, but the regular expression must match the entire directory
name. The following will match `/Users/posh/Projects/Foo` but not `/home/Users/posh/Projects/Foo`.
```json
"include_folders": [
@ -313,16 +314,17 @@ You can also combine these properties:
]
```
Note for Windows users: Windows directory separators should be specified as 4 backslashes.
##### Notes
```json
"include_folders": [
"C:\\\\Projects.*"
],
"exclude_folders": [
"C:\\\\Projects\\\\secret-project.*"
]
```
- Oh My Posh will accept both `/` and `\` as path separators for a folder and will match regardless of which
is used by the current operating system.
- Because the strings are evaluated as regular expressions, if you want to use a `\` in a Windows
directory name, you need to specify it as `\\\\`.
- The character `~` at the start of a specified folder will match the user's home directory.
- The comparison is case-insensitive on Windows and macOS, but case-sensitive on other operating systems.
This means that for user Bill, who has a user account `Bill` on Windows and `bill` on Linux, `~/Foo` might match
`C:\Users\Bill\Foo` or `C:\Users\Bill\foo` on Windows but only `/home/bill/Foo` on Linux.
### Colors

View file

@ -3,6 +3,7 @@ package main
import (
"errors"
"fmt"
"strings"
"time"
)
@ -176,9 +177,20 @@ func (segment *Segment) cwdExcluded(cwd string) bool {
}
func (segment *Segment) cwdMatchesOneOf(cwd string, regexes []string) bool {
normalizedCwd := strings.ReplaceAll(cwd, "\\", "/")
normalizedHomeDir := strings.ReplaceAll(segment.env.homeDir(), "\\", "/")
for _, element := range regexes {
pattern := fmt.Sprintf("^%s$", element)
matched := matchString(pattern, cwd)
normalizedElement := strings.ReplaceAll(element, "\\\\", "/")
if strings.HasPrefix(normalizedElement, "~") {
normalizedElement = normalizedHomeDir + normalizedElement[1:]
}
pattern := fmt.Sprintf("^%s$", normalizedElement)
goos := segment.env.getRuntimeGOOS()
if goos == windowsPlatform || goos == darwinPlatform {
pattern = "(?i)" + pattern
}
matched := matchString(pattern, normalizedCwd)
if matched {
return true
}

View file

@ -81,11 +81,15 @@ func TestShouldIncludeFolder(t *testing.T) {
{Case: "Include Mismatch / Exclude Mismatch", IncludeFolders: []string{"zProjects.*"}, ExcludeFolders: []string{"Projects/nope"}, Expected: false},
}
for _, tc := range cases {
env := new(MockedEnvironment)
env.On("getRuntimeGOOS", nil).Return(linuxPlatform)
env.On("homeDir", nil).Return("")
segment := &Segment{
Properties: map[Property]interface{}{
IncludeFolders: tc.IncludeFolders,
ExcludeFolders: tc.ExcludeFolders,
},
env: env,
}
got := segment.shouldIncludeFolder(cwd)
assert.Equal(t, tc.Expected, got, tc.Case)
@ -93,10 +97,14 @@ func TestShouldIncludeFolder(t *testing.T) {
}
func TestShouldIncludeFolderRegexInverted(t *testing.T) {
env := new(MockedEnvironment)
env.On("getRuntimeGOOS", nil).Return(linuxPlatform)
env.On("homeDir", nil).Return("")
segment := &Segment{
Properties: map[Property]interface{}{
ExcludeFolders: []string{"(?!Projects[\\/]).*"},
},
env: env,
}
// detect panic(thrown by MustCompile)
defer func() {
@ -109,10 +117,14 @@ func TestShouldIncludeFolderRegexInverted(t *testing.T) {
}
func TestShouldIncludeFolderRegexInvertedNonEscaped(t *testing.T) {
env := new(MockedEnvironment)
env.On("getRuntimeGOOS", nil).Return(linuxPlatform)
env.On("homeDir", nil).Return("")
segment := &Segment{
Properties: map[Property]interface{}{
ExcludeFolders: []string{"(?!Projects/).*"},
},
env: env,
}
// detect panic(thrown by MustCompile)
defer func() {
@ -196,3 +208,39 @@ func TestGetColors(t *testing.T) {
assert.Equal(t, tc.ExpectedColor, color, tc.Case)
}
}
func TestCwdMatchesOneOf(t *testing.T) {
cases := []struct {
GOOS string
HomeDir string
Cwd string
Pattern string
Expected bool
}{
{GOOS: linuxPlatform, HomeDir: "/home/bill", Cwd: "/home/bill", Pattern: "/home/bill", Expected: true},
{GOOS: linuxPlatform, HomeDir: "/home/bill", Cwd: "/home/bill/foo", Pattern: "~/foo", Expected: true},
{GOOS: linuxPlatform, HomeDir: "/home/bill", Cwd: "/home/bill/foo", Pattern: "~/Foo", Expected: false},
{GOOS: linuxPlatform, HomeDir: "/home/bill", Cwd: "/home/bill/foo", Pattern: "~\\\\foo", Expected: true},
{GOOS: linuxPlatform, HomeDir: "/home/bill", Cwd: "/home/bill/foo/bar", Pattern: "~/fo.*", Expected: true},
{GOOS: linuxPlatform, HomeDir: "/home/bill", Cwd: "/home/bill/foo", Pattern: "~/fo\\w", Expected: true},
{GOOS: windowsPlatform, HomeDir: "C:\\Users\\Bill", Cwd: "C:\\Users\\Bill", Pattern: "C:\\\\Users\\\\Bill", Expected: true},
{GOOS: windowsPlatform, HomeDir: "C:\\Users\\Bill", Cwd: "C:\\Users\\Bill", Pattern: "C:/Users/Bill", Expected: true},
{GOOS: windowsPlatform, HomeDir: "C:\\Users\\Bill", Cwd: "C:\\Users\\Bill", Pattern: "c:/users/bill", Expected: true},
{GOOS: windowsPlatform, HomeDir: "C:\\Users\\Bill", Cwd: "C:\\Users\\Bill", Pattern: "~", Expected: true},
{GOOS: windowsPlatform, HomeDir: "C:\\Users\\Bill", Cwd: "C:\\Users\\Bill\\Foo", Pattern: "~/Foo", Expected: true},
{GOOS: windowsPlatform, HomeDir: "C:\\Users\\Bill", Cwd: "C:\\Users\\Bill\\Foo", Pattern: "~/foo", Expected: true},
{GOOS: windowsPlatform, HomeDir: "C:\\Users\\Bill", Cwd: "C:\\Users\\Bill\\Foo\\Bar", Pattern: "~/fo.*", Expected: true},
{GOOS: windowsPlatform, HomeDir: "C:\\Users\\Bill", Cwd: "C:\\Users\\Bill\\Foo", Pattern: "~/fo\\w", Expected: true},
}
for _, tc := range cases {
env := new(MockedEnvironment)
env.On("getRuntimeGOOS", nil).Return(tc.GOOS)
env.On("homeDir", nil).Return(tc.HomeDir)
segment := &Segment{
env: env,
}
got := segment.cwdMatchesOneOf(tc.Cwd, []string{tc.Pattern})
assert.Equal(t, tc.Expected, got)
}
}