summaryrefslogtreecommitdiff
path: root/Userland
diff options
context:
space:
mode:
Diffstat (limited to 'Userland')
-rw-r--r--Userland/js.cpp109
1 files changed, 83 insertions, 26 deletions
diff --git a/Userland/js.cpp b/Userland/js.cpp
index a3cd05c5ec..4c6fbe080b 100644
--- a/Userland/js.cpp
+++ b/Userland/js.cpp
@@ -655,17 +655,75 @@ int main(int argc, char** argv)
editor.set_prompt(prompt_for_level(open_indents));
};
- auto complete = [&interpreter, &editor = *s_editor](const String& token) -> Vector<Line::CompletionSuggestion> {
- if (token.length() == 0)
- return {}; // nyeh
+ auto complete = [&interpreter](const Line::Editor& editor) -> Vector<Line::CompletionSuggestion> {
+ auto line = editor.line(editor.cursor());
+
+ JS::Lexer lexer { line };
+ enum {
+ Initial,
+ CompleteVariable,
+ CompleteNullProperty,
+ CompleteProperty,
+ } mode { Initial };
+
+ StringView variable_name;
+ StringView property_name;
- auto line = editor.line();
// we're only going to complete either
// - <N>
// where N is part of the name of a variable
// - <N>.<P>
// where N is the complete name of a variable and
// P is part of the name of one of its properties
+ auto js_token = lexer.next();
+ for (; js_token.type() != JS::TokenType::Eof; js_token = lexer.next()) {
+ switch (mode) {
+ case CompleteVariable:
+ switch (js_token.type()) {
+ case JS::TokenType::Period:
+ // ...<name> <dot>
+ mode = CompleteNullProperty;
+ break;
+ default:
+ // not a dot, reset back to initial
+ mode = Initial;
+ break;
+ }
+ break;
+ case CompleteNullProperty:
+ if (js_token.is_identifier_name()) {
+ // ...<name> <dot> <name>
+ mode = CompleteProperty;
+ property_name = js_token.value();
+ } else {
+ mode = Initial;
+ }
+ break;
+ case CompleteProperty:
+ // something came after the property access, reset to initial
+ case Initial:
+ if (js_token.is_identifier_name()) {
+ // ...<name>...
+ mode = CompleteVariable;
+ variable_name = js_token.value();
+ } else {
+ mode = Initial;
+ }
+ break;
+ }
+ }
+
+ bool last_token_has_trivia = js_token.trivia().length() > 0;
+
+ if (mode == CompleteNullProperty) {
+ mode = CompleteProperty;
+ property_name = "";
+ last_token_has_trivia = false; // <name> <dot> [tab] is sensible to complete.
+ }
+
+ if (mode == Initial || last_token_has_trivia)
+ return {}; // we do not know how to complete this
+
Vector<Line::CompletionSuggestion> results;
Function<void(const JS::Shape&, const StringView&)> list_all_properties = [&results, &list_all_properties](const JS::Shape& shape, auto& property_pattern) {
@@ -682,41 +740,40 @@ int main(int argc, char** argv)
}
};
- if (token.contains(".")) {
- auto parts = token.split('.', true);
- // refuse either `.` or `a.b.c`
- if (parts.size() > 2 || parts.size() == 0)
- return {};
-
- auto name = parts[0];
- auto property_pattern = parts[1];
-
- auto maybe_variable = interpreter->get_variable(name);
+ switch (mode) {
+ case CompleteProperty: {
+ auto maybe_variable = interpreter->get_variable(variable_name);
if (maybe_variable.is_empty()) {
- maybe_variable = interpreter->global_object().get(name);
+ maybe_variable = interpreter->global_object().get(variable_name);
if (maybe_variable.is_empty())
- return {};
+ break;
}
auto variable = maybe_variable;
if (!variable.is_object())
- return {};
+ break;
const auto* object = variable.to_object(*interpreter);
const auto& shape = object->shape();
- list_all_properties(shape, property_pattern);
+ list_all_properties(shape, property_name);
if (results.size())
- editor.suggest(property_pattern.length());
- return results;
+ editor.suggest(property_name.length());
+ break;
}
- const auto& variable = interpreter->global_object();
- list_all_properties(variable.shape(), token);
- if (results.size())
- editor.suggest(token.length());
+ case CompleteVariable: {
+ const auto& variable = interpreter->global_object();
+ list_all_properties(variable.shape(), variable_name);
+ if (results.size())
+ editor.suggest(variable_name.length());
+ break;
+ }
+ default:
+ ASSERT_NOT_REACHED();
+ }
+
return results;
};
- s_editor->on_tab_complete_first_token = [complete](auto& value) { return complete(value); };
- s_editor->on_tab_complete_other_token = [complete](auto& value) { return complete(value); };
+ s_editor->on_tab_complete = move(complete);
repl(*interpreter);
} else {
interpreter = JS::Interpreter::create<JS::GlobalObject>();