Browse Source

Merge pull request #95 from eidheim/master

Major cleanup of clang parsing and autocomplete
merge-requests/365/head
Ole Christian Eidheim 10 years ago
parent
commit
e009d2af50
  1. 2
      .gitmodules
  2. 17
      README.md
  3. 2
      libclangmm
  4. 32
      src/directories.cc
  5. 3
      src/directories.h
  6. 66
      src/notebook.cc
  7. 12
      src/notebook.h
  8. 21
      src/source.cc
  9. 8
      src/source.h
  10. 441
      src/source_clang.cc
  11. 60
      src/source_clang.h
  12. 43
      src/window.cc
  13. 2
      src/window.h

2
.gitmodules vendored

@ -1,4 +1,4 @@
[submodule "libclangmm"] [submodule "libclangmm"]
path = libclangmm path = libclangmm
url = https://github.com/cppit/libclangmm.git url = https://github.com/cppit/libclangmm.git
branch = v0.9.3 branch = v0.9.4

17
README.md

@ -8,18 +8,19 @@ towards libclang with speed and ease of use in mind.
## Features ## Features
* Platform independent * Platform independent
* Fast and responsive (written in C++) * Fast and responsive (written in C++)
* Syntax highlighting (even C++11/14, and more than 100 other file types) * Syntax highlighting for more than 100 different file types
* C++ warnings and errors on the fly * C++ warnings and errors on the fly
* C++ Fix-its * C++ Fix-its
* Automated CMake processing * Automated CMake processing
* Fast C++ autocomletion (including external libraries) * Fast C++ autocompletion
* Keyword and buffer autocomletion for other file types * Keyword and buffer autocompletion for other file types
* Tooltips showing type information and doxygen documentation * Tooltips showing type information and doxygen documentation (C++)
* Refactoring across files * Rename refactoring across files (C++)
* Highlighting of similar types * Highlighting of similar types (C++)
* Documentation search * Automated documentation search (C++)
* Go to methods and usages (C++)
* Spell checking depending on file context * Spell checking depending on file context
* Run shell commands within JuCi++, even on Windows * Run shell commands within JuCi++
* Regex search and replace * Regex search and replace
* Smart paste, keys and indentation * Smart paste, keys and indentation
* Auto-indentation of C++ file buffers through [clang-format](http://clang.llvm.org/docs/ClangFormat.html) * Auto-indentation of C++ file buffers through [clang-format](http://clang.llvm.org/docs/ClangFormat.html)

2
libclangmm

@ -1 +1 @@
Subproject commit f1f784dd02d78da35a27034c6cc854e676d4f78b Subproject commit c1f4eef139e0ca6c36b0faceaac57f519b41acca

32
src/directories.cc

@ -21,26 +21,24 @@ namespace sigc {
#endif #endif
} }
Directories::Directories() : stop_update_thread(false) { Directories::Directories() : Gtk::TreeView(), stop_update_thread(false) {
add(tree_view);
set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
tree_store = Gtk::TreeStore::create(column_record); tree_store = Gtk::TreeStore::create(column_record);
tree_view.set_model(tree_store); set_model(tree_store);
tree_view.append_column("", column_record.name); append_column("", column_record.name);
auto renderer=dynamic_cast<Gtk::CellRendererText*>(tree_view.get_column(0)->get_first_cell()); auto renderer=dynamic_cast<Gtk::CellRendererText*>(get_column(0)->get_first_cell());
tree_view.get_column(0)->add_attribute(renderer->property_foreground_rgba(), column_record.color); get_column(0)->add_attribute(renderer->property_foreground_rgba(), column_record.color);
tree_store->set_sort_column(column_record.id, Gtk::SortType::SORT_ASCENDING); tree_store->set_sort_column(column_record.id, Gtk::SortType::SORT_ASCENDING);
tree_view.set_enable_search(true); //TODO: why does this not work in OS X? set_enable_search(true); //TODO: why does this not work in OS X?
tree_view.set_search_column(column_record.name); set_search_column(column_record.name);
tree_view.signal_row_activated().connect([this](const Gtk::TreeModel::Path& path, Gtk::TreeViewColumn* column){ signal_row_activated().connect([this](const Gtk::TreeModel::Path& path, Gtk::TreeViewColumn* column){
auto iter = tree_store->get_iter(path); auto iter = tree_store->get_iter(path);
if (iter) { if (iter) {
auto path_str=iter->get_value(column_record.path); auto path_str=iter->get_value(column_record.path);
if(path_str!="") { if(path_str!="") {
if (boost::filesystem::is_directory(boost::filesystem::path(path_str))) { if (boost::filesystem::is_directory(boost::filesystem::path(path_str))) {
tree_view.row_expanded(path) ? tree_view.collapse_row(path) : tree_view.expand_row(path, false); row_expanded(path) ? collapse_row(path) : expand_row(path, false);
} else { } else {
if(on_row_activated) if(on_row_activated)
on_row_activated(path_str); on_row_activated(path_str);
@ -49,7 +47,7 @@ Directories::Directories() : stop_update_thread(false) {
} }
}); });
tree_view.signal_test_expand_row().connect([this](const Gtk::TreeModel::iterator& iter, const Gtk::TreeModel::Path& path){ signal_test_expand_row().connect([this](const Gtk::TreeModel::iterator& iter, const Gtk::TreeModel::Path& path){
if(iter->children().begin()->get_value(column_record.path)=="") { if(iter->children().begin()->get_value(column_record.path)=="") {
update_mutex.lock(); update_mutex.lock();
add_path(iter->get_value(column_record.path), *iter); add_path(iter->get_value(column_record.path), *iter);
@ -57,7 +55,7 @@ Directories::Directories() : stop_update_thread(false) {
} }
return false; return false;
}); });
tree_view.signal_row_collapsed().connect([this](const Gtk::TreeModel::iterator& iter, const Gtk::TreeModel::Path& path){ signal_row_collapsed().connect([this](const Gtk::TreeModel::iterator& iter, const Gtk::TreeModel::Path& path){
update_mutex.lock(); update_mutex.lock();
last_write_times.erase(iter->get_value(column_record.path)); last_write_times.erase(iter->get_value(column_record.path));
update_mutex.unlock(); update_mutex.unlock();
@ -128,9 +126,9 @@ void Directories::open(const boost::filesystem::path& dir_path) {
cmake=std::unique_ptr<CMake>(new CMake(dir_path)); cmake=std::unique_ptr<CMake>(new CMake(dir_path));
auto project=cmake->get_functions_parameters("project"); auto project=cmake->get_functions_parameters("project");
if(project.size()>0 && project[0].second.size()>0) if(project.size()>0 && project[0].second.size()>0)
tree_view.get_column(0)->set_title(project[0].second[0]); get_column(0)->set_title(project[0].second[0]);
else else
tree_view.get_column(0)->set_title(""); get_column(0)->set_title("");
update_mutex.lock(); update_mutex.lock();
add_path(dir_path, Gtk::TreeModel::Row()); add_path(dir_path, Gtk::TreeModel::Row());
update_mutex.unlock(); update_mutex.unlock();
@ -185,8 +183,8 @@ void Directories::select(const boost::filesystem::path &path) {
tree_store->foreach_iter([this, &path](const Gtk::TreeModel::iterator& iter){ tree_store->foreach_iter([this, &path](const Gtk::TreeModel::iterator& iter){
if(iter->get_value(column_record.path)==path.string()) { if(iter->get_value(column_record.path)==path.string()) {
auto tree_path=Gtk::TreePath(iter); auto tree_path=Gtk::TreePath(iter);
tree_view.expand_to_path(tree_path); expand_to_path(tree_path);
tree_view.set_cursor(tree_path); set_cursor(tree_path);
return true; return true;
} }
return false; return false;

3
src/directories.h

@ -10,7 +10,7 @@
#include <mutex> #include <mutex>
#include <atomic> #include <atomic>
class Directories : public Gtk::ScrolledWindow { class Directories : public Gtk::TreeView {
public: public:
class ColumnRecord : public Gtk::TreeModel::ColumnRecord { class ColumnRecord : public Gtk::TreeModel::ColumnRecord {
public: public:
@ -38,7 +38,6 @@ public:
private: private:
void add_path(const boost::filesystem::path& dir_path, const Gtk::TreeModel::Row &row); void add_path(const boost::filesystem::path& dir_path, const Gtk::TreeModel::Row &row);
Gtk::TreeView tree_view;
Glib::RefPtr<Gtk::TreeStore> tree_store; Glib::RefPtr<Gtk::TreeStore> tree_store;
ColumnRecord column_record; ColumnRecord column_record;

66
src/notebook.cc

@ -27,6 +27,17 @@ namespace sigc {
#endif #endif
} }
Notebook::TabLabel::TabLabel(const std::string &title) : Gtk::Box(Gtk::ORIENTATION_HORIZONTAL) {
label.set_text(title);
button.set_image_from_icon_name("window-close-symbolic", Gtk::ICON_SIZE_MENU);
button.set_border_width(0.0);
button.set_use_underline(false);
button.set_relief(Gtk::ReliefStyle::RELIEF_NONE);
pack_start(label, Gtk::PACK_SHRINK);
pack_end(button, Gtk::PACK_SHRINK);
show_all();
}
Notebook::Notebook() : Gtk::Notebook(), last_index(-1) { Notebook::Notebook() : Gtk::Notebook(), last_index(-1) {
Gsv::init(); Gsv::init();
@ -115,8 +126,19 @@ void Notebook::open(const boost::filesystem::path &file_path) {
#endif #endif
configure(source_views.size()-1); configure(source_views.size()-1);
//Set up tab label
std::string title=file_path.filename().string(); std::string title=file_path.filename().string();
append_page(*hboxes.back(), title); tab_labels.emplace_back(new TabLabel(title));
auto source_view=source_views.back();
tab_labels.back()->button.signal_clicked().connect([this, source_view](){
for(int c=0;c<size();c++) {
if(get_view(c)==source_view) {
close(c);
break;
}
}
});
append_page(*hboxes.back(), *tab_labels.back());
set_tab_reorderable(*hboxes.back(), true); set_tab_reorderable(*hboxes.back(), true);
show_all_children(); show_all_children();
@ -131,7 +153,6 @@ void Notebook::open(const boost::filesystem::path &file_path) {
get_current_view()->get_buffer()->set_modified(false); get_current_view()->get_buffer()->set_modified(false);
get_current_view()->grab_focus(); get_current_view()->grab_focus();
//Add star on tab label when the page is not saved: //Add star on tab label when the page is not saved:
auto source_view=get_current_view();
get_current_view()->get_buffer()->signal_modified_changed().connect([this, source_view]() { get_current_view()->get_buffer()->signal_modified_changed().connect([this, source_view]() {
std::string title=source_view->file_path.filename().string(); std::string title=source_view->file_path.filename().string();
if(source_view->get_buffer()->get_modified()) if(source_view->get_buffer()->get_modified())
@ -144,7 +165,7 @@ void Notebook::open(const boost::filesystem::path &file_path) {
} }
} }
if(page!=-1) if(page!=-1)
set_tab_label_text(*(get_nth_page(page)), title); tab_labels.at(page)->label.set_text(title);
}); });
JDEBUG("end"); JDEBUG("end");
@ -164,7 +185,7 @@ void Notebook::configure(int view_nr) {
#endif #endif
} }
bool Notebook::save(int page, bool reparse_needed) { bool Notebook::save(int page) {
JDEBUG("start"); JDEBUG("start");
if(page>=size()) { if(page>=size()) {
JDEBUG("end false"); JDEBUG("end false");
@ -199,7 +220,6 @@ bool Notebook::save(int page, bool reparse_needed) {
} }
if(filesystem::write(view->file_path, view->get_buffer())) { if(filesystem::write(view->file_path, view->get_buffer())) {
if(reparse_needed) {
if(auto clang_view=dynamic_cast<Source::ClangView*>(view)) { if(auto clang_view=dynamic_cast<Source::ClangView*>(view)) {
if(clang_view->language->get_id()=="chdr" || clang_view->language->get_id()=="cpphdr") { if(clang_view->language->get_id()=="chdr" || clang_view->language->get_id()=="cpphdr") {
for(auto a_view: source_views) { for(auto a_view: source_views) {
@ -210,7 +230,6 @@ bool Notebook::save(int page, bool reparse_needed) {
} }
} }
} }
}
view->get_buffer()->set_modified(false); view->get_buffer()->set_modified(false);
@ -248,25 +267,31 @@ bool Notebook::save(int page, bool reparse_needed) {
bool Notebook::save_current() { bool Notebook::save_current() {
if(get_current_page()==-1) if(get_current_page()==-1)
return false; return false;
return save(get_current_page(), true); return save(get_current_page());
} }
bool Notebook::close_current_page() { bool Notebook::close(int page) {
JDEBUG("start"); JDEBUG("start");
if (get_current_page()!=-1) { if (page!=-1) {
if(get_current_view()->get_buffer()->get_modified()){ auto view=get_view(page);
if(!save_modified_dialog()) { if(view->get_buffer()->get_modified()){
if(!save_modified_dialog(page)) {
JDEBUG("end false"); JDEBUG("end false");
return false; return false;
} }
} }
int page = get_current_page(); auto index=get_index(page);
int index=get_index(page); if(page==get_current_page()) {
if(last_index!=static_cast<size_t>(-1)) { if(last_index!=static_cast<size_t>(-1)) {
set_current_page(page_num(*hboxes.at(last_index))); set_current_page(page_num(*hboxes.at(last_index)));
last_index=-1; last_index=-1;
} }
}
else if(index==last_index)
last_index=-1;
else if(index<last_index && last_index!=static_cast<size_t>(-1))
last_index--;
remove_page(page); remove_page(page);
#if GTKSOURCEVIEWMM_MAJOR_VERSION > 2 & GTKSOURCEVIEWMM_MINOR_VERSION > 17 #if GTKSOURCEVIEWMM_MAJOR_VERSION > 2 & GTKSOURCEVIEWMM_MINOR_VERSION > 17
source_maps.erase(source_maps.begin()+index); source_maps.erase(source_maps.begin()+index);
@ -279,11 +304,18 @@ bool Notebook::close_current_page() {
source_views.erase(source_views.begin()+index); source_views.erase(source_views.begin()+index);
scrolled_windows.erase(scrolled_windows.begin()+index); scrolled_windows.erase(scrolled_windows.begin()+index);
hboxes.erase(hboxes.begin()+index); hboxes.erase(hboxes.begin()+index);
tab_labels.erase(tab_labels.begin()+index);
} }
JDEBUG("end true"); JDEBUG("end true");
return true; return true;
} }
bool Notebook::close_current_page() {
if(get_current_page()==-1)
return false;
return close(get_current_page());
}
boost::filesystem::path Notebook::get_current_folder() { boost::filesystem::path Notebook::get_current_folder() {
boost::filesystem::path current_path; boost::filesystem::path current_path;
@ -295,13 +327,13 @@ boost::filesystem::path Notebook::get_current_folder() {
return current_path; return current_path;
} }
bool Notebook::save_modified_dialog() { bool Notebook::save_modified_dialog(int page) {
Gtk::MessageDialog dialog((Gtk::Window&)(*get_toplevel()), "Save file!", false, Gtk::MESSAGE_QUESTION, Gtk::BUTTONS_YES_NO); Gtk::MessageDialog dialog((Gtk::Window&)(*get_toplevel()), "Save file!", false, Gtk::MESSAGE_QUESTION, Gtk::BUTTONS_YES_NO);
dialog.set_default_response(Gtk::RESPONSE_YES); dialog.set_default_response(Gtk::RESPONSE_YES);
dialog.set_secondary_text("Do you want to save: " + get_current_view()->file_path.string()+" ?"); dialog.set_secondary_text("Do you want to save: " + get_view(page)->file_path.string()+" ?");
int result = dialog.run(); int result = dialog.run();
if(result==Gtk::RESPONSE_YES) { if(result==Gtk::RESPONSE_YES) {
save_current(); save(page);
return true; return true;
} }
else if(result==Gtk::RESPONSE_NO) { else if(result==Gtk::RESPONSE_NO) {

12
src/notebook.h

@ -10,25 +10,33 @@
#include <sigc++/sigc++.h> #include <sigc++/sigc++.h>
class Notebook : public Gtk::Notebook { class Notebook : public Gtk::Notebook {
class TabLabel : public Gtk::Box {
public:
TabLabel(const std::string &title);
Gtk::Label label;
Gtk::Button button;
};
public: public:
Notebook(); Notebook();
Source::View* get_view(int page); Source::View* get_view(int page);
size_t get_index(int page); size_t get_index(int page);
int size(); int size();
Source::View* get_current_view(); Source::View* get_current_view();
bool close(int page);
bool close_current_page(); bool close_current_page();
void open(const boost::filesystem::path &file_path); void open(const boost::filesystem::path &file_path);
bool save(int page, bool reparse_needed=false); bool save(int page);
bool save_current(); bool save_current();
void configure(int view_nr); void configure(int view_nr);
boost::filesystem::path get_current_folder(); boost::filesystem::path get_current_folder();
private: private:
bool save_modified_dialog(); bool save_modified_dialog(int page);
std::vector<Source::View*> source_views; //Is NOT freed in destructor, this is intended for quick program exit. std::vector<Source::View*> source_views; //Is NOT freed in destructor, this is intended for quick program exit.
std::vector<std::unique_ptr<Gtk::Widget> > source_maps; std::vector<std::unique_ptr<Gtk::Widget> > source_maps;
std::vector<std::unique_ptr<Gtk::ScrolledWindow> > scrolled_windows; std::vector<std::unique_ptr<Gtk::ScrolledWindow> > scrolled_windows;
std::vector<std::unique_ptr<Gtk::HBox> > hboxes; std::vector<std::unique_ptr<Gtk::HBox> > hboxes;
std::vector<std::unique_ptr<TabLabel> > tab_labels;
size_t last_index; size_t last_index;
}; };

21
src/source.cc

@ -143,8 +143,7 @@ Source::View::View(const boost::filesystem::path &file_path, const boost::filesy
auto first=iter; auto first=iter;
auto second=iter; auto second=iter;
if(last_keyval_is_return) { if(last_keyval_is_return) {
while(first && !first.ends_line()) while(first && !first.ends_line() && first.backward_char()) {}
first.backward_char();
if(first.backward_char() && second.forward_char()) { if(first.backward_char() && second.forward_char()) {
get_buffer()->remove_tag_by_name("spellcheck_error", first, second); get_buffer()->remove_tag_by_name("spellcheck_error", first, second);
auto word=spellcheck_get_word(first); auto word=spellcheck_get_word(first);
@ -947,24 +946,24 @@ bool Source::View::on_key_press_event(GdkEventKey* key) {
while((*end_blank_iter==' ' || *end_blank_iter=='\t') && while((*end_blank_iter==' ' || *end_blank_iter=='\t') &&
!end_blank_iter.ends_line() && end_blank_iter.forward_char()) {} !end_blank_iter.ends_line() && end_blank_iter.forward_char()) {}
start_blank_iter.backward_char(); start_blank_iter.backward_char();
while((*start_blank_iter==' ' || *start_blank_iter=='\t' || start_blank_iter.ends_line()) && while((*start_blank_iter==' ' || *start_blank_iter=='\t') &&
!start_blank_iter.starts_line() && start_blank_iter.backward_char()) {} !start_blank_iter.starts_line() && start_blank_iter.backward_char()) {}
if(!start_blank_iter.starts_line()) {
if(start_blank_iter.starts_line() && (*start_blank_iter==' ' || *start_blank_iter=='\t'))
get_buffer()->erase(iter, end_blank_iter);
else {
start_blank_iter.forward_char(); start_blank_iter.forward_char();
get_buffer()->erase(start_blank_iter, end_blank_iter); get_buffer()->erase(start_blank_iter, end_blank_iter);
} }
else
get_buffer()->erase(iter, end_blank_iter);
iter=get_buffer()->get_insert()->get_iter();
iter=get_buffer()->get_insert()->get_iter();
int line_nr=iter.get_line(); int line_nr=iter.get_line();
auto tabs_end_iter=get_tabs_end_iter(); auto tabs_end_iter=get_tabs_end_iter();
auto line_tabs=get_line_before(tabs_end_iter); auto line_tabs=get_line_before(tabs_end_iter);
if((line_nr+1)<get_buffer()->get_line_count()) { if((line_nr+1)<get_buffer()->get_line_count()) {
auto next_line_tabs_end_iter=get_tabs_end_iter(line_nr+1); auto next_line_tabs_end_iter=get_tabs_end_iter(line_nr+1);
auto next_line_tabs=get_line_before(next_line_tabs_end_iter); auto next_line_tabs=get_line_before(next_line_tabs_end_iter);
if(iter.ends_line()) { if(iter.ends_line() && next_line_tabs.size()>line_tabs.size()) {
if(next_line_tabs.size()>line_tabs.size()) {
get_source_buffer()->insert_at_cursor("\n"+next_line_tabs); get_source_buffer()->insert_at_cursor("\n"+next_line_tabs);
scroll_to(get_source_buffer()->get_insert()); scroll_to(get_source_buffer()->get_insert());
get_source_buffer()->end_user_action(); get_source_buffer()->end_user_action();
@ -976,7 +975,6 @@ bool Source::View::on_key_press_event(GdkEventKey* key) {
get_source_buffer()->end_user_action(); get_source_buffer()->end_user_action();
return true; return true;
} }
}
//Indent right when clicking tab, no matter where in the line the cursor is. Also works on selected text. //Indent right when clicking tab, no matter where in the line the cursor is. Also works on selected text.
else if(key->keyval==GDK_KEY_Tab) { else if(key->keyval==GDK_KEY_Tab) {
//Special case if insert is at beginning of empty line: //Special case if insert is at beginning of empty line:
@ -1104,9 +1102,8 @@ bool Source::View::on_key_press_event(GdkEventKey* key) {
while(!end_sentence_iter.starts_line() && while(!end_sentence_iter.starts_line() &&
(*end_sentence_iter==' ' || *end_sentence_iter=='\t' || end_sentence_iter.ends_line()) && (*end_sentence_iter==' ' || *end_sentence_iter=='\t' || end_sentence_iter.ends_line()) &&
end_sentence_iter.backward_char()) {} end_sentence_iter.backward_char()) {}
if(!end_sentence_iter.ends_line()) if(!end_sentence_iter.ends_line() && !end_sentence_iter.starts_line())
end_sentence_iter.forward_char(); end_sentence_iter.forward_char();
if(iter==end_line_iter) { if(iter==end_line_iter) {
if((key->state&GDK_SHIFT_MASK)>0) if((key->state&GDK_SHIFT_MASK)>0)
get_buffer()->move_mark_by_name("insert", end_sentence_iter); get_buffer()->move_mark_by_name("insert", end_sentence_iter);

8
src/source.h

@ -104,8 +104,8 @@ namespace Source {
bool soft_reparse_needed=false; bool soft_reparse_needed=false;
bool full_reparse_needed=false; bool full_reparse_needed=false;
virtual void soft_reparse() {} virtual void soft_reparse() {soft_reparse_needed=false;}
virtual bool full_reparse() {return true;} virtual bool full_reparse() {full_reparse_needed=false; return true;}
protected: protected:
bool parsed=false; bool parsed=false;
Tooltips diagnostic_tooltips; Tooltips diagnostic_tooltips;
@ -133,8 +133,8 @@ namespace Source {
bool find_right_bracket_forward(Gtk::TextIter iter, Gtk::TextIter &found_iter); bool find_right_bracket_forward(Gtk::TextIter iter, Gtk::TextIter &found_iter);
bool find_left_bracket_backward(Gtk::TextIter iter, Gtk::TextIter &found_iter); bool find_left_bracket_backward(Gtk::TextIter iter, Gtk::TextIter &found_iter);
bool on_key_press_event(GdkEventKey* key); bool on_key_press_event(GdkEventKey* key) override;
bool on_button_press_event(GdkEventButton *event); bool on_button_press_event(GdkEventButton *event) override;
std::pair<char, unsigned> find_tab_char_and_size(); std::pair<char, unsigned> find_tab_char_and_size();
unsigned tab_size; unsigned tab_size;

441
src/source_clang.cc

@ -21,7 +21,7 @@ namespace sigc {
clang::Index Source::ClangViewParse::clang_index(0, 0); clang::Index Source::ClangViewParse::clang_index(0, 0);
Source::ClangViewParse::ClangViewParse(const boost::filesystem::path &file_path, const boost::filesystem::path& project_path, Glib::RefPtr<Gsv::Language> language): Source::ClangViewParse::ClangViewParse(const boost::filesystem::path &file_path, const boost::filesystem::path& project_path, Glib::RefPtr<Gsv::Language> language):
Source::View(file_path, project_path, language), parse_error(false) { Source::View(file_path, project_path, language) {
JDEBUG("start"); JDEBUG("start");
auto tag_table=get_buffer()->get_tag_table(); auto tag_table=get_buffer()->get_tag_table();
@ -35,36 +35,35 @@ Source::View(file_path, project_path, language), parse_error(false) {
parsing_in_progress=Singleton::terminal->print_in_progress("Parsing "+file_path.string()); parsing_in_progress=Singleton::terminal->print_in_progress("Parsing "+file_path.string());
//GTK-calls must happen in main thread, so the parse_thread //GTK-calls must happen in main thread, so the parse_thread
//sends signals to the main thread that it is to call the following functions: //sends signals to the main thread that it is to call the following functions:
parse_start_connection=parse_start.connect([this]{ parse_preprocess_connection=parse_preprocess.connect([this]{
if(parse_thread_buffer_map_mutex.try_lock()) { auto expected=ParseProcessState::PREPROCESSING;
parse_thread_buffer_map=get_buffer_map(); if(parse_mutex.try_lock()) {
parse_thread_mapped=true; if(parse_process_state.compare_exchange_strong(expected, ParseProcessState::PROCESSING))
parse_thread_buffer_map_mutex.unlock(); parse_thread_buffer=get_buffer()->get_text();
parse_mutex.unlock();
} }
parse_thread_go=true; else
parse_process_state.compare_exchange_strong(expected, ParseProcessState::STARTING);
}); });
parse_done_connection=parse_done.connect([this](){ parse_postprocess_connection=parse_postprocess.connect([this](){
if(parse_thread_mapped) { if(parse_mutex.try_lock()) {
if(parsing_mutex.try_lock()) { auto expected=ParseProcessState::POSTPROCESSING;
if(parse_process_state.compare_exchange_strong(expected, ParseProcessState::IDLE)) {
update_syntax(); update_syntax();
update_diagnostics(); update_diagnostics();
parsed=true; parsed=true;
set_status(""); set_status("");
parsing_mutex.unlock();
} }
parsing_in_progress->done("done"); parse_mutex.unlock();
}
else {
parse_thread_go=true;
} }
}); });
parse_fail_connection=parse_fail.connect([this](){ parse_error_connection=parse_error.connect([this](){
Singleton::terminal->print("Error: failed to reparse "+this->file_path.string()+".\n", true); Singleton::terminal->print("Error: failed to reparse "+this->file_path.string()+".\n", true);
set_status(""); set_status("");
set_info(""); set_info("");
parsing_in_progress->cancel("failed"); parsing_in_progress->cancel("failed");
}); });
init_parse(); parse_initialize();
get_buffer()->signal_changed().connect([this]() { get_buffer()->signal_changed().connect([this]() {
soft_reparse(); soft_reparse();
@ -105,78 +104,76 @@ void Source::ClangViewParse::configure() {
no_bracket_no_para_statement_regex=boost::regex("^([ \\t]*)(else|try|do) *$"); no_bracket_no_para_statement_regex=boost::regex("^([ \\t]*)(else|try|do) *$");
} }
void Source::ClangViewParse::init_parse() { void Source::ClangViewParse::parse_initialize() {
type_tooltips.hide(); type_tooltips.hide();
diagnostic_tooltips.hide(); diagnostic_tooltips.hide();
parsed=false; parsed=false;
parse_thread_go=true; if(parse_thread.joinable())
parse_thread_mapped=false; parse_thread.join();
parse_thread_stop=false; parse_state=ParseState::PROCESSING;
parse_process_state=ParseProcessState::STARTING;
auto buffer_map=get_buffer_map(); auto buffer=get_buffer()->get_text();
//Remove includes for first parse for initial syntax highlighting //Remove includes for first parse for initial syntax highlighting
auto& str=buffer_map[file_path.string()];
std::size_t pos=0; std::size_t pos=0;
while((pos=str.find("#include", pos))!=std::string::npos) { while((pos=buffer.find("#include", pos))!=std::string::npos) {
auto start_pos=pos; auto start_pos=pos;
pos=str.find('\n', pos+8); pos=buffer.find('\n', pos+8);
if(pos==std::string::npos) if(pos==std::string::npos)
break; break;
if(start_pos==0 || str[start_pos-1]=='\n') { if(start_pos==0 || buffer[start_pos-1]=='\n') {
str.replace(start_pos, pos-start_pos, pos-start_pos, ' '); buffer.replace(start_pos, pos-start_pos, pos-start_pos, ' ');
} }
pos++; pos++;
} }
clang_tu = std::unique_ptr<clang::TranslationUnit>(new clang::TranslationUnit(clang_index, file_path.string(), get_compilation_commands(), buffer_map)); clang_tu = std::unique_ptr<clang::TranslationUnit>(new clang::TranslationUnit(clang_index, file_path.string(), get_compilation_commands(), buffer.raw()));
clang_tokens=clang_tu->get_tokens(0, buffer_map.find(file_path.string())->second.size()-1); clang_tokens=clang_tu->get_tokens(0, buffer.bytes()-1);
update_syntax(); update_syntax();
set_status("parsing..."); set_status("parsing...");
if(parse_thread.joinable())
parse_thread.join();
parse_thread=std::thread([this]() { parse_thread=std::thread([this]() {
while(true) { while(true) {
while(!parse_thread_go && !parse_thread_stop) while(parse_state==ParseState::PROCESSING && parse_process_state!=ParseProcessState::STARTING && parse_process_state!=ParseProcessState::PROCESSING)
std::this_thread::sleep_for(std::chrono::milliseconds(10)); std::this_thread::sleep_for(std::chrono::milliseconds(10));
if(parse_thread_stop) if(parse_state!=ParseState::PROCESSING)
break; break;
if(!parse_thread_mapped) { auto expected=ParseProcessState::STARTING;
parse_thread_go=false; if(parse_process_state.compare_exchange_strong(expected, ParseProcessState::PREPROCESSING))
parse_start(); parse_preprocess();
} else if (parse_process_state==ParseProcessState::PROCESSING && parse_mutex.try_lock()) {
else if (parse_thread_mapped && parsing_mutex.try_lock() && parse_thread_buffer_map_mutex.try_lock()) { auto status=clang_tu->ReparseTranslationUnit(parse_thread_buffer.raw());
auto status=clang_tu->ReparseTranslationUnit(parse_thread_buffer_map); parsing_in_progress->done("done");
if(status==0) if(status==0) {
clang_tokens=clang_tu->get_tokens(0, parse_thread_buffer_map.find(file_path.string())->second.size()-1); auto expected=ParseProcessState::PROCESSING;
else if(parse_process_state.compare_exchange_strong(expected, ParseProcessState::POSTPROCESSING)) {
parse_error=true; clang_tokens=clang_tu->get_tokens(0, parse_thread_buffer.bytes()-1);
parse_thread_go=false; parse_mutex.unlock();
parsing_mutex.unlock(); parse_postprocess();
parse_thread_buffer_map_mutex.unlock();
if(status!=0) {
parse_fail();
parse_thread_stop=true;
} }
else else
parse_done(); parse_mutex.unlock();
} }
else {
parse_state=ParseState::STOP;
parse_mutex.unlock();
parse_error();
} }
});
} }
}
std::map<std::string, std::string> Source::ClangViewParse::get_buffer_map() const { });
std::map<std::string, std::string> buffer_map;
buffer_map[file_path.string()]=get_source_buffer()->get_text();
return buffer_map;
} }
void Source::ClangViewParse::soft_reparse() { void Source::ClangViewParse::soft_reparse() {
parse_thread_mapped=false; soft_reparse_needed=false;
parsed=false; parsed=false;
if(parse_state!=ParseState::PROCESSING)
return;
parse_process_state=ParseProcessState::IDLE;
delayed_reparse_connection.disconnect(); delayed_reparse_connection.disconnect();
delayed_reparse_connection=Glib::signal_timeout().connect([this]() { delayed_reparse_connection=Glib::signal_timeout().connect([this]() {
parsed=false; parsed=false;
parse_thread_go=true; auto expected=ParseProcessState::IDLE;
if(parse_process_state.compare_exchange_strong(expected, ParseProcessState::STARTING))
set_status("parsing..."); set_status("parsing...");
return false; return false;
}, 1000); }, 1000);
@ -613,27 +610,45 @@ bool Source::ClangViewParse::on_key_press_event(GdkEventKey* key) {
//// ClangViewAutocomplete /// //// ClangViewAutocomplete ///
////////////////////////////// //////////////////////////////
Source::ClangViewAutocomplete::ClangViewAutocomplete(const boost::filesystem::path &file_path, const boost::filesystem::path& project_path, Glib::RefPtr<Gsv::Language> language): Source::ClangViewAutocomplete::ClangViewAutocomplete(const boost::filesystem::path &file_path, const boost::filesystem::path& project_path, Glib::RefPtr<Gsv::Language> language):
Source::ClangViewParse(file_path, project_path, language), autocomplete_cancel_starting(false) { Source::ClangViewParse(file_path, project_path, language), autocomplete_state(AutocompleteState::IDLE) {
get_buffer()->signal_changed().connect([this](){ get_buffer()->signal_changed().connect([this](){
if(completion_dialog_shown) if(autocomplete_state==AutocompleteState::SHOWN)
delayed_reparse_connection.disconnect(); delayed_reparse_connection.disconnect();
start_autocomplete(); else {
if(!has_focus())
return;
if((last_keyval>='0' && last_keyval<='9') ||
(last_keyval>='a' && last_keyval<='z') || (last_keyval>='A' && last_keyval<='Z') ||
last_keyval=='_') {
autocomplete_check();
}
else {
if(autocomplete_state==AutocompleteState::STARTING)
autocomplete_state=AutocompleteState::CANCELED;
else {
auto iter=get_buffer()->get_insert()->get_iter();
if(last_keyval=='.' || last_keyval==':' || (last_keyval=='>' && iter.backward_char() && iter.backward_char() && *iter=='-'))
autocomplete_check();
}
}
}
}); });
get_buffer()->signal_mark_set().connect([this](const Gtk::TextBuffer::iterator& iterator, const Glib::RefPtr<Gtk::TextBuffer::Mark>& mark){ 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(mark->get_name()=="insert") {
autocomplete_cancel_starting=true; if(autocomplete_state==AutocompleteState::SHOWN)
if(completion_dialog_shown) autocomplete_dialog->hide();
completion_dialog->hide(); if(autocomplete_state==AutocompleteState::STARTING)
autocomplete_state=AutocompleteState::CANCELED;
} }
}); });
signal_scroll_event().connect([this](GdkEventScroll* event){ signal_scroll_event().connect([this](GdkEventScroll* event){
if(completion_dialog_shown) if(autocomplete_state==AutocompleteState::SHOWN)
completion_dialog->hide(); autocomplete_dialog->hide();
return false; return false;
}, false); }, false);
signal_key_release_event().connect([this](GdkEventKey* key){ signal_key_release_event().connect([this](GdkEventKey* key){
if(completion_dialog_shown) { if(autocomplete_state==AutocompleteState::SHOWN) {
if(completion_dialog->on_key_release(key)) if(autocomplete_dialog->on_key_release(key))
return true; return true;
} }
@ -641,18 +656,64 @@ Source::ClangViewParse(file_path, project_path, language), autocomplete_cancel_s
}, false); }, false);
signal_focus_out_event().connect([this](GdkEventFocus* event) { signal_focus_out_event().connect([this](GdkEventFocus* event) {
autocomplete_cancel_starting=true; if(autocomplete_state==AutocompleteState::SHOWN)
if(completion_dialog_shown) autocomplete_dialog->hide();
completion_dialog->hide(); if(autocomplete_state==AutocompleteState::STARTING)
autocomplete_state=AutocompleteState::CANCELED;
return false; return false;
}); });
autocomplete_fail_connection=autocomplete_fail.connect([this]() { autocomplete_error_connection=autocomplete_done.connect([this](){
if(autocomplete_state==AutocompleteState::CANCELED) {
set_status("");
soft_reparse();
autocomplete_state=AutocompleteState::IDLE;
autocomplete_restart();
}
else {
autocomplete_dialog_setup();
for (auto &data : autocomplete_data) {
std::stringstream ss;
std::string return_value;
for (auto &chunk : data.chunks) {
switch (chunk.kind) {
case clang::CompletionChunk_ResultType:
return_value = chunk.chunk;
break;
case clang::CompletionChunk_Informative: break;
default: ss << chunk.chunk; break;
}
}
auto row=ss.str();
auto row_insert_on_selection=row;
if (!row.empty()) {
if(!return_value.empty())
row+=" --> " + return_value;
autocomplete_dialog_rows[row] = row_insert_on_selection;
autocomplete_dialog->add_row(row, data.brief_comments);
}
}
set_status("");
if (!autocomplete_dialog_rows.empty()) {
autocomplete_state=AutocompleteState::SHOWN;
get_source_buffer()->begin_user_action();
autocomplete_dialog->show();
}
else {
autocomplete_state=AutocompleteState::IDLE;
soft_reparse();
}
}
});
autocomplete_restart_connection=autocomplete_restart.connect([this]() {
autocomplete_check();
});
autocomplete_error_connection=autocomplete_error.connect([this]() {
Singleton::terminal->print("Error: autocomplete failed, reparsing "+this->file_path.string()+"\n", true); Singleton::terminal->print("Error: autocomplete failed, reparsing "+this->file_path.string()+"\n", true);
autocomplete_state=AutocompleteState::CANCELED;
full_reparse(); full_reparse();
autocomplete_starting=false;
autocomplete_cancel_starting=false;
}); });
do_delete_object_connection=do_delete_object.connect([this](){ do_delete_object_connection=do_delete_object.connect([this](){
@ -662,88 +723,35 @@ Source::ClangViewParse(file_path, project_path, language), autocomplete_cancel_s
delete this; delete this;
}); });
do_full_reparse.connect([this](){ do_full_reparse.connect([this](){
init_parse(); parse_initialize();
full_reparse_running=false; full_reparse_running=false;
}); });
} }
bool Source::ClangViewAutocomplete::on_key_press_event(GdkEventKey *key) { bool Source::ClangViewAutocomplete::on_key_press_event(GdkEventKey *key) {
last_keyval=key->keyval; last_keyval=key->keyval;
if(completion_dialog_shown) { if(autocomplete_state==AutocompleteState::SHOWN) {
if(completion_dialog->on_key_press(key)) if(autocomplete_dialog->on_key_press(key))
return true; return true;
} }
return ClangViewParse::on_key_press_event(key); return ClangViewParse::on_key_press_event(key);
} }
void Source::ClangViewAutocomplete::start_autocomplete() { void Source::ClangViewAutocomplete::autocomplete_dialog_setup() {
if(!has_focus())
return;
auto iter=get_buffer()->get_insert()->get_iter();
if(!((last_keyval>='0' && last_keyval<='9') ||
(last_keyval>='a' && last_keyval<='z') || (last_keyval>='A' && last_keyval<='Z') ||
last_keyval=='_' || last_keyval=='.' || last_keyval==':' ||
(last_keyval=='>' && iter.backward_char() && iter.backward_char() && *iter=='-'))) {
autocomplete_cancel_starting=true;
return;
}
std::string line=" "+get_line_before();
if((std::count(line.begin(), line.end(), '\"')%2)!=1 && line.find("//")==std::string::npos) {
const boost::regex in_specified_namespace("^(.*[a-zA-Z0-9_\\)\\]\\>])(->|\\.|::)([a-zA-Z0-9_]*)$");
const boost::regex within_namespace("^(.*)([^a-zA-Z0-9_]+)([a-zA-Z0-9_]{3,})$");
boost::smatch sm;
if(boost::regex_match(line, sm, in_specified_namespace)) {
prefix_mutex.lock();
prefix=sm[3].str();
prefix_mutex.unlock();
if((prefix.size()==0 || prefix[0]<'0' || prefix[0]>'9') && !autocomplete_starting && !completion_dialog_shown) {
autocomplete();
}
else if(last_keyval=='.' && autocomplete_starting)
autocomplete_cancel_starting=true;
}
else if(boost::regex_match(line, sm, within_namespace)) {
prefix_mutex.lock();
prefix=sm[3].str();
prefix_mutex.unlock();
if((prefix.size()==0 || prefix[0]<'0' || prefix[0]>'9') && !autocomplete_starting && !completion_dialog_shown) {
autocomplete();
}
}
else
autocomplete_cancel_starting=true;
if(autocomplete_starting || completion_dialog_shown)
delayed_reparse_connection.disconnect();
}
}
void Source::ClangViewAutocomplete::autocomplete() {
if(parse_thread_stop) {
return;
}
if(!autocomplete_starting) {
autocomplete_starting=true;
autocomplete_cancel_starting=false;
std::shared_ptr<std::vector<AutoCompleteData> > ac_data=std::make_shared<std::vector<AutoCompleteData> >();
autocomplete_done_connection.disconnect();
autocomplete_done_connection=autocomplete_done.connect([this, ac_data](){
autocomplete_starting=false;
if(!autocomplete_cancel_starting) {
auto start_iter=get_buffer()->get_insert()->get_iter(); auto start_iter=get_buffer()->get_insert()->get_iter();
if(prefix.size()>0 && !start_iter.backward_chars(prefix.size())) if(prefix.size()>0 && !start_iter.backward_chars(prefix.size()))
return; return;
completion_dialog=std::unique_ptr<CompletionDialog>(new CompletionDialog(*this, get_buffer()->create_mark(start_iter))); autocomplete_dialog=std::unique_ptr<CompletionDialog>(new CompletionDialog(*this, get_buffer()->create_mark(start_iter)));
auto rows=std::make_shared<std::unordered_map<std::string, std::string> >(); autocomplete_dialog_rows.clear();
completion_dialog->on_hide=[this](){ autocomplete_dialog->on_hide=[this](){
get_source_buffer()->end_user_action(); get_source_buffer()->end_user_action();
completion_dialog_shown=false; autocomplete_state=AutocompleteState::IDLE;
parsed=false; parsed=false;
soft_reparse(); soft_reparse();
}; };
completion_dialog->on_select=[this, rows](const std::string& selected, bool hide_window) { autocomplete_dialog->on_select=[this](const std::string& selected, bool hide_window) {
auto row = rows->at(selected); auto row = autocomplete_dialog_rows.at(selected);
get_buffer()->erase(completion_dialog->start_mark->get_iter(), get_buffer()->get_insert()->get_iter()); get_buffer()->erase(autocomplete_dialog->start_mark->get_iter(), get_buffer()->get_insert()->get_iter());
auto iter=get_buffer()->get_insert()->get_iter(); auto iter=get_buffer()->get_insert()->get_iter();
if(*iter=='<' || *iter=='(') { if(*iter=='<' || *iter=='(') {
auto bracket_pos=row.find(*iter); auto bracket_pos=row.find(*iter);
@ -751,8 +759,9 @@ void Source::ClangViewAutocomplete::autocomplete() {
row=row.substr(0, bracket_pos); row=row.substr(0, bracket_pos);
} }
} }
get_buffer()->insert(completion_dialog->start_mark->get_iter(), row); get_buffer()->insert(autocomplete_dialog->start_mark->get_iter(), row);
if(hide_window) { if(hide_window) {
autocomplete_state=AutocompleteState::IDLE;
auto para_pos=row.find('('); auto para_pos=row.find('(');
auto angle_pos=row.find('<'); auto angle_pos=row.find('<');
size_t start_pos=std::string::npos; size_t start_pos=std::string::npos;
@ -765,85 +774,114 @@ void Source::ClangViewAutocomplete::autocomplete() {
start_pos=para_pos; start_pos=para_pos;
end_pos=row.size()-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);
}
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--;
}
}
}
if(start_pos!=std::string::npos && end_pos!=std::string::npos) { if(start_pos!=std::string::npos && end_pos!=std::string::npos) {
auto start_offset=completion_dialog->start_mark->get_iter().get_offset()+start_pos+1; auto start_offset=autocomplete_dialog->start_mark->get_iter().get_offset()+start_pos+1;
auto end_offset=completion_dialog->start_mark->get_iter().get_offset()+end_pos; auto end_offset=autocomplete_dialog->start_mark->get_iter().get_offset()+end_pos;
if(start_offset!=end_offset) if(start_offset!=end_offset)
get_buffer()->select_range(get_buffer()->get_iter_at_offset(start_offset), get_buffer()->get_iter_at_offset(end_offset)); 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==':') {
autocomplete_state=AutocompleteState::IDLE;
autocomplete_restart();
} }
};
for (auto &data : *ac_data) {
std::stringstream ss;
std::string return_value;
for (auto &chunk : data.chunks) {
switch (chunk.kind) {
case clang::CompletionChunk_ResultType:
return_value = chunk.chunk;
break;
case clang::CompletionChunk_Informative: break;
default: ss << chunk.chunk; break;
} }
} }
auto ss_str=ss.str(); };
if (ss_str.length() > 0) { // if length is 0 the result is empty
(*rows)[ss.str() + " --> " + return_value] = ss_str;
completion_dialog->add_row(ss.str() + " --> " + return_value, data.brief_comments);
} }
void Source::ClangViewAutocomplete::autocomplete_check() {
auto iter=get_buffer()->get_insert()->get_iter();
if(iter.backward_char() && iter.backward_char() && (get_source_buffer()->iter_has_context_class(iter, "string") ||
get_source_buffer()->iter_has_context_class(iter, "comment")))
return;
std::string line=" "+get_line_before();
const boost::regex in_specified_namespace("^(.*[a-zA-Z0-9_\\)\\]\\>])(->|\\.|::)([a-zA-Z0-9_]*)$");
const boost::regex within_namespace("^(.*)([^a-zA-Z0-9_]+)([a-zA-Z0-9_]{3,})$");
boost::smatch sm;
if(boost::regex_match(line, sm, in_specified_namespace)) {
prefix_mutex.lock();
prefix=sm[3].str();
prefix_mutex.unlock();
if(autocomplete_state==AutocompleteState::IDLE && (prefix.size()==0 || prefix[0]<'0' || prefix[0]>'9'))
autocomplete();
} }
set_status(""); else if(boost::regex_match(line, sm, within_namespace)) {
if (!rows->empty()) { prefix_mutex.lock();
completion_dialog_shown=true; prefix=sm[3].str();
get_source_buffer()->begin_user_action(); prefix_mutex.unlock();
completion_dialog->show(); if(autocomplete_state==AutocompleteState::IDLE && (prefix.size()==0 || prefix[0]<'0' || prefix[0]>'9'))
autocomplete();
} }
else if(autocomplete_state!=AutocompleteState::IDLE)
soft_reparse(); delayed_reparse_connection.disconnect();
} }
else {
set_status(""); void Source::ClangViewAutocomplete::autocomplete() {
soft_reparse(); if(parse_state!=ParseState::PROCESSING)
start_autocomplete(); return;
if(autocomplete_state==AutocompleteState::STARTING) {
autocomplete_state=AutocompleteState::CANCELED;
return;
} }
}); if(autocomplete_state==AutocompleteState::CANCELED)
return;
autocomplete_state=AutocompleteState::STARTING;
std::shared_ptr<std::map<std::string, std::string> > buffer_map=std::make_shared<std::map<std::string, std::string> >(); autocomplete_data.clear();
auto ustr=get_buffer()->get_text();
set_status("autocomplete...");
if(autocomplete_thread.joinable())
autocomplete_thread.join();
auto buffer=std::make_shared<Glib::ustring>(get_buffer()->get_text());
auto iter=get_buffer()->get_insert()->get_iter(); auto iter=get_buffer()->get_insert()->get_iter();
auto line_nr=iter.get_line()+1; auto line_nr=iter.get_line()+1;
auto column_nr=iter.get_line_offset()+1; auto column_nr=iter.get_line_offset()+1;
auto pos=iter.get_offset()-1; auto pos=iter.get_offset()-1;
while(pos>=0 && ((ustr[pos]>='a' && ustr[pos]<='z') || (ustr[pos]>='A' && ustr[pos]<='Z') || (ustr[pos]>='0' && ustr[pos]<='9') || ustr[pos]=='_')) { while(pos>=0 && (((*buffer)[pos]>='a' && (*buffer)[pos]<='z') || ((*buffer)[pos]>='A' && (*buffer)[pos]<='Z') ||
ustr.replace(pos, 1, " "); ((*buffer)[pos]>='0' && (*buffer)[pos]<='9') || (*buffer)[pos]=='_')) {
buffer->replace(pos, 1, " ");
column_nr--; column_nr--;
pos--; pos--;
} }
(*buffer_map)[this->file_path.string()]=std::move(ustr); //TODO: does this work? autocomplete_thread=std::thread([this, line_nr, column_nr, buffer](){
set_status("autocomplete..."); parse_mutex.lock();
if(autocomplete_thread.joinable()) if(parse_state==ParseState::PROCESSING) {
autocomplete_thread.join(); parse_process_state=ParseProcessState::IDLE;
autocomplete_thread=std::thread([this, ac_data, line_nr, column_nr, buffer_map](){ autocomplete_data=autocomplete_get_suggestions(buffer->raw(), line_nr, column_nr);
parsing_mutex.lock(); }
if(!parse_thread_stop) if(parse_state==ParseState::PROCESSING)
*ac_data=get_autocomplete_suggestions(line_nr, column_nr, *buffer_map);
if(!parse_thread_stop)
autocomplete_done(); autocomplete_done();
else else
autocomplete_fail(); autocomplete_error();
parsing_mutex.unlock(); parse_mutex.unlock();
}); });
} }
}
std::vector<Source::ClangViewAutocomplete::AutoCompleteData> Source::ClangViewAutocomplete::get_autocomplete_suggestions(int line_number, int column, std::map<std::string, std::string>& buffer_map) { std::vector<Source::ClangViewAutocomplete::AutoCompleteData> Source::ClangViewAutocomplete::autocomplete_get_suggestions(const std::string &buffer, int line_number, int column) {
std::vector<AutoCompleteData> suggestions; std::vector<AutoCompleteData> suggestions;
auto results=clang_tu->get_code_completions(buffer_map, line_number, column); auto results=clang_tu->get_code_completions(buffer, line_number, column);
if(results.cx_results==NULL) { if(results.cx_results==NULL) {
parse_thread_stop=true; auto expected=ParseState::PROCESSING;
parse_state.compare_exchange_strong(expected, ParseState::RESTARTING);
return suggestions; return suggestions;
} }
if(!autocomplete_cancel_starting) { if(autocomplete_state==AutocompleteState::STARTING) {
prefix_mutex.lock(); prefix_mutex.lock();
auto prefix_copy=prefix; auto prefix_copy=prefix;
prefix_mutex.unlock(); prefix_mutex.unlock();
@ -871,7 +909,7 @@ std::vector<Source::ClangViewAutocomplete::AutoCompleteData> Source::ClangViewAu
void Source::ClangViewAutocomplete::async_delete() { void Source::ClangViewAutocomplete::async_delete() {
parsing_in_progress->cancel("canceled, freeing resources in the background"); parsing_in_progress->cancel("canceled, freeing resources in the background");
parse_thread_stop=true; parse_state=ParseState::STOP;
delete_thread=std::thread([this](){ delete_thread=std::thread([this](){
//TODO: Is it possible to stop the clang-process in progress? //TODO: Is it possible to stop the clang-process in progress?
if(full_reparse_thread.joinable()) if(full_reparse_thread.joinable())
@ -885,10 +923,16 @@ void Source::ClangViewAutocomplete::async_delete() {
} }
bool Source::ClangViewAutocomplete::full_reparse() { bool Source::ClangViewAutocomplete::full_reparse() {
if(!full_reparse_running && !parse_error) { full_reparse_needed=false;
if(!full_reparse_running) {
auto expected=ParseState::PROCESSING;
if(!parse_state.compare_exchange_strong(expected, ParseState::RESTARTING)) {
expected=ParseState::RESTARTING;
if(!parse_state.compare_exchange_strong(expected, ParseState::RESTARTING))
return false;
}
soft_reparse_needed=false; soft_reparse_needed=false;
full_reparse_running=true; full_reparse_running=true;
parse_thread_stop=true;
if(full_reparse_thread.joinable()) if(full_reparse_thread.joinable())
full_reparse_thread.join(); full_reparse_thread.join();
full_reparse_thread=std::thread([this](){ full_reparse_thread=std::thread([this](){
@ -963,7 +1007,7 @@ Source::ClangViewAutocomplete(file_path, project_path, language) {
iter.forward_char(); iter.forward_char();
} }
get_buffer()->place_cursor(iter); get_buffer()->place_cursor(iter);
while(g_main_context_pending(NULL)) while(g_main_context_pending(NULL)) //TODO: minor: might crash if the buffer is saved and closed really fast right after doing auto indent
g_main_context_iteration(NULL, false); g_main_context_iteration(NULL, false);
scroll_to(get_buffer()->get_insert(), 0.0, 1.0, 0.5); scroll_to(get_buffer()->get_insert(), 0.0, 1.0, 0.5);
} }
@ -1349,11 +1393,12 @@ Source::ClangView::ClangView(const boost::filesystem::path &file_path, const boo
void Source::ClangView::async_delete() { void Source::ClangView::async_delete() {
delayed_reparse_connection.disconnect(); delayed_reparse_connection.disconnect();
parse_done_connection.disconnect(); parse_postprocess_connection.disconnect();
parse_start_connection.disconnect(); parse_preprocess_connection.disconnect();
parse_fail_connection.disconnect(); parse_error_connection.disconnect();
autocomplete_done_connection.disconnect(); autocomplete_error_connection.disconnect();
autocomplete_fail_connection.disconnect(); autocomplete_restart_connection.disconnect();
autocomplete_error_connection.disconnect();
do_restart_parse_connection.disconnect(); do_restart_parse_connection.disconnect();
delayed_tag_similar_tokens_connection.disconnect(); delayed_tag_similar_tokens_connection.disconnect();
ClangViewAutocomplete::async_delete(); ClangViewAutocomplete::async_delete();

60
src/source_clang.h

@ -12,6 +12,9 @@
namespace Source { namespace Source {
class ClangViewParse : public View { class ClangViewParse : public View {
protected:
enum class ParseState {PROCESSING, RESTARTING, STOP};
enum class ParseProcessState {IDLE, STARTING, PREPROCESSING, PROCESSING, POSTPROCESSING};
public: public:
class TokenRange { class TokenRange {
public: public:
@ -22,26 +25,23 @@ namespace Source {
}; };
ClangViewParse(const boost::filesystem::path &file_path, const boost::filesystem::path& project_path, Glib::RefPtr<Gsv::Language> language); ClangViewParse(const boost::filesystem::path &file_path, const boost::filesystem::path& project_path, Glib::RefPtr<Gsv::Language> language);
bool on_key_press_event(GdkEventKey* key) override;
void configure() override; void configure() override;
void soft_reparse() override; void soft_reparse() override;
protected: protected:
void init_parse(); void parse_initialize();
std::unique_ptr<clang::TranslationUnit> clang_tu; std::unique_ptr<clang::TranslationUnit> clang_tu;
std::mutex parsing_mutex;
std::unique_ptr<clang::Tokens> clang_tokens; std::unique_ptr<clang::Tokens> clang_tokens;
sigc::connection delayed_reparse_connection; sigc::connection delayed_reparse_connection;
std::shared_ptr<Terminal::InProgress> parsing_in_progress; std::shared_ptr<Terminal::InProgress> parsing_in_progress;
std::thread parse_thread;
std::atomic<bool> parse_thread_stop;
std::atomic<bool> parse_error;
void show_diagnostic_tooltips(const Gdk::Rectangle &rectangle) override; void show_diagnostic_tooltips(const Gdk::Rectangle &rectangle) override;
void show_type_tooltips(const Gdk::Rectangle &rectangle) override; void show_type_tooltips(const Gdk::Rectangle &rectangle) override;
bool on_key_press_event(GdkEventKey* key) override;
boost::regex bracket_regex; boost::regex bracket_regex;
boost::regex no_bracket_statement_regex; boost::regex no_bracket_statement_regex;
boost::regex no_bracket_no_para_statement_regex; boost::regex no_bracket_no_para_statement_regex;
@ -49,28 +49,30 @@ namespace Source {
std::set<int> diagnostic_offsets; std::set<int> diagnostic_offsets;
std::vector<FixIt> fix_its; std::vector<FixIt> fix_its;
sigc::connection parse_done_connection; std::thread parse_thread;
sigc::connection parse_start_connection; std::mutex parse_mutex;
sigc::connection parse_fail_connection; std::atomic<ParseState> parse_state;
std::atomic<ParseProcessState> parse_process_state;
sigc::connection parse_preprocess_connection;
sigc::connection parse_postprocess_connection;
sigc::connection parse_error_connection;
private: private:
std::map<std::string, std::string> get_buffer_map() const; Glib::Dispatcher parse_preprocess;
Glib::Dispatcher parse_postprocess;
Glib::Dispatcher parse_error;
Glib::ustring parse_thread_buffer;
void update_syntax(); void update_syntax();
std::set<std::string> last_syntax_tags; std::set<std::string> last_syntax_tags;
void update_diagnostics(); void update_diagnostics();
static clang::Index clang_index; static clang::Index clang_index;
std::vector<std::string> get_compilation_commands(); std::vector<std::string> get_compilation_commands();
Glib::Dispatcher parse_done;
Glib::Dispatcher parse_start;
Glib::Dispatcher parse_fail;
std::map<std::string, std::string> parse_thread_buffer_map;
std::mutex parse_thread_buffer_map_mutex;
std::atomic<bool> parse_thread_go;
std::atomic<bool> parse_thread_mapped;
}; };
class ClangViewAutocomplete : public ClangViewParse { class ClangViewAutocomplete : public ClangViewParse {
protected:
enum class AutocompleteState {IDLE, STARTING, CANCELED, SHOWN};
public: public:
class AutoCompleteData { class AutoCompleteData {
public: public:
@ -81,26 +83,30 @@ namespace Source {
}; };
ClangViewAutocomplete(const boost::filesystem::path &file_path, const boost::filesystem::path& project_path, Glib::RefPtr<Gsv::Language> language); ClangViewAutocomplete(const boost::filesystem::path &file_path, const boost::filesystem::path& project_path, Glib::RefPtr<Gsv::Language> language);
bool on_key_press_event(GdkEventKey* key) override;
virtual void async_delete(); virtual void async_delete();
bool full_reparse() override; bool full_reparse() override;
protected: protected:
bool on_key_press_event(GdkEventKey* key) override;
std::thread autocomplete_thread; std::thread autocomplete_thread;
sigc::connection autocomplete_done_connection; sigc::connection autocomplete_done_connection;
sigc::connection autocomplete_fail_connection; sigc::connection autocomplete_restart_connection;
sigc::connection autocomplete_error_connection;
sigc::connection do_delete_object_connection; sigc::connection do_delete_object_connection;
sigc::connection do_restart_parse_connection; sigc::connection do_restart_parse_connection;
private: private:
void start_autocomplete(); std::atomic<AutocompleteState> autocomplete_state;
void autocomplete_dialog_setup();
void autocomplete_check();
void autocomplete(); void autocomplete();
std::unique_ptr<CompletionDialog> completion_dialog; std::vector<AutoCompleteData> autocomplete_data;
bool completion_dialog_shown=false; std::unique_ptr<CompletionDialog> autocomplete_dialog;
std::vector<AutoCompleteData> get_autocomplete_suggestions(int line_number, int column, std::map<std::string, std::string>& buffer_map); std::unordered_map<std::string, std::string> autocomplete_dialog_rows;
std::vector<AutoCompleteData> autocomplete_get_suggestions(const std::string &buffer, int line_number, int column);
Glib::Dispatcher autocomplete_done; Glib::Dispatcher autocomplete_done;
Glib::Dispatcher autocomplete_fail; Glib::Dispatcher autocomplete_restart;
bool autocomplete_starting=false; Glib::Dispatcher autocomplete_error;
std::atomic<bool> autocomplete_cancel_starting;
guint last_keyval=0; guint last_keyval=0;
std::string prefix; std::string prefix;
std::mutex prefix_mutex; std::mutex prefix_mutex;

43
src/window.cc

@ -34,7 +34,8 @@ Window::Window() : compiling(false) {
add(vpaned); add(vpaned);
directory_and_notebook_panes.pack1(*Singleton::directories, Gtk::SHRINK); directories_scrolled_window.add(*Singleton::directories);
directory_and_notebook_panes.pack1(directories_scrolled_window, Gtk::SHRINK);
notebook_vbox.pack_start(notebook); notebook_vbox.pack_start(notebook);
notebook_vbox.pack_end(entry_box, Gtk::PACK_SHRINK); notebook_vbox.pack_end(entry_box, Gtk::PACK_SHRINK);
directory_and_notebook_panes.pack2(notebook_vbox, Gtk::SHRINK); directory_and_notebook_panes.pack2(notebook_vbox, Gtk::SHRINK);
@ -96,12 +97,9 @@ Window::Window() : compiling(false) {
if(view->full_reparse_needed) { if(view->full_reparse_needed) {
if(!view->full_reparse()) if(!view->full_reparse())
Singleton::terminal->async_print("Error: failed to reparse "+view->file_path.string()+". Please reopen the file manually.\n", true); Singleton::terminal->async_print("Error: failed to reparse "+view->file_path.string()+". Please reopen the file manually.\n", true);
view->full_reparse_needed=false;
} }
else if(view->soft_reparse_needed) { else if(view->soft_reparse_needed)
view->soft_reparse(); view->soft_reparse();
view->soft_reparse_needed=false;
}
view->set_status(view->status); view->set_status(view->status);
view->set_info(view->info); view->set_info(view->info);
@ -336,10 +334,12 @@ void Window::set_menu_actions() {
}); });
menu->add_action("source_center_cursor", [this]() { menu->add_action("source_center_cursor", [this]() {
if(notebook.get_current_page()!=-1) { if(notebook.get_current_page()!=-1) {
auto view=notebook.get_current_view();
while(g_main_context_pending(NULL)) while(g_main_context_pending(NULL))
g_main_context_iteration(NULL, false); g_main_context_iteration(NULL, false);
if(notebook.get_current_page()!=-1) if(notebook.get_current_page()!=-1 && notebook.get_current_view()==view)
notebook.get_current_view()->scroll_to(notebook.get_current_view()->get_buffer()->get_insert(), 0.0, 1.0, 0.5); view->scroll_to(view->get_buffer()->get_insert(), 0.0, 1.0, 0.5);
} }
}); });
@ -397,20 +397,21 @@ void Window::set_menu_actions() {
notebook.open(declaration_file); notebook.open(declaration_file);
auto line=static_cast<int>(location.line)-1; auto line=static_cast<int>(location.line)-1;
auto index=static_cast<int>(location.index)-1; auto index=static_cast<int>(location.index)-1;
auto buffer=notebook.get_current_view()->get_buffer(); auto view=notebook.get_current_view();
line=std::min(line, buffer->get_line_count()-1); line=std::min(line, view->get_buffer()->get_line_count()-1);
if(line>=0) { if(line>=0) {
auto iter=buffer->get_iter_at_line(line); auto iter=view->get_buffer()->get_iter_at_line(line);
while(!iter.ends_line()) while(!iter.ends_line())
iter.forward_char(); iter.forward_char();
auto end_line_index=iter.get_line_index(); auto end_line_index=iter.get_line_index();
index=std::min(index, end_line_index); index=std::min(index, end_line_index);
buffer->place_cursor(buffer->get_iter_at_line_index(line, index));
while(g_main_context_pending(NULL)) while(g_main_context_pending(NULL))
g_main_context_iteration(NULL, false); g_main_context_iteration(NULL, false);
if(notebook.get_current_page()!=-1) if(notebook.get_current_page()!=-1 && notebook.get_current_view()==view) {
notebook.get_current_view()->scroll_to(notebook.get_current_view()->get_buffer()->get_insert(), 0.0, 1.0, 0.5); view->get_buffer()->place_cursor(view->get_buffer()->get_iter_at_line_index(line, index));
view->scroll_to(view->get_buffer()->get_insert(), 0.0, 1.0, 0.5);
}
} }
} }
} }
@ -845,16 +846,18 @@ void Window::goto_line_entry() {
if(notebook.get_current_page()!=-1) { if(notebook.get_current_page()!=-1) {
entry_box.entries.emplace_back("", [this](const std::string& content){ entry_box.entries.emplace_back("", [this](const std::string& content){
if(notebook.get_current_page()!=-1) { if(notebook.get_current_page()!=-1) {
auto buffer=notebook.get_current_view()->get_buffer(); auto view=notebook.get_current_view();
try { try {
auto line = stoi(content); auto line = stoi(content);
if(line>0 && line<=buffer->get_line_count()) { if(line>0 && line<=view->get_buffer()->get_line_count()) {
line--; line--;
buffer->place_cursor(buffer->get_iter_at_line(line));
while(g_main_context_pending(NULL)) while(g_main_context_pending(NULL))
g_main_context_iteration(NULL, false); g_main_context_iteration(NULL, false);
if(notebook.get_current_page()!=-1) if(notebook.get_current_page()!=-1 && notebook.get_current_view()==view) {
notebook.get_current_view()->scroll_to(buffer->get_insert(), 0.0, 1.0, 0.5); view->get_buffer()->place_cursor(view->get_buffer()->get_iter_at_line(line));
view->scroll_to(view->get_buffer()->get_insert(), 0.0, 1.0, 0.5);
}
} }
} }
catch(const std::exception &e) {} catch(const std::exception &e) {}
@ -884,6 +887,7 @@ void Window::rename_token_entry() {
label_it->update(0, ""); label_it->update(0, "");
entry_box.entries.emplace_back(token->spelling, [this, token](const std::string& content){ entry_box.entries.emplace_back(token->spelling, [this, token](const std::string& content){
if(notebook.get_current_page()!=-1 && content!=token->spelling) { if(notebook.get_current_page()!=-1 && content!=token->spelling) {
std::vector<int> modified_pages;
for(int c=0;c<notebook.size();c++) { for(int c=0;c<notebook.size();c++) {
auto view=notebook.get_view(c); auto view=notebook.get_view(c);
if(view->rename_similar_tokens) { if(view->rename_similar_tokens) {
@ -891,9 +895,12 @@ void Window::rename_token_entry() {
if(number>0) { if(number>0) {
Singleton::terminal->print("Replaced "+std::to_string(number)+" occurrences in file "+view->file_path.string()+"\n"); Singleton::terminal->print("Replaced "+std::to_string(number)+" occurrences in file "+view->file_path.string()+"\n");
notebook.save(c); notebook.save(c);
modified_pages.emplace_back(c);
} }
} }
} }
for(auto &page: modified_pages)
notebook.get_view(page)->soft_reparse_needed=false;
entry_box.hide(); entry_box.hide();
} }
}); });

2
src/window.h

@ -11,6 +11,7 @@ public:
Window(); Window();
Notebook notebook; Notebook notebook;
protected:
bool on_key_press_event(GdkEventKey *event) override; bool on_key_press_event(GdkEventKey *event) override;
bool on_delete_event(GdkEventAny *event) override; bool on_delete_event(GdkEventAny *event) override;
@ -19,6 +20,7 @@ private:
Gtk::Paned directory_and_notebook_panes; Gtk::Paned directory_and_notebook_panes;
Gtk::VBox notebook_vbox; Gtk::VBox notebook_vbox;
Gtk::VBox terminal_vbox; Gtk::VBox terminal_vbox;
Gtk::ScrolledWindow directories_scrolled_window;
Gtk::ScrolledWindow terminal_scrolled_window; Gtk::ScrolledWindow terminal_scrolled_window;
Gtk::HBox info_and_status_hbox; Gtk::HBox info_and_status_hbox;
Gtk::AboutDialog about; Gtk::AboutDialog about;

Loading…
Cancel
Save