| 
									
										
										
										
											2020-05-22 03:52:34 +04:30
										 |  |  | /*
 | 
					
						
							| 
									
										
										
										
											2021-04-28 22:46:44 +02:00
										 |  |  |  * Copyright (c) 2020, the SerenityOS developers. | 
					
						
							| 
									
										
										
										
											2020-05-22 03:52:34 +04:30
										 |  |  |  * | 
					
						
							| 
									
										
										
										
											2021-04-22 01:24:48 -07:00
										 |  |  |  * SPDX-License-Identifier: BSD-2-Clause | 
					
						
							| 
									
										
										
										
											2020-05-22 03:52:34 +04:30
										 |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #pragma once
 | 
					
						
							| 
									
										
										
										
											2020-05-28 20:40:53 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-22 03:52:34 +04:30
										 |  |  | #include <AK/Forward.h>
 | 
					
						
							|  |  |  | #include <AK/String.h>
 | 
					
						
							|  |  |  | #include <AK/Utf32View.h>
 | 
					
						
							|  |  |  | #include <AK/Utf8View.h>
 | 
					
						
							|  |  |  | #include <LibLine/Style.h>
 | 
					
						
							|  |  |  | #include <stdlib.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | namespace Line { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // FIXME: These objects are pretty heavy since they store two copies of text
 | 
					
						
							| 
									
										
										
										
											2020-05-23 03:19:48 +04:30
										 |  |  | //        somehow get rid of one.
 | 
					
						
							| 
									
										
										
										
											2020-05-22 03:52:34 +04:30
										 |  |  | struct CompletionSuggestion { | 
					
						
							| 
									
										
										
										
											2020-05-22 22:40:42 +04:30
										 |  |  | private: | 
					
						
							|  |  |  |     struct ForSearchTag { | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | public: | 
					
						
							|  |  |  |     static constexpr ForSearchTag ForSearch {}; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-23 03:19:48 +04:30
										 |  |  |     // Intentionally not explicit. (To allow suggesting bare strings)
 | 
					
						
							| 
									
										
										
										
											2020-05-22 03:52:34 +04:30
										 |  |  |     CompletionSuggestion(const String& completion) | 
					
						
							|  |  |  |         : CompletionSuggestion(completion, "", {}) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-22 22:40:42 +04:30
										 |  |  |     CompletionSuggestion(const String& completion, ForSearchTag) | 
					
						
							|  |  |  |         : text_string(completion) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-11 00:55:02 +01:00
										 |  |  |     CompletionSuggestion(StringView completion, StringView trailing_trivia) | 
					
						
							| 
									
										
										
										
											2020-05-22 03:52:34 +04:30
										 |  |  |         : CompletionSuggestion(completion, trailing_trivia, {}) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-11 00:55:02 +01:00
										 |  |  |     CompletionSuggestion(StringView completion, StringView trailing_trivia, Style style); | 
					
						
							| 
									
										
										
										
											2020-05-22 03:52:34 +04:30
										 |  |  | 
 | 
					
						
							|  |  |  |     bool operator==(const CompletionSuggestion& suggestion) const | 
					
						
							|  |  |  |     { | 
					
						
							| 
									
										
										
										
											2020-05-22 22:40:42 +04:30
										 |  |  |         return suggestion.text_string == text_string; | 
					
						
							| 
									
										
										
										
											2020-05-22 03:52:34 +04:30
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Vector<u32> text; | 
					
						
							|  |  |  |     Vector<u32> trailing_trivia; | 
					
						
							|  |  |  |     Style style; | 
					
						
							|  |  |  |     size_t start_index { 0 }; | 
					
						
							| 
									
										
										
										
											2020-10-03 17:24:49 +03:30
										 |  |  |     size_t input_offset { 0 }; | 
					
						
							| 
									
										
										
										
											2020-05-22 03:52:34 +04:30
										 |  |  | 
 | 
					
						
							|  |  |  |     Utf32View text_view; | 
					
						
							|  |  |  |     Utf32View trivia_view; | 
					
						
							|  |  |  |     String text_string; | 
					
						
							| 
									
										
										
										
											2020-05-22 22:40:42 +04:30
										 |  |  |     bool is_valid { false }; | 
					
						
							| 
									
										
										
										
											2020-05-22 03:52:34 +04:30
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class SuggestionManager { | 
					
						
							|  |  |  |     friend class Editor; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | public: | 
					
						
							|  |  |  |     void set_suggestions(Vector<CompletionSuggestion>&& suggestions); | 
					
						
							| 
									
										
										
										
											2020-05-22 14:02:17 +04:30
										 |  |  |     void set_current_suggestion_initiation_index(size_t start_index); | 
					
						
							| 
									
										
										
										
											2020-05-22 03:52:34 +04:30
										 |  |  | 
 | 
					
						
							|  |  |  |     size_t count() const { return m_suggestions.size(); } | 
					
						
							|  |  |  |     size_t display_length() const { return m_last_shown_suggestion_display_length; } | 
					
						
							| 
									
										
										
										
											2020-05-22 14:02:17 +04:30
										 |  |  |     size_t start_index() const { return m_last_displayed_suggestion_index; } | 
					
						
							| 
									
										
										
										
											2020-05-22 03:52:34 +04:30
										 |  |  |     size_t next_index() const { return m_next_suggestion_index; } | 
					
						
							| 
									
										
										
										
											2020-05-22 14:02:17 +04:30
										 |  |  |     void set_start_index(size_t index) const { m_last_displayed_suggestion_index = index; } | 
					
						
							| 
									
										
										
										
											2020-05-22 03:52:34 +04:30
										 |  |  | 
 | 
					
						
							|  |  |  |     size_t for_each_suggestion(Function<IterationDecision(const CompletionSuggestion&, size_t)>) const; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     enum CompletionMode { | 
					
						
							|  |  |  |         DontComplete, | 
					
						
							|  |  |  |         CompletePrefix, | 
					
						
							|  |  |  |         ShowSuggestions, | 
					
						
							|  |  |  |         CycleSuggestions, | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     class CompletionAttemptResult { | 
					
						
							|  |  |  |     public: | 
					
						
							|  |  |  |         CompletionMode new_completion_mode; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         ssize_t new_cursor_offset { 0 }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         struct { | 
					
						
							|  |  |  |             size_t start; | 
					
						
							|  |  |  |             size_t end; | 
					
						
							|  |  |  |         } offset_region_to_remove { 0, 0 }; // The region to remove as defined by [start, end) translated by (old_cursor + new_cursor_offset)
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Vector<Utf32View> insert {}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Optional<Style> style_to_apply {}; | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     CompletionAttemptResult attempt_completion(CompletionMode, size_t initiation_start_index); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     void next(); | 
					
						
							|  |  |  |     void previous(); | 
					
						
							|  |  |  |     void set_suggestion_variants(size_t static_offset, size_t invariant_offset, size_t suggestion_index) const | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         m_next_suggestion_index = suggestion_index; | 
					
						
							|  |  |  |         m_next_suggestion_static_offset = static_offset; | 
					
						
							|  |  |  |         m_next_suggestion_invariant_offset = invariant_offset; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const CompletionSuggestion& suggest(); | 
					
						
							|  |  |  |     const CompletionSuggestion& current_suggestion() const { return m_last_shown_suggestion; } | 
					
						
							|  |  |  |     bool is_current_suggestion_complete() const { return m_last_shown_suggestion_was_complete; } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     void reset() | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         m_last_shown_suggestion = String::empty(); | 
					
						
							|  |  |  |         m_last_shown_suggestion_display_length = 0; | 
					
						
							|  |  |  |         m_suggestions.clear(); | 
					
						
							|  |  |  |         m_last_displayed_suggestion_index = 0; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | private: | 
					
						
							|  |  |  |     SuggestionManager() | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Vector<CompletionSuggestion> m_suggestions; | 
					
						
							|  |  |  |     CompletionSuggestion m_last_shown_suggestion { String::empty() }; | 
					
						
							|  |  |  |     size_t m_last_shown_suggestion_display_length { 0 }; | 
					
						
							|  |  |  |     bool m_last_shown_suggestion_was_complete { false }; | 
					
						
							|  |  |  |     mutable size_t m_next_suggestion_index { 0 }; | 
					
						
							|  |  |  |     mutable size_t m_next_suggestion_invariant_offset { 0 }; | 
					
						
							|  |  |  |     mutable size_t m_next_suggestion_static_offset { 0 }; | 
					
						
							|  |  |  |     size_t m_largest_common_suggestion_prefix_length { 0 }; | 
					
						
							| 
									
										
										
										
											2020-05-22 14:02:17 +04:30
										 |  |  |     mutable size_t m_last_displayed_suggestion_index { 0 }; | 
					
						
							| 
									
										
										
										
											2020-05-22 03:52:34 +04:30
										 |  |  |     size_t m_selected_suggestion_index { 0 }; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | } |