Skip to content

Commit 24d19c5

Browse files
nixel2007Copilot
andcommitted
feat: built-in completions command with shell script generation
Добавлена команда `completions` на уровне cli-библиотеки, которая формирует скрипты автодополнения для bash, zsh и PowerShell. Новые возможности ПараметрКоманды: - ПоставщикДополнения(Объект, ИмяМетода) — регистрация поставщика вариантов автодополнения значений параметра. - ПолучитьЗначенияДополнения() — вызов поставщика и безопасное получение списка вариантов (с обработкой исключений). - ПолучитьИмя/ПолучитьСинонимы/ПолучитьТипПараметра — публичные аксессоры для расширенной работы с параметрами. Новые API КомандаПриложения: - ПолучитьОпцииПараметрами/ПолучитьАргументыПараметрами — доступ к объектам ПараметрКоманды (в отличие от ПолучитьТаблицуОпций, которая отдаёт только их свойства). КонсольноеПриложение автоматически регистрирует подкоманду `completions --shell bash|zsh|pwsh`. Команда обходит дерево подкоманд, опрашивает поставщиков дополнения у каждого параметра и запекает полученные списки в итоговый скрипт — никакого runtime обращения к приложению из скрипта автодополнения. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 7a34f87 commit 24d19c5

8 files changed

Lines changed: 773 additions & 1 deletion

packagedef

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
//
55

66
Описание.Имя("cli")
7-
.Версия("0.11.0")
7+
.Версия("0.12.0")
88
.Автор("Khorev Aleksey")
99
.АдресАвтора("khorevaa@gmail.com")
1010
.Описание("Данный пакет облегчает создание консольных приложений на Oscript")
Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
// Генератор скрипта автодополнения для GNU Bash.
2+
3+
Функция Оболочка() Экспорт
4+
Возврат "bash";
5+
КонецФункции
6+
7+
Функция СформироватьСкрипт(ИмяПриложения, Модель) Экспорт
8+
9+
ИмяФункции = "_" + ИмяФункцииИзИмени(ИмяПриложения) + "_completions";
10+
11+
СтрокиСкрипта = Новый Массив();
12+
СтрокиСкрипта.Добавить("# " + ИмяПриложения + " bash completions");
13+
СтрокиСкрипта.Добавить("# Добавьте строку ниже в ~/.bashrc для активации:");
14+
СтрокиСкрипта.Добавить("# source <(" + ИмяПриложения + " completions --shell bash)");
15+
СтрокиСкрипта.Добавить("");
16+
СтрокиСкрипта.Добавить(ИмяФункции + "() {");
17+
СтрокиСкрипта.Добавить(" local cur prev words cword");
18+
СтрокиСкрипта.Добавить(" _init_completion 2>/dev/null || {");
19+
СтрокиСкрипта.Добавить(" COMPREPLY=()");
20+
СтрокиСкрипта.Добавить(" cur=""${COMP_WORDS[COMP_CWORD]}""");
21+
СтрокиСкрипта.Добавить(" prev=""${COMP_WORDS[COMP_CWORD-1]}""");
22+
СтрокиСкрипта.Добавить(" words=(""${COMP_WORDS[@]}"")");
23+
СтрокиСкрипта.Добавить(" cword=$COMP_CWORD");
24+
СтрокиСкрипта.Добавить(" }");
25+
СтрокиСкрипта.Добавить("");
26+
27+
ВсеИменаВерхнегоУровня = Новый Массив();
28+
Для Каждого Описание Из Модель Цикл
29+
Для Каждого Имя Из Описание.Имена Цикл
30+
ВсеИменаВерхнегоУровня.Добавить(Имя);
31+
КонецЦикла;
32+
КонецЦикла;
33+
34+
СтрокиСкрипта.Добавить(" local commands=""" + СтрСоединить(ВсеИменаВерхнегоУровня, " ") + """");
35+
СтрокиСкрипта.Добавить("");
36+
СтрокиСкрипта.Добавить(" if [ $cword -eq 1 ]; then");
37+
СтрокиСкрипта.Добавить(" COMPREPLY=($(compgen -W ""$commands"" -- ""$cur""))");
38+
СтрокиСкрипта.Добавить(" return 0");
39+
СтрокиСкрипта.Добавить(" fi");
40+
СтрокиСкрипта.Добавить("");
41+
СтрокиСкрипта.Добавить(" local command=""${words[1]}""");
42+
СтрокиСкрипта.Добавить(" case ""$command"" in");
43+
44+
Для Каждого Описание Из Модель Цикл
45+
ДобавитьВеткуКоманды(СтрокиСкрипта, Описание, " ");
46+
КонецЦикла;
47+
48+
СтрокиСкрипта.Добавить(" *)");
49+
СтрокиСкрипта.Добавить(" ;;");
50+
СтрокиСкрипта.Добавить(" esac");
51+
СтрокиСкрипта.Добавить("");
52+
СтрокиСкрипта.Добавить(" return 0");
53+
СтрокиСкрипта.Добавить("}");
54+
СтрокиСкрипта.Добавить("");
55+
СтрокиСкрипта.Добавить("complete -F " + ИмяФункции + " " + ИмяПриложения);
56+
57+
Возврат СтрСоединить(СтрокиСкрипта, Символы.ПС);
58+
59+
КонецФункции
60+
61+
Процедура ДобавитьВеткуКоманды(СтрокиСкрипта, Описание, Отступ)
62+
63+
ПаттернCase = СтрСоединить(Описание.Имена, "|");
64+
СтрокиСкрипта.Добавить(Отступ + ПаттернCase + ")");
65+
66+
ИменаОпций = ИменаПараметровДляКомплита(Описание.Опции);
67+
68+
Для Каждого Опция Из Описание.Опции Цикл
69+
Если Опция.Значения.Количество() = 0 Тогда
70+
Продолжить;
71+
КонецЕсли;
72+
73+
Для Каждого ИмяОпции Из Опция.Имена Цикл
74+
СтрокиСкрипта.Добавить(Отступ + " if [ ""$prev"" = """ + ИмяОпции + """ ]; then");
75+
СтрокиСкрипта.Добавить(Отступ + " COMPREPLY=($(compgen -W """ + СтрСоединить(Опция.Значения, " ") + """ -- ""$cur""))");
76+
СтрокиСкрипта.Добавить(Отступ + " return 0");
77+
СтрокиСкрипта.Добавить(Отступ + " fi");
78+
КонецЦикла;
79+
КонецЦикла;
80+
81+
СтрокиСкрипта.Добавить(Отступ + " if [[ ""$cur"" == -* ]]; then");
82+
СтрокиСкрипта.Добавить(Отступ + " COMPREPLY=($(compgen -W """ + СтрСоединить(ИменаОпций, " ") + """ -- ""$cur""))");
83+
СтрокиСкрипта.Добавить(Отступ + " return 0");
84+
СтрокиСкрипта.Добавить(Отступ + " fi");
85+
86+
Если Описание.Подкоманды.Количество() > 0 Тогда
87+
88+
ИменаПодкоманд = Новый Массив();
89+
Для Каждого Под Из Описание.Подкоманды Цикл
90+
Для Каждого Имя Из Под.Имена Цикл
91+
ИменаПодкоманд.Добавить(Имя);
92+
КонецЦикла;
93+
КонецЦикла;
94+
95+
СтрокиСкрипта.Добавить(Отступ + " if [ $cword -eq 2 ]; then");
96+
СтрокиСкрипта.Добавить(Отступ + " COMPREPLY=($(compgen -W """ + СтрСоединить(ИменаПодкоманд, " ") + """ -- ""$cur""))");
97+
СтрокиСкрипта.Добавить(Отступ + " return 0");
98+
СтрокиСкрипта.Добавить(Отступ + " fi");
99+
100+
СтрокиСкрипта.Добавить(Отступ + " local subcommand=""${words[2]}""");
101+
СтрокиСкрипта.Добавить(Отступ + " case ""$subcommand"" in");
102+
Для Каждого Под Из Описание.Подкоманды Цикл
103+
ДобавитьВеткуПодкоманды(СтрокиСкрипта, Под, Отступ + " ");
104+
КонецЦикла;
105+
СтрокиСкрипта.Добавить(Отступ + " *)");
106+
СтрокиСкрипта.Добавить(Отступ + " ;;");
107+
СтрокиСкрипта.Добавить(Отступ + " esac");
108+
109+
Иначе
110+
111+
ЗначенияАргументов = СобратьЗначенияАргументов(Описание);
112+
Если ЗначенияАргументов.Количество() > 0 Тогда
113+
СтрокиСкрипта.Добавить(Отступ + " COMPREPLY=($(compgen -W """ + СтрСоединить(ЗначенияАргументов, " ") + """ -- ""$cur""))");
114+
КонецЕсли;
115+
116+
КонецЕсли;
117+
118+
СтрокиСкрипта.Добавить(Отступ + " ;;");
119+
120+
КонецПроцедуры
121+
122+
Процедура ДобавитьВеткуПодкоманды(СтрокиСкрипта, Описание, Отступ)
123+
124+
ПаттернCase = СтрСоединить(Описание.Имена, "|");
125+
СтрокиСкрипта.Добавить(Отступ + ПаттернCase + ")");
126+
127+
ИменаОпций = ИменаПараметровДляКомплита(Описание.Опции);
128+
129+
Для Каждого Опция Из Описание.Опции Цикл
130+
Если Опция.Значения.Количество() = 0 Тогда
131+
Продолжить;
132+
КонецЕсли;
133+
Для Каждого ИмяОпции Из Опция.Имена Цикл
134+
СтрокиСкрипта.Добавить(Отступ + " if [ ""$prev"" = """ + ИмяОпции + """ ]; then");
135+
СтрокиСкрипта.Добавить(Отступ + " COMPREPLY=($(compgen -W """ + СтрСоединить(Опция.Значения, " ") + """ -- ""$cur""))");
136+
СтрокиСкрипта.Добавить(Отступ + " return 0");
137+
СтрокиСкрипта.Добавить(Отступ + " fi");
138+
КонецЦикла;
139+
КонецЦикла;
140+
141+
СтрокиСкрипта.Добавить(Отступ + " if [[ ""$cur"" == -* ]]; then");
142+
СтрокиСкрипта.Добавить(Отступ + " COMPREPLY=($(compgen -W """ + СтрСоединить(ИменаОпций, " ") + """ -- ""$cur""))");
143+
СтрокиСкрипта.Добавить(Отступ + " return 0");
144+
СтрокиСкрипта.Добавить(Отступ + " fi");
145+
146+
ЗначенияАргументов = СобратьЗначенияАргументов(Описание);
147+
Если ЗначенияАргументов.Количество() > 0 Тогда
148+
СтрокиСкрипта.Добавить(Отступ + " COMPREPLY=($(compgen -W """ + СтрСоединить(ЗначенияАргументов, " ") + """ -- ""$cur""))");
149+
КонецЕсли;
150+
151+
СтрокиСкрипта.Добавить(Отступ + " ;;");
152+
153+
КонецПроцедуры
154+
155+
Функция ИменаПараметровДляКомплита(Опции)
156+
157+
Результат = Новый Массив();
158+
Результат.Добавить("--help");
159+
160+
Для Каждого Опция Из Опции Цикл
161+
Для Каждого Имя Из Опция.Имена Цикл
162+
Результат.Добавить(Имя);
163+
КонецЦикла;
164+
КонецЦикла;
165+
166+
Возврат Результат;
167+
168+
КонецФункции
169+
170+
Функция СобратьЗначенияАргументов(Описание)
171+
172+
Результат = Новый Массив();
173+
Для Каждого Аргумент Из Описание.Аргументы Цикл
174+
Для Каждого Значение Из Аргумент.Значения Цикл
175+
Результат.Добавить(Значение);
176+
КонецЦикла;
177+
КонецЦикла;
178+
179+
Возврат Результат;
180+
181+
КонецФункции
182+
183+
Функция ИмяФункцииИзИмени(ИмяПриложения)
184+
185+
Результат = "";
186+
Для Инд = 1 По СтрДлина(ИмяПриложения) Цикл
187+
Символ = Сред(ИмяПриложения, Инд, 1);
188+
Если (Символ >= "a" И Символ <= "z")
189+
Или (Символ >= "A" И Символ <= "Z")
190+
Или (Символ >= "0" И Символ <= "9")
191+
Или Символ = "_" Тогда
192+
Результат = Результат + Символ;
193+
Иначе
194+
Результат = Результат + "_";
195+
КонецЕсли;
196+
КонецЦикла;
197+
198+
Возврат Результат;
199+
200+
КонецФункции
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
// Генератор скрипта автодополнения для PowerShell.
2+
3+
Функция Оболочка() Экспорт
4+
Возврат "pwsh";
5+
КонецФункции
6+
7+
Функция СформироватьСкрипт(ИмяПриложения, Модель) Экспорт
8+
9+
Строки = Новый Массив();
10+
Строки.Добавить("# " + ИмяПриложения + " PowerShell completions");
11+
Строки.Добавить("# Активируйте строкой в профиле PowerShell ($PROFILE):");
12+
Строки.Добавить("# " + ИмяПриложения + " completions --shell pwsh | Out-String | Invoke-Expression");
13+
Строки.Добавить("");
14+
Строки.Добавить("Register-ArgumentCompleter -Native -CommandName '" + ИмяПриложения + "' -ScriptBlock {");
15+
Строки.Добавить(" param($wordToComplete, $commandAst, $cursorPosition)");
16+
Строки.Добавить("");
17+
Строки.Добавить(" $tokens = $commandAst.CommandElements | ForEach-Object { $_.Extent.Text }");
18+
Строки.Добавить(" $command = if ($tokens.Count -ge 2) { $tokens[1] } else { '' }");
19+
Строки.Добавить(" $subcommand = if ($tokens.Count -ge 3) { $tokens[2] } else { '' }");
20+
Строки.Добавить(" $prev = if ($tokens.Count -ge 2) { $tokens[$tokens.Count - 2] } else { '' }");
21+
Строки.Добавить(" $position = $tokens.Count - 1");
22+
Строки.Добавить(" if (-not [string]::IsNullOrEmpty($wordToComplete)) { $position = $position - 1 }");
23+
Строки.Добавить("");
24+
25+
СтрокаКоманд = " $commands = @(";
26+
ПерваяКоманда = Истина;
27+
Для Каждого Описание Из Модель Цикл
28+
Для Каждого Имя Из Описание.Имена Цикл
29+
Если Не ПерваяКоманда Тогда
30+
СтрокаКоманд = СтрокаКоманд + ", ";
31+
КонецЕсли;
32+
СтрокаКоманд = СтрокаКоманд + "'" + Имя + "'";
33+
ПерваяКоманда = Ложь;
34+
КонецЦикла;
35+
КонецЦикла;
36+
СтрокаКоманд = СтрокаКоманд + ")";
37+
Строки.Добавить(СтрокаКоманд);
38+
39+
Строки.Добавить("");
40+
Строки.Добавить(" if ($position -le 1) {");
41+
Строки.Добавить(" return $commands | Where-Object { $_ -like ""$wordToComplete*"" } | ForEach-Object {");
42+
Строки.Добавить(" [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)");
43+
Строки.Добавить(" }");
44+
Строки.Добавить(" }");
45+
Строки.Добавить("");
46+
Строки.Добавить(" switch ($command) {");
47+
48+
Для Каждого Описание Из Модель Цикл
49+
ДобавитьВеткуКоманды(Строки, Описание, " ");
50+
КонецЦикла;
51+
52+
Строки.Добавить(" default { }");
53+
Строки.Добавить(" }");
54+
Строки.Добавить("}");
55+
56+
Возврат СтрСоединить(Строки, Символы.ПС);
57+
58+
КонецФункции
59+
60+
Процедура ДобавитьВеткуКоманды(Строки, Описание, Отступ)
61+
62+
Для Каждого Имя Из Описание.Имена Цикл
63+
Строки.Добавить(Отступ + "'" + Имя + "' {");
64+
65+
Если Описание.Подкоманды.Количество() > 0 Тогда
66+
ПодкомандыМассив = "@(";
67+
ПерваяП = Истина;
68+
Для Каждого Под Из Описание.Подкоманды Цикл
69+
Для Каждого ИмяП Из Под.Имена Цикл
70+
Если Не ПерваяП Тогда
71+
ПодкомандыМассив = ПодкомандыМассив + ", ";
72+
КонецЕсли;
73+
ПодкомандыМассив = ПодкомандыМассив + "'" + ИмяП + "'";
74+
ПерваяП = Ложь;
75+
КонецЦикла;
76+
КонецЦикла;
77+
ПодкомандыМассив = ПодкомандыМассив + ")";
78+
79+
Строки.Добавить(Отступ + " $subs = " + ПодкомандыМассив);
80+
Строки.Добавить(Отступ + " if ($position -le 2) {");
81+
Строки.Добавить(Отступ + " return $subs | Where-Object { $_ -like ""$wordToComplete*"" } | ForEach-Object {");
82+
Строки.Добавить(Отступ + " [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)");
83+
Строки.Добавить(Отступ + " }");
84+
Строки.Добавить(Отступ + " }");
85+
86+
Строки.Добавить(Отступ + " switch ($subcommand) {");
87+
Для Каждого Под Из Описание.Подкоманды Цикл
88+
ДобавитьВеткуПодкоманды(Строки, Под, Отступ + " ");
89+
КонецЦикла;
90+
Строки.Добавить(Отступ + " default { }");
91+
Строки.Добавить(Отступ + " }");
92+
Иначе
93+
ДобавитьТелоПараметров(Строки, Описание, Отступ + " ");
94+
КонецЕсли;
95+
96+
Строки.Добавить(Отступ + " return");
97+
Строки.Добавить(Отступ + "}");
98+
КонецЦикла;
99+
100+
КонецПроцедуры
101+
102+
Процедура ДобавитьВеткуПодкоманды(Строки, Описание, Отступ)
103+
104+
Для Каждого Имя Из Описание.Имена Цикл
105+
Строки.Добавить(Отступ + "'" + Имя + "' {");
106+
ДобавитьТелоПараметров(Строки, Описание, Отступ + " ");
107+
Строки.Добавить(Отступ + " return");
108+
Строки.Добавить(Отступ + "}");
109+
КонецЦикла;
110+
111+
КонецПроцедуры
112+
113+
Процедура ДобавитьТелоПараметров(Строки, Описание, Отступ)
114+
115+
Для Каждого Опция Из Описание.Опции Цикл
116+
Если Опция.Значения.Количество() = 0 Тогда
117+
Продолжить;
118+
КонецЕсли;
119+
Для Каждого ИмяОпции Из Опция.Имена Цикл
120+
МассивЗначений = "@(";
121+
ПерваяЗ = Истина;
122+
Для Каждого Значение Из Опция.Значения Цикл
123+
Если Не ПерваяЗ Тогда
124+
МассивЗначений = МассивЗначений + ", ";
125+
КонецЕсли;
126+
МассивЗначений = МассивЗначений + "'" + Значение + "'";
127+
ПерваяЗ = Ложь;
128+
КонецЦикла;
129+
МассивЗначений = МассивЗначений + ")";
130+
Строки.Добавить(Отступ + "if ($prev -eq '" + ИмяОпции + "') {");
131+
Строки.Добавить(Отступ + " return " + МассивЗначений + " | Where-Object { $_ -like ""$wordToComplete*"" } | ForEach-Object {");
132+
Строки.Добавить(Отступ + " [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)");
133+
Строки.Добавить(Отступ + " }");
134+
Строки.Добавить(Отступ + "}");
135+
КонецЦикла;
136+
КонецЦикла;
137+
138+
ЕстьЗначенияАргументов = Ложь;
139+
МассивАргументов = "@(";
140+
ПерваяА = Истина;
141+
Для Каждого Аргумент Из Описание.Аргументы Цикл
142+
Для Каждого Значение Из Аргумент.Значения Цикл
143+
Если Не ПерваяА Тогда
144+
МассивАргументов = МассивАргументов + ", ";
145+
КонецЕсли;
146+
МассивАргументов = МассивАргументов + "'" + Значение + "'";
147+
ПерваяА = Ложь;
148+
ЕстьЗначенияАргументов = Истина;
149+
КонецЦикла;
150+
КонецЦикла;
151+
МассивАргументов = МассивАргументов + ")";
152+
153+
Если ЕстьЗначенияАргументов Тогда
154+
Строки.Добавить(Отступ + "return " + МассивАргументов + " | Where-Object { $_ -like ""$wordToComplete*"" } | ForEach-Object {");
155+
Строки.Добавить(Отступ + " [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)");
156+
Строки.Добавить(Отступ + "}");
157+
КонецЕсли;
158+
159+
КонецПроцедуры

0 commit comments

Comments
 (0)