feat(path): add colored full path

This commit is contained in:
Jan De Dobbeleer 2023-04-20 07:00:09 +02:00 committed by Jan De Dobbeleer
parent 4e9e753e13
commit facc24d290
3 changed files with 148 additions and 107 deletions

View file

@ -73,6 +73,8 @@ const (
MaxWidth properties.Property = "max_width"
// Hides the root location if it doesn't fit in max_depth. Used in Agnoster Short
HideRootLocation properties.Property = "hide_root_location"
// A foreground color cycle
Cycle properties.Property = "cycle"
)
func (pt *Path) Template() string {
@ -233,41 +235,39 @@ func (pt *Path) pathDepth(pwd string) int {
}
func (pt *Path) getAgnosterPath() string {
var buffer strings.Builder
folderIcon := pt.props.GetString(FolderIcon, "..")
separator := pt.getFolderSeparator()
elements := strings.Split(pt.relative, pt.env.PathSeparator())
if pt.root != pt.env.PathSeparator() {
elements = append([]string{pt.root}, elements...)
splitted := strings.Split(pt.relative, pt.env.PathSeparator())
if pt.root == pt.env.PathSeparator() {
pt.root = splitted[0]
splitted = splitted[1:]
}
n := len(elements)
buffer.WriteString(elements[0])
for i := 2; i < n; i++ {
buffer.WriteString(fmt.Sprintf("%s%s", separator, folderIcon))
var elements []string
n := len(splitted)
for i := 1; i < n; i++ {
elements = append(elements, folderIcon)
}
if n > 1 {
buffer.WriteString(fmt.Sprintf("%s%s", separator, elements[n-1]))
}
return buffer.String()
elements = append(elements, splitted[n-1])
return pt.colorizePath(pt.root, elements)
}
func (pt *Path) getAgnosterLeftPath() string {
var buffer strings.Builder
folderIcon := pt.props.GetString(FolderIcon, "..")
separator := pt.getFolderSeparator()
elements := strings.Split(pt.relative, pt.env.PathSeparator())
if pt.root != pt.env.PathSeparator() {
elements = append([]string{pt.root}, elements...)
splitted := strings.Split(pt.relative, pt.env.PathSeparator())
if pt.root == pt.env.PathSeparator() {
pt.root = splitted[0]
splitted = splitted[1:]
}
n := len(elements)
buffer.WriteString(elements[0])
if n > 1 {
buffer.WriteString(fmt.Sprintf("%s%s", separator, elements[1]))
var elements []string
n := len(splitted)
elements = append(elements, splitted[0])
for i := 1; i < n; i++ {
elements = append(elements, folderIcon)
}
for i := 2; i < n; i++ {
buffer.WriteString(fmt.Sprintf("%s%s", separator, folderIcon))
}
return buffer.String()
return pt.colorizePath(pt.root, elements)
}
func (pt *Path) getRelevantLetter(folder string) string {
@ -284,43 +284,47 @@ func (pt *Path) getRelevantLetter(folder string) string {
}
func (pt *Path) getLetterPath() string {
var buffer strings.Builder
separator := pt.getFolderSeparator()
elements := strings.Split(pt.relative, pt.env.PathSeparator())
if pt.root != pt.env.PathSeparator() {
elements = append([]string{pt.root}, elements...)
splitted := strings.Split(pt.relative, pt.env.PathSeparator())
if pt.root == pt.env.PathSeparator() {
pt.root = splitted[0]
splitted = splitted[1:]
}
n := len(elements)
pt.root = pt.getRelevantLetter(pt.root)
var elements []string
n := len(splitted)
for i := 0; i < n-1; i++ {
letter := pt.getRelevantLetter(elements[i])
if i != 0 {
buffer.WriteString(separator)
}
buffer.WriteString(letter)
letter := pt.getRelevantLetter(splitted[i])
elements = append(elements, letter)
}
buffer.WriteString(fmt.Sprintf("%s%s", separator, elements[n-1]))
return buffer.String()
elements = append(elements, splitted[n-1])
return pt.colorizePath(pt.root, elements)
}
func (pt *Path) getUniqueLettersPath(maxWidth int) string {
var buffer strings.Builder
separator := pt.getFolderSeparator()
elements := strings.Split(pt.relative, pt.env.PathSeparator())
if pt.root != pt.env.PathSeparator() {
elements = append([]string{pt.root}, elements...)
splitted := strings.Split(pt.relative, pt.env.PathSeparator())
if pt.root == pt.env.PathSeparator() {
pt.root = splitted[0]
splitted = splitted[1:]
}
if maxWidth > 0 {
path := strings.Join(elements, separator)
path := strings.Join(splitted, separator)
if len(path) <= maxWidth {
return path
return pt.colorizePath(pt.root, splitted)
}
}
n := len(elements)
pt.root = pt.getRelevantLetter(pt.root)
var elements []string
n := len(splitted)
letters := make(map[string]bool)
letters[pt.root] = true
for i := 0; i < n-1; i++ {
folder := elements[i]
folder := splitted[i]
letter := pt.getRelevantLetter(folder)
for letters[letter] {
if letter == folder {
@ -329,32 +333,33 @@ func (pt *Path) getUniqueLettersPath(maxWidth int) string {
letter += folder[len(letter) : len(letter)+1]
}
letters[letter] = true
if i != 0 {
buffer.WriteString(separator)
}
buffer.WriteString(letter)
elements = append(elements, letter)
// only return early on maxWidth > 0
// this enables the powerlevel10k behavior
if maxWidth > 0 {
trailing := strings.Join(elements[i+1:], separator)
leftover := maxWidth - buffer.Len() - len(trailing) - len(separator)
list := splitted[i+1:]
list = append(list, elements...)
current := strings.Join(list, separator)
leftover := maxWidth - len(current) - len(pt.root) - len(separator)
if leftover >= 0 {
buffer.WriteString(fmt.Sprintf("%s%s", separator, trailing))
return buffer.String()
elements = append(elements, strings.Join(splitted[i+1:], separator))
return pt.colorizePath(pt.root, elements)
}
}
}
buffer.WriteString(fmt.Sprintf("%s%s", separator, elements[n-1]))
return buffer.String()
elements = append(elements, splitted[n-1])
return pt.colorizePath(pt.root, elements)
}
func (pt *Path) getAgnosterFullPath() string {
path := strings.Trim(pt.relative, pt.env.PathSeparator())
path = pt.replaceFolderSeparators(path)
splitted := strings.Split(pt.relative, pt.env.PathSeparator())
if pt.root == pt.env.PathSeparator() {
return path
pt.root = splitted[0]
splitted = splitted[1:]
}
return pt.root + pt.getFolderSeparator() + path
return pt.colorizePath(pt.root, splitted)
}
func (pt *Path) getAgnosterShortPath() string {
@ -372,11 +377,11 @@ func (pt *Path) getAgnosterShortPath() string {
return pt.getAgnosterFullPath()
}
pathSeparator := pt.env.PathSeparator()
folderSeparator := pt.getFolderSeparator()
rel := strings.TrimPrefix(pt.relative, pathSeparator)
splitted := strings.Split(rel, pathSeparator)
splitPos := pathDepth - maxDepth
var buffer strings.Builder
// var buffer strings.Builder
var elements []string
// unix root, needs to be replaced with the folder we're in at root level
root := pt.root
room := pathDepth - maxDepth
@ -384,29 +389,24 @@ func (pt *Path) getAgnosterShortPath() string {
root = splitted[0]
room--
}
if hideRootLocation || room > 0 {
elements = append(elements, folderIcon)
}
if hideRootLocation {
buffer.WriteString(folderIcon)
} else {
buffer.WriteString(root)
if room > 0 {
buffer.WriteString(folderSeparator)
buffer.WriteString(folderIcon)
}
root = ""
}
for i := splitPos; i < pathDepth; i++ {
buffer.WriteString(fmt.Sprintf("%s%s", folderSeparator, splitted[i]))
elements = append(elements, splitted[i])
}
return buffer.String()
return pt.colorizePath(root, elements)
}
func (pt *Path) getFullPath() string {
rel := pt.relative
pathSeparator := pt.env.PathSeparator()
if len(pt.root) != 0 && pt.root != pathSeparator && !strings.HasSuffix(pt.root, pathSeparator) {
rel = pathSeparator + rel
}
path := pt.replaceFolderSeparators(rel)
return pt.root + path
elements := strings.Split(pt.relative, pt.env.PathSeparator())
return pt.colorizePath(pt.root, elements)
}
func (pt *Path) getFolderPath() string {
@ -573,3 +573,31 @@ func (pt *Path) replaceFolderSeparators(pwd string) string {
pwd = strings.ReplaceAll(pwd, defaultSeparator, folderSeparator)
return pwd
}
func (pt *Path) colorizePath(root string, elements []string) string {
cycle := pt.props.GetStringArray(Cycle, []string{})
folderSeparator := pt.getFolderSeparator()
if len(cycle) != 0 {
colorize := "<%s>%s</>"
if len(root) != 0 {
root = fmt.Sprintf(colorize, cycle[0], root)
cycle = append(cycle[1:], cycle[0])
}
for i, element := range elements {
if len(element) == 0 {
continue
}
colored := fmt.Sprintf(colorize, cycle[0], element)
elements[i] = colored
cycle = append(cycle[1:], cycle[0])
}
}
if root == pt.env.PathSeparator() || len(root) == 0 {
return root + strings.Join(elements, folderSeparator)
}
return root + folderSeparator + strings.Join(elements, folderSeparator)
}

View file

@ -956,6 +956,7 @@ func TestAgnosterPath(t *testing.T) {
PWD string
GOOS string
PathSeparator string
Cycle []string
}{
{
Case: "Windows registry drive case sensitive",
@ -1073,9 +1074,17 @@ func TestAgnosterPath(t *testing.T) {
PWD: "/mnt/folder/location",
PathSeparator: "/",
},
{
Case: "Unix, colorize",
Expected: "<blue>mnt</> > <yellow>f</> > <blue>location</>",
Home: homeDir,
PWD: "/mnt/folder/location",
PathSeparator: "/",
Cycle: []string{"blue", "yellow"},
},
}
for _, tc := range cases { //nolint:dupl
for _, tc := range cases {
env := new(mock.MockedEnvironment)
env.On("Home").Return(tc.Home)
env.On("PathSeparator").Return(tc.PathSeparator)
@ -1093,6 +1102,7 @@ func TestAgnosterPath(t *testing.T) {
FolderSeparatorIcon: " > ",
FolderIcon: "f",
HomeIcon: "~",
Cycle: tc.Cycle,
},
}
path.setPaths()
@ -1229,7 +1239,7 @@ func TestAgnosterLeftPath(t *testing.T) {
},
}
for _, tc := range cases { //nolint:dupl
for _, tc := range cases {
env := new(mock.MockedEnvironment)
env.On("Home").Return(tc.Home)
env.On("PathSeparator").Return(tc.PathSeparator)

View file

@ -10,36 +10,39 @@ Display the current path.
## Sample Configuration
import Config from '@site/src/components/Config.js';
import Config from "@site/src/components/Config.js";
<Config data={{
"type": "path",
"style": "powerline",
"powerline_symbol": "\uE0B0",
"foreground": "#ffffff",
"background": "#61AFEF",
"properties": {
"style": "folder",
"mapped_locations": {
"C:\\temp": "\ue799"
}
}
}}/>
<Config
data={{
type: "path",
style: "powerline",
powerline_symbol: "\uE0B0",
foreground: "#ffffff",
background: "#61AFEF",
properties: {
style: "folder",
mapped_locations: {
"C:\\temp": "\ue799",
},
},
}}
/>
## Properties
| Name | Type | Description |
| --------------------------- | --------- | ---------------------------------------------------------------------------------------------------------------------------- |
| `folder_separator_icon` | `string` | the symbol to use as a separator between folders - defaults to platform path separator |
| `folder_separator_template` | `string` | the [template][templates] to use as a separator between folders - defaults to `` |
| `home_icon` | `string` | the icon to display when at `$HOME`, 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` |
| `style` | `enum` | how to display the current path |
| `mixed_threshold` | `number` | the maximum length of a path segment that will be displayed when using `Mixed` - defaults to `4` |
| `max_depth` | `number` | maximum path depth to display before shortening when using `agnoster_short`, defaults to `1` |
| `max_width` | `number` | maximum path length to display when using `powerlevel`, defaults to `0` |
| `hide_root_location` | `boolean` | hides the root location if it doesn't fit in the last `max_depth` folders, when using `agnoster_short` - defaults to `false` |
| Name | Type | Description |
| --------------------------- | ---------- | ---------------------------------------------------------------------------------------------------------------------------- |
| `folder_separator_icon` | `string` | the symbol to use as a separator between folders - defaults to platform path separator |
| `folder_separator_template` | `string` | the [template][templates] to use as a separator between folders - defaults to `` |
| `home_icon` | `string` | the icon to display when at `$HOME`, 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` |
| `style` | `enum` | how to display the current path |
| `mixed_threshold` | `number` | the maximum length of a path segment that will be displayed when using `Mixed` - defaults to `4` |
| `max_depth` | `number` | maximum path depth to display before shortening when using `agnoster_short`, defaults to `1` |
| `max_width` | `number` | maximum path length to display when using `powerlevel`, defaults to `0` |
| `hide_root_location` | `boolean` | hides the root location if it doesn't fit in the last `max_depth` folders, when using `agnoster_short` - defaults to `false` |
| `cycle` | `[]string` | a list of foreground colors to cycle through to colorize the individual path folders |
## Mapped Locations