mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 21:22:46 +00:00 
			
		
		
		
	LibLine: Unify completion hooks and adapt its users
LibLine should ultimately not care about what a "token" means in the context of its user, so force the user to split the buffer itself. This also allows the users to pick up contextual clues as well, since they have to lex the line themselves. This commit pacthes Shell and the JS repl to better handle completions, so certain wrong behaviours are now corrected as well: - JS repl can now complete "Object . getOw<tab>" - Shell can now complete "echo | ca<tab>" and paths inside strings
This commit is contained in:
		
							parent
							
								
									d18f6e82eb
								
							
						
					
					
						commit
						7fba21aefc
					
				
					 6 changed files with 179 additions and 171 deletions
				
			
		
							
								
								
									
										109
									
								
								Userland/js.cpp
									
										
									
									
									
								
							
							
						
						
									
										109
									
								
								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>(); | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 AnotherTest
						AnotherTest