diff --git a/NEWS.md b/NEWS.md index 6f12b8ff389cd..82138657080e9 100644 --- a/NEWS.md +++ b/NEWS.md @@ -101,6 +101,8 @@ New library features the uniquing checking ([#53474]) * `RegexMatch` objects can now be used to construct `NamedTuple`s and `Dict`s ([#50988]) * `Lockable` is now exported ([#54595]) +* The a new method `readdir(DirEntry, path)` will now return directory contents along with the + type of the entries in a vector of new `DirEntry` objects to provide more efficient `isfile` etc. checks ([#55393]) Standard library changes ------------------------ diff --git a/base/exports.jl b/base/exports.jl index dbe12f933e597..791b40d8539ff 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -836,6 +836,7 @@ export close, closewrite, countlines, + DirEntry, eachline, readeach, eof, diff --git a/base/file.jl b/base/file.jl index e1b8e8a748fae..18492a7a40604 100644 --- a/base/file.jl +++ b/base/file.jl @@ -8,6 +8,7 @@ export chown, cp, cptree, + DirEntry, diskstat, hardlink, mkdir, @@ -983,13 +984,13 @@ isblockdev(obj::DirEntry) = (isunknown(obj) || islink(obj)) ? isblockdev(obj.pat realpath(obj::DirEntry) = realpath(obj.path) """ - _readdirx(dir::AbstractString=pwd(); sort::Bool = true) -> Vector{DirEntry} + readdir(::Type{DirEntry}, dir::AbstractString=pwd(); sort::Bool = true) -> Vector{DirEntry} Return a vector of [`DirEntry`](@ref) objects representing the contents of the directory `dir`, or the current working directory if not given. If `sort` is true, the returned vector is sorted by name. -Unlike [`readdir`](@ref), `_readdirx` returns [`DirEntry`](@ref) objects, which contain the name of the +The [`DirEntry`](@ref) objects that are returned contain the name of the file, the directory it is in, and the type of the file which is determined during the directory scan. This means that calls to [`isfile`](@ref), [`isdir`](@ref), [`islink`](@ref), [`isfifo`](@ref), [`issocket`](@ref), [`ischardev`](@ref), and [`isblockdev`](@ref) can be made on the @@ -998,12 +999,12 @@ cannot be determined without a stat call. In these cases the `rawtype` field of object will be 0 (`UV_DIRENT_UNKNOWN`) and [`isfile`](@ref) etc. will fall back to a `stat` call. ```julia -for obj in _readdirx() +for obj in readdir(DirEntry, ".") isfile(obj) && println("\$(obj.name) is a file with path \$(obj.path)") end ``` """ -_readdirx(dir::AbstractString=pwd(); sort::Bool=true) = _readdir(dir; return_objects=true, sort)::Vector{DirEntry} +readdir(::Type{DirEntry}, dir::AbstractString=pwd(); sort::Bool=true) = _readdir(dir; return_objects=true, sort)::Vector{DirEntry} function _readdir(dir::AbstractString; return_objects::Bool=false, join::Bool=false, sort::Bool=true) # Allocate space for uv_fs_t struct @@ -1093,7 +1094,7 @@ function walkdir(root; topdown=true, follow_symlinks=false, onerror=throw) end return end - entries = tryf(_readdirx, root) + entries = tryf(p -> readdir(DirEntry, p), root) entries === nothing && return dirs = Vector{String}() files = Vector{String}() diff --git a/doc/src/base/file.md b/doc/src/base/file.md index 22799f882bb26..aa793957abc12 100644 --- a/doc/src/base/file.md +++ b/doc/src/base/file.md @@ -7,6 +7,7 @@ Base.Filesystem.pwd Base.Filesystem.cd(::AbstractString) Base.Filesystem.cd(::Function) Base.Filesystem.readdir +Base.FileSystem.DirEntry Base.Filesystem.walkdir Base.Filesystem.mkdir Base.Filesystem.mkpath diff --git a/stdlib/REPL/src/REPLCompletions.jl b/stdlib/REPL/src/REPLCompletions.jl index bc8006ec2ed5d..ead37ed93e1c3 100644 --- a/stdlib/REPL/src/REPLCompletions.jl +++ b/stdlib/REPL/src/REPLCompletions.jl @@ -8,7 +8,6 @@ using Core: Const const CC = Core.Compiler using Base.Meta using Base: propertynames, something, IdSet -using Base.Filesystem: _readdirx abstract type Completion end @@ -335,7 +334,7 @@ function cache_PATH() end path_entries = try - _readdirx(pathdir) + readdir(DirEntry, pathdir) catch e # Bash allows dirs in PATH that can't be read, so we should as well. if isa(e, Base.IOError) || isa(e, Base.ArgumentError) @@ -398,9 +397,9 @@ function complete_path(path::AbstractString; end entries = try if isempty(dir) - _readdirx() + readdir(DirEntry) elseif isdir(dir) - _readdirx(dir) + readdir(DirEntry, dir) else return Completion[], dir, false end @@ -1432,7 +1431,7 @@ function completions(string::String, pos::Int, context_module::Module=Main, shif complete_loading_candidates!(suggestions, s, dir) end isdir(dir) || continue - for entry in _readdirx(dir) + for entry in readdir(DirEntry, dir) pname = entry.name if pname[1] != '.' && pname != "METADATA" && pname != "REQUIRE" && startswith(pname, s) diff --git a/stdlib/REPL/src/docview.jl b/stdlib/REPL/src/docview.jl index 5086aa0c9485c..d9204f25e1cab 100644 --- a/stdlib/REPL/src/docview.jl +++ b/stdlib/REPL/src/docview.jl @@ -11,8 +11,6 @@ import Base.Docs: doc, formatdoc, parsedoc, apropos using Base: with_output_color, mapany, isdeprecated, isexported -using Base.Filesystem: _readdirx - using InteractiveUtils: subtypes using Unicode: normalize @@ -365,7 +363,7 @@ function find_readme(m::Module)::Union{String, Nothing} path = dirname(mpath) top_path = pkgdir(m) while true - for entry in _readdirx(path; sort=true) + for entry in readdir(DirEntry, path; sort=true) isfile(entry) && (lowercase(entry.name) in ["readme.md", "readme"]) || continue return entry.path end diff --git a/test/file.jl b/test/file.jl index f82b2a0fd8f39..176f5b22dfd06 100644 --- a/test/file.jl +++ b/test/file.jl @@ -31,8 +31,8 @@ if !Sys.iswindows() || Sys.windows_version() >= Sys.WINDOWS_VISTA_VER symlink(subdir, dirlink) @test stat(dirlink) == stat(subdir) @test readdir(dirlink) == readdir(subdir) - @test map(o->o.names, Base.Filesystem._readdirx(dirlink)) == map(o->o.names, Base.Filesystem._readdirx(subdir)) - @test realpath.(Base.Filesystem._readdirx(dirlink)) == realpath.(Base.Filesystem._readdirx(subdir)) + @test map(o->o.names, readdir(DirEntry, dirlink)) == map(o->o.names, readdir(DirEntry, subdir)) + @test realpath.(readdir(DirEntry, dirlink)) == realpath.(readdir(DirEntry, subdir)) # relative link relsubdirlink = joinpath(subdir, "rel_subdirlink") @@ -40,7 +40,7 @@ if !Sys.iswindows() || Sys.windows_version() >= Sys.WINDOWS_VISTA_VER symlink(reldir, relsubdirlink) @test stat(relsubdirlink) == stat(subdir2) @test readdir(relsubdirlink) == readdir(subdir2) - @test Base.Filesystem._readdirx(relsubdirlink) == Base.Filesystem._readdirx(subdir2) + @test readdir(DirEntry, relsubdirlink) == readdir(DirEntry, subdir2) # creation of symlink to directory that does not yet exist new_dir = joinpath(subdir, "new_dir") @@ -59,7 +59,7 @@ if !Sys.iswindows() || Sys.windows_version() >= Sys.WINDOWS_VISTA_VER mkdir(new_dir) touch(foo_file) @test readdir(new_dir) == readdir(nedlink) - @test realpath.(Base.Filesystem._readdirx(new_dir)) == realpath.(Base.Filesystem._readdirx(nedlink)) + @test realpath.(readdir(DirEntry, new_dir)) == realpath.(readdir(DirEntry, nedlink)) rm(foo_file) rm(new_dir) @@ -1444,10 +1444,10 @@ rm(dirwalk, recursive=true) touch(randstring()) end @test issorted(readdir()) - @test issorted(Base.Filesystem._readdirx()) - @test map(o->o.name, Base.Filesystem._readdirx()) == readdir() - @test map(o->o.path, Base.Filesystem._readdirx()) == readdir(join=true) - @test count(isfile, readdir(join=true)) == count(isfile, Base.Filesystem._readdirx()) + @test issorted(readdir(DirEntry)) + @test map(o->o.name, readdir(DirEntry)) == readdir() + @test map(o->o.path, readdir(DirEntry)) == readdir(join=true) + @test count(isfile, readdir(join=true)) == count(isfile, readdir(DirEntry, )) end end end