diff --git a/0.19.0-release-notes.md b/0.19.0-release-notes.md index 15bcfa7ab..92a5a40df 100644 --- a/0.19.0-release-notes.md +++ b/0.19.0-release-notes.md @@ -35,3 +35,6 @@ and compiled, even if it is not executed: - A new type of interactive abbreviation: `edit:command-abbr` ([#1472](https://b.elv.sh/1472)). + +- A new `path:join` command and `path:separator` and `path:list-separator` + variables ([#1562](https://b.elv.sh/1562)). diff --git a/pkg/mods/path/path.go b/pkg/mods/path/path.go index 83812f73a..5858d5118 100644 --- a/pkg/mods/path/path.go +++ b/pkg/mods/path/path.go @@ -7,10 +7,15 @@ import ( "src.elv.sh/pkg/eval" "src.elv.sh/pkg/eval/errs" + "src.elv.sh/pkg/eval/vars" ) // Ns is the namespace for the re: module. var Ns = eval.BuildNsNamed("path"). + AddVars(map[string]vars.Var{ + "list-separator": vars.NewReadOnly(string(filepath.ListSeparator)), + "separator": vars.NewReadOnly(string(filepath.Separator)), + }). AddGoFns(map[string]any{ "abs": filepath.Abs, "base": filepath.Base, @@ -21,10 +26,31 @@ var Ns = eval.BuildNsNamed("path"). "is-abs": filepath.IsAbs, "is-dir": isDir, "is-regular": isRegular, + "join": filepath.Join, "temp-dir": tempDir, "temp-file": tempFile, }).Ns() +//elvdoc:var list-separator +// +// OS-specific path list separator. Colon on UNIX and semi-colon on Windows. This variable is +// read-only. +// +// ```elvish-transcript +// ~> put $path:list-separator +// ▶ : +// ``` + +//elvdoc:var separator +// +// OS-specific path separator. Forward slash on UNIX and backslash on Windows. This variable is +// read-only. +// +// ```elvish-transcript +// ~> put $path:separator +// ▶ / +// ``` + //elvdoc:fn abs // // ```elvish @@ -131,6 +157,24 @@ var Ns = eval.BuildNsNamed("path"). // external `realpath` or `readlink` command found on many systems. See the [Go // documentation](https://pkg.go.dev/path/filepath#EvalSymlinks) for more details. +//elvdoc:fn join +// +// ```elvish +// path:join $path-component... +// ``` +// +// Join joins any number of path elements into a single path, separating them with an OS specific +// separator. Empty elements are ignored. The result is cleaned. However, if the argument list is +// empty or all its elements are empty, Join returns an empty string. On Windows, the result will +// only be a UNC path if the first non-empty element is a UNC path. +// +// ```elvish-transcript +// ~> path:join home user bin +// ▶ home/user/bin +// ~> path:join $path:separator home user bin +// ▶ /home/user/bin +// ``` + //elvdoc:fn is-dir // // ```elvish diff --git a/pkg/mods/path/path_test.go b/pkg/mods/path/path_test.go index d357a0b0e..34dd97a48 100644 --- a/pkg/mods/path/path_test.go +++ b/pkg/mods/path/path_test.go @@ -36,6 +36,8 @@ func TestPath(t *testing.T) { // This block of tests is not meant to be comprehensive. Their primary purpose is to simply // ensure the Elvish command is correctly mapped to the relevant Go function. We assume the // Go function behaves correctly. + That("put $path:list-separator").Puts(string(filepath.ListSeparator)), + That("put $path:separator").Puts(string(filepath.Separator)), That("path:abs a/b/c.png").Puts(absPath), That("path:base a/b/d.png").Puts("d.png"), That("path:clean ././x").Puts("x"), @@ -45,6 +47,7 @@ func TestPath(t *testing.T) { That("path:ext a/b/s").Puts(""), That("path:is-abs a/b/s").Puts(false), That("path:is-abs "+absPath).Puts(true), + That("path:join a b c").Puts(filepath.Join("a", "b", "c")), // Elvish "path:" module functions that are not trivial wrappers around a Go stdlib function // should have comprehensive tests below this comment.