Browse Source

Language protocol: on signature help, show parameters only for current parameter position. Also improved signature help on named parameters

pipelines/235045657
eidheim 5 years ago
parent
commit
fcbcc4e90e
  1. 7
      src/source.hpp
  2. 106
      src/source_language_protocol.cpp
  3. 4
      src/source_language_protocol.hpp

7
src/source.hpp

@ -125,14 +125,21 @@ namespace Source {
std::vector<FixIt> fix_its; std::vector<FixIt> fix_its;
/// Returns true if code iter (not part of comment or string, and not whitespace) is found.
/// Iter will not be moved if iter is already a code iter.
bool backward_to_code(Gtk::TextIter &iter); bool backward_to_code(Gtk::TextIter &iter);
/// Returns true if code iter (not part of comment or string, and not whitespace) is found.
/// Iter will not be moved if iter is already a code iter.
bool forward_to_code(Gtk::TextIter &iter); bool forward_to_code(Gtk::TextIter &iter);
/// Iter will not be moved if iter is already a code iter (not part of comment or string, and not whitespace) or at line start
void backward_to_code_or_line_start(Gtk::TextIter &iter); void backward_to_code_or_line_start(Gtk::TextIter &iter);
/// If closing bracket is found, continues until the open bracket. /// If closing bracket is found, continues until the open bracket.
/// Returns if open bracket is found that has no corresponding closing bracket. /// Returns if open bracket is found that has no corresponding closing bracket.
/// Else, return at start of line. /// Else, return at start of line.
Gtk::TextIter get_start_of_expression(Gtk::TextIter iter); Gtk::TextIter get_start_of_expression(Gtk::TextIter iter);
/// Iter will not be moved if iter is already at close symbol.
bool find_close_symbol_forward(Gtk::TextIter iter, Gtk::TextIter &found_iter, unsigned int positive_char, unsigned int negative_char); bool find_close_symbol_forward(Gtk::TextIter iter, Gtk::TextIter &found_iter, unsigned int positive_char, unsigned int negative_char);
/// Iter will not be moved if iter is already at open symbol.
bool find_open_symbol_backward(Gtk::TextIter iter, Gtk::TextIter &found_iter, unsigned int positive_char, unsigned int negative_char); bool find_open_symbol_backward(Gtk::TextIter iter, Gtk::TextIter &found_iter, unsigned int positive_char, unsigned int negative_char);
long symbol_count(Gtk::TextIter iter, unsigned int positive_char, unsigned int negative_char = 0); long symbol_count(Gtk::TextIter iter, unsigned int positive_char, unsigned int negative_char = 0);
bool is_templated_function(Gtk::TextIter iter, Gtk::TextIter &parenthesis_end_iter); bool is_templated_function(Gtk::TextIter iter, Gtk::TextIter &parenthesis_end_iter);

106
src/source_language_protocol.cpp

@ -1402,7 +1402,7 @@ void Source::LanguageProtocolView::setup_autocomplete() {
}, false); }, false);
// Remove argument completions // Remove argument completions
if(!has_named_parameters()) { // Do not remove named parameters in for instance Python if(!get_named_parameter_symbol()) { // Do not remove named parameters in for instance Python
signal_key_press_event().connect([this](GdkEventKey *event) { signal_key_press_event().connect([this](GdkEventKey *event) {
if(autocomplete_show_arguments && CompletionDialog::get() && CompletionDialog::get()->is_visible() && if(autocomplete_show_arguments && CompletionDialog::get() && CompletionDialog::get()->is_visible() &&
event->keyval != GDK_KEY_Down && event->keyval != GDK_KEY_Up && event->keyval != GDK_KEY_Down && event->keyval != GDK_KEY_Up &&
@ -1524,12 +1524,46 @@ void Source::LanguageProtocolView::setup_autocomplete() {
if(autocomplete_show_arguments) { if(autocomplete_show_arguments) {
if(!capabilities.signature_help) if(!capabilities.signature_help)
return; return;
client->write_request(this, "textDocument/signatureHelp", R"("textDocument":{"uri":")" + uri + R"("}, "position": {"line": )" + std::to_string(line_number - 1) + ", \"character\": " + std::to_string(column - 1) + "}", [this, &result_processed](const boost::property_tree::ptree &result, bool error) { dispatcher.post([this, line_number, column, &result_processed] {
// Find current parameter number and previously used named parameters
unsigned current_parameter_position = 0;
auto named_parameter_symbol = get_named_parameter_symbol();
std::set<std::string> used_named_parameters;
auto iter = get_buffer()->get_insert()->get_iter();
int para_count = 0;
while(iter.backward_char() && backward_to_code(iter)) {
if(para_count == 0) {
if(named_parameter_symbol && (*iter == ',' || *iter == '(')) {
auto next = iter;
if(next.forward_char() && forward_to_code(next)) {
auto pair = get_token_iters(next);
if(pair.first != pair.second) {
auto symbol = pair.second;
if(forward_to_code(symbol) && *symbol == static_cast<unsigned char>(*named_parameter_symbol))
used_named_parameters.emplace(get_buffer()->get_text(pair.first, pair.second));
}
}
}
if(*iter == ',')
++current_parameter_position;
else if(*iter == '(')
break;
}
if(*iter == '(')
++para_count;
else if(*iter == ')')
--para_count;
}
bool using_named_parameters = named_parameter_symbol && !(current_parameter_position > 0 && used_named_parameters.empty());
client->write_request(this, "textDocument/signatureHelp", R"("textDocument":{"uri":")" + uri + R"("}, "position": {"line": )" + std::to_string(line_number - 1) + ", \"character\": " + std::to_string(column - 1) + "}", [this, &result_processed, current_parameter_position, using_named_parameters, used_named_parameters = std::move(used_named_parameters)](const boost::property_tree::ptree &result, bool error) {
if(!error) { if(!error) {
auto signatures = result.get_child("signatures", boost::property_tree::ptree()); auto signatures = result.get_child("signatures", boost::property_tree::ptree());
for(auto signature_it = signatures.begin(); signature_it != signatures.end(); ++signature_it) { for(auto signature_it = signatures.begin(); signature_it != signatures.end(); ++signature_it) {
auto parameters = signature_it->second.get_child("parameters", boost::property_tree::ptree()); auto parameters = signature_it->second.get_child("parameters", boost::property_tree::ptree());
unsigned parameter_position = 0;
for(auto parameter_it = parameters.begin(); parameter_it != parameters.end(); ++parameter_it) { for(auto parameter_it = parameters.begin(); parameter_it != parameters.end(); ++parameter_it) {
if(parameter_position == current_parameter_position || using_named_parameters) {
auto label = parameter_it->second.get<std::string>("label", ""); auto label = parameter_it->second.get<std::string>("label", "");
auto insert = label; auto insert = label;
auto documentation = parameter_it->second.get<std::string>("documentation", ""); auto documentation = parameter_it->second.get<std::string>("documentation", "");
@ -1541,13 +1575,20 @@ void Source::LanguageProtocolView::setup_autocomplete() {
kind = documentation_pt->get<std::string>("kind", ""); kind = documentation_pt->get<std::string>("kind", "");
} }
} }
if(documentation == "null") // Python erroneously returns "null" when a parameter is not documented
documentation.clear();
if(!using_named_parameters || used_named_parameters.find(insert) == used_named_parameters.end()) {
autocomplete->rows.emplace_back(std::move(label)); autocomplete->rows.emplace_back(std::move(label));
autocomplete_rows.emplace_back(AutocompleteRow{std::move(insert), {}, std::move(documentation), std::move(kind)}); autocomplete_rows.emplace_back(AutocompleteRow{std::move(insert), {}, std::move(documentation), std::move(kind)});
} }
} }
parameter_position++;
}
}
} }
result_processed.set_value(); result_processed.set_value();
}); });
});
} }
else { else {
client->write_request(this, "textDocument/completion", R"("textDocument":{"uri":")" + uri + R"("}, "position": {"line": )" + std::to_string(line_number - 1) + ", \"character\": " + std::to_string(column - 1) + "}", [this, &result_processed](const boost::property_tree::ptree &result, bool error) { client->write_request(this, "textDocument/completion", R"("textDocument":{"uri":")" + uri + R"("}, "position": {"line": )" + std::to_string(line_number - 1) + ", \"character\": " + std::to_string(column - 1) + "}", [this, &result_processed](const boost::property_tree::ptree &result, bool error) {
@ -1562,67 +1603,40 @@ void Source::LanguageProtocolView::setup_autocomplete() {
begin = result.begin(); begin = result.begin();
end = result.end(); end = result.end();
} }
std::string prefix;
{
LockGuard lock(autocomplete->prefix_mutex);
prefix = autocomplete->prefix;
}
for(auto it = begin; it != end; ++it) { for(auto it = begin; it != end; ++it) {
auto label = it->second.get<std::string>("label", ""); auto label = it->second.get<std::string>("label", "");
if(starts_with(label, prefix)) {
auto detail = it->second.get<std::string>("detail", ""); auto detail = it->second.get<std::string>("detail", "");
auto documentation = it->second.get<std::string>("documentation", ""); auto documentation = it->second.get<std::string>("documentation", "");
std::string kind; std::string documentation_kind;
if(documentation.empty()) { if(documentation.empty()) {
auto documentation_pt = it->second.get_child_optional("documentation"); auto documentation_pt = it->second.get_child_optional("documentation");
if(documentation_pt) { if(documentation_pt) {
documentation = documentation_pt->get<std::string>("value", ""); documentation = documentation_pt->get<std::string>("value", "");
kind = documentation_pt->get<std::string>("kind", ""); documentation_kind = documentation_pt->get<std::string>("kind", "");
} }
} }
auto insert = it->second.get<std::string>("insertText", ""); auto insert = it->second.get<std::string>("insertText", "");
if(insert.empty()) if(insert.empty())
insert = it->second.get<std::string>("textEdit.newText", ""); insert = it->second.get<std::string>("textEdit.newText", "");
if(!insert.empty()) { if(insert.empty())
// In case ( is missing in insert but is present in label
if(label.size() > insert.size() && label.back() == ')' && insert.find('(') == std::string::npos) {
auto pos = label.find('(');
if(pos != std::string::npos && pos == insert.size() && pos + 1 < label.size()) {
if(pos + 2 == label.size()) // If no parameters
insert += "()";
else
insert += "(${1:" + label.substr(pos + 1, label.size() - 1 - (pos + 1)) + "})";
}
}
}
else {
insert = label; insert = label;
if(!insert.empty()) {
auto kind = it->second.get<int>("kind", 0); auto kind = it->second.get<int>("kind", 0);
if(kind >= 2 && kind <= 3) { if(kind >= 2 && kind <= 3 && insert.find('(') == std::string::npos) // If kind is method or function, but parentheses are missing
bool found_bracket = false;
for(auto &chr : insert) {
if(chr == '(' || chr == '{') {
found_bracket = true;
break;
}
}
if(!found_bracket)
insert += "(${1:})"; insert += "(${1:})";
}
}
if(!label.empty()) {
std::string prefix;
{
LockGuard lock(autocomplete->prefix_mutex);
prefix = autocomplete->prefix;
}
if(starts_with(label, prefix)) {
autocomplete->rows.emplace_back(std::move(label)); autocomplete->rows.emplace_back(std::move(label));
autocomplete_rows.emplace_back(AutocompleteRow{std::move(insert), std::move(detail), std::move(documentation), std::move(kind)}); autocomplete_rows.emplace_back(AutocompleteRow{std::move(insert), std::move(detail), std::move(documentation), std::move(documentation_kind)});
} }
} }
} }
if(autocomplete_enable_snippets) { if(autocomplete_enable_snippets) {
std::string prefix;
{
LockGuard lock(autocomplete->prefix_mutex);
prefix = autocomplete->prefix;
}
LockGuard lock(snippets_mutex); LockGuard lock(snippets_mutex);
if(snippets) { if(snippets) {
for(auto &snippet : *snippets) { for(auto &snippet : *snippets) {
@ -1675,8 +1689,8 @@ void Source::LanguageProtocolView::setup_autocomplete() {
if(hide_window) { if(hide_window) {
if(autocomplete_show_arguments) { if(autocomplete_show_arguments) {
if(has_named_parameters()) // Do not select named parameters in for instance Python if(auto symbol = get_named_parameter_symbol()) // Do not select named parameters in for instance Python
get_buffer()->insert(CompletionDialog::get()->start_mark->get_iter(), insert); get_buffer()->insert(CompletionDialog::get()->start_mark->get_iter(), insert + *symbol);
else { else {
get_buffer()->insert(CompletionDialog::get()->start_mark->get_iter(), insert); get_buffer()->insert(CompletionDialog::get()->start_mark->get_iter(), insert);
int start_offset = CompletionDialog::get()->start_mark->get_iter().get_offset(); int start_offset = CompletionDialog::get()->start_mark->get_iter().get_offset();
@ -1720,10 +1734,10 @@ void Source::LanguageProtocolView::setup_autocomplete() {
}; };
} }
bool Source::LanguageProtocolView::has_named_parameters() { boost::optional<char> Source::LanguageProtocolView::get_named_parameter_symbol() {
if(language_id == "python") // TODO: add more languages that supports named parameters if(language_id == "python") // TODO: add more languages that supports named parameters
return true; return '=';
return false; return {};
} }
void Source::LanguageProtocolView::update_type_coverage() { void Source::LanguageProtocolView::update_type_coverage() {

4
src/source_language_protocol.hpp

@ -210,7 +210,9 @@ namespace Source {
bool autocomplete_show_arguments = false; bool autocomplete_show_arguments = false;
sigc::connection autocomplete_delayed_show_arguments_connection; sigc::connection autocomplete_delayed_show_arguments_connection;
bool has_named_parameters(); /// If language supports named parameters, returns the symbol separating the named parameter and value,
/// for instance '=' for Python
boost::optional<char> get_named_parameter_symbol();
std::vector<LanguageProtocol::Diagnostic> last_diagnostics; std::vector<LanguageProtocol::Diagnostic> last_diagnostics;

Loading…
Cancel
Save