LexerAutoComplete.cpp 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687
  1. /*
  2. * Copyright (c) 2020, Itamar S. <itamar8910@gmail.com>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include "LexerAutoComplete.h"
  7. #include <AK/Debug.h>
  8. #include <AK/HashTable.h>
  9. #include <LibCpp/Lexer.h>
  10. namespace LanguageServers::Cpp {
  11. LexerAutoComplete::LexerAutoComplete(ClientConnection& connection, const FileDB& filedb)
  12. : AutoCompleteEngine(connection, filedb)
  13. {
  14. }
  15. Vector<GUI::AutocompleteProvider::Entry> LexerAutoComplete::get_suggestions(const String& file, const GUI::TextPosition& autocomplete_position)
  16. {
  17. auto document = filedb().get(file);
  18. if (!document) {
  19. dbgln("didn't find document for {}", file);
  20. return {};
  21. }
  22. auto code = document->text();
  23. auto lines = code.split('\n', true);
  24. Cpp::Lexer lexer(code);
  25. auto tokens = lexer.lex();
  26. auto index_of_target_token = token_in_position(tokens, autocomplete_position);
  27. if (!index_of_target_token.has_value())
  28. return {};
  29. auto suggestions = identifier_prefixes(lines, tokens, index_of_target_token.value());
  30. if constexpr (AUTOCOMPLETE_DEBUG) {
  31. for (auto& suggestion : suggestions) {
  32. dbgln("suggestion: {}", suggestion.completion);
  33. }
  34. }
  35. return suggestions;
  36. }
  37. StringView LexerAutoComplete::text_of_token(const Vector<String>& lines, const Cpp::Token& token)
  38. {
  39. VERIFY(token.start().line == token.end().line);
  40. VERIFY(token.start().column <= token.end().column);
  41. return lines[token.start().line].substring_view(token.start().column, token.end().column - token.start().column + 1);
  42. }
  43. Optional<size_t> LexerAutoComplete::token_in_position(const Vector<Cpp::Token>& tokens, const GUI::TextPosition& position)
  44. {
  45. for (size_t token_index = 0; token_index < tokens.size(); ++token_index) {
  46. auto& token = tokens[token_index];
  47. if (token.start().line != token.end().line)
  48. continue;
  49. if (token.start().line != position.line())
  50. continue;
  51. if (token.start().column + 1 > position.column() || token.end().column + 1 < position.column())
  52. continue;
  53. return token_index;
  54. }
  55. return {};
  56. }
  57. Vector<GUI::AutocompleteProvider::Entry> LexerAutoComplete::identifier_prefixes(const Vector<String>& lines, const Vector<Cpp::Token>& tokens, size_t target_token_index)
  58. {
  59. auto partial_input = text_of_token(lines, tokens[target_token_index]);
  60. Vector<GUI::AutocompleteProvider::Entry> suggestions;
  61. HashTable<String> suggestions_lookup; // To avoid duplicate results
  62. for (size_t i = 0; i < target_token_index; ++i) {
  63. auto& token = tokens[i];
  64. if (token.type() != Cpp::Token::Type::Identifier)
  65. continue;
  66. auto text = text_of_token(lines, token);
  67. if (text.starts_with(partial_input) && suggestions_lookup.set(text) == AK::HashSetResult::InsertedNewEntry) {
  68. suggestions.append({ text, partial_input.length(), GUI::AutocompleteProvider::CompletionKind::Identifier });
  69. }
  70. }
  71. return suggestions;
  72. }
  73. }