# How does nested tab completion work in the REPL?

**URL:** <https://discourse.julialang.org/t/how-does-nested-tab-completion-work-in-the-repl/102953>\
**Category:** General Usage\
**Tags:** repl, repl-autocomplete\
**Created:** [August 18, 2023, 1:20pm UTC](https://discourse.julialang.org/t/how-does-nested-tab-completion-work-in-the-repl/102953 "2023-08-18T13:20:51Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![mkitti](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mkitti/32/12459_2.png) [@mkitti](https://discourse.julialang.org/u/mkitti)\
**Post date:** [August 18, 2023, 1:20pm UTC](https://discourse.julialang.org/t/how-does-nested-tab-completion-work-in-the-repl/102953/1 "2023-08-18T13:20:51Z")

</div>

How does nested tab completion work in the REPL?

I notice I can get this to work with nested `NamedTuple` but I am struggling to get it to work with a custom type.

---

<div class="post-metadata">

**Author:** ![Oscar\_Smith](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/oscar_smith/32/25343_2.png) [@Oscar\_Smith](https://discourse.julialang.org/u/Oscar_Smith)\
**Post date:** [August 18, 2023, 1:50pm UTC](https://discourse.julialang.org/t/how-does-nested-tab-completion-work-in-the-repl/102953/2 "2023-08-18T13:50:04Z")

</div>

The short answer is that it’s very ad-hoc. 1.10 makes it a bunch better, but we still have some hard-coded hacks for things like AbstractDict.

---

<div class="post-metadata">

**Author:** ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)\
**Post date:** [August 18, 2023, 1:54pm UTC](https://discourse.julialang.org/t/how-does-nested-tab-completion-work-in-the-repl/102953/3 "2023-08-18T13:54:13Z")

</div>

> [@mkitti](#):
>
> How does nested tab completion work in the REPL?

Aside from special cases, generically it uses [`propertynames`](https://docs.julialang.org/en/v1/base/base/#Base.propertynames) to find the tab completions after a `.`. So, if you have a custom `getproperty` then you will need to overload `Base.propertynames` to provide completions. But this doesn’t nest.

---

<div class="post-metadata">

**Author:** ![mkitti](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mkitti/32/12459_2.png) [@mkitti](https://discourse.julialang.org/u/mkitti)\
**Post date:** [August 18, 2023, 2:03pm UTC](https://discourse.julialang.org/t/how-does-nested-tab-completion-work-in-the-repl/102953/4 "2023-08-18T14:03:56Z")

</div>

Setup:

```julia
begin
    using HDF5, Downloads
    url = "https://github.com/evetion/SpaceLiDAR-artifacts/releases/download/v0.3.0/ATL08_20201121151145_08920913_006_01.h5";

    fn = Downloads.download(url, basename(url))
    h5f = h5open(fn)
end

begin
	 
   h5_to_tuple(h5o::Union{HDF5.File,HDF5.Group}) = 
    NamedTuple(Symbol.(keys(h5o)) .=> h5_to_tuple.(getindex.((h5o,), keys(h5o))))
    h5_to_tuple(h5o) = h5o
    nt = h5_to_tuple(h5f)
end

```

This works in the REPL:

```julia
julia> nt.[TAB]

METADATA ancillary_data ds_geosegments ds_metrics ds_surf_type gt1l gt1r
gt2l gt2r gt3l gt3r orbit_info quality_assessment
julia> nt.gt2l.[TAB]

land_segments signal_photons
julia> nt.gt2l.land_segments.[TAB]

asr atlas_pa beam_azimuth beam_coelev brightness_flag canopy cloud_flag_atm
cloud_fold_flag delta_time delta_time_beg delta_time_end dem_flag dem_h dem_removal_flag
h_dif_ref last_seg_extend latitude latitude_20m layer_flag longitude longitude_20m
msw_flag n_seg_ph night_flag ph_ndx_beg ph_removal_flag psf_flag rgt
sat_flag segment_id_beg segment_id_end segment_landcover segment_snowcover segment_watermask sigma_across
sigma_along sigma_atlas_land sigma_h sigma_topo snr solar_azimuth solar_elevation
surf_type terrain terrain_flg urban_flag
julia> nt.gt2l.land_segments.sat_flag.[TAB]

file id xfer
julia> nt.gt2l.land_segments.sat_flag
🔢 HDF5.Dataset: /gt2l/land_segments/sat_flag (file: ATL08_20201121151145_08920913_006_01.h5 xfer_mode: 0)
├─ 🏷️ DIMENSION_LIST
├─ 🏷️ _FillValue
├─ 🏷️ contentType
├─ 🏷️ coordinates
├─ 🏷️ description
├─ 🏷️ flag_meanings
├─ 🏷️ flag_values
├─ 🏷️ long_name
├─ 🏷️ source
├─ 🏷️ units
├─ 🏷️ valid_max
└─ 🏷️ valid_min

```

As in this issue:

> <https://github.com/JuliaIO/HDF5.jl/issues/1096>
>
> Is there a path to HDF5.jl supporting tab completion? It would be very powerful …for navigating opened h5 files but I'm guessing there is a good reason that it doesn't already exist.

This however does not work:

```julia
begin
	struct H5Indexer{O <: Union{HDF5.File,HDF5.Group}, C <: NamedTuple}
        parent::O
        children::C
	    function H5Indexer(p)
			pairs = Pair{Symbol,Any}[
				Symbol(k) => index(getindex(p,k))
				for k in keys(p)
			]
			#a = NamedTuple(Symbol(k) => v for (k,v) in attrs(p))
            #push!(pairs, :attrs => a)
            c = NamedTuple(pairs)
	    	new{typeof(p),typeof(c)}(p,c)
		end
	end
	index(h5o::Union{HDF5.File,HDF5.Group}) =
        H5Indexer(h5o)
    index(x) = x
	Base.parent(h5idx::H5Indexer) = getfield(h5idx, :parent)
	Base.getproperty(h5idx::H5Indexer, s::Symbol) =
		(@inline; getfield(getfield(h5idx, :children), s))
	Base.propertynames(h5idx::H5Indexer) =
        propertynames(getfield(h5idx, :children))
	Base.fieldnames(H5IDX::Type{H5Indexer{<: Any, C}}) where C =
		(@inline; fieldnames(C))
	Base.show(io::IO, m::MIME"text/plain", h5idx::H5Indexer) =
		show(io, m, getfield(h5idx, :parent))
end

```

I get the following behavior:

```julia
julia> h5idx.[TAB]                                     
METADATA ancillary_data ds_geosegments ds_metrics ds_surf_type gt1l gt1r gt2l gt2r gt3l gt3r orbit_info quality_assessment                                      
                                              
julia> h5idx.gt1r.[TAB]
# no suggestions 

```

What is special about a `NamedTuple`?

---

<div class="post-metadata">

**Author:** ![Oscar\_Smith](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/oscar_smith/32/25343_2.png) [@Oscar\_Smith](https://discourse.julialang.org/u/Oscar_Smith)\
**Post date:** [August 18, 2023, 2:11pm UTC](https://discourse.julialang.org/t/how-does-nested-tab-completion-work-in-the-repl/102953/5 "2023-08-18T14:11:57Z")

</div>

As of 1.10, this works.

```julia
h5idx.gt1r.land_segments.[TAB]
asr atlas_pa beam_azimuth beam_coelev brightness_flag canopy
cloud_flag_atm cloud_fold_flag delta_time delta_time_beg delta_time_end dem_flag
dem_h dem_removal_flag h_dif_ref last_seg_extend latitude latitude_20m
layer_flag longitude longitude_20m msw_flag n_seg_ph night_flag
ph_ndx_beg ph_removal_flag psf_flag rgt sat_flag segment_id_beg
segment_id_end segment_landcover segment_snowcover segment_watermask sigma_across sigma_along
sigma_atlas_land sigma_h sigma_topo snr solar_azimuth solar_elevation

```

---

<div class="post-metadata">

**Author:** ![mkitti](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mkitti/32/12459_2.png) [@mkitti](https://discourse.julialang.org/u/mkitti)\
**Post date:** [August 18, 2023, 2:33pm UTC](https://discourse.julialang.org/t/how-does-nested-tab-completion-work-in-the-repl/102953/6 "2023-08-18T14:33:03Z")

</div>

Awesome. I just need to download the beta now.

---

<div class="post-metadata">

**Author:** ![bjarthur](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/bjarthur/32/9638_2.png) [@bjarthur](https://discourse.julialang.org/u/bjarthur)\
**Post date:** [August 18, 2025, 7:11pm UTC](https://discourse.julialang.org/t/how-does-nested-tab-completion-work-in-the-repl/102953/7 "2025-08-18T19:11:16Z")

</div>

sorry to necropost here, but tab completion on 1.10 and 1.11 doesn’t work for me. after downloading the hdf5 as quoted below, i get

```julia-auto
julia> h5f.[TAB]
filename
id

```

only after converting to a NamedTuple does it work:

```julia-auto
julia> nt.[TAB]
METADATA ancillary_data ds_geosegments
ds_metrics ds_surf_type gt1l
gt1r gt2l gt2r
gt3l gt3r orbit_info
quality_assessment

```

> [@mkitti](#):
>
> ```julia-auto
> begin
> using HDF5, Downloads
> url = "https://github.com/evetion/SpaceLiDAR-artifacts/releases/download/v0.3.0/ATL08_20201121151145_08920913_006_01.h5";
> 
> fn = Downloads.download(url, basename(url))
> h5f = h5open(fn)
> end
> 
> ```

---

<div class="post-metadata">

**Author:** ![bjarthur](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/bjarthur/32/9638_2.png) [@bjarthur](https://discourse.julialang.org/u/bjarthur)\
**Post date:** [August 18, 2025, 7:15pm UTC](https://discourse.julialang.org/t/how-does-nested-tab-completion-work-in-the-repl/102953/8 "2025-08-18T19:15:31Z")

</div>

oh wait, i get it now. it does work if you define all of these new methods:

```julia-auto
begin
	struct H5Indexer{O <: Union{HDF5.File,HDF5.Group}, C <: NamedTuple}
        parent::O
        children::C
	    function H5Indexer(p)
			pairs = Pair{Symbol,Any}[
				Symbol(k) => index(getindex(p,k))
				for k in keys(p)
			]
			#a = NamedTuple(Symbol(k) => v for (k,v) in attrs(p))
            #push!(pairs, :attrs => a)
            c = NamedTuple(pairs)
	    	new{typeof(p),typeof(c)}(p,c)
		end
	end
	index(h5o::Union{HDF5.File,HDF5.Group}) =
        H5Indexer(h5o)
    index(x) = x
	Base.parent(h5idx::H5Indexer) = getfield(h5idx, :parent)
	Base.getproperty(h5idx::H5Indexer, s::Symbol) =
		(@inline; getfield(getfield(h5idx, :children), s))
	Base.propertynames(h5idx::H5Indexer) =
        propertynames(getfield(h5idx, :children))
	Base.fieldnames(H5IDX::Type{H5Indexer{<: Any, C}}) where C =
		(@inline; fieldnames(C))
	Base.show(io::IO, m::MIME"text/plain", h5idx::H5Indexer) =
		show(io, m, getfield(h5idx, :parent))
end

```

```julia-auto
h5idx = index(h5f)

julia> h5idx.[TAB]
METADATA ancillary_data ds_geosegments
ds_metrics ds_surf_type gt1l
gt1r gt2l gt2r
gt3l gt3r orbit_info
quality_assessment

```
