Skip to content
Draft
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
5fe9c29
add completions command with bash support
Copilot Apr 22, 2026
217fbfe
address review: use Russian naming conventions
Copilot Apr 22, 2026
080f005
generate bash completions dynamically from registered commands via au…
Copilot Apr 22, 2026
f7ca444
include subcommands in bash completions via nested case statements
Copilot Apr 22, 2026
491dd4b
Use DI injection for КомандаПриложения beans and СборщикОпций for Опи…
Copilot Apr 22, 2026
807b8f5
Add error handling for ОписаниеКоманды call in ПолучитьОпцииКоманды
Copilot Apr 22, 2026
e8a4bdc
Remove СборщикОпций, use КонсольноеПриложение injection and CLI libra…
Copilot Apr 22, 2026
e588683
Fix spacing inconsistency in assignment operators
Copilot Apr 22, 2026
d8404c6
Fix КомандаCompletions: avoid DI cycle and use valid multi-line strin…
nixel2007 Apr 22, 2026
5a53682
Bump autumn-logos from 1.2.0 to 1.3.1
nixel2007 Apr 22, 2026
cd4e354
Smart completions for 'ovm use' and 'ovm install' version aliases
nixel2007 Apr 22, 2026
54711e8
Extend smart completions to 'uninstall', 'which', 'run'
nixel2007 Apr 22, 2026
932167e
feat: use completions from autumn-cli, switch install to declarative …
nfedkin Apr 22, 2026
4a06e32
chore: синхронизация оверлея autumn-cli (генераторы-желуди)
nfedkin Apr 22, 2026
0d04c38
refactor(completions): lightweight version-alias getter + fix indenta…
nixel2007 Apr 23, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions features/completions.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#language: ru

Функциональность: Генерация скрипта автодополнения

Как пользователь ovm
Я хочу получить скрипт автодополнения для моей оболочки
Чтобы удобно использовать ovm в терминале

Контекст:
Допустим Я устанавливаю переменной среды "OVM_INSTALL_PATH" значение "./temp/ovm"

Сценарий: Генерация completions для bash
Когда Я выполняю команду "oscript ./src/cmd/ovm.os completions --shell bash"
Тогда я вижу в консоли вывод "_ovm_completions"
И я вижу в консоли вывод "complete -F _ovm_completions ovm"

Сценарий: Генерация completions для bash с коротким флагом
Когда Я выполняю команду "oscript ./src/cmd/ovm.os completions -s bash"
Тогда я вижу в консоли вывод "_ovm_completions"
И я вижу в консоли вывод "complete -F _ovm_completions ovm"

Сценарий: Ошибка при указании неподдерживаемой оболочки
Когда Я выполняю команду "oscript ./src/cmd/ovm.os completions --shell zsh"
Тогда я вижу в консоли вывод "не поддерживается"
240 changes: 240 additions & 0 deletions src/cmd/Классы/КомандаCompletions.os
Original file line number Diff line number Diff line change
@@ -0,0 +1,240 @@
#Использовать annotations

&ЛогOVM
Перем Лог;

&Пластилин(Тип = "ТаблицаЗначений", Значение = "КомандаПриложения")
Перем КомандыПриложения;

Перем Рефлектор;

&Опция(Имя = "shell s", Описание = "Тип оболочки для генерации автодополнения (поддерживается: bash)")
&ТСтрока
&ПоУмолчанию("bash")
&ВОкружении("OVM_COMPLETIONS_SHELL")
Перем ТипОболочки;

&КомандаПриложения(Имя = "completions", Описание = "Вывести скрипт автодополнения команд для выбранной оболочки")
Процедура ПриСозданииОбъекта()
Рефлектор = Новый Рефлектор();
КонецПроцедуры

&ВыполнениеКоманды
Процедура ВыполнениеКоманды() Экспорт

Если НРег(ТипОболочки) = "bash" Тогда
Лог.Информация(СкриптАвтодополненияBash());
Иначе
ВызватьИсключение СтрШаблон("Оболочка ""%1"" не поддерживается. Поддерживаемые оболочки: bash", ТипОболочки);
КонецЕсли;

КонецПроцедуры

Функция СкриптАвтодополненияBash()

ДеревоКоманд = ПолучитьДеревоКоманд();

МассивИменВсехКоманд = Новый Массив();
СтрокиCase = Новый Массив();

Для Каждого ОписаниеКоманды Из ДеревоКоманд Цикл
Для Каждого ИмяКоманды Из ОписаниеКоманды.Имена Цикл
МассивИменВсехКоманд.Добавить(ИмяКоманды);
КонецЦикла;

ПаттернCase = СтрСоединить(ОписаниеКоманды.Имена, "|");

Если ОписаниеКоманды.Подкоманды.Количество() > 0 Тогда
МассивИменПодкоманд = Новый Массив();
МассивПодкомандCase = Новый Массив();

Для Каждого ОписаниеПодкоманды Из ОписаниеКоманды.Подкоманды Цикл
Для Каждого ИмяПодкоманды Из ОписаниеПодкоманды.Имена Цикл
МассивИменПодкоманд.Добавить(ИмяПодкоманды);
КонецЦикла;

ПаттернПодкомандCase = СтрСоединить(ОписаниеПодкоманды.Имена, "|");
СтрокаОпцийПодкоманды = СтрСоединить(ОписаниеПодкоманды.Опции, " ");

МассивПодкомандCase.Добавить(" " + ПаттернПодкомандCase + ")");
МассивПодкомандCase.Добавить(" COMPREPLY=($(compgen -W """ + СтрокаОпцийПодкоманды + """ -- ""$cur""))");
МассивПодкомандCase.Добавить(" ;;");
КонецЦикла;

ИменаПодкоманд = СтрСоединить(МассивИменПодкоманд, " ");
СтрокаОпцийРодителя = СтрСоединить(ОписаниеКоманды.Опции, " ");
ТелоПодкомандCase = СтрСоединить(МассивПодкомандCase, Символы.ПС);

СтрокиCase.Добавить(" " + ПаттернCase + ")");
СтрокиCase.Добавить(" if [ $cword -eq 2 ]; then");
СтрокиCase.Добавить(" COMPREPLY=($(compgen -W """ + ИменаПодкоманд + " " + СтрокаОпцийРодителя + """ -- ""$cur""))");
СтрокиCase.Добавить(" else");
СтрокиCase.Добавить(" local subcommand=""${words[2]}""");
СтрокиCase.Добавить(" case ""$subcommand"" in");
СтрокиCase.Добавить(ТелоПодкомандCase);
СтрокиCase.Добавить(" esac");
СтрокиCase.Добавить(" fi");
СтрокиCase.Добавить(" ;;");
Иначе
СтрокаОпций = СтрСоединить(ОписаниеКоманды.Опции, " ");

СтрокиCase.Добавить(" " + ПаттернCase + ")");
СтрокиCase.Добавить(" COMPREPLY=($(compgen -W """ + СтрокаОпций + """ -- ""$cur""))");
СтрокиCase.Добавить(" ;;");
КонецЕсли;
КонецЦикла;

ВсеКоманды = СтрСоединить(МассивИменВсехКоманд, " ");
ТелоCase = СтрСоединить(СтрокиCase, Символы.ПС);

Скрипт =
"# ovm bash completions
# Добавьте строку ниже в ~/.bashrc для активации автодополнения:
# source <(ovm completions --shell bash)

_ovm_completions() {
Comment thread
nixel2007 marked this conversation as resolved.
Outdated
local cur prev words cword
_init_completion 2>/dev/null || {
COMPREPLY=()
cur=""${COMP_WORDS[COMP_CWORD]}""
prev=""${COMP_WORDS[COMP_CWORD-1]}""
words=(""${COMP_WORDS[@]}"")
cword=$COMP_CWORD
}

local commands=""" + ВсеКоманды + """

if [ $cword -eq 1 ]; then
COMPREPLY=($(compgen -W ""$commands"" -- ""$cur""))
return 0
fi

local command=""${words[1]}""

case ""$command"" in
" + ТелоCase + "
*)
;;
esac

return 0
}

complete -F _ovm_completions ovm";

Возврат Скрипт;

КонецФункции

Функция ПолучитьДеревоКоманд()

// Первый проход: собрать все команды с их именами желудей и родителями
ВсеИнфо = Новый Соответствие();

Для Каждого ДанныеЖелудя Из КомандыПриложения Цикл
ИмяЖелудя = ДанныеЖелудя.Имя;
ОпределениеЖелудя = ДанныеЖелудя.ОпределениеЖелудя;
Желудь = ДанныеЖелудя.Желудь;
Конструктор = ОпределениеЖелудя.Завязь().ДанныеМетода();

АннотацияКоманды = РаботаСАннотациями.НайтиАннотацию(Конструктор.Аннотации, "КомандаПриложения");
Если АннотацияКоманды = Неопределено Тогда
Продолжить;
КонецЕсли;

ИмяКоманды = РаботаСАннотациями.ПолучитьЗначениеПараметраАннотации(АннотацияКоманды, "Имя");
Если НЕ ЗначениеЗаполнено(ИмяКоманды) Тогда
Продолжить;
КонецЕсли;

Родитель = РаботаСАннотациями.ПолучитьЗначениеПараметраАннотации(АннотацияКоманды, "Родитель", "");

ИнфоКоманды = Новый Структура("ИмяЖелудя, Родитель, Имена, Опции");
ИнфоКоманды.ИмяЖелудя = ИмяЖелудя;
ИнфоКоманды.Родитель = Родитель;
ИнфоКоманды.Имена = СтрРазделить(ИмяКоманды, " ");
ИнфоКоманды.Опции = ПолучитьОпцииКоманды(Желудь, ОпределениеЖелудя);

ВсеИнфо.Вставить(ИмяЖелудя, ИнфоКоманды);
КонецЦикла;

// Второй проход: собрать дерево — верхнеуровневые команды со своими подкомандами
Результат = Новый Массив();

Для Каждого КлючЗначение Из ВсеИнфо Цикл
ИнфоКоманды = КлючЗначение.Значение;

Если ЗначениеЗаполнено(ИнфоКоманды.Родитель) Тогда
Продолжить;
КонецЕсли;

Подкоманды = НайтиПодкоманды(ИнфоКоманды.ИмяЖелудя, ВсеИнфо);

ОписаниеКоманды = Новый Структура("Имена, Опции, Подкоманды");
ОписаниеКоманды.Имена = ИнфоКоманды.Имена;
ОписаниеКоманды.Опции = ИнфоКоманды.Опции;
ОписаниеКоманды.Подкоманды = Подкоманды;

Результат.Добавить(ОписаниеКоманды);
КонецЦикла;

Возврат Результат;

КонецФункции

Функция НайтиПодкоманды(ИмяРодителя, ВсеИнфо)

Результат = Новый Массив();

Для Каждого КлючЗначение Из ВсеИнфо Цикл
ИнфоКоманды = КлючЗначение.Значение;

Если НРег(ИнфоКоманды.Родитель) = НРег(ИмяРодителя) Тогда
ОписаниеПодкоманды = Новый Структура("Имена, Опции");
ОписаниеПодкоманды.Имена = ИнфоКоманды.Имена;
ОписаниеПодкоманды.Опции = ИнфоКоманды.Опции;
Результат.Добавить(ОписаниеПодкоманды);
КонецЕсли;
КонецЦикла;

Возврат Результат;

КонецФункции

Функция ПолучитьОпцииКоманды(Желудь, ОпределениеЖелудя)

Опции = Новый Массив();
Опции.Добавить("--help");

Если Рефлектор.МетодСуществует(Желудь, "ОписаниеКоманды") Тогда
Попытка
Сборщик = Новый СборщикОпций();
Желудь.ОписаниеКоманды(Сборщик);
Для Каждого ИмяОпции Из Сборщик.ПолучитьОпции() Цикл
Опции.Добавить(ИмяОпции);
КонецЦикла;
Исключение
Лог.Предупреждение("Не удалось получить опции команды через ОписаниеКоманды: %1", ОписаниеОшибки());
КонецПопытки;
Иначе
Свойства = ОпределениеЖелудя.Свойства();
Для Каждого Свойство Из Свойства Цикл
АннотацияОпции = РаботаСАннотациями.ПолучитьАннотацию(Свойство, "Опция");
Если АннотацияОпции = Неопределено Тогда
Продолжить;
КонецЕсли;

ИмяОпции = РаботаСАннотациями.ПолучитьЗначениеПараметраАннотации(АннотацияОпции, "Имя");
Для Каждого ЧастьОпции Из СтрРазделить(ИмяОпции, " ") Цикл
Если СтрДлина(ЧастьОпции) = 1 Тогда
Опции.Добавить("-" + ЧастьОпции);
Иначе
Опции.Добавить("--" + ЧастьОпции);
КонецЕсли;
КонецЦикла;
КонецЦикла;
КонецЕсли;

Возврат Опции;

КонецФункции
110 changes: 110 additions & 0 deletions src/cmd/Классы/СборщикОпций.os
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
// Вспомогательный класс для сбора имён опций команды.
// Используется как заглушка вместо КомандаПриложения при вызове ОписаниеКоманды(),
// чтобы получить зарегистрированные опции без запуска команды.

Перем Опции; // Массив - собранные имена опций

Процедура ПриСозданииОбъекта()
Comment thread
nixel2007 marked this conversation as resolved.
Outdated
Опции = Новый Массив();
КонецПроцедуры

// Перехватывает регистрацию опции и возвращает себя для поддержки цепочек вызовов.
//
// Параметры:
// Имя - Строка - имя опции (может включать синонимы через пробел, например "clean c")
// Значение - Произвольный - значение по умолчанию (не используется)
// Описание - Строка - описание (не используется)
//
// Возвращаемое значение:
// СборщикОпций - ЭтотОбъект для цепочки вызовов
Функция Опция(Имя, Значение = "", Описание = "") Экспорт

Для Каждого ЧастьИмени Из СтрРазделить(Имя, " ", Ложь) Цикл
Если СтрДлина(ЧастьИмени) = 1 Тогда
Опции.Добавить("-" + ЧастьИмени);
Иначе
Опции.Добавить("--" + ЧастьИмени);
КонецЕсли;
КонецЦикла;

Возврат ЭтотОбъект;

КонецФункции

// Игнорирует регистрацию аргумента (позиционные аргументы не нужны для автодополнения опций).
//
// Возвращаемое значение:
// СборщикОпций - ЭтотОбъект для цепочки вызовов
Функция Аргумент(Имя, Значение = "", Описание = "") Экспорт
Возврат ЭтотОбъект;
КонецФункции

// Методы цепочки вызовов — возвращают ЭтотОбъект, не выполняя реальных действий.

Функция ТСтрока() Экспорт
Возврат ЭтотОбъект;
КонецФункции

Функция ТЧисло() Экспорт
Возврат ЭтотОбъект;
КонецФункции

Функция ТБулево() Экспорт
Возврат ЭтотОбъект;
КонецФункции

Функция ТДата(ФорматДаты = "") Экспорт
Возврат ЭтотОбъект;
КонецФункции

Функция ТМассивСтрок() Экспорт
Возврат ЭтотОбъект;
КонецФункции

Функция ТМассивЧисел() Экспорт
Возврат ЭтотОбъект;
КонецФункции

Функция ТМассивДат() Экспорт
Возврат ЭтотОбъект;
КонецФункции

Функция ВОкружении(Переменная) Экспорт
Возврат ЭтотОбъект;
КонецФункции

Функция Флаговый() Экспорт
Возврат ЭтотОбъект;
КонецФункции

Функция Флаг() Экспорт
Возврат ЭтотОбъект;
КонецФункции

Функция СкрытьВСправке() Экспорт
Возврат ЭтотОбъект;
КонецФункции

Функция ПоУмолчанию(Значение) Экспорт
Возврат ЭтотОбъект;
КонецФункции

Функция Обязательный(Признак = Истина) Экспорт
Возврат ЭтотОбъект;
КонецФункции

Функция ПодробноеОписание(Описание) Экспорт
Возврат ЭтотОбъект;
КонецФункции

Функция Псевдоним(Имя) Экспорт
Возврат ЭтотОбъект;
КонецФункции

// Возвращает собранные имена опций.
//
// Возвращаемое значение:
// Массив - список строк вида "--name" и "-n"
Функция ПолучитьОпции() Экспорт
Возврат Опции;
КонецФункции