feat: mapped_locations_enabled

This commit is contained in:
TravisTX 2020-12-24 14:17:00 -07:00 committed by Jan De Dobbeleer
parent 7ff4954766
commit 0e5e65d9e3
13 changed files with 134 additions and 138 deletions

View file

@ -33,17 +33,18 @@ Display the current path.
- folder_icon: `string` - the icon to use as a folder indication - defaults to `..` - folder_icon: `string` - the icon to use as a folder indication - defaults to `..`
- windows_registry_icon: `string` - the icon to display when in the Windows registry - defaults to `\uE0B1` - windows_registry_icon: `string` - the icon to display when in the Windows registry - defaults to `\uE0B1`
- style: `enum` - how to display the current path - style: `enum` - how to display the current path
- mapped_locations: `map[string]string` - custom glyph/text for specific paths(only when `style` is set to `agnoster`, - mapped_locations: `map[string]string` - custom glyph/text for specific paths (only when `mapped_locations_enabled`
`agnoster_full`, `agnoster_short`, `short`, or `folder`) is set to `true`)
- mapped_locations_enabled: `boolean` - replace known locations in the path with the replacements before applying the
style. defaults to `true`
## Style ## Style
Style sets the way the path is displayed. Based on previous experience and popular themes, there are 4 flavors. Style sets the way the path is displayed. Based on previous experience and popular themes, there are 5 flavors.
- agnoster - agnoster
- agnoster_full - agnoster_full
- agnoster_short - agnoster_short
- short
- full - full
- folder - folder
@ -61,15 +62,9 @@ Renders each folder name separated by the `folder_separator_icon`.
When more than 1 level deep, it renders one `folder_icon` followed by the name of the current folder separated by the `folder_separator_icon`. When more than 1 level deep, it renders one `folder_icon` followed by the name of the current folder separated by the `folder_separator_icon`.
### Short
Display `$PWD` as a string, replace `$HOME` with the `home_icon` if you're inside the `$HOME` location or
one of its children.
Specific folders can be customized using the `mapped_locations` property.
### Full ### Full
Display `$PWD` as a string Display `$PWD` as a string.
### Folder ### Folder

View file

@ -36,6 +36,8 @@ const (
Folder string = "folder" Folder string = "folder"
// MappedLocations allows overriding certain location with an icon // MappedLocations allows overriding certain location with an icon
MappedLocations Property = "mapped_locations" MappedLocations Property = "mapped_locations"
// MappedLocationsEnabled enables overriding certain locations with an icon
MappedLocationsEnabled Property = "mapped_locations_enabled"
) )
func (pt *path) enabled() bool { func (pt *path) enabled() bool {
@ -51,9 +53,10 @@ func (pt *path) string() string {
case AgnosterShort: case AgnosterShort:
return pt.getAgnosterShortPath() return pt.getAgnosterShortPath()
case Short: case Short:
return pt.getShortPath() // "short" is a duplicate of "full", just here for backwards compatibility
fallthrough
case Full: case Full:
return pt.env.getcwd() return pt.getFullPath()
case Folder: case Folder:
return pt.getFolderPath() return pt.getFolderPath()
default: default:
@ -66,8 +69,67 @@ func (pt *path) init(props *properties, env environmentInfo) {
pt.env = env pt.env = env
} }
func (pt *path) getShortPath() string { func (pt *path) getAgnosterPath() string {
buffer := new(bytes.Buffer)
pwd := pt.getPwd()
buffer.WriteString(pt.rootLocation())
pathDepth := pt.pathDepth(pwd)
for i := 1; i < pathDepth; i++ {
buffer.WriteString(fmt.Sprintf("%s%s", pt.props.getString(FolderSeparatorIcon, pt.env.getPathSeperator()), pt.props.getString(FolderIcon, "..")))
}
if pathDepth > 0 {
buffer.WriteString(fmt.Sprintf("%s%s", pt.props.getString(FolderSeparatorIcon, pt.env.getPathSeperator()), base(pwd, pt.env)))
}
return buffer.String()
}
func (pt *path) getAgnosterFullPath() string {
pwd := pt.getPwd()
pathSeparator := pt.env.getPathSeperator()
folderSeparator := pt.props.getString(FolderSeparatorIcon, pathSeparator)
if string(pwd[0]) == pathSeparator {
pwd = pwd[1:]
}
return strings.ReplaceAll(pwd, pathSeparator, folderSeparator)
}
func (pt *path) getAgnosterShortPath() string {
pathSeparator := pt.env.getPathSeperator()
folderSeparator := pt.props.getString(FolderSeparatorIcon, pathSeparator)
folderIcon := pt.props.getString(FolderIcon, "..")
root := pt.rootLocation()
pwd := pt.getPwd()
base := base(pwd, pt.env)
pathDepth := pt.pathDepth(pwd)
if pathDepth <= 0 {
return root
}
if pathDepth == 1 {
return fmt.Sprintf("%s%s%s", root, folderSeparator, base)
}
return fmt.Sprintf("%s%s%s%s%s", root, folderSeparator, folderIcon, folderSeparator, base)
}
func (pt *path) getFullPath() string {
return pt.getPwd()
}
func (pt *path) getFolderPath() string {
pwd := pt.getPwd()
return base(pwd, pt.env)
}
func (pt *path) getPwd() string {
pwd := pt.env.getcwd() pwd := pt.env.getcwd()
if pt.props.getBool(MappedLocationsEnabled, true) {
pwd = pt.replaceMappedLocations(pwd)
}
return pwd
}
func (pt *path) replaceMappedLocations(pwd string) string {
if strings.HasPrefix(pwd, "Microsoft.PowerShell.Core\\FileSystem::") { if strings.HasPrefix(pwd, "Microsoft.PowerShell.Core\\FileSystem::") {
pwd = strings.Replace(pwd, "Microsoft.PowerShell.Core\\FileSystem::", "", 1) pwd = strings.Replace(pwd, "Microsoft.PowerShell.Core\\FileSystem::", "", 1)
} }
@ -104,57 +166,12 @@ func (pt *path) getShortPath() string {
return pwd return pwd
} }
func (pt *path) getAgnosterPath() string {
buffer := new(bytes.Buffer)
pwd := pt.getShortPath()
buffer.WriteString(pt.rootLocation())
pathDepth := pt.pathDepth(pwd)
for i := 1; i < pathDepth; i++ {
buffer.WriteString(fmt.Sprintf("%s%s", pt.props.getString(FolderSeparatorIcon, pt.env.getPathSeperator()), pt.props.getString(FolderIcon, "..")))
}
if pathDepth > 0 {
buffer.WriteString(fmt.Sprintf("%s%s", pt.props.getString(FolderSeparatorIcon, pt.env.getPathSeperator()), base(pwd, pt.env)))
}
return buffer.String()
}
func (pt *path) getAgnosterFullPath() string {
pwd := pt.getShortPath()
pathSeparator := pt.env.getPathSeperator()
folderSeparator := pt.props.getString(FolderSeparatorIcon, pathSeparator)
if string(pwd[0]) == pathSeparator {
pwd = pwd[1:]
}
return strings.ReplaceAll(pwd, pathSeparator, folderSeparator)
}
func (pt *path) getAgnosterShortPath() string {
pathSeparator := pt.env.getPathSeperator()
folderSeparator := pt.props.getString(FolderSeparatorIcon, pathSeparator)
folderIcon := pt.props.getString(FolderIcon, "..")
root := pt.rootLocation()
base := base(pt.env.getcwd(), pt.env)
pathDepth := pt.pathDepth(pt.getShortPath())
if pathDepth <= 0 {
return root
}
if pathDepth == 1 {
return fmt.Sprintf("%s%s%s", root, folderSeparator, base)
}
return fmt.Sprintf("%s%s%s%s%s", root, folderSeparator, folderIcon, folderSeparator, base)
}
func (pt *path) getFolderPath() string {
pwd := pt.getShortPath()
return base(pwd, pt.env)
}
func (pt *path) inHomeDir(pwd string) bool { func (pt *path) inHomeDir(pwd string) bool {
return strings.HasPrefix(pwd, pt.env.homeDir()) return strings.HasPrefix(pwd, pt.env.homeDir())
} }
func (pt *path) rootLocation() string { func (pt *path) rootLocation() string {
pwd := pt.getShortPath() pwd := pt.getPwd()
pwd = strings.TrimPrefix(pwd, pt.env.getPathSeperator()) pwd = strings.TrimPrefix(pwd, pt.env.getPathSeperator())
splitted := strings.Split(pwd, pt.env.getPathSeperator()) splitted := strings.Split(pwd, pt.env.getPathSeperator())
rootLocation := splitted[0] rootLocation := splitted[0]
@ -162,9 +179,6 @@ func (pt *path) rootLocation() string {
} }
func (pt *path) pathDepth(pwd string) int { func (pt *path) pathDepth(pwd string) int {
if pt.inHomeDir(pwd) {
pwd = strings.Replace(pwd, pt.env.homeDir(), "root", 1)
}
splitted := strings.Split(pwd, pt.env.getPathSeperator()) splitted := strings.Split(pwd, pt.env.getPathSeperator())
var validParts []string var validParts []string
for _, part := range splitted { for _, part := range splitted {

View file

@ -1,7 +1,6 @@
package main package main
import ( import (
"math/rand"
"testing" "testing"
"github.com/distatus/battery" "github.com/distatus/battery"
@ -129,7 +128,6 @@ func (env *MockedEnvironment) doGet(url string) ([]byte, error) {
} }
const ( const (
homeGates = "/home/gates"
homeBill = "/home/bill" homeBill = "/home/bill"
homeJan = "/usr/home/jan" homeJan = "/usr/home/jan"
homeBillWindows = "C:\\Users\\Bill" homeBillWindows = "C:\\Users\\Bill"
@ -148,10 +146,9 @@ func TestIsInHomeDirTrue(t *testing.T) {
} }
func TestIsInHomeDirLevelTrue(t *testing.T) { func TestIsInHomeDirLevelTrue(t *testing.T) {
level := rand.Intn(100)
home := homeBill home := homeBill
pwd := home pwd := home
for i := 0; i < level; i++ { for i := 0; i < 99; i++ {
pwd += levelDir pwd += levelDir
} }
env := new(MockedEnvironment) env := new(MockedEnvironment)
@ -290,71 +287,23 @@ func TestIsInHomeDirFalse(t *testing.T) {
assert.False(t, got) assert.False(t, got)
} }
func TestPathDepthInHome(t *testing.T) { func TestPathDepthMultipleLevelsDeep(t *testing.T) {
home := homeBill
pwd := home
env := new(MockedEnvironment)
env.On("homeDir", nil).Return(home)
env.On("getPathSeperator", nil).Return("/")
path := &path{
env: env,
}
got := path.pathDepth(pwd)
assert.Equal(t, 0, got)
}
func TestPathDepthInHomeTrailing(t *testing.T) {
home := "/home/bill/"
pwd := home + "/"
env := new(MockedEnvironment)
env.On("homeDir", nil).Return(home)
env.On("getPathSeperator", nil).Return("/")
path := &path{
env: env,
}
got := path.pathDepth(pwd)
assert.Equal(t, 0, got)
}
func TestPathDepthInHomeMultipleLevelsDeep(t *testing.T) {
level := rand.Intn(100)
home := homeBill
pwd := home
for i := 0; i < level; i++ {
pwd += levelDir
}
env := new(MockedEnvironment)
env.On("homeDir", nil).Return(home)
env.On("getPathSeperator", nil).Return("/")
path := &path{
env: env,
}
got := path.pathDepth(pwd)
assert.Equal(t, level, got)
}
func TestPathDepthOutsideHomeMultipleLevelsDeep(t *testing.T) {
level := rand.Intn(100)
home := homeGates
pwd := "/usr" pwd := "/usr"
for i := 0; i < level; i++ { for i := 0; i < 99; i++ {
pwd += levelDir pwd += levelDir
} }
env := new(MockedEnvironment) env := new(MockedEnvironment)
env.On("homeDir", nil).Return(home)
env.On("getPathSeperator", nil).Return("/") env.On("getPathSeperator", nil).Return("/")
path := &path{ path := &path{
env: env, env: env,
} }
got := path.pathDepth(pwd) got := path.pathDepth(pwd)
assert.Equal(t, level, got) assert.Equal(t, 99, got)
} }
func TestPathDepthOutsideHomeZeroLevelsDeep(t *testing.T) { func TestPathDepthZeroLevelsDeep(t *testing.T) {
home := homeGates
pwd := "/usr/" pwd := "/usr/"
env := new(MockedEnvironment) env := new(MockedEnvironment)
env.On("homeDir", nil).Return(home)
env.On("getPathSeperator", nil).Return("/") env.On("getPathSeperator", nil).Return("/")
path := &path{ path := &path{
env: env, env: env,
@ -363,11 +312,9 @@ func TestPathDepthOutsideHomeZeroLevelsDeep(t *testing.T) {
assert.Equal(t, 0, got) assert.Equal(t, 0, got)
} }
func TestPathDepthOutsideHomeOneLevelDeep(t *testing.T) { func TestPathDepthOneLevelDeep(t *testing.T) {
home := homeGates
pwd := "/usr/location" pwd := "/usr/location"
env := new(MockedEnvironment) env := new(MockedEnvironment)
env.On("homeDir", nil).Return(home)
env.On("getPathSeperator", nil).Return("/") env.On("getPathSeperator", nil).Return("/")
path := &path{ path := &path{
env: env, env: env,
@ -624,3 +571,43 @@ func TestWritePathInfoUnixOutsideHomeOneLevels(t *testing.T) {
got := testWritePathInfo(home, "/mnt/folder/location", "/") got := testWritePathInfo(home, "/mnt/folder/location", "/")
assert.Equal(t, want, got) assert.Equal(t, want, got)
} }
func TestGetPwd(t *testing.T) {
cases := []struct {
MappedLocationsEnabled bool
Pwd string
Expected string
}{
{MappedLocationsEnabled: true, Pwd: "", Expected: ""},
{MappedLocationsEnabled: true, Pwd: "/usr", Expected: "/usr"},
{MappedLocationsEnabled: true, Pwd: "/usr/home", Expected: "~"},
{MappedLocationsEnabled: true, Pwd: "/usr/home/abc", Expected: "~/abc"},
{MappedLocationsEnabled: true, Pwd: "/a/b/c/d", Expected: "#"},
{MappedLocationsEnabled: true, Pwd: "/a/b/c/d/e/f/g", Expected: "#/e/f/g"},
{MappedLocationsEnabled: true, Pwd: "/z/y/x/w", Expected: "/z/y/x/w"},
{MappedLocationsEnabled: false, Pwd: "", Expected: ""},
{MappedLocationsEnabled: false, Pwd: "/usr/home/abc", Expected: "/usr/home/abc"},
{MappedLocationsEnabled: false, Pwd: "/a/b/c/d/e/f/g", Expected: "/a/b/c/d/e/f/g"},
}
for _, tc := range cases {
env := new(MockedEnvironment)
env.On("getPathSeperator", nil).Return("/")
env.On("homeDir", nil).Return("/usr/home")
env.On("getcwd", nil).Return(tc.Pwd)
path := &path{
env: env,
props: &properties{
values: map[Property]interface{}{
MappedLocationsEnabled: tc.MappedLocationsEnabled,
MappedLocations: map[string]string{
"/a/b/c/d": "#",
},
},
},
}
got := path.getPwd()
assert.Equal(t, tc.Expected, got)
}
}

View file

@ -19,7 +19,7 @@
"foreground": "#ffffff", "foreground": "#ffffff",
"background": "#C678DD", "background": "#C678DD",
"properties": { "properties": {
"style": "short" "style": "full"
} }
}, },
{ {

View file

@ -10,7 +10,7 @@
"foreground": "#ffffff", "foreground": "#ffffff",
"properties": { "properties": {
"prefix": "", "prefix": "",
"style": "short" "style": "full"
} }
}, },
{ {

View file

@ -56,7 +56,7 @@
"style": "plain", "style": "plain",
"foreground": "#ffffff", "foreground": "#ffffff",
"properties": { "properties": {
"style": "short", "style": "full",
"prefix": "<#CB4B16>┖[</>", "prefix": "<#CB4B16>┖[</>",
"postfix": "<#CB4B16>]></>" "postfix": "<#CB4B16>]></>"
} }

View file

@ -27,12 +27,12 @@
"style": "plain", "style": "plain",
"foreground": "#ffffff", "foreground": "#ffffff",
"background": "#007ACC", "background": "#007ACC",
"properties" : { "properties": {
"folder_icon": "\uF115", "folder_icon": "\uF115",
"folder_separator_icon": "\uE0B1", "folder_separator_icon": "\uE0B1",
"style": "short", "style": "full",
"prefix": "<transparent>\uE0B0</> ", "prefix": "<transparent>\uE0B0</> ",
"postfix": " " "postfix": " "
} }
}, },
{ {

View file

@ -22,7 +22,7 @@
"properties": { "properties": {
"folder_icon": "\uF115", "folder_icon": "\uF115",
"folder_separator_icon": "\uE0B1", "folder_separator_icon": "\uE0B1",
"style": "short" "style": "full"
} }
}, },
{ {

View file

@ -27,7 +27,7 @@
"properties": { "properties": {
"folder_icon": "\uF115", "folder_icon": "\uF115",
"folder_separator_icon": "\uE0B1", "folder_separator_icon": "\uE0B1",
"style": "short" "style": "full"
} }
}, },
{ {

View file

@ -25,7 +25,7 @@
"foreground": "#26C6DA", "foreground": "#26C6DA",
"background": "#546E7A", "background": "#546E7A",
"properties": { "properties": {
"style": "short", "style": "full",
"postfix": " " "postfix": " "
} }
}, },

View file

@ -28,7 +28,7 @@
"foreground": "#77E4F7", "foreground": "#77E4F7",
"properties": { "properties": {
"prefix": "", "prefix": "",
"style": "short" "style": "full"
} }
}, },
{ {

View file

@ -20,8 +20,8 @@
"powerline_symbol": "\uE0B0", "powerline_symbol": "\uE0B0",
"foreground": "#100e23", "foreground": "#100e23",
"background": "#91ddff", "background": "#91ddff",
"properties" : { "properties": {
"style": "short" "style": "full"
} }
}, },
{ {

View file

@ -24,7 +24,7 @@
"style": "plain", "style": "plain",
"foreground": "#0973C0", "foreground": "#0973C0",
"properties": { "properties": {
"style": "short" "style": "full"
} }
}, },
{ {