Skip to content

Commit 1ca5dd6

Browse files
authored
Merge pull request #12387 from rouault/completion_improvements
Add bash completion for input layer name, and ...
2 parents 8245b82 + e1f970f commit 1ca5dd6

File tree

8 files changed

+350
-35
lines changed

8 files changed

+350
-35
lines changed

apps/gdal.cpp

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -121,9 +121,16 @@ MAIN_START(argc, argv)
121121
args.push_back(argv[i]);
122122
CSLDestroy(argv);
123123

124+
alg->SetCalledFromCommandLine();
125+
124126
if (!alg->ParseCommandLineArguments(args))
125127
{
126-
if (strstr(CPLGetLastErrorMsg(), "Do you mean") == nullptr)
128+
if (strstr(CPLGetLastErrorMsg(), "Do you mean") == nullptr &&
129+
strstr(CPLGetLastErrorMsg(), "Should be one among") == nullptr &&
130+
strstr(CPLGetLastErrorMsg(), "Potential values for argument") ==
131+
nullptr &&
132+
strstr(CPLGetLastErrorMsg(),
133+
"Single potential value for argument") == nullptr)
127134
{
128135
fprintf(stderr, "%s", alg->GetUsageForCLI(true).c_str());
129136
}
@@ -140,8 +147,6 @@ MAIN_START(argc, argv)
140147
alg->IsProgressBarRequested() ? GDALTermProgress : nullptr;
141148
void *pProgressData = nullptr;
142149

143-
alg->SetCalledFromCommandLine();
144-
145150
int ret = 0;
146151
if (alg->Run(pfnProgress, pProgressData) && alg->Finalize())
147152
{

apps/gdalalg_raster_pipeline.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -481,8 +481,27 @@ bool GDALRasterPipelineAlgorithm::ParseCommandLineArguments(
481481
steps.back().args.push_back("streamed_dataset");
482482
}
483483

484+
bool helpRequested = false;
485+
if (IsCalledFromCommandLine())
486+
{
487+
for (auto &step : steps)
488+
step.alg->SetCalledFromCommandLine();
489+
490+
for (const std::string &v : args)
491+
{
492+
if (cpl::ends_with(v, "=?"))
493+
helpRequested = true;
494+
}
495+
}
496+
484497
if (steps.size() < 2)
485498
{
499+
if (!steps.empty() && helpRequested)
500+
{
501+
steps.back().alg->ParseCommandLineArguments(steps.back().args);
502+
return false;
503+
}
504+
486505
ReportError(CE_Failure, CPLE_AppDefined,
487506
"At least 2 steps must be provided");
488507
return false;
@@ -506,6 +525,11 @@ bool GDALRasterPipelineAlgorithm::ParseCommandLineArguments(
506525
}
507526
if (steps.back().alg->GetName() != GDALRasterWriteAlgorithm::NAME)
508527
{
528+
if (helpRequested)
529+
{
530+
steps.back().alg->ParseCommandLineArguments(steps.back().args);
531+
return false;
532+
}
509533
ReportError(CE_Failure, CPLE_AppDefined, "Last step should be '%s'",
510534
GDALRasterWriteAlgorithm::NAME);
511535
return false;

apps/gdalalg_vector_info.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,11 @@ GDALVectorInfoAlgorithm::GDALVectorInfoAlgorithm()
3333
AddOpenOptionsArg(&m_openOptions);
3434
AddInputFormatsArg(&m_inputFormats)
3535
.AddMetadataItem(GAAMDI_REQUIRED_CAPABILITIES, {GDAL_DCAP_VECTOR});
36-
AddInputDatasetArg(&m_dataset, GDAL_OF_VECTOR).AddAlias("dataset");
37-
AddLayerNameArg(&m_layerNames).SetMutualExclusionGroup("layer-sql");
36+
auto &datasetArg =
37+
AddInputDatasetArg(&m_dataset, GDAL_OF_VECTOR).AddAlias("dataset");
38+
auto &layerArg =
39+
AddLayerNameArg(&m_layerNames).SetMutualExclusionGroup("layer-sql");
40+
SetAutoCompleteFunctionForLayerName(layerArg, datasetArg);
3841
AddArg("features", 0,
3942
_("List all features (beware of RAM consumption on large layers)"),
4043
&m_listFeatures);

apps/gdalalg_vector_pipeline.cpp

Lines changed: 38 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -66,19 +66,22 @@ void GDALVectorPipelineStepAlgorithm::AddInputArgs(bool hiddenForCLI)
6666
.AddMetadataItem(GAAMDI_REQUIRED_CAPABILITIES, {GDAL_DCAP_VECTOR})
6767
.SetHiddenForCLI(hiddenForCLI);
6868
AddOpenOptionsArg(&m_openOptions).SetHiddenForCLI(hiddenForCLI);
69-
AddInputDatasetArg(&m_inputDataset, GDAL_OF_VECTOR,
70-
/* positionalAndRequired = */ !hiddenForCLI)
71-
.SetMinCount(1)
72-
.SetMaxCount((GetName() == GDALVectorPipelineAlgorithm::NAME ||
73-
GetName() == GDALVectorConcatAlgorithm::NAME)
74-
? INT_MAX
75-
: 1)
76-
.SetHiddenForCLI(hiddenForCLI);
69+
auto &datasetArg =
70+
AddInputDatasetArg(&m_inputDataset, GDAL_OF_VECTOR,
71+
/* positionalAndRequired = */ !hiddenForCLI)
72+
.SetMinCount(1)
73+
.SetMaxCount((GetName() == GDALVectorPipelineAlgorithm::NAME ||
74+
GetName() == GDALVectorConcatAlgorithm::NAME)
75+
? INT_MAX
76+
: 1)
77+
.SetHiddenForCLI(hiddenForCLI);
7778
if (GetName() != GDALVectorSQLAlgorithm::NAME)
7879
{
79-
AddArg("input-layer", 'l', _("Input layer name(s)"), &m_inputLayerNames)
80-
.AddAlias("layer")
81-
.SetHiddenForCLI(hiddenForCLI);
80+
auto &layerArg = AddArg("input-layer", 'l', _("Input layer name(s)"),
81+
&m_inputLayerNames)
82+
.AddAlias("layer")
83+
.SetHiddenForCLI(hiddenForCLI);
84+
SetAutoCompleteFunctionForLayerName(layerArg, datasetArg);
8285
}
8386
}
8487

@@ -496,8 +499,27 @@ bool GDALVectorPipelineAlgorithm::ParseCommandLineArguments(
496499
steps.back().args.push_back("streamed_dataset");
497500
}
498501

502+
bool helpRequested = false;
503+
if (IsCalledFromCommandLine())
504+
{
505+
for (auto &step : steps)
506+
step.alg->SetCalledFromCommandLine();
507+
508+
for (const std::string &v : args)
509+
{
510+
if (cpl::ends_with(v, "=?"))
511+
helpRequested = true;
512+
}
513+
}
514+
499515
if (steps.size() < 2)
500516
{
517+
if (!steps.empty() && helpRequested)
518+
{
519+
steps.back().alg->ParseCommandLineArguments(steps.back().args);
520+
return false;
521+
}
522+
501523
ReportError(CE_Failure, CPLE_AppDefined,
502524
"At least 2 steps must be provided");
503525
return false;
@@ -525,6 +547,11 @@ bool GDALVectorPipelineAlgorithm::ParseCommandLineArguments(
525547
}
526548
if (steps.back().alg->GetName() != GDALVectorWriteAlgorithm::NAME)
527549
{
550+
if (helpRequested)
551+
{
552+
steps.back().alg->ParseCommandLineArguments(steps.back().args);
553+
return false;
554+
}
528555
ReportError(CE_Failure, CPLE_AppDefined, "Last step should be '%s'",
529556
GDALVectorWriteAlgorithm::NAME);
530557
return false;

autotest/utilities/test_gdal.py

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ def test_gdal_completion(gdal_path):
185185
)
186186
assert (
187187
out
188-
== "** description:\\ Target\\ resolution\\ (in\\ destination\\ CRS\\ units)"
188+
== "** \xC2\xA0description:\\ Target\\ resolution\\ (in\\ destination\\ CRS\\ units)"
189189
)
190190

191191
out = gdaltest.runexternal(
@@ -437,6 +437,66 @@ def test_gdal_completion_pipeline(gdal_path, subcommand):
437437
assert "set-type" in out
438438

439439

440+
def test_gdal_completion_gdal_vector_info_layer(gdal_path):
441+
442+
out = gdaltest.runexternal(
443+
f"{gdal_path} completion gdal vector info ../ogr/data/poly.shp --layer"
444+
).split(" ")
445+
assert out == ["poly"]
446+
447+
out = gdaltest.runexternal(
448+
f"{gdal_path} completion gdal vector info ../ogr/data/poly.shp --layer p"
449+
).split(" ")
450+
assert out == ["poly"]
451+
452+
out = gdaltest.runexternal(
453+
f"{gdal_path} completion gdal vector info ../ogr/data/poly.shp --layer poly"
454+
).split(" ")
455+
assert out == [""]
456+
457+
out = gdaltest.runexternal(
458+
f"{gdal_path} completion gdal vector info ../ogr/data/poly.shp --layer poly XX"
459+
).split(" ")
460+
assert out == [""]
461+
462+
463+
def test_gdal_completion_gdal_vector_pipeline_read_layer(gdal_path):
464+
465+
out = gdaltest.runexternal(
466+
f"{gdal_path} completion gdal vector pipeline read ../ogr/data/poly.shp --layer"
467+
).split(" ")
468+
assert out == ["poly"]
469+
470+
471+
def test_gdal_question_mark(gdal_path):
472+
473+
_, err = gdaltest.runexternal_out_and_err(
474+
f"{gdal_path} vector info ../ogr/data/poly.shp --layer=?"
475+
)
476+
assert "Single potential value for argument 'layer' is 'poly'" in err
477+
478+
_, err = gdaltest.runexternal_out_and_err(
479+
f"{gdal_path} vector pipeline read ../ogr/data/poly.shp --layer=?"
480+
)
481+
assert "Single potential value for argument 'input-layer' is 'poly'" in err
482+
483+
_, err = gdaltest.runexternal_out_and_err(
484+
f"{gdal_path} raster reproject --resampling=?"
485+
)
486+
assert (
487+
"Potential values for argument 'resampling' are:\n- nearest\n- bilinear"
488+
in err.replace("\r\n", "\n")
489+
)
490+
491+
_, err = gdaltest.runexternal_out_and_err(
492+
f"{gdal_path} raster pipeline read ../gcore/data/byte.tif ! reproject --resampling=?"
493+
)
494+
assert (
495+
"Potential values for argument 'resampling' are:\n- nearest\n- bilinear"
496+
in err.replace("\r\n", "\n")
497+
)
498+
499+
440500
def test_gdal_algorithm_getter_setter():
441501

442502
with pytest.raises(

doc/source/programs/gdal_syntax.rst

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,3 +74,53 @@ The following command lines are valid:
7474
gdal raster convert --input input.tif --output output.tif
7575
gdal raster convert --creation-option COMPRESS=LZW --creation-option TILED=YES --overwrite input.tif output.tif
7676
gdal raster convert input.tif output.tif--co COMPRESS=LZW,TILED=YES --overwrite
77+
78+
79+
Suggestions for argument values
80+
+++++++++++++++++++++++++++++++
81+
82+
As an alternative to :ref:`gdal_bash_completion`, it is possible to ask for
83+
potential enumerated values for an argument by appending ``=?`` to the argument name.
84+
85+
.. example::
86+
:title: Asking for the layer names of a vector dataset
87+
88+
.. code-block:: bash
89+
90+
gdal vector info my.gpkg --layer=?
91+
92+
can return:
93+
94+
.. code-block::
95+
96+
ERROR 1: info: Potential values for argument 'layer' are:
97+
- towns
98+
- countries
99+
100+
101+
.. example::
102+
:title: Asking for the allowed values of the resampling argument
103+
104+
.. code-block:: bash
105+
106+
gdal raster reproject --resampling=?
107+
108+
returns:
109+
110+
.. code-block::
111+
112+
ERROR 1: reproject: Potential values for argument 'resampling' are:
113+
- nearest
114+
- bilinear
115+
- cubic
116+
- cubicspline
117+
- lanczos
118+
- average
119+
- rms
120+
- mode
121+
- min
122+
- max
123+
- med
124+
- q1
125+
- q3
126+
- sum

0 commit comments

Comments
 (0)