diff --git a/src/selection_dialog.cpp b/src/selection_dialog.cpp index fc603a4..d6f312c 100644 --- a/src/selection_dialog.cpp +++ b/src/selection_dialog.cpp @@ -390,7 +390,8 @@ void CompletionDialog::select(bool hide_window) { } bool CompletionDialog::on_key_release(GdkEventKey *event) { - if(event->keyval == GDK_KEY_Down || event->keyval == GDK_KEY_KP_Down || event->keyval == GDK_KEY_Up || event->keyval == GDK_KEY_KP_Up) + if(event->keyval == GDK_KEY_Down || event->keyval == GDK_KEY_KP_Down || event->keyval == GDK_KEY_Up || event->keyval == GDK_KEY_KP_Up || + (event->keyval >= GDK_KEY_Shift_L && event->keyval <= GDK_KEY_Hyper_R)) return false; if(show_offset > text_view->get_buffer()->get_insert()->get_iter().get_offset()) diff --git a/src/source_language_protocol.cpp b/src/source_language_protocol.cpp index f9a009c..159d7f1 100644 --- a/src/source_language_protocol.cpp +++ b/src/source_language_protocol.cpp @@ -1183,27 +1183,15 @@ void Source::LanguageProtocolView::show_type_tooltips(const Gdk::Rectangle &rect auto token_iters = get_token_iters(get_buffer()->get_iter_at_offset(offset)); type_tooltips.emplace_back(this, token_iters.first, token_iters.second, [this, offset, contents = std::move(contents)](Tooltip &tooltip) mutable { bool first = true; - if(language_id == "python") { - std::string function; + if(language_id == "python") { // Python might support markdown in the future for(auto &content : contents) { if(!first) tooltip.buffer->insert_at_cursor("\n\n"); first = false; - if(content.kind == "python") { + if(content.kind == "python") tooltip.insert_code(content.value, content.kind); - auto pos = content.value.find('('); - if(pos != std::string::npos) - function = content.value.substr(0, pos + 1); - } - else { - if(!function.empty()) { - while(starts_with(content.value, function)) { - auto pos = content.value.find("\n\n", function.size()); - content.value.erase(0, pos != std::string::npos ? pos + 2 : pos); - } - } - tooltip.insert_with_links_tagged(content.value); - } + else + tooltip.insert_docstring(content.value); } } else { @@ -1211,7 +1199,7 @@ void Source::LanguageProtocolView::show_type_tooltips(const Gdk::Rectangle &rect if(!first) tooltip.buffer->insert_at_cursor("\n\n"); first = false; - if(content.kind == "plaintext" || content.kind.empty() || (language_id == "python" && content.kind == "markdown")) // Python might support markdown in the future + if(content.kind == "plaintext" || content.kind.empty()) tooltip.insert_with_links_tagged(content.value); else if(content.kind == "markdown") tooltip.insert_markdown(content.value); @@ -1754,17 +1742,8 @@ void Source::LanguageProtocolView::setup_autocomplete() { if(autocomplete.detail.empty() && autocomplete.documentation.empty()) return nullptr; return [this, autocomplete = std::move(autocomplete)](Tooltip &tooltip) mutable { - if(language_id == "python") { - auto pos = autocomplete.insert.find('('); - if(pos != std::string::npos) { - auto function = autocomplete.insert.substr(0, pos + 1); - while(starts_with(autocomplete.documentation, function)) { - auto pos = autocomplete.documentation.find("\n\n", function.size()); - autocomplete.documentation.erase(0, pos != std::string::npos ? pos + 2 : pos); - } - } - tooltip.insert_with_links_tagged(autocomplete.documentation); - } + if(language_id == "python") // Python might support markdown in the future + tooltip.insert_docstring(autocomplete.documentation); else { if(!autocomplete.detail.empty()) { tooltip.insert_code(autocomplete.detail, language); @@ -1773,7 +1752,7 @@ void Source::LanguageProtocolView::setup_autocomplete() { if(!autocomplete.documentation.empty()) { if(tooltip.buffer->size() > 0) tooltip.buffer->insert_at_cursor("\n\n"); - if(autocomplete.kind == "plaintext" || autocomplete.kind.empty() || (language_id == "python" && autocomplete.kind == "markdown")) // Python might support markdown in the future + if(autocomplete.kind == "plaintext" || autocomplete.kind.empty()) tooltip.insert_with_links_tagged(autocomplete.documentation); else if(autocomplete.kind == "markdown") tooltip.insert_markdown(autocomplete.documentation); diff --git a/src/tooltips.cpp b/src/tooltips.cpp index f045dd2..028e22e 100644 --- a/src/tooltips.cpp +++ b/src/tooltips.cpp @@ -582,16 +582,16 @@ void Tooltip::insert_markdown(const std::string &input) { auto i_saved = i; i++; if(i < to) { - bool escaped = false; + bool two_backticks = false; if(input[i] == '`') { - escaped = true; + two_backticks = true; i++; } if(i < to) { auto start = i; for(; i < to; i++) { if(input[i] == '`') { - if(!escaped) + if(!two_backticks) break; if(i + 1 < to && input[i + 1] == '`') break; @@ -602,7 +602,7 @@ void Tooltip::insert_markdown(const std::string &input) { return false; } buffer->insert_with_tag(buffer->get_insert()->get_iter(), input.substr(start, i - start), code_tag); - if(escaped) + if(two_backticks) i++; return true; } @@ -1385,6 +1385,94 @@ void Tooltip::insert_doxygen(const std::string &input_, bool remove_delimiters) insert_markdown(markdown); } +void Tooltip::insert_docstring(const std::string &input_) { + create_tags(); + + // Workaround for python-language-server that returns unnecessary function signatures + const static std::regex regex("^([a-zA-Z0-9_]+\\([^\n]*\\)( -> [^\n]+)?(\n|$))+(\n|$)", std::regex::extended | std::regex::optimize); + std::smatch sm; + const std::string &input = std::regex_search(input_, sm, regex) ? sm.suffix() : input_; + + std::string partial; + partial.reserve(input.size()); + size_t i = 0; + + auto parse_backtick = [&] { + if(input[i] == '`') { + insert_with_links_tagged(partial); + partial.clear(); + auto i_saved = i; + i++; + if(i < input.size()) { + bool two_backticks = false; + if(input[i] == '`') { + two_backticks = true; + i++; + } + if(i < input.size()) { + auto start = i; + for(; i < input.size(); i++) { + if(input[i] == '`') { + if(!two_backticks) + break; + if(i + 1 < input.size() && input[i + 1] == '`') + break; + } + } + if(i == input.size()) { + i = i_saved; + return false; + } + if(!two_backticks && i + 1 < input.size() && input[i + 1] == '_') { // Is a link + insert_with_links_tagged(input.substr(start, i - start)); + ++i; + } + else { + buffer->insert_with_tag(buffer->get_insert()->get_iter(), input.substr(start, i - start), code_tag); + if(two_backticks) + i++; + } + return true; + } + } + i = i_saved; + } + return false; + }; + + bool after_newline = true; + for(; i < input.size(); ++i) { + if(parse_backtick()) + continue; + if(after_newline) { + after_newline = false; + auto i_saved = i; + static std::string utf8_space = " "; + while(starts_with(input, i, utf8_space)) + i += utf8_space.size(); + while(i < input.size() && (input[i] == ' ' || input[i] == '\t')) + ++i; + if(starts_with(input, i, ">>>")) { + insert_with_links_tagged(partial); + partial.clear(); + auto pos = input.find("\n\n", i + 3); + insert_code(input.substr(i_saved, pos != std::string::npos ? pos - i_saved : pos), {}, true); + buffer->insert_at_cursor("\n\n"); + if(pos == std::string::npos) + break; + i = pos + 1; + continue; + } + i = i_saved; + } + if(input[i] == '\n') + after_newline = true; + partial += input[i]; + } + if(!partial.empty()) + insert_with_links_tagged(partial); +} + void Tooltip::remove_trailing_newlines() { auto end = buffer->end(); while(end.starts_line() && end.backward_char()) { diff --git a/src/tooltips.hpp b/src/tooltips.hpp index 29ea699..8b3023d 100644 --- a/src/tooltips.hpp +++ b/src/tooltips.hpp @@ -28,6 +28,8 @@ public: void insert_markdown(const std::string &text); void insert_code(const std::string &code, boost::variant> language = Glib::RefPtr{}, bool block = false); void insert_doxygen(const std::string &input, bool remove_delimiters); + /// Inserts python docstring + void insert_docstring(const std::string &input); /// Remove empty lines at end of buffer void remove_trailing_newlines();