Providers.cpp 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. /*
  2. * Copyright (c) 2021, Spencer Dixon <spencercdixon@gmail.com>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include "Providers.h"
  7. #include "FuzzyMatch.h"
  8. #include <AK/URL.h>
  9. #include <LibCore/DirIterator.h>
  10. #include <LibCore/File.h>
  11. #include <LibCore/StandardPaths.h>
  12. #include <LibDesktop/Launcher.h>
  13. #include <LibGUI/Clipboard.h>
  14. #include <LibGUI/FileIconProvider.h>
  15. #include <LibJS/Interpreter.h>
  16. #include <LibJS/Lexer.h>
  17. #include <LibJS/Parser.h>
  18. #include <LibJS/Runtime/GlobalObject.h>
  19. #include <unistd.h>
  20. namespace Assistant {
  21. void AppResult::activate() const
  22. {
  23. if (chdir(Core::StandardPaths::home_directory().characters()) < 0) {
  24. perror("chdir");
  25. exit(1);
  26. }
  27. m_app_file->spawn();
  28. }
  29. void CalculatorResult::activate() const
  30. {
  31. GUI::Clipboard::the().set_plain_text(title());
  32. }
  33. void FileResult::activate() const
  34. {
  35. Desktop::Launcher::open(URL::create_with_file_protocol(title()));
  36. }
  37. void URLResult::activate() const
  38. {
  39. Desktop::Launcher::open(URL::create_with_url_or_path(title()));
  40. }
  41. void AppProvider::query(String const& query, Function<void(Vector<NonnullRefPtr<Result>>)> on_complete)
  42. {
  43. if (query.starts_with("="))
  44. return;
  45. Vector<NonnullRefPtr<Result>> results;
  46. Desktop::AppFile::for_each([&](NonnullRefPtr<Desktop::AppFile> app_file) {
  47. auto match_result = fuzzy_match(query, app_file->name());
  48. if (!match_result.matched)
  49. return;
  50. auto icon = GUI::FileIconProvider::icon_for_executable(app_file->executable());
  51. results.append(adopt_ref(*new AppResult(icon.bitmap_for_size(16), app_file->name(), {}, app_file, match_result.score)));
  52. });
  53. on_complete(results);
  54. }
  55. void CalculatorProvider::query(String const& query, Function<void(Vector<NonnullRefPtr<Result>>)> on_complete)
  56. {
  57. if (!query.starts_with("="))
  58. return;
  59. auto vm = JS::VM::create();
  60. auto interpreter = JS::Interpreter::create<JS::GlobalObject>(*vm);
  61. auto source_code = query.substring(1);
  62. auto parser = JS::Parser(JS::Lexer(source_code));
  63. auto program = parser.parse_program();
  64. if (parser.has_errors())
  65. return;
  66. interpreter->run(interpreter->global_object(), *program);
  67. if (interpreter->exception())
  68. return;
  69. auto result = interpreter->vm().last_value();
  70. String calculation;
  71. if (!result.is_number()) {
  72. calculation = "0";
  73. } else {
  74. calculation = result.to_string_without_side_effects();
  75. }
  76. Vector<NonnullRefPtr<Result>> results;
  77. results.append(adopt_ref(*new CalculatorResult(calculation)));
  78. on_complete(results);
  79. }
  80. void FileProvider::query(const String& query, Function<void(Vector<NonnullRefPtr<Result>>)> on_complete)
  81. {
  82. build_filesystem_cache();
  83. if (m_fuzzy_match_work)
  84. m_fuzzy_match_work->cancel();
  85. m_fuzzy_match_work = Threading::BackgroundAction<Vector<NonnullRefPtr<Result>>>::create([this, query](auto& task) {
  86. Vector<NonnullRefPtr<Result>> results;
  87. for (auto& path : m_full_path_cache) {
  88. if (task.is_cancelled())
  89. return results;
  90. auto match_result = fuzzy_match(query, path);
  91. if (!match_result.matched)
  92. continue;
  93. if (match_result.score < 0)
  94. continue;
  95. results.append(adopt_ref(*new FileResult(path, match_result.score)));
  96. }
  97. return results; }, [on_complete = move(on_complete)](auto results) { on_complete(results); });
  98. }
  99. void FileProvider::build_filesystem_cache()
  100. {
  101. if (m_full_path_cache.size() > 0 || m_building_cache)
  102. return;
  103. m_building_cache = true;
  104. m_work_queue.enqueue("/");
  105. Threading::BackgroundAction<int>::create([this](auto&) {
  106. while (!m_work_queue.is_empty()) {
  107. auto start = m_work_queue.dequeue();
  108. Core::DirIterator di(start, Core::DirIterator::SkipDots);
  109. while (di.has_next()) {
  110. auto path = di.next_full_path();
  111. struct stat st = {};
  112. if (lstat(path.characters(), &st) < 0) {
  113. perror("stat");
  114. continue;
  115. }
  116. if (S_ISLNK(st.st_mode))
  117. continue;
  118. m_full_path_cache.append(path);
  119. if (S_ISDIR(st.st_mode)) {
  120. m_work_queue.enqueue(path);
  121. }
  122. }
  123. }
  124. return 0; }, [this](auto) { m_building_cache = false; });
  125. }
  126. void URLProvider::query(String const& query, Function<void(Vector<NonnullRefPtr<Result>>)> on_complete)
  127. {
  128. URL url = URL(query);
  129. if (url.scheme().is_empty())
  130. url.set_scheme("http");
  131. if (url.host().is_empty())
  132. url.set_host(query);
  133. if (url.paths().is_empty())
  134. url.set_paths({ "" });
  135. if (!url.is_valid())
  136. return;
  137. Vector<NonnullRefPtr<Result>> results;
  138. results.append(adopt_ref(*new URLResult(url)));
  139. on_complete(results);
  140. }
  141. }