/* Copyright (c) 2016 Microsoft Corporation. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: Gabriel Ebner, Leonardo de Moura, Sebastian Ullrich */ #if defined(LEAN_SERVER) #include "shell/completion.h" #include "shell/server.h" #include "util/bitap_fuzzy_search.h" #include "library/protected.h" #include "library/scoped_ext.h" #include "frontends/lean/util.h" namespace lean { #define LEAN_FUZZY_MAX_ERRORS 3 #define LEAN_FUZZY_MAX_ERRORS_FACTOR 3 /** \brief Return an (atomic) name if \c n can be referenced by this atomic name in the given environment. */ optional is_essentially_atomic(environment const & env, name const & n) { if (n.is_atomic()) return optional(n); list const & ns_list = get_namespaces(env); for (name const & ns : ns_list) { if (is_prefix_of(ns, n)) { auto n_prime = n.replace_prefix(ns, name()); if (n_prime.is_atomic() && !is_protected(env, n)) return optional(n_prime); break; } } if (auto it = is_uniquely_aliased(env, n)) if (it->is_atomic()) return it; return optional(); } unsigned get_fuzzy_match_max_errors(unsigned prefix_sz) { unsigned r = (prefix_sz / LEAN_FUZZY_MAX_ERRORS_FACTOR); if (r > LEAN_FUZZY_MAX_ERRORS) return LEAN_FUZZY_MAX_ERRORS; return r; } optional exact_prefix_match(environment const & env, std::string const & pattern, declaration const & d) { if (auto it = is_essentially_atomic(env, d.get_name())) { std::string it_str = it->to_string(); // if pattern "perfectly" matches beginning of declaration name, we just display d on the top of the list if (it_str.compare(0, pattern.size(), pattern) == 0) return it; } else { std::string d_str = d.get_name().to_string(); if (d_str.compare(0, pattern.size(), pattern) == 0) return optional(d.get_name()); } return optional(); } std::vector get_completions(std::string const & pattern, environment const & env, options const & opts) { std::vector completions; unsigned max_results = get_auto_completion_max_results(opts); unsigned max_errors = get_fuzzy_match_max_errors(pattern.size()); std::vector> exact_matches; std::vector> selected; bitap_fuzzy_search matcher(pattern, max_errors); env.for_each_declaration([&](declaration const & d) { if (is_projection(env, d.get_name())) return; if (auto it = exact_prefix_match(env, pattern, d)) { exact_matches.emplace_back(*it, d.get_name()); } else { std::string text = d.get_name().to_string(); if (matcher.match(text)) selected.emplace_back(text, d.get_name()); } }); unsigned num_results = 0; if (!exact_matches.empty()) { std::sort(exact_matches.begin(), exact_matches.end(), [](pair const & p1, pair const & p2) { return p1.first.size() < p2.first.size(); }); for (pair const & p : exact_matches) { completions.push_back(serialize_decl(p.first, p.second, env, opts)); num_results++; if (num_results >= max_results) break; } } unsigned sz = selected.size(); if (sz == 1) { completions.push_back(serialize_decl(selected[0].second, env, opts)); } else if (sz > 1) { std::vector> next_selected; for (unsigned k = 0; k <= max_errors && num_results < max_results; k++) { bitap_fuzzy_search matcher(pattern, k); for (auto const & s : selected) { if (matcher.match(s.first)) { completions.push_back(serialize_decl(s.second, env, opts)); num_results++; if (num_results >= max_results) break; } else { next_selected.push_back(s); } } std::swap(selected, next_selected); next_selected.clear(); } } return completions; } } #endif