mirror of
https://github.com/RGBCube/serenity
synced 2025-07-24 17:07:35 +00:00
Shell: Allow completions to request further action from the shell
The shell now expects a JSON object of the form {"kind":<kind>,...} per line in the standard output of the completion process, where 'kind' is one of: - "plain": Just a plain suggestion. - "program": Prompts the shell to complete a program name starting with the given "name". - "proxy": Prompts the shell to act as if a completion for "argv" was requested. - "path": Prompts the shell to complete a path given the "base" and "part" (same as completing part in cwd=base).
This commit is contained in:
parent
4ede121d31
commit
4ea9ca06b4
2 changed files with 55 additions and 23 deletions
|
@ -1409,8 +1409,12 @@ void Shell::highlight(Line::Editor& editor) const
|
||||||
|
|
||||||
Vector<Line::CompletionSuggestion> Shell::complete()
|
Vector<Line::CompletionSuggestion> Shell::complete()
|
||||||
{
|
{
|
||||||
auto line = m_editor->line(m_editor->cursor());
|
m_completion_stack_info = {};
|
||||||
|
return complete(m_editor->line(m_editor->cursor()));
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector<Line::CompletionSuggestion> Shell::complete(StringView line)
|
||||||
|
{
|
||||||
Parser parser(line, m_is_interactive);
|
Parser parser(line, m_is_interactive);
|
||||||
|
|
||||||
auto ast = parser.parse();
|
auto ast = parser.parse();
|
||||||
|
@ -1850,29 +1854,53 @@ ErrorOr<Vector<Line::CompletionSuggestion>> Shell::complete_via_program_itself(s
|
||||||
{ "/", "rx" },
|
{ "/", "rx" },
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
execute_node->for_each_entry(*this, [&](NonnullRefPtr<AST::Value> entry) -> IterationDecision {
|
{
|
||||||
auto result = entry->resolve_as_string(*this);
|
TemporaryChange change(m_is_interactive, false);
|
||||||
JsonParser parser(result);
|
execute_node->for_each_entry(*this, [&](NonnullRefPtr<AST::Value> entry) -> IterationDecision {
|
||||||
auto parsed_result = parser.parse();
|
auto result = entry->resolve_as_string(*this);
|
||||||
if (parsed_result.is_error())
|
JsonParser parser(result);
|
||||||
return IterationDecision::Continue;
|
auto parsed_result = parser.parse();
|
||||||
auto parsed = parsed_result.release_value();
|
if (parsed_result.is_error())
|
||||||
if (parsed.is_object()) {
|
return IterationDecision::Continue;
|
||||||
auto& object = parsed.as_object();
|
auto parsed = parsed_result.release_value();
|
||||||
Line::CompletionSuggestion suggestion {
|
if (parsed.is_object()) {
|
||||||
object.get("completion").as_string_or(""),
|
auto& object = parsed.as_object();
|
||||||
object.get("trailing_trivia").as_string_or(""),
|
auto kind = object.get("kind").as_string_or("plain");
|
||||||
object.get("display_trivia").as_string_or(""),
|
if (kind == "path") {
|
||||||
};
|
auto base = object.get("base").as_string_or("");
|
||||||
suggestion.static_offset = object.get("static_offset").to_u64(0);
|
auto part = object.get("part").as_string_or("");
|
||||||
suggestion.invariant_offset = object.get("invariant_offset").to_u64(0);
|
auto executable_only = object.get("executable_only").to_bool(false) ? ExecutableOnly::Yes : ExecutableOnly::No;
|
||||||
suggestions.append(move(suggestion));
|
suggestions.extend(complete_path(base, part, part.length(), executable_only, nullptr, nullptr));
|
||||||
} else {
|
} else if (kind == "program") {
|
||||||
suggestions.append(parsed.to_string());
|
auto name = object.get("name").as_string_or("");
|
||||||
}
|
suggestions.extend(complete_program_name(name, name.length()));
|
||||||
|
} else if (kind == "proxy") {
|
||||||
|
if (m_completion_stack_info.size_free() < 4 * KiB) {
|
||||||
|
dbgln("Not enough stack space, recursion?");
|
||||||
|
return IterationDecision::Continue;
|
||||||
|
}
|
||||||
|
auto argv = object.get("argv").as_string_or("");
|
||||||
|
dbgln("Proxy completion for {}", argv);
|
||||||
|
suggestions.extend(complete(argv));
|
||||||
|
} else if (kind == "plain") {
|
||||||
|
Line::CompletionSuggestion suggestion {
|
||||||
|
object.get("completion").as_string_or(""),
|
||||||
|
object.get("trailing_trivia").as_string_or(""),
|
||||||
|
object.get("display_trivia").as_string_or(""),
|
||||||
|
};
|
||||||
|
suggestion.static_offset = object.get("static_offset").to_u64(0);
|
||||||
|
suggestion.invariant_offset = object.get("invariant_offset").to_u64(0);
|
||||||
|
suggestions.append(move(suggestion));
|
||||||
|
} else {
|
||||||
|
dbgln("LibLine: Unhandled completion kind: {}", kind);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
suggestions.append(parsed.to_string());
|
||||||
|
}
|
||||||
|
|
||||||
return IterationDecision::Continue;
|
return IterationDecision::Continue;
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
auto pgid = getpgrp();
|
auto pgid = getpgrp();
|
||||||
tcsetpgrp(STDOUT_FILENO, pgid);
|
tcsetpgrp(STDOUT_FILENO, pgid);
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
#include <AK/CircularQueue.h>
|
#include <AK/CircularQueue.h>
|
||||||
#include <AK/HashMap.h>
|
#include <AK/HashMap.h>
|
||||||
#include <AK/NonnullOwnPtrVector.h>
|
#include <AK/NonnullOwnPtrVector.h>
|
||||||
|
#include <AK/StackInfo.h>
|
||||||
#include <AK/String.h>
|
#include <AK/String.h>
|
||||||
#include <AK/StringBuilder.h>
|
#include <AK/StringBuilder.h>
|
||||||
#include <AK/StringView.h>
|
#include <AK/StringView.h>
|
||||||
|
@ -223,6 +224,7 @@ public:
|
||||||
|
|
||||||
void highlight(Line::Editor&) const;
|
void highlight(Line::Editor&) const;
|
||||||
Vector<Line::CompletionSuggestion> complete();
|
Vector<Line::CompletionSuggestion> complete();
|
||||||
|
Vector<Line::CompletionSuggestion> complete(StringView);
|
||||||
Vector<Line::CompletionSuggestion> complete_program_name(StringView, size_t offset, EscapeMode = EscapeMode::Bareword);
|
Vector<Line::CompletionSuggestion> complete_program_name(StringView, size_t offset, EscapeMode = EscapeMode::Bareword);
|
||||||
Vector<Line::CompletionSuggestion> complete_variable(StringView, size_t offset);
|
Vector<Line::CompletionSuggestion> complete_variable(StringView, size_t offset);
|
||||||
Vector<Line::CompletionSuggestion> complete_user(StringView, size_t offset);
|
Vector<Line::CompletionSuggestion> complete_user(StringView, size_t offset);
|
||||||
|
@ -419,6 +421,8 @@ private:
|
||||||
mutable bool m_last_continuation_state { false }; // false == not needed.
|
mutable bool m_last_continuation_state { false }; // false == not needed.
|
||||||
|
|
||||||
Optional<size_t> m_history_autosave_time;
|
Optional<size_t> m_history_autosave_time;
|
||||||
|
|
||||||
|
StackInfo m_completion_stack_info;
|
||||||
};
|
};
|
||||||
|
|
||||||
[[maybe_unused]] static constexpr bool is_word_character(char c)
|
[[maybe_unused]] static constexpr bool is_word_character(char c)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue