Repository: vega/altair

Issue:
Methods missing from chart completion menu
It seems like several methods such as `save`, `to_dict`, `to_json` etc are missing from the (auto/tab) completion menu on charts. Ideally all of these should be discoverable via the completion menu. @ChristopherDavisUCI  do you have any idea of what might be going on here since you were working on other aspects of completion previously?

Example:

```py
import altair as alt
from vega_datasets import data


cars = data.cars.url

scatter = alt.Chart(cars).mark_point().encode(
    x='Weight_in_lbs:Q',
    y='Horsepower:Q',
    color='Origin:N')

dir(scatter)
```

```
['align',
 'autosize',
 'background',
 'bounds',
 'center',
 'config',
 'data',
 'datasets',
 'description',
 'encoding',
 'height',
 'mark',
 'name',
 'padding',
 'params',
 'projection',
 'resolve',
 'selection',
 'spacing',
 'title',
 'transform',
 'usermeta',
 'view',
 'width']
```


Discussion:
In case it helps to debug, just some first thoughts. For the class itself, `dir` returns those attributes as well but not if its instantiated:

```python
In [8]: dir(alt.Chart)
Out[8]: 
['__add__',
 '__and__',
 '__class__',
 '__delattr__',
 '__dict__',
 '__dir__',
 '__doc__',
 '__eq__',
 '__format__',
 '__ge__',
 '__getattr__',
 '__getattribute__',
 '__getitem__',
 '__getstate__',
 '__gt__',
 '__hash__',
 '__init__',
 '__init_subclass__',
 '__le__',
 '__lt__',
 '__module__',
 '__ne__',
 '__new__',
 '__or__',
 '__reduce__',
 '__reduce_ex__',
 '__repr__',
 '__setattr__',
 '__setitem__',
 '__sizeof__',
 '__str__',
 '__subclasshook__',
 '__weakref__',
 '_add_transform',
 '_class_is_valid_at_instantiation',
 '_counter',
 '_default_wrapper_classes',
 '_get',
 '_get_name',
 '_repr_mimebundle_',
 '_rootschema',
 '_schema',
 '_set_resolve',
 '_validator',
 'add_params',
 'add_selection',
 'configure',
 'configure_arc',
 'configure_area',
 'configure_axis',
 'configure_axisBand',
 'configure_axisBottom',
 'configure_axisDiscrete',
 'configure_axisLeft',
 'configure_axisPoint',
 'configure_axisQuantitative',
 'configure_axisRight',
 'configure_axisTemporal',
 'configure_axisTop',
 'configure_axisX',
 'configure_axisXBand',
 'configure_axisXDiscrete',
 'configure_axisXPoint',
 'configure_axisXQuantitative',
 'configure_axisXTemporal',
 'configure_axisY',
 'configure_axisYBand',
 'configure_axisYDiscrete',
 'configure_axisYPoint',
 'configure_axisYQuantitative',
 'configure_axisYTemporal',
 'configure_bar',
 'configure_boxplot',
 'configure_circle',
 'configure_concat',
 'configure_errorband',
 'configure_errorbar',
 'configure_facet',
 'configure_geoshape',
 'configure_header',
 'configure_headerColumn',
 'configure_headerFacet',
 'configure_headerRow',
 'configure_image',
 'configure_legend',
 'configure_line',
 'configure_mark',
 'configure_point',
 'configure_projection',
 'configure_range',
 'configure_rect',
 'configure_rule',
 'configure_scale',
 'configure_selection',
 'configure_square',
 'configure_text',
 'configure_tick',
 'configure_title',
 'configure_trail',
 'configure_view',
 'copy',
 'display',
 'encode',
 'facet',
 'from_dict',
 'from_json',
 'interactive',
 'mark_arc',
 'mark_area',
 'mark_bar',
 'mark_boxplot',
 'mark_circle',
 'mark_errorband',
 'mark_errorbar',
 'mark_geoshape',
 'mark_image',
 'mark_line',
 'mark_point',
 'mark_rect',
 'mark_rule',
 'mark_square',
 'mark_text',
 'mark_tick',
 'mark_trail',
 'project',
 'properties',
 'repeat',
 'resolve_axis',
 'resolve_legend',
 'resolve_references',
 'resolve_scale',
 'save',
 'serve',
 'show',
 'to_dict',
 'to_html',
 'to_json',
 'transform_aggregate',
 'transform_bin',
 'transform_calculate',
 'transform_density',
 'transform_filter',
 'transform_flatten',
 'transform_fold',
 'transform_impute',
 'transform_joinaggregate',
 'transform_loess',
 'transform_lookup',
 'transform_pivot',
 'transform_quantile',
 'transform_regression',
 'transform_sample',
 'transform_stack',
 'transform_timeunit',
 'transform_window',
 'validate',
 'validate_property']

In [9]: dir(alt.Chart())
Out[9]: 
['align',
 'autosize',
 'background',
 'bounds',
 'center',
 'config',
 'data',
 'datasets',
 'description',
 'encoding',
 'height',
 'mark',
 'name',
 'padding',
 'params',
 'projection',
 'resolve',
 'spacing',
 'title',
 'transform',
 'usermeta',
 'view',
 'width']
```

`SchemaProperties` in `utils.py` and `SchemaBase` in `schemapi.py` both have `__dir__` methods defined which override what is returned by `dir(...)` so maybe one of those methods should either be extended. Maybe it's the one from `SchemaBase` as `alt.Chart()` inherits from it:

```python
In [12]: isinstance(alt.Chart(), SchemaBase)
Out[12]: True
```

Alternative could be to define a `__dir__` method for the top-level chart objects. Probably also worth checking if this was already the case in Altair 4.
I agree with @binste that `SchemaBase` overriding `__dir__` seems to create the abbreviated list of attributes: https://github.com/altair-viz/altair/blob/af756e9a9549b2bf0f88685d2f93e6224665bf18/altair/utils/schemapi.py#L517-L518

I would be a little hesitant to change this because it's clearly done intentionally, unless we can see why that change was made in the first place and why it doesn't need to be made for `Chart` objects.

I tried a few experiments changing the definition of `__dir__` in the `Chart` class in `altair/vegalite/v5/api.py`:
```
__dir__ = object.__dir__
```
and
```
def __dir__(self):
        ds = core.SchemaBase.__dir__(self)
        missing = [x for x in ["save", "to_dict", "to_json"] if x not in ds]
        return ds + missing
```

Do either of those appeal to you?  (Maybe changing `__dir__` somewhere else, like the class `TopLevelSpec` or `TopLevelUnitSpec` would be closer to what you want?)


Resolving patch:
diff --git a/altair/utils/schemapi.py b/altair/utils/schemapi.py
--- a/altair/utils/schemapi.py
+++ b/altair/utils/schemapi.py
@@ -515,7 +515,7 @@ def validate_property(cls, name, value, schema=None):
         )
 
     def __dir__(self):
-        return list(self._kwds.keys())
+        return sorted(super().__dir__() + list(self._kwds.keys()))
 
 
 def _passthrough(*args, **kwds):
diff --git a/tools/schemapi/schemapi.py b/tools/schemapi/schemapi.py
--- a/tools/schemapi/schemapi.py
+++ b/tools/schemapi/schemapi.py
@@ -513,7 +513,7 @@ def validate_property(cls, name, value, schema=None):
         )
 
     def __dir__(self):
-        return list(self._kwds.keys())
+        return sorted(super().__dir__() + list(self._kwds.keys()))
 
 
 def _passthrough(*args, **kwds):


Test patch: