Browse Source

Added current parameter completion that shows possible arguments. Also fixes some issues with non-interactive completion, optimizations to selection/completion dialogs, and some cleanup."

merge-requests/365/head
eidheim 8 years ago
parent
commit
ae1b183b87
  1. 2
      libclangmm
  2. 1
      src/CMakeLists.txt
  3. 165
      src/autocomplete.cc
  4. 182
      src/autocomplete.h
  5. 2
      src/debug_lldb.h
  6. 50
      src/directories.cc
  7. 28
      src/project.cc
  8. 35
      src/selection_dialog.cc
  9. 11
      src/selection_dialog.h
  10. 35
      src/source.cc
  11. 235
      src/source_clang.cc
  12. 13
      src/source_clang.h
  13. 5
      src/source_diff.cc
  14. 4
      src/source_spellcheck.cc
  15. 108
      src/window.cc

2
libclangmm

@ -1 +1 @@
Subproject commit d978517ec201cb2421168417124de12ff7ef8457
Subproject commit 0ab1692d125b1e3b742a4b43abe37fc2e84e5f2c

1
src/CMakeLists.txt

@ -19,6 +19,7 @@ set(project_files
#Files used both in ../src and ../tests
set(project_shared_files
autocomplete.cc
cmake.cc
compile_commands.cc
ctags.cc

165
src/autocomplete.cc

@ -0,0 +1,165 @@
#include "autocomplete.h"
#include "selection_dialog.h"
Autocomplete::Autocomplete(Gtk::TextView *view, bool &interactive_completion, guint &last_keyval, bool strip_word)
: view(view), interactive_completion(interactive_completion), strip_word(strip_word), state(State::IDLE) {
view->get_buffer()->signal_changed().connect([this, &last_keyval] {
if(CompletionDialog::get() && CompletionDialog::get()->is_visible()) {
cancel_reparse();
return;
}
if(!this->view->has_focus())
return;
if(is_continue_key(last_keyval) && (this->interactive_completion || state != State::IDLE))
run();
else {
stop();
if(is_restart_key(last_keyval) && this->interactive_completion)
run();
}
});
view->get_buffer()->signal_mark_set().connect([this](const Gtk::TextBuffer::iterator &iterator, const Glib::RefPtr<Gtk::TextBuffer::Mark> &mark) {
if(mark->get_name() == "insert")
stop();
});
view->signal_key_release_event().connect([this](GdkEventKey *key) {
if(CompletionDialog::get() && CompletionDialog::get()->is_visible()) {
if(CompletionDialog::get()->on_key_release(key))
return true;
}
return false;
}, false);
view->signal_focus_out_event().connect([this](GdkEventFocus *event) {
stop();
return false;
});
}
void Autocomplete::run() {
if(run_check()) {
if(!is_processing())
return;
if(state == State::CANCELED)
state = State::RESTARTING;
if(state != State::IDLE)
return;
state = State::STARTING;
before_add_rows();
if(thread.joinable())
thread.join();
auto buffer = view->get_buffer()->get_text();
auto iter = view->get_buffer()->get_insert()->get_iter();
auto line_nr = iter.get_line() + 1;
auto column_nr = iter.get_line_index() + 1;
if(strip_word) {
auto pos = iter.get_offset() - 1;
while(pos >= 0 && ((buffer[pos] >= 'a' && buffer[pos] <= 'z') || (buffer[pos] >= 'A' && buffer[pos] <= 'Z') ||
(buffer[pos] >= '0' && buffer[pos] <= '9') || buffer[pos] == '_')) {
buffer.replace(pos, 1, " ");
column_nr--;
pos--;
}
}
thread = std::thread([this, line_nr, column_nr, buffer = std::move(buffer)] {
auto lock = get_parse_lock();
if(!is_processing())
return;
stop_parse();
auto &buffer_raw = const_cast<std::string &>(buffer.raw());
rows.clear();
add_rows(buffer_raw, line_nr, column_nr);
if(is_processing()) {
dispatcher.post([this]() {
after_add_rows();
if(state == State::RESTARTING) {
state = State::IDLE;
reparse();
run();
}
else if(state == State::CANCELED || rows.empty()) {
state = State::IDLE;
reparse();
}
else {
auto start_iter = view->get_buffer()->get_insert()->get_iter();
if(prefix.size() > 0 && !start_iter.backward_chars(prefix.size())) {
state = State::IDLE;
reparse();
return;
}
CompletionDialog::create(view, view->get_buffer()->create_mark(start_iter));
setup_initial_dialog();
setup_dialog();
for(auto &row : rows) {
CompletionDialog::get()->add_row(row.first);
row.first.clear();
}
state = State::IDLE;
view->get_buffer()->begin_user_action();
CompletionDialog::get()->show();
}
});
}
else {
dispatcher.post([this] {
state = State::CANCELED;
on_add_rows_error();
});
}
});
}
if(state != State::IDLE)
cancel_reparse();
}
void Autocomplete::stop() {
if(state == State::STARTING || state == State::RESTARTING)
state = State::CANCELED;
}
void Autocomplete::setup_initial_dialog() {
CompletionDialog::get()->on_hide = [this]() {
view->get_buffer()->end_user_action();
tooltips.hide();
tooltips.clear();
reparse();
};
CompletionDialog::get()->on_changed = [this](unsigned int index, const std::string &text) {
if(index >= rows.size()) {
tooltips.hide();
return;
}
auto tooltip = rows[index].second;
if(tooltip.empty())
tooltips.hide();
else {
tooltips.clear();
auto create_tooltip_buffer = [ this, tooltip = std::move(tooltip) ]() {
auto tooltip_buffer = Gtk::TextBuffer::create(view->get_buffer()->get_tag_table());
tooltip_buffer->insert_with_tag(tooltip_buffer->get_insert()->get_iter(), tooltip, "def:note");
return tooltip_buffer;
};
auto iter = CompletionDialog::get()->start_mark->get_iter();
tooltips.emplace_back(create_tooltip_buffer, view, view->get_buffer()->create_mark(iter), view->get_buffer()->create_mark(iter));
tooltips.show(true);
}
};
}

182
src/autocomplete.h

@ -1,13 +1,13 @@
#ifndef JUCI_AUTOCOMPLETE_H_
#define JUCI_AUTOCOMPLETE_H_
#include "dispatcher.h"
#include "selection_dialog.h"
#include "tooltips.h"
#include <atomic>
#include <thread>
template <class Suggestion>
class Autocomplete {
Gtk::TextView *view;
bool &interactive_completion;
/// Some libraries/utilities, like libclang, require that autocomplete is started at the beginning of a word
bool strip_word;
@ -18,7 +18,7 @@ public:
std::string prefix;
std::mutex prefix_mutex;
std::unordered_map<std::string, std::pair<std::string, std::string>> rows;
std::vector<std::pair<std::string, std::string>> rows;
Tooltips tooltips;
std::atomic<State> state;
@ -35,180 +35,22 @@ public:
std::function<bool(guint last_keyval)> is_restart_key = [](guint) { return false; };
std::function<bool()> run_check = [] { return false; };
std::function<void()> before_get_suggestions = [] {};
std::function<void()> after_get_suggestions = [] {};
std::function<void()> on_get_suggestions_error = [] {};
std::function<void()> before_add_rows = [] {};
std::function<void()> after_add_rows = [] {};
std::function<void()> on_add_rows_error = [] {};
std::function<std::shared_ptr<std::vector<Suggestion>>(std::string &buffer, int line_number, int column)> get_suggestions = [](std::string &, int, int) { return std::make_shared<std::vector<Suggestion>>(); };
std::function<void(Suggestion &)> foreach_suggestion = [](Suggestion &) {};
/// The handler is not run in the main loop.
std::function<void(std::string &buffer, int line_number, int column)> add_rows = [](std::string &, int, int) {};
std::function<void()> setup_dialog = [] {};
Autocomplete(Gtk::TextView *view, bool &interactive_completion, guint &last_keyval, bool strip_word): view(view), strip_word(strip_word), state(State::IDLE) {
view->get_buffer()->signal_changed().connect([this, &interactive_completion, &last_keyval] {
if(CompletionDialog::get() && CompletionDialog::get()->is_visible()) {
cancel_reparse();
return;
}
if(!this->view->has_focus())
return;
if(is_continue_key(last_keyval) && (interactive_completion || state != State::IDLE))
run();
else {
if(state == State::STARTING || state == State::RESTARTING)
state = State::CANCELED;
if(is_restart_key(last_keyval) && interactive_completion)
run();
}
});
view->get_buffer()->signal_mark_set().connect([this](const Gtk::TextBuffer::iterator &iterator, const Glib::RefPtr<Gtk::TextBuffer::Mark> &mark) {
if(mark->get_name() == "insert") {
if(state == State::STARTING || state == State::RESTARTING)
state = State::CANCELED;
}
});
view->signal_key_release_event().connect([this](GdkEventKey *key) {
if(CompletionDialog::get() && CompletionDialog::get()->is_visible()) {
if(CompletionDialog::get()->on_key_release(key))
return true;
}
return false;
}, false);
view->signal_focus_out_event().connect([this](GdkEventFocus *event) {
if(state == State::STARTING || state == State::RESTARTING)
state = State::CANCELED;
return false;
});
}
void run() {
if(run_check()) {
if(!is_processing())
return;
if(state == State::CANCELED)
state = State::RESTARTING;
if(state != State::IDLE)
return;
state = State::STARTING;
before_get_suggestions();
Autocomplete(Gtk::TextView *view, bool &interactive_completion, guint &last_keyval, bool strip_word);
if(thread.joinable())
thread.join();
auto buffer = std::make_shared<Glib::ustring>(view->get_buffer()->get_text());
auto iter = view->get_buffer()->get_insert()->get_iter();
auto line_nr = iter.get_line() + 1;
auto column_nr = iter.get_line_index() + 1;
if(strip_word) {
auto pos = iter.get_offset() - 1;
while(pos >= 0 && (((*buffer)[pos] >= 'a' && (*buffer)[pos] <= 'z') || ((*buffer)[pos] >= 'A' && (*buffer)[pos] <= 'Z') ||
((*buffer)[pos] >= '0' && (*buffer)[pos] <= '9') || (*buffer)[pos] == '_')) {
buffer->replace(pos, 1, " ");
column_nr--;
pos--;
}
}
thread = std::thread([this, line_nr, column_nr, buffer]() {
auto lock = get_parse_lock();
if(!is_processing())
return;
stop_parse();
auto &buffer_raw = const_cast<std::string &>(buffer->raw());
auto suggestions = get_suggestions(buffer_raw, line_nr, column_nr);
if(is_processing()) {
dispatcher.post([this, suggestions] {
if(state == State::CANCELED) {
after_get_suggestions();
reparse();
state = State::IDLE;
}
else if(state == State::RESTARTING) {
after_get_suggestions();
reparse();
state = State::IDLE;
run();
}
else {
auto start_iter = view->get_buffer()->get_insert()->get_iter();
if(prefix.size() > 0 && !start_iter.backward_chars(prefix.size()))
return;
CompletionDialog::create(view, view->get_buffer()->create_mark(start_iter));
rows.clear();
setup_initial_dialog();
setup_dialog();
for(auto &suggestion : *suggestions)
foreach_suggestion(suggestion);
suggestions->clear();
after_get_suggestions();
state = State::IDLE;
if(!rows.empty()) {
view->get_buffer()->begin_user_action();
CompletionDialog::get()->show();
}
else
reparse();
}
});
}
else {
dispatcher.post([this] {
state = State::CANCELED;
on_get_suggestions_error();
});
}
});
}
if(state != State::IDLE)
cancel_reparse();
}
void run();
void stop();
private:
void setup_initial_dialog() {
CompletionDialog::get()->on_hide = [this]() {
view->get_buffer()->end_user_action();
tooltips.hide();
tooltips.clear();
reparse();
};
CompletionDialog::get()->on_changed = [this](const std::string &selected) {
if(selected.empty()) {
tooltips.hide();
return;
}
auto tooltip = std::make_shared<std::string>(rows.at(selected).second);
if(tooltip->empty())
tooltips.hide();
else {
tooltips.clear();
auto create_tooltip_buffer = [this, tooltip]() {
auto tooltip_buffer = Gtk::TextBuffer::create(view->get_buffer()->get_tag_table());
tooltip_buffer->insert_with_tag(tooltip_buffer->get_insert()->get_iter(), *tooltip, "def:note");
return tooltip_buffer;
};
auto iter = CompletionDialog::get()->start_mark->get_iter();
tooltips.emplace_back(create_tooltip_buffer, view, view->get_buffer()->create_mark(iter), view->get_buffer()->create_mark(iter));
tooltips.show(true);
}
};
}
void setup_initial_dialog();
};
#endif // JUCI_AUTOCOMPLETE_H_

2
src/debug_lldb.h

@ -40,7 +40,9 @@ namespace Debug {
}
std::unordered_map<std::string, std::function<void(const lldb::SBProcess &)>> on_start;
/// The handlers are not run in the main loop.
std::unordered_map<std::string, std::function<void(int exit_status)>> on_exit;
/// The handlers are not run in the main loop.
std::unordered_map<std::string, std::function<void(const lldb::SBEvent &)>> on_event;
std::mutex mutex;

50
src/directories.cc

@ -163,10 +163,9 @@ Directories::Directories() : Gtk::ListViewText(1) {
if(menu_popup_row_path.empty())
return;
EntryBox::get().clear();
auto source_path=std::make_shared<boost::filesystem::path>(menu_popup_row_path);
EntryBox::get().entries.emplace_back("", [this, source_path](const std::string &content) {
bool is_directory=boost::filesystem::is_directory(*source_path);
auto target_path = (is_directory ? *source_path : source_path->parent_path())/content;
EntryBox::get().entries.emplace_back("", [this, source_path=menu_popup_row_path](const std::string &content) {
bool is_directory=boost::filesystem::is_directory(source_path);
auto target_path = (is_directory ? source_path : source_path.parent_path())/content;
if(!boost::filesystem::exists(target_path)) {
if(filesystem::write(target_path, "")) {
update();
@ -206,10 +205,9 @@ Directories::Directories() : Gtk::ListViewText(1) {
if(menu_popup_row_path.empty())
return;
EntryBox::get().clear();
auto source_path=std::make_shared<boost::filesystem::path>(menu_popup_row_path);
EntryBox::get().entries.emplace_back("", [this, source_path](const std::string &content) {
bool is_directory=boost::filesystem::is_directory(*source_path);
auto target_path = (is_directory ? *source_path : source_path->parent_path())/content;
EntryBox::get().entries.emplace_back("", [this, source_path=menu_popup_row_path](const std::string &content) {
bool is_directory=boost::filesystem::is_directory(source_path);
auto target_path = (is_directory ? source_path : source_path.parent_path())/content;
if(!boost::filesystem::exists(target_path)) {
boost::system::error_code ec;
boost::filesystem::create_directory(target_path, ec);
@ -252,11 +250,10 @@ Directories::Directories() : Gtk::ListViewText(1) {
if(menu_popup_row_path.empty())
return;
EntryBox::get().clear();
auto source_path=std::make_shared<boost::filesystem::path>(menu_popup_row_path);
EntryBox::get().entries.emplace_back(menu_popup_row_path.filename().string(), [this, source_path](const std::string &content){
bool is_directory=boost::filesystem::is_directory(*source_path);
EntryBox::get().entries.emplace_back(menu_popup_row_path.filename().string(), [this, source_path=menu_popup_row_path](const std::string &content){
bool is_directory=boost::filesystem::is_directory(source_path);
auto target_path=source_path->parent_path()/content;
auto target_path=source_path.parent_path()/content;
if(boost::filesystem::exists(target_path)) {
Terminal::get().print("Error: could not rename to "+target_path.string()+": already exists\n", true);
@ -264,12 +261,12 @@ Directories::Directories() : Gtk::ListViewText(1) {
}
if(is_directory)
this->remove_path(*source_path);
this->remove_path(source_path);
boost::system::error_code ec;
boost::filesystem::rename(*source_path, target_path, ec);
boost::filesystem::rename(source_path, target_path, ec);
if(ec) {
Terminal::get().print("Error: could not rename "+source_path->string()+": "+ec.message()+'\n', true);
Terminal::get().print("Error: could not rename "+source_path.string()+": "+ec.message()+'\n', true);
return;
}
update();
@ -279,9 +276,9 @@ Directories::Directories() : Gtk::ListViewText(1) {
for(size_t c=0;c<Notebook::get().size();c++) {
auto view=Notebook::get().get_view(c);
if(is_directory) {
if(filesystem::file_in_path(view->file_path, *source_path)) {
if(filesystem::file_in_path(view->file_path, source_path)) {
auto file_it=view->file_path.begin();
for(auto source_it=source_path->begin();source_it!=source_path->end();source_it++)
for(auto source_it=source_path.begin();source_it!=source_path.end();source_it++)
file_it++;
auto new_file_path=target_path;
for(;file_it!=view->file_path.end();file_it++)
@ -289,7 +286,7 @@ Directories::Directories() : Gtk::ListViewText(1) {
view->rename(new_file_path);
}
}
else if(view->file_path==*source_path) {
else if(view->file_path==source_path) {
view->rename(target_path);
std::string old_language_id;
@ -651,25 +648,24 @@ void Directories::remove_path(const boost::filesystem::path &dir_path) {
}
}
void Directories::colorize_path(const boost::filesystem::path &dir_path_, bool include_parent_paths) {
auto it=directories.find(dir_path_.string());
void Directories::colorize_path(const boost::filesystem::path &dir_path, bool include_parent_paths) {
auto it=directories.find(dir_path.string());
if(it==directories.end())
return;
if(it!=directories.end() && it->second.repository) {
auto dir_path=std::make_shared<boost::filesystem::path>(dir_path_);
auto repository=it->second.repository;
std::thread git_status_thread([this, dir_path, repository, include_parent_paths] {
auto status=std::make_shared<Git::Repository::Status>();
Git::Repository::Status status;
try {
*status=repository->get_status();
status=repository->get_status();
}
catch(const std::exception &e) {
Terminal::get().async_print(std::string("Error (git): ")+e.what()+'\n', true);
}
dispatcher.post([this, dir_path, include_parent_paths, status] {
auto it=directories.find(dir_path->string());
dispatcher.post([this, dir_path=std::move(dir_path), include_parent_paths, status=std::move(status)] {
auto it=directories.find(dir_path.string());
if(it==directories.end())
return;
@ -698,9 +694,9 @@ void Directories::colorize_path(const boost::filesystem::path &dir_path_, bool i
auto name=Glib::Markup::escape_text(child.get_value(column_record.name));
auto path=child.get_value(column_record.path);
Gdk::RGBA *color;
if(status->modified.find(path.generic_string())!=status->modified.end())
if(status.modified.find(path.generic_string())!=status.modified.end())
color=&yellow;
else if(status->added.find(path.generic_string())!=status->added.end())
else if(status.added.find(path.generic_string())!=status.added.end())
color=&green;
else
color=&normal_color;

28
src/project.cc

@ -549,7 +549,7 @@ void Project::Clang::debug_backtrace() {
}
else
SelectionDialog::create(true, true);
auto rows=std::make_shared<std::unordered_map<std::string, Debug::LLDB::Frame> >();
std::vector<Debug::LLDB::Frame> rows;
if(backtrace.size()==0) {
Info::get().print("No backtrace found");
return;
@ -569,7 +569,7 @@ void Project::Clang::debug_backtrace() {
auto file_path=frame.file_path.filename().string();
row+=":<b>"+Glib::Markup::escape_text(file_path)+":"+std::to_string(frame.line_nr)+"</b> - "+Glib::Markup::escape_text(frame.function_name);
}
(*rows)[row]=frame;
rows.emplace_back(frame);
SelectionDialog::get()->add_row(row);
if(!cursor_set && view && frame.file_path==view->file_path) {
SelectionDialog::get()->set_cursor_at_last_row();
@ -577,8 +577,10 @@ void Project::Clang::debug_backtrace() {
}
}
SelectionDialog::get()->on_select=[this, rows](const std::string& selected, bool hide_window) {
auto frame=rows->at(selected);
SelectionDialog::get()->on_select=[this, rows=std::move(rows)](unsigned int index, const std::string &text, bool hide_window) {
if(index>=rows.size())
return;
auto frame=rows[index];
if(!frame.file_path.empty()) {
Notebook::get().open(frame.file_path);
if(auto view=Notebook::get().get_current_view()) {
@ -607,7 +609,7 @@ void Project::Clang::debug_show_variables() {
}
else
SelectionDialog::create(true, true);
auto rows=std::make_shared<std::unordered_map<std::string, Debug::LLDB::Variable> >();
auto rows=std::make_shared<std::vector<Debug::LLDB::Variable>>();
if(variables.size()==0) {
Info::get().print("No variables found");
return;
@ -616,12 +618,14 @@ void Project::Clang::debug_show_variables() {
for(auto &variable: variables) {
std::string row="#"+std::to_string(variable.thread_index_id)+":#"+std::to_string(variable.frame_index)+":"+variable.file_path.filename().string()+":"+std::to_string(variable.line_nr)+" - <b>"+Glib::Markup::escape_text(variable.name)+"</b>";
(*rows)[row]=variable;
rows->emplace_back(variable);
SelectionDialog::get()->add_row(row);
}
SelectionDialog::get()->on_select=[this, rows](const std::string& selected, bool hide_window) {
auto variable=rows->at(selected);
SelectionDialog::get()->on_select=[this, rows](unsigned int index, const std::string &text, bool hide_window) {
if(index>=rows->size())
return;
auto variable=(*rows)[index];
Debug::LLDB::get().select_frame(variable.frame_index, variable.thread_index_id);
if(!variable.file_path.empty()) {
Notebook::get().open(variable.file_path);
@ -639,14 +643,14 @@ void Project::Clang::debug_show_variables() {
debug_variable_tooltips.clear();
};
SelectionDialog::get()->on_changed=[this, rows, view, iter](const std::string &selected) {
if(selected.empty()) {
SelectionDialog::get()->on_changed=[this, rows, view, iter](unsigned int index, const std::string &text) {
if(index>=rows->size()) {
debug_variable_tooltips.hide();
return;
}
debug_variable_tooltips.clear();
auto create_tooltip_buffer=[this, rows, view, selected]() {
auto variable=rows->at(selected);
auto create_tooltip_buffer=[this, rows, view, index]() {
auto variable=(*rows)[index];
auto tooltip_buffer=view?Gtk::TextBuffer::create(view->get_buffer()->get_tag_table()):Gtk::TextBuffer::create();
Glib::ustring value=variable.value;

35
src/selection_dialog.cc

@ -22,11 +22,13 @@ SelectionDialogBase::ListViewText::ListViewText(bool use_markup) : Gtk::TreeView
void SelectionDialogBase::ListViewText::append(const std::string& value) {
auto new_row=list_store->append();
new_row->set_value(column_record.text, value);
new_row->set_value(column_record.index, size++);
}
void SelectionDialogBase::ListViewText::clear() {
unset_model();
list_store.reset();
size=0;
}
SelectionDialogBase::SelectionDialogBase(Gtk::TextView *text_view, Glib::RefPtr<Gtk::TextBuffer::Mark> start_mark, bool show_search_entry, bool use_markup):
@ -116,14 +118,18 @@ void SelectionDialogBase::cursor_changed() {
if(!is_visible())
return;
auto it=list_view_text.get_selection()->get_selected();
std::string row;
unsigned int index=static_cast<unsigned int>(-1);
if(it)
it->get_value(0, row);
if(last_row==row)
index=it->get_value(list_view_text.column_record.index);
if(last_index==index)
return;
if(on_changed)
on_changed(row);
last_row=row;
if(on_changed) {
std::string text;
if(it)
text=it->get_value(list_view_text.column_record.text);
on_changed(index, text);
}
last_index=index;
}
void SelectionDialogBase::add_row(const std::string& row) {
list_view_text.append(row);
@ -165,12 +171,13 @@ void SelectionDialogBase::hide() {
if(on_hide)
on_hide();
list_view_text.clear();
last_index=static_cast<unsigned int>(-1);
}
std::unique_ptr<SelectionDialog> SelectionDialog::instance;
SelectionDialog::SelectionDialog(Gtk::TextView *text_view, Glib::RefPtr<Gtk::TextBuffer::Mark> start_mark, bool show_search_entry, bool use_markup) : SelectionDialogBase(text_view, start_mark, show_search_entry, use_markup) {
std::shared_ptr<std::string> search_key(new std::string());
auto search_key=std::make_shared<std::string>();
auto filter_model=Gtk::TreeModelFilter::create(list_view_text.get_model());
filter_model->set_visible_func([this, search_key](const Gtk::TreeModel::const_iterator& iter){
@ -211,10 +218,10 @@ SelectionDialog::SelectionDialog(Gtk::TextView *text_view, Glib::RefPtr<Gtk::Tex
auto activate=[this](){
auto it=list_view_text.get_selection()->get_selected();
if(on_select && it) {
std::string row;
it->get_value(0, row);
auto index=it->get_value(list_view_text.column_record.index);
auto text=it->get_value(list_view_text.column_record.text);
hide();
on_select(row, true);
on_select(index, text, true);
}
else
hide();
@ -301,7 +308,7 @@ std::unique_ptr<CompletionDialog> CompletionDialog::instance;
CompletionDialog::CompletionDialog(Gtk::TextView *text_view, Glib::RefPtr<Gtk::TextBuffer::Mark> start_mark) : SelectionDialogBase(text_view, start_mark, false, false) {
show_offset=text_view->get_buffer()->get_insert()->get_iter().get_offset();
std::shared_ptr<std::string> search_key(new std::string());
auto search_key=std::make_shared<std::string>();
auto filter_model=Gtk::TreeModelFilter::create(list_view_text.get_model());
if(show_offset==start_mark->get_iter().get_offset()) {
filter_model->set_visible_func([this, search_key](const Gtk::TreeModel::const_iterator& iter){
@ -347,9 +354,9 @@ void CompletionDialog::select(bool hide_window) {
auto it=list_view_text.get_selection()->get_selected();
if(on_select && it) {
std::string row;
it->get_value(0, row);
on_select(row, hide_window);
auto index=it->get_value(list_view_text.column_record.index);
auto text=it->get_value(list_view_text.column_record.text);
on_select(index, text, hide_window);
}
if(hide_window)
hide();

11
src/selection_dialog.h

@ -10,18 +10,21 @@ class SelectionDialogBase {
public:
ColumnRecord() {
add(text);
add(index);
}
Gtk::TreeModelColumn<std::string> text;
Gtk::TreeModelColumn<unsigned int> index;
};
public:
bool use_markup;
ColumnRecord column_record;
ListViewText(bool use_markup);
void append(const std::string& value);
void clear();
private:
Glib::RefPtr<Gtk::ListStore> list_store;
ColumnRecord column_record;
Gtk::CellRendererText cell_renderer;
unsigned int size=0;
};
class SearchEntry : public Gtk::Entry {
@ -43,8 +46,8 @@ public:
std::function<void()> on_show;
std::function<void()> on_hide;
std::function<void(const std::string& selected, bool hide_window)> on_select;
std::function<void(const std::string &selected)> on_changed;
std::function<void(unsigned int index, const std::string &text, bool hide_window)> on_select;
std::function<void(unsigned int index, const std::string &text)> on_changed;
Glib::RefPtr<Gtk::TextBuffer::Mark> start_mark;
protected:
@ -58,7 +61,7 @@ protected:
SearchEntry search_entry;
bool show_search_entry;
std::string last_row;
unsigned int last_index=static_cast<unsigned int>(-1);
};
class SelectionDialog : public SelectionDialogBase {

35
src/source.cc

@ -422,27 +422,27 @@ Source::View::View(const boost::filesystem::path &file_path, Glib::RefPtr<Gsv::L
}
set_tab_char_and_size(tab_char, tab_size);
std::shared_ptr<std::string> comment_characters;
std::string comment_characters;
if(is_bracket_language)
comment_characters=std::make_shared<std::string>("//");
comment_characters="//";
else if(language) {
if(language->get_id()=="cmake" || language->get_id()=="makefile" || language->get_id()=="python" ||
language->get_id()=="python3" || language->get_id()=="sh" || language->get_id()=="perl" ||
language->get_id()=="ruby" || language->get_id()=="r" || language->get_id()=="asm" ||
language->get_id()=="automake")
comment_characters=std::make_shared<std::string>("#");
comment_characters="#";
else if(language->get_id()=="latex" || language->get_id()=="matlab" || language->get_id()=="octave" ||
language->get_id()=="bibtex")
comment_characters=std::make_shared<std::string>("%");
comment_characters="%";
else if(language->get_id()=="fortran")
comment_characters=std::make_shared<std::string>("!");
comment_characters="!";
else if(language->get_id()=="pascal")
comment_characters=std::make_shared<std::string>("//");
comment_characters="//";
else if(language->get_id()=="lua")
comment_characters=std::make_shared<std::string>("--");
comment_characters="--";
}
if(comment_characters) {
toggle_comments=[this, comment_characters] {
if(!comment_characters.empty()) {
toggle_comments=[this, comment_characters=std::move(comment_characters)] {
std::vector<int> lines;
Gtk::TextIter selection_start, selection_end;
get_buffer()->get_selection_bounds(selection_start, selection_end);
@ -470,12 +470,12 @@ Source::View::View(const boost::filesystem::path &file_path, Glib::RefPtr<Gsv::L
else {
lines.emplace_back(line);
line_added=true;
for(size_t c=0;c<comment_characters->size();++c) {
for(size_t c=0;c<comment_characters.size();++c) {
if(iter.ends_line()) {
break;
}
else if(*iter==static_cast<unsigned int>((*comment_characters)[c])) {
if(c<comment_characters->size()-1) {
else if(*iter==static_cast<unsigned int>(comment_characters[c])) {
if(c<comment_characters.size()-1) {
iter.forward_char();
continue;
}
@ -503,14 +503,14 @@ Source::View::View(const boost::filesystem::path &file_path, Glib::RefPtr<Gsv::L
}
}
if(lines.size()) {
auto comment_characters_and_space=*comment_characters+' ';
auto comment_characters_and_space=comment_characters+' ';
get_buffer()->begin_user_action();
for(auto &line: lines) {
auto iter=get_buffer()->get_iter_at_line(line);
iter.forward_chars(min_indentation);
if(lines_commented) {
auto end_iter=iter;
end_iter.forward_chars(comment_characters->size()+static_cast<int>(extra_spaces));
end_iter.forward_chars(comment_characters.size()+static_cast<int>(extra_spaces));
while(*iter==' ' || *iter=='\t') {
iter.forward_char();
end_iter.forward_char();
@ -1772,6 +1772,8 @@ bool Source::View::on_key_press_event_basic(GdkEventKey* key) {
break;
}
}
if(get_buffer()->size()==1) // special case due to backward_char(s) returning false when moving to start of buffer
do_smart_backspace=false;
if(do_smart_backspace) {
auto previous_line_end_iter=iter;
if(previous_line_end_iter.backward_chars(line.size()+1)) {
@ -2367,10 +2369,11 @@ bool Source::View::on_key_press_event_smart_inserts(GdkEventKey *key) {
// Insert ()
if(key->keyval==GDK_KEY_parenleft && allow_insertion(iter)) {
if(symbol_count(iter, '(', ')')==0) {
get_buffer()->insert_at_cursor("()");
auto iter=get_buffer()->get_insert()->get_iter();
get_buffer()->insert_at_cursor(")");
iter=get_buffer()->get_insert()->get_iter();
iter.backward_char();
get_buffer()->place_cursor(iter);
get_buffer()->insert_at_cursor("(");
scroll_to(get_buffer()->get_insert());
return true;
}

235
src/source_clang.cc

@ -465,9 +465,50 @@ Source::ClangViewAutocomplete::ClangViewAutocomplete(const boost::filesystem::pa
parse_process_state=ParseProcessState::IDLE;
};
autocomplete.is_continue_key=[this](guint keyval) {
// Activate argument completions
get_buffer()->signal_changed().connect([this] {
if(!interactive_completion)
return;
if(CompletionDialog::get() && CompletionDialog::get()->is_visible())
return;
if(!has_focus())
return;
if(show_arguments)
autocomplete.stop();
show_arguments=false;
delayed_show_arguments_connection.disconnect();
delayed_show_arguments_connection=Glib::signal_timeout().connect([this]() {
if(get_buffer()->get_has_selection())
return false;
if(CompletionDialog::get() && CompletionDialog::get()->is_visible())
return false;
if(!has_focus())
return false;
if(is_possible_parameter()) {
autocomplete.stop();
autocomplete.run();
}
return false;
}, 500);
}, false);
// Remove argument completions
signal_key_press_event().connect([this](GdkEventKey *key) {
if(show_arguments && CompletionDialog::get() && CompletionDialog::get()->is_visible() &&
key->keyval != GDK_KEY_Down && key->keyval != GDK_KEY_Up &&
key->keyval != GDK_KEY_Return && key->keyval != GDK_KEY_KP_Enter &&
key->keyval != GDK_KEY_ISO_Left_Tab && key->keyval != GDK_KEY_Tab &&
(key->keyval < GDK_KEY_Shift_L || key->keyval > GDK_KEY_Hyper_R)) {
get_buffer()->erase(CompletionDialog::get()->start_mark->get_iter(), get_buffer()->get_insert()->get_iter());
CompletionDialog::get()->hide();
}
return false;
}, false);
autocomplete.is_continue_key=[](guint keyval) {
if((keyval>='0' && keyval<='9') || (keyval>='a' && keyval<='z') || (keyval>='A' && keyval<='Z') || keyval=='_')
return true;
return false;
};
@ -481,8 +522,12 @@ Source::ClangViewAutocomplete::ClangViewAutocomplete(const boost::filesystem::pa
autocomplete.run_check=[this]() {
auto iter=get_buffer()->get_insert()->get_iter();
if(iter.backward_char() && iter.backward_char() && !is_code_iter(iter))
iter.backward_char();
if(!is_code_iter(iter))
return false;
show_arguments=false;
std::string line=" "+get_line_before();
const static std::regex in_specified_namespace("^.*[a-zA-Z0-9_\\)\\]\\>](->|\\.|::)([a-zA-Z0-9_]*)$");
const static std::regex within_namespace("^.*[^a-zA-Z0-9_]+([a-zA-Z0-9_]{3,})$");
@ -503,38 +548,54 @@ Source::ClangViewAutocomplete::ClangViewAutocomplete(const boost::filesystem::pa
if(autocomplete.prefix.size()==0 || autocomplete.prefix[0]<'0' || autocomplete.prefix[0]>'9')
return true;
}
else if(is_possible_parameter()) {
show_arguments=true;
std::unique_lock<std::mutex> lock(autocomplete.prefix_mutex);
autocomplete.prefix="";
return true;
}
else if(!interactive_completion) {
auto end_iter=get_buffer()->get_insert()->get_iter();
auto iter=end_iter;
while(iter.backward_char() && autocomplete.is_continue_key(*iter)) {}
if(iter!=end_iter)
iter.forward_char();
std::unique_lock<std::mutex> lock(autocomplete.prefix_mutex);
autocomplete.prefix=get_buffer()->get_text(iter, end_iter);
return true;
}
return false;
};
autocomplete.before_get_suggestions=[this] {
autocomplete.before_add_rows=[this] {
status_state="autocomplete...";
if(update_status_state)
update_status_state(this);
};
autocomplete.after_get_suggestions=[this] {
autocomplete.after_add_rows=[this] {
status_state="";
if(update_status_state)
update_status_state(this);
};
autocomplete.on_get_suggestions_error=[this] {
autocomplete.on_add_rows_error=[this] {
Terminal::get().print("Error: autocomplete failed, reparsing "+this->file_path.string()+"\n", true);
full_reparse();
};
autocomplete.get_suggestions=[this](std::string &buffer, int line_number, int column) {
auto suggestions=std::make_shared<std::vector<Suggestion>>();
autocomplete.add_rows=[this](std::string &buffer, int line_number, int column) {
if(this->language && (this->language->get_id()=="chdr" || this->language->get_id()=="cpphdr"))
clangmm::remove_include_guard(buffer);
auto results=clang_tu->get_code_completions(buffer, line_number, column);
if(results.cx_results==nullptr) {
auto expected=ParseState::PROCESSING;
parse_state.compare_exchange_strong(expected, ParseState::RESTARTING);
return suggestions;
return;
}
if(autocomplete.state==Autocomplete<Suggestion>::State::STARTING) {
if(autocomplete.state==Autocomplete::State::STARTING) {
std::string prefix_copy;
{
std::lock_guard<std::mutex> lock(autocomplete.prefix_mutex);
@ -544,42 +605,60 @@ Source::ClangViewAutocomplete::ClangViewAutocomplete(const boost::filesystem::pa
for (unsigned i = 0; i < results.size(); ++i) {
auto result=results.get(i);
if(result.available()) {
auto chunks=result.get_chunks();
bool match=false;
for(auto &chunk: chunks) {
if(chunk.kind!=clangmm::CompletionChunk_ResultType && chunk.kind!=clangmm::CompletionChunk_Informative) {
if(chunk.chunk.size()>=prefix_copy.size() && chunk.chunk.compare(0, prefix_copy.size(), prefix_copy)==0)
match=true;
break;
std::string text;
if(show_arguments) {
class Recursive {
public:
static void f(const clangmm::CompletionString &completion_string, std::string &text) {
for(unsigned i = 0; i < completion_string.get_num_chunks(); ++i) {
auto kind=static_cast<clangmm::CompletionChunkKind>(clang_getCompletionChunkKind(completion_string.cx_completion_sting, i));
if(kind==clangmm::CompletionChunk_Optional)
f(clangmm::CompletionString(clang_getCompletionChunkCompletionString(completion_string.cx_completion_sting, i)), text);
else if(kind==clangmm::CompletionChunk_CurrentParameter) {
auto chunk_cstr=clangmm::String(clang_getCompletionChunkText(completion_string.cx_completion_sting, i));
text+=chunk_cstr.c_str;
}
}
}
};
Recursive::f(result, text);
if(!text.empty()) {
bool already_added=false;
for(auto &pair: autocomplete.rows) {
if(pair.first==text) {
already_added=true;
break;
}
}
if(!already_added)
autocomplete.rows.emplace_back(std::move(text), result.get_brief_comment());
}
}
if(match) {
suggestions->emplace_back(std::move(chunks));
suggestions->back().brief_comments=result.get_brief_comments();
else {
std::string return_text;
bool match=false;
for(unsigned i = 0; i < result.get_num_chunks(); ++i) {
auto kind=static_cast<clangmm::CompletionChunkKind>(clang_getCompletionChunkKind(result.cx_completion_sting, i));
if(kind!=clangmm::CompletionChunk_Informative) {
auto chunk_cstr=clangmm::String(clang_getCompletionChunkText(result.cx_completion_sting, i));
if(kind==clangmm::CompletionChunk_TypedText) {
if(strlen(chunk_cstr.c_str)>=prefix_copy.size() && prefix_copy.compare(0, prefix_copy.size(), chunk_cstr.c_str, prefix_copy.size())==0)
match = true;
else
break;
}
if(kind==clangmm::CompletionChunk_ResultType)
return_text=std::string("")+chunk_cstr.c_str;
else
text+=chunk_cstr.c_str;
}
}
if(match && !text.empty())
autocomplete.rows.emplace_back(std::move(text), result.get_brief_comment());
}
}
}
}
return suggestions;
};
autocomplete.foreach_suggestion=[this](Suggestion &suggestion) {
std::string row;
std::string return_value;
for(auto &chunk : suggestion.chunks) {
if(chunk.kind == clangmm::CompletionChunk_ResultType)
return_value = chunk.chunk;
else if(chunk.kind != clangmm::CompletionChunk_Informative)
row += chunk.chunk;
}
suggestion.chunks.clear();
if(!row.empty()) {
auto row_insert_on_selection = row;
if(!return_value.empty())
row += "" + return_value;
autocomplete.rows[row] = std::pair<std::string, std::string>(std::move(row_insert_on_selection), std::move(suggestion.brief_comments));
CompletionDialog::get()->add_row(row);
}
};
autocomplete.setup_dialog=[this] {
@ -587,8 +666,15 @@ Source::ClangViewAutocomplete::ClangViewAutocomplete(const boost::filesystem::pa
hide_tooltips();
};
CompletionDialog::get()->on_select=[this](const std::string &selected, bool hide_window) {
auto row = autocomplete.rows.at(selected).first;
CompletionDialog::get()->on_select=[this](unsigned int index, const std::string &text, bool hide_window) {
if(index>=autocomplete.rows.size())
return;
std::string row;
auto pos=text.find("");
if(pos!=std::string::npos)
row=text.substr(0, pos);
else
row=text;
//erase existing variable or function before insert iter
get_buffer()->erase(CompletionDialog::get()->start_mark->get_iter(), get_buffer()->get_insert()->get_iter());
//do not insert template argument or function parameters if they already exist
@ -607,47 +693,73 @@ Source::ClangViewAutocomplete::ClangViewAutocomplete(const boost::filesystem::pa
get_buffer()->insert(CompletionDialog::get()->start_mark->get_iter(), row);
//if selection is finalized, select text inside template arguments or function parameters
if(hide_window) {
auto para_pos=row.find('(');
auto angle_pos=row.find('<');
size_t start_pos=std::string::npos;
size_t end_pos=std::string::npos;
if(angle_pos<para_pos) {
start_pos=angle_pos;
end_pos=row.find('>');
if(show_arguments) {
start_pos=0;
end_pos=row.size();
}
else if(para_pos!=std::string::npos) {
start_pos=para_pos;
end_pos=row.size()-1;
}
if(start_pos==std::string::npos || end_pos==std::string::npos) {
if((start_pos=row.find('\"'))!=std::string::npos)
end_pos=row.find('\"', start_pos+1);
else {
auto para_pos=row.find('(');
auto angle_pos=row.find('<');
if(angle_pos<para_pos) {
start_pos=angle_pos+1;
end_pos=row.find('>');
}
else if(para_pos!=std::string::npos) {
start_pos=para_pos+1;
end_pos=row.size()-1;
}
if(start_pos==std::string::npos || end_pos==std::string::npos) {
if((start_pos=row.find('\"'))!=std::string::npos) {
end_pos=row.find('\"', start_pos+1);
++start_pos;
}
}
}
if(start_pos==std::string::npos || end_pos==std::string::npos) {
if((start_pos=row.find(' '))!=std::string::npos) {
if((start_pos=row.find("expression", start_pos+1))!=std::string::npos) {
end_pos=start_pos+10;
start_pos--;
std::vector<std::string> parameters={"expression", "arguments", "identifier", "type name", "qualifier::name", "macro", "condition"};
for(auto &parameter: parameters) {
if((start_pos=row.find(parameter, start_pos+1))!=std::string::npos) {
end_pos=start_pos+parameter.size();
break;
}
}
}
}
if(start_pos!=std::string::npos && end_pos!=std::string::npos) {
auto start_offset=CompletionDialog::get()->start_mark->get_iter().get_offset()+start_pos+1;
auto end_offset=CompletionDialog::get()->start_mark->get_iter().get_offset()+end_pos;
if(start_offset!=end_offset)
int start_offset=CompletionDialog::get()->start_mark->get_iter().get_offset()+start_pos;
int end_offset=CompletionDialog::get()->start_mark->get_iter().get_offset()+end_pos;
auto size=get_buffer()->size();
if(start_offset!=end_offset && start_offset<size && end_offset<size)
get_buffer()->select_range(get_buffer()->get_iter_at_offset(start_offset), get_buffer()->get_iter_at_offset(end_offset));
}
else {
//new autocomplete after for instance when selecting "std::"
auto iter=get_buffer()->get_insert()->get_iter();
if(iter.backward_char() && *iter==':')
if(iter.backward_char() && *iter==':') {
autocomplete.run();
return;
}
}
}
};
};
}
bool Source::ClangViewAutocomplete::is_possible_parameter() {
auto iter=get_buffer()->get_insert()->get_iter();
if(iter.backward_char() && (!interactive_completion || last_keyval=='(' || last_keyval==',' || last_keyval==' ' ||
last_keyval==GDK_KEY_Return || last_keyval==GDK_KEY_KP_Enter)) {
while((*iter==' ' || *iter=='\t' || *iter=='\n' || *iter=='\r') && iter.backward_char()) {}
if(*iter=='(' || *iter==',')
return true;
}
return false;
}
const std::unordered_map<std::string, std::string> &Source::ClangViewAutocomplete::autocomplete_manipulators_map() {
//TODO: feel free to add more
static std::unordered_map<std::string, std::string> map={
@ -1552,7 +1664,7 @@ void Source::ClangView::full_reparse() {
return;
}
}
autocomplete.state=Autocomplete<ClangViewAutocomplete::Suggestion>::State::IDLE;
autocomplete.state=Autocomplete::State::IDLE;
soft_reparse_needed=false;
full_reparse_running=true;
if(full_reparse_thread.joinable())
@ -1571,6 +1683,7 @@ void Source::ClangView::full_reparse() {
}
void Source::ClangView::async_delete() {
delayed_show_arguments_connection.disconnect();
delayed_tag_similar_identifiers_connection.disconnect();
parsing_in_progress->cancel("canceled, freeing resources in the background");

13
src/source_clang.h

@ -58,18 +58,13 @@ namespace Source {
class ClangViewAutocomplete : public virtual ClangViewParse {
public:
class Suggestion {
public:
explicit Suggestion(std::vector<clangmm::CompletionChunk> &&chunks) :
chunks(chunks) { }
std::vector<clangmm::CompletionChunk> chunks;
std::string brief_comments;
};
ClangViewAutocomplete(const boost::filesystem::path &file_path, Glib::RefPtr<Gsv::Language> language);
protected:
Autocomplete<Suggestion> autocomplete;
Autocomplete autocomplete;
sigc::connection delayed_show_arguments_connection;
private:
bool is_possible_parameter();
bool show_arguments;
const std::unordered_map<std::string, std::string> &autocomplete_manipulators_map();
};

5
src/source_diff.cc

@ -259,15 +259,14 @@ void Source::DiffView::configure() {
}
}
catch(const std::exception &e) {
auto e_what=std::make_shared<std::string>(e.what());
dispatcher.post([this, e_what] {
dispatcher.post([this, e_what=e.what()] {
get_buffer()->remove_tag(renderer->tag_added, get_buffer()->begin(), get_buffer()->end());
get_buffer()->remove_tag(renderer->tag_modified, get_buffer()->begin(), get_buffer()->end());
get_buffer()->remove_tag(renderer->tag_removed, get_buffer()->begin(), get_buffer()->end());
get_buffer()->remove_tag(renderer->tag_removed_below, get_buffer()->begin(), get_buffer()->end());
get_buffer()->remove_tag(renderer->tag_removed_above, get_buffer()->begin(), get_buffer()->end());
renderer->queue_draw();
Terminal::get().print("Error (git): "+*e_what+'\n', true);
Terminal::get().print(std::string("Error (git): ")+e_what+'\n', true);
});
}
});

4
src/source_spellcheck.cc

@ -168,10 +168,10 @@ Source::SpellCheckView::SpellCheckView() : Gsv::View() {
return false;
for(auto &suggestion: suggestions)
SelectionDialog::get()->add_row(suggestion);
SelectionDialog::get()->on_select=[this, word](const std::string& selected, bool hide_window) {
SelectionDialog::get()->on_select=[this, word](unsigned int index, const std::string &text, bool hide_window) {
get_buffer()->begin_user_action();
get_buffer()->erase(word.first, word.second);
get_buffer()->insert(get_buffer()->get_insert()->get_iter(), selected);
get_buffer()->insert(get_buffer()->get_insert()->get_iter(), text);
get_buffer()->end_user_action();
};
hide_tooltips();

108
src/window.cc

@ -628,21 +628,23 @@ void Window::set_menu_actions() {
else
SelectionDialog::create(true, true);
auto rows=std::make_shared<std::unordered_map<std::string, Source::Offset> >();
std::vector<Source::Offset> rows;
std::string line;
while(std::getline(*stream, line)) {
auto location=Ctags::get_location(line, true);
std::string row=location.file_path.string()+":"+std::to_string(location.line+1)+": "+location.source;
(*rows)[row]=Source::Offset(location.line, location.index, location.file_path);
rows.emplace_back(Source::Offset(location.line, location.index, location.file_path));
SelectionDialog::get()->add_row(row);
}
if(rows->size()==0)
if(rows.size()==0)
return;
SelectionDialog::get()->on_select=[this, rows, path](const std::string &selected, bool hide_window) {
auto offset=rows->at(selected);
SelectionDialog::get()->on_select=[this, rows=std::move(rows), path=std::move(path)](unsigned int index, const std::string &text, bool hide_window) {
if(index>=rows.size())
return;
auto offset=rows[index];
auto full_path=path/offset.file_path;
if(!boost::filesystem::is_regular_file(full_path))
return;
@ -693,7 +695,7 @@ void Window::set_menu_actions() {
for(auto view: Notebook::get().get_views())
buffer_paths.emplace(view->file_path.string());
auto paths=std::make_shared<std::unordered_map<std::string, boost::filesystem::path>>();
std::vector<boost::filesystem::path> paths;
// populate with all files in search_path
for (boost::filesystem::recursive_directory_iterator iter(search_path), end; iter != end; iter++) {
auto path = iter->path();
@ -708,22 +710,21 @@ void Window::set_menu_actions() {
auto row_str = filesystem::get_relative_path(path, search_path).string();
if(buffer_paths.count(path.string()))
row_str="<b>"+row_str+"</b>";
paths->emplace(row_str, path);
paths.emplace_back(path);
SelectionDialog::get()->add_row(row_str);
}
if(paths->empty()) {
if(paths.empty()) {
Info::get().print("No files found in current project");
return;
}
SelectionDialog::get()->on_select=[this, paths](const std::string &selected, bool hide_window) {
auto it=paths->find(selected);
if(it!=paths->end()) {
Notebook::get().open(it->second);
if (auto view=Notebook::get().get_current_view())
view->hide_tooltips();
}
SelectionDialog::get()->on_select=[this, paths=std::move(paths)](unsigned int index, const std::string &text, bool hide_window) {
if(index>=paths.size())
return;
Notebook::get().open(paths[index]);
if (auto view=Notebook::get().get_current_view())
view->hide_tooltips();
};
if(view)
@ -825,7 +826,7 @@ void Window::set_menu_actions() {
if(!locations.empty()) {
auto dialog_iter=view->get_iter_for_dialog();
SelectionDialog::create(view, view->get_buffer()->create_mark(dialog_iter), true, true);
auto rows=std::make_shared<std::unordered_map<std::string, Source::Offset> >();
std::vector<Source::Offset> rows;
auto project_path=Project::Build::create(view->file_path)->project_path;
if(project_path.empty()) {
if(!Directories::get().path.empty())
@ -838,26 +839,28 @@ void Window::set_menu_actions() {
if(path.empty())
path=location.file_path.filename();
auto row=path.string()+":"+std::to_string(location.line+1);
(*rows)[row]=location;
rows.emplace_back(location);
SelectionDialog::get()->add_row(row);
}
if(rows->size()==0)
if(rows.size()==0)
return;
else if(rows->size()==1) {
auto location=*rows->begin();
if(!boost::filesystem::is_regular_file(location.second.file_path))
else if(rows.size()==1) {
auto location=*rows.begin();
if(!boost::filesystem::is_regular_file(location.file_path))
return;
Notebook::get().open(location.second.file_path);
Notebook::get().open(location.file_path);
auto view=Notebook::get().get_current_view();
auto line=static_cast<int>(location.second.line);
auto index=static_cast<int>(location.second.index);
auto line=static_cast<int>(location.line);
auto index=static_cast<int>(location.index);
view->place_cursor_at_line_index(line, index);
view->scroll_to_cursor_delayed(view, true, false);
return;
}
SelectionDialog::get()->on_select=[rows](const std::string &selected, bool hide_window) {
auto location=rows->at(selected);
SelectionDialog::get()->on_select=[rows=std::move(rows)](unsigned int index, const std::string &text, bool hide_window) {
if(index>=rows.size())
return;
auto location=rows[index];
if(!boost::filesystem::is_regular_file(location.file_path))
return;
Notebook::get().open(location.file_path);
@ -890,7 +893,7 @@ void Window::set_menu_actions() {
if(!usages.empty()) {
auto dialog_iter=view->get_iter_for_dialog();
SelectionDialog::create(view, view->get_buffer()->create_mark(dialog_iter), true, true);
auto rows=std::make_shared<std::unordered_map<std::string, Source::Offset> >();
std::vector<Source::Offset> rows;
auto iter=view->get_buffer()->get_insert()->get_iter();
for(auto &usage: usages) {
@ -902,7 +905,7 @@ void Window::set_menu_actions() {
current_page=false;
}
row+=std::to_string(usage.first.line+1)+": "+usage.second;
(*rows)[row]=usage.first;
rows.emplace_back(usage.first);
SelectionDialog::get()->add_row(row);
//Set dialog cursor to the last row if the textview cursor is at the same line
@ -912,10 +915,12 @@ void Window::set_menu_actions() {
}
}
if(rows->size()==0)
if(rows.size()==0)
return;
SelectionDialog::get()->on_select=[this, rows](const std::string &selected, bool hide_window) {
auto offset=rows->at(selected);
SelectionDialog::get()->on_select=[this, rows=std::move(rows)](unsigned int index, const std::string &text, bool hide_window) {
if(index>=rows.size())
return;
auto offset=rows[index];
if(!boost::filesystem::is_regular_file(offset.file_path))
return;
Notebook::get().open(offset.file_path);
@ -937,16 +942,18 @@ void Window::set_menu_actions() {
if(!methods.empty()) {
auto dialog_iter=view->get_iter_for_dialog();
SelectionDialog::create(view, view->get_buffer()->create_mark(dialog_iter), true, true);
auto rows=std::make_shared<std::unordered_map<std::string, Source::Offset> >();
std::vector<Source::Offset> rows;
auto iter=view->get_buffer()->get_insert()->get_iter();
for(auto &method: methods) {
(*rows)[method.second]=method.first;
rows.emplace_back(method.first);
SelectionDialog::get()->add_row(method.second);
if(iter.get_line()>=static_cast<int>(method.first.line))
SelectionDialog::get()->set_cursor_at_last_row();
}
SelectionDialog::get()->on_select=[view, rows](const std::string& selected, bool hide_window) {
auto offset=rows->at(selected);
SelectionDialog::get()->on_select=[view, rows=std::move(rows)](unsigned int index, const std::string &text, bool hide_window) {
if(index>=rows.size())
return;
auto offset=rows[index];
view->get_buffer()->place_cursor(view->get_buffer()->get_iter_at_line_index(offset.line, offset.index));
view->scroll_to(view->get_buffer()->get_insert(), 0.0, 1.0, 0.5);
view->hide_tooltips();
@ -971,16 +978,16 @@ void Window::set_menu_actions() {
EntryBox::get().buttons.back().activate();
return;
}
auto method=std::make_shared<std::string>(view->get_method());
if(method->empty())
auto method=view->get_method();
if(method.empty())
return;
EntryBox::get().clear();
EntryBox::get().labels.emplace_back();
EntryBox::get().labels.back().set_text(*method);
EntryBox::get().buttons.emplace_back(button_text, [this, method](){
EntryBox::get().labels.back().set_text(method);
EntryBox::get().buttons.emplace_back(button_text, [this, method=std::move(method)](){
if(auto view=Notebook::get().get_current_view()) {
view->get_buffer()->insert_at_cursor(*method);
view->get_buffer()->insert_at_cursor(method);
EntryBox::get().clear();
}
});
@ -1033,8 +1040,8 @@ void Window::set_menu_actions() {
menu.add_action("project_set_run_arguments", [this]() {
auto project=Project::create();
auto run_arguments=std::make_shared<std::pair<std::string, std::string> >(project->get_run_arguments());
if(run_arguments->second.empty())
auto run_arguments=project->get_run_arguments();
if(run_arguments.second.empty())
return;
EntryBox::get().clear();
@ -1044,8 +1051,8 @@ void Window::set_menu_actions() {
label_it->set_text("Synopsis: [environment_variable=value]... executable [argument]...\nSet empty to let juCi++ deduce executable.");
};
label_it->update(0, "");
EntryBox::get().entries.emplace_back(run_arguments->second, [this, run_arguments](const std::string& content){
Project::run_arguments[run_arguments->first]=content;
EntryBox::get().entries.emplace_back(run_arguments.second, [this, run_arguments_first=std::move(run_arguments.first)](const std::string &content){
Project::run_arguments[run_arguments_first]=content;
EntryBox::get().hide();
}, 50);
auto entry_it=EntryBox::get().entries.begin();
@ -1130,8 +1137,8 @@ void Window::set_menu_actions() {
#ifdef JUCI_ENABLE_DEBUG
menu.add_action("debug_set_run_arguments", [this]() {
auto project=Project::create();
auto run_arguments=std::make_shared<std::pair<std::string, std::string> >(project->debug_get_run_arguments());
if(run_arguments->second.empty())
auto run_arguments=project->debug_get_run_arguments();
if(run_arguments.second.empty())
return;
EntryBox::get().clear();
@ -1141,8 +1148,8 @@ void Window::set_menu_actions() {
label_it->set_text("Synopsis: [environment_variable=value]... executable [argument]...\nSet empty to let juCi++ deduce executable.");
};
label_it->update(0, "");
EntryBox::get().entries.emplace_back(run_arguments->second, [this, run_arguments](const std::string& content){
Project::debug_run_arguments[run_arguments->first]=content;
EntryBox::get().entries.emplace_back(run_arguments.second, [this, run_arguments_first=std::move(run_arguments.first)](const std::string& content){
Project::debug_run_arguments[run_arguments_first]=content;
EntryBox::get().hide();
}, 50);
auto entry_it=EntryBox::get().entries.begin();
@ -1559,11 +1566,10 @@ void Window::rename_token_entry() {
if(view->get_token_spelling && view->rename_similar_tokens) {
auto spelling=std::make_shared<std::string>(view->get_token_spelling());
if(!spelling->empty()) {
auto iter=std::make_shared<Gtk::TextIter>(view->get_buffer()->get_insert()->get_iter());
EntryBox::get().entries.emplace_back(*spelling, [this, view, spelling, iter](const std::string& content){
EntryBox::get().entries.emplace_back(*spelling, [this, view, spelling, iter=view->get_buffer()->get_insert()->get_iter()](const std::string& content){
//TODO: gtk needs a way to check if iter is valid without dumping g_error message
//iter->get_buffer() will print such a message, but no segfault will occur
if(Notebook::get().get_current_view()==view && content!=*spelling && iter->get_buffer() && view->get_buffer()->get_insert()->get_iter()==*iter)
if(Notebook::get().get_current_view()==view && content!=*spelling && iter.get_buffer() && view->get_buffer()->get_insert()->get_iter()==iter)
view->rename_similar_tokens(content);
else
Info::get().print("Operation canceled");

Loading…
Cancel
Save