-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add relativePath proc and test #8166
Conversation
It should be named |
@Araq What about just "makeRelative"? Or "convertToRelative"? |
@Varriount I have proc getRelPathFromRel(path, baseDir, curDir: string): string =
## Convert 'path' to a relative path from baseDir.
##
## Both 'path' and 'baseDir' must be relative paths from 'curDir'.
## This proc never read filesystem.
## 'baseDir' is always assumed to be a directory even if that path is actually a file.
runnableExamples:
doAssert getRelPath("a/b", "../a", "/x/y") == "../y/a/b" I'm not sure whether this proc should be added to standard library. |
/cc @demotomohiro
I don't really care for the name though, but |
If this pull request is merged, I will send pull request to add
#path "a" and ".." are relative to "/x/y"
doAssert getRelativePathFromRelative("a", "..", "/x/y") == "y/a" calculate a relative path of "a" relative to "..". |
I like |
I commited proc relativePathHelper(path, baseDir, curDir: string = ""): bool =
return relativePath(path.unixToNativePath,
baseDir.unixToNativePath,
curDir.unixToNativePath) |
tests/stdlib/tospaths.nim
Outdated
path.unixToNativePath("a"), | ||
baseDir.unixToNativePath("a"), | ||
curDir.unixToNativePath("a")) | ||
#debugEcho r |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
instead of leaving debugging comments, how about:
if r != res.unixToNativePath:
print whatever you want
return r == res.unixToNativePath
relativePath(dir) => same as relativePath(dir, cwd)
doAssert relativePath("foo/bar", "foo") == "bar"
doAssert relativePath("/foo/bar", "/foo") == "bar"
doAssert relativePath("/foo/bar", "foo") => throws
doAssert relativePath("foo/bar", "/foo") => throws user is free to use these to avoid throwing in last 2 cases where one path is rel the other abs:
that way, interface is cleaner and more compositional; it's also more similar to what's done in other languages, eg:
|
actually since tospaths.nim does that, maybe just using |
@timotheecour Thank you for viewing my PR. 3rd param in For example, echo relativePath(".", "..", "a") I researched Test Python's os.path.relpath and pathlib.PurePosixPath.relative_to pathlib.PurePosixPath( . ).relative_to( .. ) => ValueError: '.' does not start with '..' Test C++ boost::filesystem::relative std.path.relativePath in D: Struct std::path::Path in Rust: Nim compiler already have |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't like it at all. It adds over 300 lines of code, is very low level everywhere (hard to maintain), and I can't specify at runtime if the path should be treated case sensitive.
proc isSep(c: char): bool {.noSideEffect.} = c in {DirSep, AltSep} | ||
|
||
proc cmpCharInPath(a, b: char): bool {.noSideEffect.} = | ||
when FileSystemCaseSensitive: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not sure if I like FileSystemCaseSensitive as a constant. It is not uncommon to have an ntfs hard drive mounted somewhere in a unix file system (the Windows partition).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's how it's done and it works well. There is no way to get this info in a portable way at runtime.
|
||
result = if numUp > 0: ParDir & (DirSep & ParDir).repeat(numUp-1) else: "" | ||
if pos < path.len: | ||
return result / path.substr(pos) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is way too low level. Just normalize the input paths and strip the common prefix.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
btw if you're gonna go that route (normalize), one way to do it would be to move os.normalizePath
to ospaths.normalizePath
(as mentioned in TODO here: #6587 (comment)) (but doing so in a separate PR as it's a useful change by itself)
I really want a
we need to be able to call relativePath with 1 param (as in other languages):
other bugs
|
As it has been said, my implementation in |
the other thing I was thinking about before @Araq 's comment was to reuse this:
with or maybe integrating @Araq 's |
Thank you for reviewing my PR. You don't like my code and there is already It accepts only absolute path types. proc relativeTo*(fullPath: AbsoluteFile, baseFilename: AbsoluteDir;
sep = DirSep): RelativeFile = When I give it relative paths, it doesn't work with some relative paths. I think there are 3 ways to make
2 parameters vs 3 parametersIf one of
3 parameters version convert relative one to absolute path by prepending 3rd parameter. If both input paths are relative path and In 3 parameters version, 3rd parameter is a parent directory of |
This code echo "foo", "foo/bar", "foo/bar/abc". for i in parentDirs("foo/bar/abc", true):
echo i It can be used to find common parent directory, but it has order of n^2 time complexity.
It should be separate procedure defind in os module as |
no, using something like zip it's still O(n) I think you can do smthg like this (untested) while true:
if finished(myIter1): ...
if finished(myIter2): ...
let dir1 = myIter1()
let dir2 = myIter2()
... let me know if smthg like that could work (if it doesn't i can try to look into it more, but I think something like zip can be used on iterator pairs) |
=> #9523 as for the |
btw: I'll reply to your other comments from #8166 (comment) later |
I don't understand why it is O(n) using zip. If input paths were normalized, consecutive directory separators are collapsed, "." (current directory) are removed and ".." are removed if possible and ".." appear only in head of the path. let normPath = normalizedPath(path)
let normBase = normalizedPath(baseDir)
let m = min(normPath.len, normBase.len)
var commonParentPos = 0
for i in 0..<m:
if not cmpCharInPath(path[i], baseDir[i]):
break
if path[i] in {DirSep, AltSep}:
commonParentPos = i This is not tested but I don't think |
right using normalizedPath code should be much simpler i hope; still remains issue of #9523 to allow 1 param relativePath |
Merged my own stuff instead. But thank you for your efforts. |
Where is this functionality now? |
|
Add a getRelPathFromAbs proc in ospaths module that convert given absolute path to a relative path.