Browse Source

Merge pull request #54 from eidheim/master

Many bug fixes and improved clang indentation
merge-requests/365/head
Jørgen Lien Sellæg 10 years ago
parent
commit
67e6c6e38a
  1. 4
      src/CMakeLists.txt
  2. 13
      src/directories.cc
  3. 6
      src/juci.cc
  4. 2
      src/logging.h
  5. 4
      src/menu.cc
  6. 49
      src/notebook.cc
  7. 2
      src/notebook.h
  8. 6
      src/selectiondialog.cc
  9. 457
      src/source.cc
  10. 24
      src/source.h
  11. 65
      src/sourcefile.cc
  12. 4
      src/sourcefile.h
  13. 42
      src/terminal.cc
  14. 24
      src/tooltips.cc
  15. 29
      src/window.cc

4
src/CMakeLists.txt

@ -3,7 +3,9 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3 -std=c++11 -pthread -Wall -Wno-reord
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_LIST_DIR}/cmake/Modules/") set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_LIST_DIR}/cmake/Modules/")
if(APPLE) if(APPLE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -L/usr/local/opt/gettext/lib -I/usr/local/opt/gettext/include -undefined dynamic_lookup") #T set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -undefined dynamic_lookup")
link_directories(/usr/local/lib /usr/local/opt/gettext/lib)
include_directories(/usr/local/opt/gettext/include)
set(CMAKE_MACOSX_RPATH 1) set(CMAKE_MACOSX_RPATH 1)
set(ENV{PKG_CONFIG_PATH} "$ENV{PKG_CONFIG_PATH}:/usr/local/lib/pkgconfig:/opt/X11/lib/pkgconfig") set(ENV{PKG_CONFIG_PATH} "$ENV{PKG_CONFIG_PATH}:/usr/local/lib/pkgconfig:/opt/X11/lib/pkgconfig")
endif() endif()

13
src/directories.cc

@ -23,7 +23,7 @@ namespace sigc {
} }
Directories::Directories() : stop_update_thread(false) { Directories::Directories() : stop_update_thread(false) {
DEBUG("adding treeview to scrolledwindow"); DEBUG("start");
add(tree_view); add(tree_view);
set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC); set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
tree_store = Gtk::TreeStore::create(column_record); tree_store = Gtk::TreeStore::create(column_record);
@ -34,7 +34,6 @@ Directories::Directories() : stop_update_thread(false) {
tree_view.set_search_column(column_record.name); tree_view.set_search_column(column_record.name);
tree_view.signal_row_activated().connect([this](const Gtk::TreeModel::Path& path, Gtk::TreeViewColumn* column){ tree_view.signal_row_activated().connect([this](const Gtk::TreeModel::Path& path, Gtk::TreeViewColumn* column){
INFO("Directory navigation");
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);
@ -115,11 +114,10 @@ Directories::~Directories() {
} }
void Directories::open(const boost::filesystem::path& dir_path) { void Directories::open(const boost::filesystem::path& dir_path) {
DEBUG("start");
if(dir_path=="") if(dir_path=="")
return; return;
INFO("Open folder");
tree_store->clear(); tree_store->clear();
update_mutex.lock(); update_mutex.lock();
last_write_times.clear(); last_write_times.clear();
@ -138,18 +136,21 @@ void Directories::open(const boost::filesystem::path& dir_path) {
current_path=dir_path; current_path=dir_path;
DEBUG("Folder opened"); DEBUG("end");
} }
void Directories::update() { void Directories::update() {
DEBUG("start");
update_mutex.lock(); update_mutex.lock();
for(auto &last_write_time: last_write_times) { for(auto &last_write_time: last_write_times) {
add_path(last_write_time.first, last_write_time.second.first); add_path(last_write_time.first, last_write_time.second.first);
} }
update_mutex.unlock(); update_mutex.unlock();
DEBUG("end");
} }
void Directories::select(const boost::filesystem::path &path) { void Directories::select(const boost::filesystem::path &path) {
DEBUG("start");
if(current_path=="") if(current_path=="")
return; return;
@ -189,10 +190,10 @@ void Directories::select(const boost::filesystem::path &path) {
} }
return false; return false;
}); });
DEBUG("end");
} }
bool Directories::ignored(std::string path) { bool Directories::ignored(std::string path) {
DEBUG("Checking if file-/directory is filtered");
std::transform(path.begin(), path.end(), path.begin(), ::tolower); std::transform(path.begin(), path.end(), path.begin(), ::tolower);
for(std::string &i : Singleton::Config::directories()->exceptions) { for(std::string &i : Singleton::Config::directories()->exceptions) {

6
src/juci.cc

@ -6,9 +6,9 @@
using namespace std; //TODO: remove using namespace std; //TODO: remove
void init_logging() { void init_logging() {
add_common_attributes(); boost::log::add_common_attributes();
add_file_log(keywords::file_name = Singleton::log_dir() + "juci.log", boost::log::add_file_log(boost::log::keywords::file_name = Singleton::log_dir() + "juci.log",
keywords::auto_flush = true); boost::log::keywords::auto_flush = true);
INFO("Logging initalized"); INFO("Logging initalized");
} }

2
src/logging.h

@ -10,8 +10,6 @@
#include <boost/log/sources/severity_logger.hpp> #include <boost/log/sources/severity_logger.hpp>
#include <boost/log/sources/record_ostream.hpp> #include <boost/log/sources/record_ostream.hpp>
using namespace boost::log;
#define TRACE_VAR(x) BOOST_LOG_TRIVIAL(trace) << "** Trace: " << __FILE__ << "@" << __func__ << "(" << __LINE__ << "): " << #x << "=<" << x << ">"; #define TRACE_VAR(x) BOOST_LOG_TRIVIAL(trace) << "** Trace: " << __FILE__ << "@" << __func__ << "(" << __LINE__ << "): " << #x << "=<" << x << ">";
#define DEBUG_VAR(x) BOOST_LOG_TRIVIAL(debug) << "** Debug: " << __FILE__ << "@" << __func__ << "(" << __LINE__ << "): " << #x << "=<" << x << ">"; #define DEBUG_VAR(x) BOOST_LOG_TRIVIAL(debug) << "** Debug: " << __FILE__ << "@" << __func__ << "(" << __LINE__ << "): " << #x << "=<" << x << ">";
#define INFO_VAR(x) BOOST_LOG_TRIVIAL(info) << "** Info: " << __FILE__ << "@" << __func__ << "(" << __LINE__ << "): " << #x << "=<" << x << ">"; #define INFO_VAR(x) BOOST_LOG_TRIVIAL(info) << "** Info: " << __FILE__ << "@" << __func__ << "(" << __LINE__ << "): " << #x << "=<" << x << ">";

4
src/menu.cc

@ -1,8 +1,7 @@
#include "menu.h" #include "menu.h"
#include "logging.h" #include <iostream>
Menu::Menu() : box(Gtk::ORIENTATION_VERTICAL) { Menu::Menu() : box(Gtk::ORIENTATION_VERTICAL) {
INFO("Creating menu");
action_group = Gtk::ActionGroup::create(); action_group = Gtk::ActionGroup::create();
ui_manager = Gtk::UIManager::create(); ui_manager = Gtk::UIManager::create();
@ -15,7 +14,6 @@ Menu::Menu() : box(Gtk::ORIENTATION_VERTICAL) {
action_group->add(Gtk::Action::create("SourceMenu", "_Source")); action_group->add(Gtk::Action::create("SourceMenu", "_Source"));
action_group->add(Gtk::Action::create("PluginMenu", "_Plugins")); action_group->add(Gtk::Action::create("PluginMenu", "_Plugins"));
action_group->add(Gtk::Action::create("HelpMenu", "Help")); action_group->add(Gtk::Action::create("HelpMenu", "Help"));
INFO("Menu created");
} }
Gtk::Widget& Menu::get_widget() { Gtk::Widget& Menu::get_widget() {

49
src/notebook.cc

@ -32,21 +32,15 @@ int Notebook::size() {
} }
Source::View* Notebook::get_view(int page) { Source::View* Notebook::get_view(int page) {
if(page>=size())
return nullptr;
return source_views.at(page); return source_views.at(page);
} }
Source::View* Notebook::get_current_view() { Source::View* Notebook::get_current_view() {
INFO("Getting sourceview");
if(get_current_page()==-1)
return nullptr;
return get_view(get_current_page()); return get_view(get_current_page());
} }
void Notebook::open(const boost::filesystem::path &file_path) { void Notebook::open(const boost::filesystem::path &file_path) {
INFO("Notebook open file"); DEBUG("start");
INFO("Notebook create page");
for(int c=0;c<size();c++) { for(int c=0;c<size();c++) {
if(file_path==get_view(c)->file_path) { if(file_path==get_view(c)->file_path) {
set_current_page(c); set_current_page(c);
@ -112,22 +106,28 @@ void Notebook::open(const boost::filesystem::path &file_path) {
}); });
get_current_view()->on_update_status=[this](Source::View* view, const std::string &status) { get_current_view()->on_update_status=[this](Source::View* view, const std::string &status) {
if(get_current_view()==view) if(get_current_page()!=-1 && get_current_view()==view)
Singleton::status()->set_text(status); Singleton::status()->set_text(status);
}; };
DEBUG("end");
} }
bool Notebook::save(int page) { bool Notebook::save(int page, bool reparse_needed) {
if(page>=size()) DEBUG("start");
if(page>=size()) {
DEBUG("end false");
return false; return false;
}
auto view=get_view(page); auto view=get_view(page);
if (view->file_path != "" && view->get_buffer()->get_modified()) { if (view->file_path != "" && view->get_buffer()->get_modified()) {
if(juci::filesystem::write(view->file_path, view->get_buffer())) { if(juci::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)) {
for(auto a_view: source_views) { for(auto a_view: source_views) {
if(auto a_clang_view=dynamic_cast<Source::ClangView*>(a_view)) { if(auto a_clang_view=dynamic_cast<Source::ClangView*>(a_view)) {
if(clang_view!=a_clang_view) if(clang_view!=a_clang_view)
a_clang_view->start_reparse_needed=true; a_clang_view->reparse_needed=true;
}
} }
} }
} }
@ -145,53 +145,54 @@ bool Notebook::save(int page) {
if(source_clang_view->restart_parse()) if(source_clang_view->restart_parse())
Singleton::terminal()->async_print("Reparsing "+source_clang_view->file_path.string()+"\n"); Singleton::terminal()->async_print("Reparsing "+source_clang_view->file_path.string()+"\n");
else else
Singleton::terminal()->async_print("Already reparsing "+source_clang_view->file_path.string()+". Please reopen the file manually.\n"); Singleton::terminal()->async_print("Error: failed to reparse "+source_clang_view->file_path.string()+". Please reopen the file manually.\n");
} }
} }
} }
} }
} }
DEBUG("end true");
return true; return true;
} }
Singleton::terminal()->print("Error: could not save file " +view->file_path.string()+"\n"); Singleton::terminal()->print("Error: could not save file " +view->file_path.string()+"\n");
} }
DEBUG("end false");
return false; return false;
} }
bool Notebook::save_current() { bool Notebook::save_current() {
INFO("Notebook save current file");
if(get_current_page()==-1) if(get_current_page()==-1)
return false; return false;
return save(get_current_page()); return save(get_current_page(), true);
} }
bool Notebook::close_current_page() { bool Notebook::close_current_page() {
INFO("Notebook close page"); DEBUG("start");
if (size() != 0) { if (get_current_page()!=-1) {
if(get_current_view()->get_buffer()->get_modified()){ if(get_current_view()->get_buffer()->get_modified()){
if(!save_modified_dialog()) if(!save_modified_dialog()) {
DEBUG("end false");
return false; return false;
} }
}
int page = get_current_page(); int page = get_current_page();
remove_page(page); remove_page(page);
if(get_current_page()==-1)
Singleton::status()->set_text("");
auto source_view=source_views.at(page); auto source_view=source_views.at(page);
source_views.erase(source_views.begin()+ page);
scrolled_windows.erase(scrolled_windows.begin()+page);
hboxes.erase(hboxes.begin()+page);
if(auto source_clang_view=dynamic_cast<Source::ClangView*>(source_view)) if(auto source_clang_view=dynamic_cast<Source::ClangView*>(source_view))
source_clang_view->async_delete(); source_clang_view->async_delete();
else else
delete source_view; delete source_view;
source_views.erase(source_views.begin()+ page);
scrolled_windows.erase(scrolled_windows.begin()+page);
hboxes.erase(hboxes.begin()+page);
} }
DEBUG("end true");
return true; return true;
} }
bool Notebook::save_modified_dialog() { bool Notebook::save_modified_dialog() {
INFO("Notebook::save_modified_dialog");
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_secondary_text("Do you want to save: " + get_current_view()->file_path.string()+" ?"); dialog.set_secondary_text("Do you want to save: " + get_current_view()->file_path.string()+" ?");
int result = dialog.run(); int result = dialog.run();
if(result==Gtk::RESPONSE_YES) { if(result==Gtk::RESPONSE_YES) {

2
src/notebook.h

@ -18,7 +18,7 @@ public:
Source::View* get_current_view(); Source::View* get_current_view();
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 save(int page, bool reparse_needed=false);
bool save_current(); bool save_current();
private: private:

6
src/selectiondialog.cc

@ -121,7 +121,6 @@ void SelectionDialogBase::update_tooltips() {
} }
void SelectionDialogBase::move() { void SelectionDialogBase::move() {
INFO("SelectionDialog set position");
Gdk::Rectangle rectangle; Gdk::Rectangle rectangle;
text_view.get_iter_location(start_mark->get_iter(), rectangle); text_view.get_iter_location(start_mark->get_iter(), rectangle);
int buffer_x=rectangle.get_x(); int buffer_x=rectangle.get_x();
@ -134,8 +133,6 @@ void SelectionDialogBase::move() {
} }
void SelectionDialogBase::resize() { void SelectionDialogBase::resize() {
INFO("SelectionDialog set size");
if(list_view_text.get_realized()) { if(list_view_text.get_realized()) {
int row_width=0, row_height; int row_width=0, row_height;
Gdk::Rectangle rect; Gdk::Rectangle rect;
@ -214,6 +211,7 @@ void SelectionDialog::show() {
else { else {
auto last_it=list_view_text.get_model()->children().end(); auto last_it=list_view_text.get_model()->children().end();
last_it--; last_it--;
if(last_it)
list_view_text.set_cursor(list_view_text.get_model()->get_path(last_it)); list_view_text.set_cursor(list_view_text.get_model()->get_path(last_it));
} }
return true; return true;
@ -273,6 +271,7 @@ bool SelectionDialog::on_key_press(GdkEventKey* key) {
else { else {
auto last_it=list_view_text.get_model()->children().end(); auto last_it=list_view_text.get_model()->children().end();
last_it--; last_it--;
if(last_it)
list_view_text.set_cursor(list_view_text.get_model()->get_path(last_it)); list_view_text.set_cursor(list_view_text.get_model()->get_path(last_it));
} }
return true; return true;
@ -422,6 +421,7 @@ bool CompletionDialog::on_key_press(GdkEventKey* key) {
else { else {
auto last_it=list_view_text.get_model()->children().end(); auto last_it=list_view_text.get_model()->children().end();
last_it--; last_it--;
if(last_it)
list_view_text.set_cursor(list_view_text.get_model()->get_path(last_it)); list_view_text.set_cursor(list_view_text.get_model()->get_path(last_it));
} }
select(false); select(false);

457
src/source.cc

@ -51,8 +51,8 @@ AspellConfig* Source::View::spellcheck_config=NULL;
Source::View::View(const boost::filesystem::path &file_path): file_path(file_path) { Source::View::View(const boost::filesystem::path &file_path): file_path(file_path) {
set_smart_home_end(Gsv::SMART_HOME_END_BEFORE); set_smart_home_end(Gsv::SMART_HOME_END_BEFORE);
get_source_buffer()->begin_not_undoable_action(); get_source_buffer()->begin_not_undoable_action();
if(!juci::filesystem::read(file_path, get_buffer())) if(juci::filesystem::read(file_path, get_buffer())==-1)
Singleton::terminal()->print("Error: "+file_path.string()+" is not a valid UTF-8 file.\n"); Singleton::terminal()->print("Error: "+file_path.string()+" is not a valid UTF-8 file.");
get_source_buffer()->end_not_undoable_action(); get_source_buffer()->end_not_undoable_action();
get_buffer()->place_cursor(get_buffer()->get_iter_at_offset(0)); get_buffer()->place_cursor(get_buffer()->get_iter_at_offset(0));
search_settings = gtk_source_search_settings_new(); search_settings = gtk_source_search_settings_new();
@ -186,14 +186,19 @@ Source::View::View(const boost::filesystem::path &file_path): file_path(file_pat
delayed_spellcheck_suggestions_connection.disconnect(); delayed_spellcheck_suggestions_connection.disconnect();
auto iter=get_buffer()->get_insert()->get_iter(); auto iter=get_buffer()->get_insert()->get_iter();
bool ends_line=iter.ends_line();
if(iter.backward_char()) { if(iter.backward_char()) {
auto context_iter=iter; auto context_iter=iter;
if(context_iter.backward_char()) { bool backward_success=true;
if(ends_line || *iter=='/' || *iter=='*') //iter_has_context_class is sadly bugged
backward_success=context_iter.backward_char();
if(backward_success) {
if((spellcheck_all && !get_source_buffer()->iter_has_context_class(context_iter, "no-spell-check")) || get_source_buffer()->iter_has_context_class(context_iter, "comment") || get_source_buffer()->iter_has_context_class(context_iter, "string")) { if((spellcheck_all && !get_source_buffer()->iter_has_context_class(context_iter, "no-spell-check")) || get_source_buffer()->iter_has_context_class(context_iter, "comment") || get_source_buffer()->iter_has_context_class(context_iter, "string")) {
if(*iter==32 || *iter==45) { //Might have used space or - to split two words if(last_keyval_is_backspace && !is_word_iter(iter) && iter.forward_char()) {} //backspace fix
if(!is_word_iter(iter)) { //Might have used space or - to split two words
auto first=iter; auto first=iter;
auto second=iter; auto second=iter;
if(first.backward_char() && second.forward_char()) { if(first.backward_char() && second.forward_char() && !second.starts_line()) {
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);
spellcheck_word(word.first, word.second); spellcheck_word(word.first, word.second);
@ -330,6 +335,9 @@ Source::View::~View() {
g_clear_object(&search_context); g_clear_object(&search_context);
g_clear_object(&search_settings); g_clear_object(&search_settings);
delayed_tooltips_connection.disconnect();
delayed_spellcheck_suggestions_connection.disconnect();
if(spellcheck_checker!=NULL) if(spellcheck_checker!=NULL)
delete_aspell_speller(spellcheck_checker); delete_aspell_speller(spellcheck_checker);
} }
@ -396,8 +404,18 @@ void Source::View::replace_all(const std::string &replacement) {
} }
void Source::View::paste() { void Source::View::paste() {
Gtk::Clipboard::get()->request_text([this](const Glib::ustring& text){ auto text=Gtk::Clipboard::get()->wait_for_text();
auto line=get_line_before_insert(); //replace stand-alone carriage returns (which makes clang return wrong line index) with newlines
for(auto it=text.begin();it!=text.end();it++) {
if(*it=='\r') {
auto it2=it;
it2++;
if(it2==text.end() || *it2!='\n')
text.replace(it, it2, "\n");
}
}
auto line=get_line_before();
std::smatch sm; std::smatch sm;
std::string prefix_tabs; std::string prefix_tabs;
if(!get_buffer()->get_has_selection() && std::regex_match(line, sm, tabs_regex) && sm[2].str().size()==0) { if(!get_buffer()->get_has_selection() && std::regex_match(line, sm, tabs_regex) && sm[2].str().size()==0) {
@ -419,14 +437,17 @@ void Source::View::paste() {
paste_line=true; paste_line=true;
} }
if(paste_line) { if(paste_line) {
bool empty_line=true;
std::string line=text.substr(start_line, end_line-start_line); std::string line=text.substr(start_line, end_line-start_line);
size_t tabs=0; size_t tabs=0;
for(auto chr: line) { for(auto chr: line) {
if(chr==tab_char) if(chr==tab_char)
tabs++; tabs++;
else else {
empty_line=false;
break; break;
} }
}
if(first_paste_line) { if(first_paste_line) {
if(tabs!=0) { if(tabs!=0) {
first_paste_line_has_tabs=true; first_paste_line_has_tabs=true;
@ -434,7 +455,7 @@ void Source::View::paste() {
} }
first_paste_line=false; first_paste_line=false;
} }
else else if(!empty_line)
paste_line_tabs=std::min(paste_line_tabs, tabs); paste_line_tabs=std::min(paste_line_tabs, tabs);
start_line=end_line+1; start_line=end_line+1;
@ -458,15 +479,28 @@ void Source::View::paste() {
paste_line=true; paste_line=true;
} }
if(paste_line) { if(paste_line) {
std::string line=text.substr(start_line, end_line-start_line);
size_t line_tabs=0;
for(auto chr: line) {
if(chr==tab_char)
line_tabs++;
else
break;
}
auto tabs=paste_line_tabs;
if(!(first_paste_line && !first_paste_line_has_tabs) && line_tabs<paste_line_tabs) {
tabs=line_tabs;
}
if(first_paste_line) { if(first_paste_line) {
if(first_paste_line_has_tabs) if(first_paste_line_has_tabs)
get_buffer()->insert_at_cursor(text.substr(start_line+paste_line_tabs, end_line-start_line-paste_line_tabs)); get_buffer()->insert_at_cursor(text.substr(start_line+tabs, end_line-start_line-tabs));
else else
get_buffer()->insert_at_cursor(text.substr(start_line, end_line-start_line)); get_buffer()->insert_at_cursor(text.substr(start_line, end_line-start_line));
first_paste_line=false; first_paste_line=false;
} }
else else
get_buffer()->insert_at_cursor('\n'+prefix_tabs+text.substr(start_line+paste_line_tabs, end_line-start_line-paste_line_tabs)); get_buffer()->insert_at_cursor('\n'+prefix_tabs+text.substr(start_line+tabs, end_line-start_line-tabs));
start_line=end_line+1; start_line=end_line+1;
paste_line=false; paste_line=false;
} }
@ -475,9 +509,10 @@ void Source::View::paste() {
scroll_to(get_buffer()->get_insert()); scroll_to(get_buffer()->get_insert());
get_source_buffer()->end_user_action(); get_source_buffer()->end_user_action();
} }
else else {
Gtk::Clipboard::get()->set_text(text);
get_buffer()->paste_clipboard(Gtk::Clipboard::get()); get_buffer()->paste_clipboard(Gtk::Clipboard::get());
}); }
} }
void Source::View::set_status(const std::string &status) { void Source::View::set_status(const std::string &status) {
@ -486,22 +521,144 @@ void Source::View::set_status(const std::string &status) {
on_update_status(this, status); on_update_status(this, status);
} }
string Source::View::get_line(size_t line_number) { std::string Source::View::get_line(const Gtk::TextIter &iter) {
Gtk::TextIter line_it = get_source_buffer()->get_iter_at_line(line_number); auto line_start_it = get_buffer()->get_iter_at_line(iter.get_line());
Gtk::TextIter line_end_it = line_it; auto line_end_it = iter;
while(!line_end_it.ends_line()) while(!line_end_it.ends_line() && line_end_it.forward_char()) {}
line_end_it++; std::string line(get_source_buffer()->get_text(line_start_it, line_end_it));
std::string line(get_source_buffer()->get_text(line_it, line_end_it));
return line; return line;
} }
string Source::View::get_line_before_insert() { std::string Source::View::get_line(Glib::RefPtr<Gtk::TextBuffer::Mark> mark) {
Gtk::TextIter insert_it = get_source_buffer()->get_insert()->get_iter(); return get_line(mark->get_iter());
Gtk::TextIter line_it = get_source_buffer()->get_iter_at_line(insert_it.get_line()); }
std::string line(get_source_buffer()->get_text(line_it, insert_it));
std::string Source::View::get_line(int line_nr) {
return get_line(get_buffer()->get_iter_at_line(line_nr));
}
std::string Source::View::get_line() {
return get_line(get_buffer()->get_insert());
}
std::string Source::View::get_line_before(const Gtk::TextIter &iter) {
auto line_it = get_source_buffer()->get_iter_at_line(iter.get_line());
std::string line(get_source_buffer()->get_text(line_it, iter));
return line; return line;
} }
std::string Source::View::get_line_before(Glib::RefPtr<Gtk::TextBuffer::Mark> mark) {
return get_line_before(mark->get_iter());
}
std::string Source::View::get_line_before() {
return get_line_before(get_buffer()->get_insert());
}
bool Source::View::find_start_of_closed_expression(Gtk::TextIter iter, Gtk::TextIter &found_iter) {
int count1=0;
int count2=0;
bool ignore=false;
do {
if(!get_source_buffer()->iter_has_context_class(iter, "comment") && !get_source_buffer()->iter_has_context_class(iter, "string")) {
if(*iter=='\'') {
auto before_iter=iter;
before_iter.backward_char();
auto before_before_iter=before_iter;
before_before_iter.backward_char();
if(*before_iter!='\\' || *before_before_iter=='\\')
ignore=!ignore;
}
else if(!ignore) {
if(*iter==')')
count1++;
else if(*iter==']')
count2++;
else if(*iter=='(')
count1--;
else if(*iter=='[')
count2--;
}
}
if(iter.starts_line() && count1<=0 && count2<=0) {
auto insert_iter=get_buffer()->get_insert()->get_iter();
while(iter!=insert_iter && *iter==static_cast<unsigned char>(tab_char) && iter.forward_char()) {}
found_iter=iter;
return true;
}
} while(iter.backward_char());
return false;
}
bool Source::View::find_open_expression_symbol(Gtk::TextIter iter, const Gtk::TextIter &until_iter, Gtk::TextIter &found_iter) {
int count1=0;
int count2=0;
bool ignore=false;
while(iter!=until_iter && iter.backward_char()) {
if(!get_source_buffer()->iter_has_context_class(iter, "comment") && !get_source_buffer()->iter_has_context_class(iter, "string")) {
if(*iter=='\'') {
auto before_iter=iter;
before_iter.backward_char();
auto before_before_iter=before_iter;
before_before_iter.backward_char();
if(*before_iter!='\\' || *before_before_iter=='\\')
ignore=!ignore;
}
else if(!ignore) {
if(*iter==')')
count1++;
else if(*iter==']')
count2++;
else if(*iter=='(')
count1--;
else if(*iter=='[')
count2--;
}
if(count1<0 || count2<0) {
found_iter=iter;
return true;
}
}
}
return false;
}
bool Source::View::find_right_bracket_forward(Gtk::TextIter iter, Gtk::TextIter &found_iter) {
int count=0;
bool ignore=false;
while(iter.forward_char()) {
if(!get_source_buffer()->iter_has_context_class(iter, "comment") && !get_source_buffer()->iter_has_context_class(iter, "string")) {
if(*iter=='\'') {
auto before_iter=iter;
before_iter.backward_char();
auto before_before_iter=before_iter;
before_before_iter.backward_char();
if(*before_iter!='\\' || *before_before_iter=='\\')
ignore=!ignore;
}
else if(!ignore) {
if(*iter=='}') {
if(count==0) {
found_iter=iter;
return true;
}
count--;
}
else if(*iter=='{')
count++;
}
}
}
return false;
}
//Basic indentation //Basic indentation
bool Source::View::on_key_press_event(GdkEventKey* key) { bool Source::View::on_key_press_event(GdkEventKey* key) {
if(spellcheck_suggestions_dialog_shown) { if(spellcheck_suggestions_dialog_shown) {
@ -509,18 +666,23 @@ bool Source::View::on_key_press_event(GdkEventKey* key) {
return true; return true;
} }
if(key->keyval==GDK_KEY_BackSpace)
last_keyval_is_backspace=true;
else
last_keyval_is_backspace=false;
get_source_buffer()->begin_user_action(); get_source_buffer()->begin_user_action();
//Indent as in next or previous line //Indent as in next or previous line
if(key->keyval==GDK_KEY_Return && !get_buffer()->get_has_selection()) { if(key->keyval==GDK_KEY_Return && !get_buffer()->get_has_selection()) {
auto insert_it=get_buffer()->get_insert()->get_iter(); auto insert_it=get_buffer()->get_insert()->get_iter();
int line_nr=insert_it.get_line(); int line_nr=insert_it.get_line();
auto line=get_line_before_insert(); auto line=get_line_before();
std::smatch sm; std::smatch sm;
if(std::regex_match(line, sm, tabs_regex)) { if(std::regex_match(line, sm, tabs_regex)) {
if((line_nr+1)<get_buffer()->get_line_count()) { if((line_nr+1)<get_buffer()->get_line_count()) {
string next_line=get_line(line_nr+1); string next_line=get_line(line_nr+1);
auto line_end_iter=get_buffer()->get_iter_at_line(line_nr+1); auto line_end_iter=get_buffer()->get_iter_at_line(line_nr+1);
line_end_iter--; if(line_end_iter.backward_char()) {
std::smatch sm2; std::smatch sm2;
if(insert_it==line_end_iter && std::regex_match(next_line, sm2, tabs_regex)) { if(insert_it==line_end_iter && std::regex_match(next_line, sm2, tabs_regex)) {
if(sm2[1].str().size()>sm[1].str().size()) { if(sm2[1].str().size()>sm[1].str().size()) {
@ -531,6 +693,7 @@ bool Source::View::on_key_press_event(GdkEventKey* key) {
} }
} }
} }
}
get_source_buffer()->insert_at_cursor("\n"+sm[1].str()); get_source_buffer()->insert_at_cursor("\n"+sm[1].str());
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();
@ -545,6 +708,7 @@ bool Source::View::on_key_press_event(GdkEventKey* key) {
int line_end=selection_end.get_line(); int line_end=selection_end.get_line();
for(int line=line_start;line<=line_end;line++) { for(int line=line_start;line<=line_end;line++) {
Gtk::TextIter line_it = get_source_buffer()->get_iter_at_line(line); Gtk::TextIter line_it = get_source_buffer()->get_iter_at_line(line);
if(!get_buffer()->get_has_selection() || line_it!=selection_end)
get_source_buffer()->insert(line_it, tab); get_source_buffer()->insert(line_it, tab);
} }
get_source_buffer()->end_user_action(); get_source_buffer()->end_user_action();
@ -558,59 +722,51 @@ bool Source::View::on_key_press_event(GdkEventKey* key) {
int line_end=selection_end.get_line(); int line_end=selection_end.get_line();
unsigned indent_left_steps=tab_size; unsigned indent_left_steps=tab_size;
std::vector<bool> ignore_line;
for(int line_nr=line_start;line_nr<=line_end;line_nr++) { for(int line_nr=line_start;line_nr<=line_end;line_nr++) {
auto line_it = get_source_buffer()->get_iter_at_line(line_nr);
if(!get_buffer()->get_has_selection() || line_it!=selection_end) {
string line=get_line(line_nr); string line=get_line(line_nr);
std::smatch sm; std::smatch sm;
if(std::regex_match(line, sm, tabs_regex) && sm[1].str().size()>0) { if(std::regex_match(line, sm, tabs_regex) && (sm[1].str().size()>0 || sm[2].str().size()==0)) {
if(sm[2].str().size()>0) {
indent_left_steps=std::min(indent_left_steps, (unsigned)sm[1].str().size()); indent_left_steps=std::min(indent_left_steps, (unsigned)sm[1].str().size());
ignore_line.push_back(false);
}
else if((unsigned)sm[1].str().size()<indent_left_steps)
ignore_line.push_back(true);
else
ignore_line.push_back(false);
} }
else { else {
get_source_buffer()->end_user_action(); get_source_buffer()->end_user_action();
return true; return true;
} }
} }
}
for(int line_nr=line_start;line_nr<=line_end;line_nr++) { for(int line_nr=line_start;line_nr<=line_end;line_nr++) {
Gtk::TextIter line_it = get_source_buffer()->get_iter_at_line(line_nr); Gtk::TextIter line_it = get_source_buffer()->get_iter_at_line(line_nr);
Gtk::TextIter line_plus_it=line_it; Gtk::TextIter line_plus_it=line_it;
if(!get_buffer()->get_has_selection() || line_it!=selection_end) {
for(unsigned c=0;c<indent_left_steps;c++) if(indent_left_steps==0 || line_plus_it.forward_chars(indent_left_steps))
line_plus_it++; if(!ignore_line.at(line_nr-line_start))
get_source_buffer()->erase(line_it, line_plus_it); get_source_buffer()->erase(line_it, line_plus_it);
} }
}
get_source_buffer()->end_user_action(); get_source_buffer()->end_user_action();
return true; return true;
} }
//"Smart" backspace key //"Smart" backspace key TODO: remove commented out lines if this works
else if(key->keyval==GDK_KEY_BackSpace && !get_buffer()->get_has_selection()) { else if(key->keyval==GDK_KEY_BackSpace && !get_buffer()->get_has_selection()) {
auto insert_it=get_buffer()->get_insert()->get_iter(); auto insert_it=get_buffer()->get_insert()->get_iter();
int line_nr=insert_it.get_line(); //int line_nr=insert_it.get_line();
auto line=get_line_before_insert(); auto line=get_line_before();
std::smatch sm; std::smatch sm;
if(std::regex_match(line, sm, tabs_regex) && sm[1].str().size()==line.size()) { if(std::regex_match(line, sm, tabs_regex) && sm[1].str().size()==line.size()) {
if((line_nr-1)>=0) { auto line_start_iter=insert_it;
string previous_line=get_line(line_nr-1); if(line_start_iter.backward_chars(line.size()))
std::smatch sm2; get_buffer()->erase(insert_it, line_start_iter);
if(std::regex_match(previous_line, sm2, tabs_regex)) {
if(line.size()==sm2[1].str().size() || line.size()==sm2[1].str().size()+tab_size || line.size()==sm2[1].str().size()-tab_size) {
auto previous_line_end_it=insert_it;
for(unsigned c=0;c<line.size();c++)
previous_line_end_it--;
previous_line_end_it--;
get_source_buffer()->erase(previous_line_end_it, insert_it);
get_source_buffer()->end_user_action();
return true;
}
}
}
if(line.size()>=tab_size) {
auto insert_minus_tab_it=insert_it;
for(unsigned c=0;c<tab_size;c++)
insert_minus_tab_it--;
get_source_buffer()->erase(insert_minus_tab_it, insert_it);
get_source_buffer()->end_user_action();
return true;
}
} }
} }
@ -622,7 +778,7 @@ bool Source::View::on_key_press_event(GdkEventKey* key) {
bool Source::View::on_button_press_event(GdkEventButton *event) { bool Source::View::on_button_press_event(GdkEventButton *event) {
if(event->type==GDK_2BUTTON_PRESS) { if(event->type==GDK_2BUTTON_PRESS) {
Gtk::TextIter start, end; Gtk::TextIter start, end;
if(get_buffer()->get_selection_bounds(start, end)) { get_buffer()->get_selection_bounds(start, end);
auto iter=start; auto iter=start;
while((*iter>=48 && *iter<=57) || (*iter>=65 && *iter<=90) || (*iter>=97 && *iter<=122) || *iter==95) { while((*iter>=48 && *iter<=57) || (*iter>=65 && *iter<=90) || (*iter>=97 && *iter<=122) || *iter==95) {
start=iter; start=iter;
@ -636,13 +792,12 @@ bool Source::View::on_button_press_event(GdkEventButton *event) {
get_buffer()->select_range(start, end); get_buffer()->select_range(start, end);
return true; return true;
} }
}
return Gsv::View::on_button_press_event(event); return Gsv::View::on_button_press_event(event);
} }
std::pair<char, unsigned> Source::View::find_tab_char_and_size() { std::pair<char, unsigned> Source::View::find_tab_char_and_size() {
const std::regex indent_regex("^([ \t]+).*$"); const std::regex indent_regex("^([ \t]+)(.*)$");
auto size=get_buffer()->get_line_count(); auto size=get_buffer()->get_line_count();
std::unordered_map<char, size_t> tab_chars; std::unordered_map<char, size_t> tab_chars;
std::unordered_map<unsigned, size_t> tab_sizes; std::unordered_map<unsigned, size_t> tab_sizes;
@ -650,8 +805,17 @@ std::pair<char, unsigned> Source::View::find_tab_char_and_size() {
for(int c=0;c<size;c++) { for(int c=0;c<size;c++) {
auto line=get_line(c); auto line=get_line(c);
std::smatch sm; std::smatch sm;
std::string str;
if(std::regex_match(line, sm, indent_regex)) { if(std::regex_match(line, sm, indent_regex)) {
auto str=sm[1].str(); if(sm[2].str().size()==0)
continue;
str=sm[1].str();
}
else {
str="";
if(line.size()==0)
continue;
}
long tab_diff=abs(static_cast<long>(str.size()-last_tab_size)); long tab_diff=abs(static_cast<long>(str.size()-last_tab_size));
if(tab_diff>0) { if(tab_diff>0) {
@ -673,7 +837,6 @@ std::pair<char, unsigned> Source::View::find_tab_char_and_size() {
tab_chars[str[0]]=1; tab_chars[str[0]]=1;
} }
} }
}
char found_tab_char=0; char found_tab_char=0;
size_t occurences=0; size_t occurences=0;
for(auto &tab_char: tab_chars) { for(auto &tab_char: tab_chars) {
@ -693,16 +856,20 @@ std::pair<char, unsigned> Source::View::find_tab_char_and_size() {
return {found_tab_char, found_tab_size}; return {found_tab_char, found_tab_size};
} }
bool Source::View::is_word_iter(const Gtk::TextIter& iter) {
return ((*iter>=48 && *iter<=57) || (*iter>=65 && *iter<=90) || (*iter>=97 && *iter<=122) || *iter==39 || *iter>=128);
}
std::pair<Gtk::TextIter, Gtk::TextIter> Source::View::spellcheck_get_word(Gtk::TextIter iter) { std::pair<Gtk::TextIter, Gtk::TextIter> Source::View::spellcheck_get_word(Gtk::TextIter iter) {
auto start=iter; auto start=iter;
auto end=iter; auto end=iter;
while((*iter>=48 && *iter<=57) || (*iter>=65 && *iter<=90) || (*iter>=97 && *iter<=122) || *iter==39 || *iter>=128) { while(is_word_iter(iter)) {
start=iter; start=iter;
if(!iter.backward_char()) if(!iter.backward_char())
break; break;
} }
while((*end>=48 && *end<=57) || (*end>=65 && *end<=90) || (*end>=97 && *end<=122) || *iter==39 || *end>=128) { while(is_word_iter(end)) {
if(!end.forward_char()) if(!end.forward_char())
break; break;
} }
@ -763,6 +930,7 @@ clang::Index Source::ClangViewParse::clang_index(0, 0);
Source::ClangViewParse::ClangViewParse(const boost::filesystem::path &file_path, const boost::filesystem::path& project_path): Source::ClangViewParse::ClangViewParse(const boost::filesystem::path &file_path, const boost::filesystem::path& project_path):
Source::View(file_path), project_path(project_path) { Source::View(file_path), project_path(project_path) {
DEBUG("start");
auto scheme = get_source_buffer()->get_style_scheme(); auto scheme = get_source_buffer()->get_style_scheme();
auto tag_table=get_buffer()->get_tag_table(); auto tag_table=get_buffer()->get_tag_table();
for (auto &item : Singleton::Config::source()->clang_types) { for (auto &item : Singleton::Config::source()->clang_types) {
@ -770,7 +938,6 @@ Source::View(file_path), project_path(project_path) {
auto style = scheme->get_style(item.second); auto style = scheme->get_style(item.second);
auto tag = get_source_buffer()->create_tag(item.second); auto tag = get_source_buffer()->create_tag(item.second);
if (style) { if (style) {
DEBUG("Style " + item.second + " found in style " + scheme->get_name());
if (style->property_foreground_set()) if (style->property_foreground_set())
tag->property_foreground() = style->property_foreground(); tag->property_foreground() = style->property_foreground();
if (style->property_background_set()) if (style->property_background_set())
@ -782,10 +949,9 @@ Source::View(file_path), project_path(project_path) {
// // if (style->property_line_background_set()) tag->property_line_background() = style->property_line_background(); // // if (style->property_line_background_set()) tag->property_line_background() = style->property_line_background();
// // if (style->property_underline_set()) tag->property_underline() = style->property_underline(); // // if (style->property_underline_set()) tag->property_underline() = style->property_underline();
} else } else
DEBUG("Style " + item.second + " not found in " + scheme->get_name()); INFO("Style " + item.second + " not found in " + scheme->get_name());
} }
} }
INFO("Tagtable filled");
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
@ -801,14 +967,12 @@ Source::View(file_path), project_path(project_path) {
parse_done.connect([this](){ parse_done.connect([this](){
if(parse_thread_mapped) { if(parse_thread_mapped) {
if(parsing_mutex.try_lock()) { if(parsing_mutex.try_lock()) {
INFO("Updating syntax");
update_syntax(); update_syntax();
update_diagnostics(); update_diagnostics();
update_types(); update_types();
source_readable=true; source_readable=true;
set_status(""); set_status("");
parsing_mutex.unlock(); parsing_mutex.unlock();
INFO("Syntax updated");
} }
parsing_in_progress->done("done"); parsing_in_progress->done("done");
} }
@ -816,6 +980,11 @@ Source::View(file_path), project_path(project_path) {
parse_thread_go=true; parse_thread_go=true;
} }
}); });
parse_fail.connect([this](){
Singleton::terminal()->print("Error: failed to reparse "+this->file_path.string()+".\n");
set_status("");
parsing_in_progress->cancel("failed");
});
init_parse(); init_parse();
get_buffer()->signal_changed().connect([this]() { get_buffer()->signal_changed().connect([this]() {
@ -827,18 +996,21 @@ Source::View(file_path), project_path(project_path) {
bracket_regex=std::regex(std::string("^(")+tab_char+"*).*\\{ *$"); bracket_regex=std::regex(std::string("^(")+tab_char+"*).*\\{ *$");
no_bracket_statement_regex=std::regex(std::string("^(")+tab_char+"*)(if|for|else if|catch|while) *\\(.*[^;}] *$"); no_bracket_statement_regex=std::regex(std::string("^(")+tab_char+"*)(if|for|else if|catch|while) *\\(.*[^;}] *$");
no_bracket_no_para_statement_regex=std::regex(std::string("^(")+tab_char+"*)(else|try|do) *$"); no_bracket_no_para_statement_regex=std::regex(std::string("^(")+tab_char+"*)(else|try|do) *$");
DEBUG("end");
}
Source::ClangViewParse::~ClangViewParse() {
delayed_reparse_connection.disconnect();
} }
void Source::ClangViewParse::init_parse() { void Source::ClangViewParse::init_parse() {
type_tooltips.hide(); type_tooltips.hide();
diagnostic_tooltips.hide(); diagnostic_tooltips.hide();
get_buffer()->remove_all_tags(get_buffer()->begin(), get_buffer()->end());
source_readable=false; source_readable=false;
parse_thread_go=true; parse_thread_go=true;
parse_thread_mapped=false; parse_thread_mapped=false;
parse_thread_stop=false; parse_thread_stop=false;
auto buffer_map=get_buffer_map(); auto buffer_map=get_buffer_map();
//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()]; auto& str=buffer_map[file_path.string()];
@ -870,10 +1042,16 @@ void Source::ClangViewParse::init_parse() {
parse_start(); parse_start();
} }
else if (parse_thread_mapped && parsing_mutex.try_lock() && parse_thread_buffer_map_mutex.try_lock()) { else if (parse_thread_mapped && parsing_mutex.try_lock() && parse_thread_buffer_map_mutex.try_lock()) {
reparse(parse_thread_buffer_map); int status=reparse(parse_thread_buffer_map);
parse_thread_go=false; parse_thread_go=false;
if(status!=0)
parse_thread_stop=true;
parsing_mutex.unlock(); parsing_mutex.unlock();
parse_thread_buffer_map_mutex.unlock(); parse_thread_buffer_map_mutex.unlock();
if(status!=0) {
parse_fail();
return;
}
parse_done(); parse_done();
} }
} }
@ -899,6 +1077,7 @@ void Source::ClangViewParse::start_reparse() {
parse_thread_mapped=false; parse_thread_mapped=false;
source_readable=false; source_readable=false;
delayed_reparse_connection.disconnect(); delayed_reparse_connection.disconnect();
if(!parse_thread_stop) {
delayed_reparse_connection=Glib::signal_timeout().connect([this]() { delayed_reparse_connection=Glib::signal_timeout().connect([this]() {
source_readable=false; source_readable=false;
parse_thread_go=true; parse_thread_go=true;
@ -906,9 +1085,13 @@ void Source::ClangViewParse::start_reparse() {
return false; return false;
}, 1000); }, 1000);
} }
}
int Source::ClangViewParse::reparse(const std::map<std::string, std::string> &buffer) { int Source::ClangViewParse::reparse(const std::map<std::string, std::string> &buffer) {
int status = clang_tu->ReparseTranslationUnit(buffer); int status = clang_tu->ReparseTranslationUnit(buffer);
if(status!=0) {
return status;
}
clang_tokens=clang_tu->get_tokens(0, parse_thread_buffer_map.find(file_path.string())->second.size()-1); clang_tokens=clang_tu->get_tokens(0, parse_thread_buffer_map.find(file_path.string())->second.size()-1);
return status; return status;
} }
@ -1021,11 +1204,11 @@ void Source::ClangViewParse::update_diagnostics() {
} }
auto end_line=get_line(diagnostic.offsets.second.line-1); //index is sometimes off the line auto end_line=get_line(diagnostic.offsets.second.line-1); //index is sometimes off the line
auto end_line_index=diagnostic.offsets.second.index-1; auto end_line_index=diagnostic.offsets.second.index-1;
if(end_line_index>=end_line.size()) { if(end_line_index>end_line.size()) {
if(end_line.size()==0) if(end_line.size()==0)
end_line_index=0; end_line_index=0;
else else
end_line_index=end_line.size()-1; end_line_index=end_line.size();
} }
auto start=get_buffer()->get_iter_at_line_index(diagnostic.offsets.first.line-1, start_line_index); auto start=get_buffer()->get_iter_at_line_index(diagnostic.offsets.first.line-1, start_line_index);
auto end=get_buffer()->get_iter_at_line_index(diagnostic.offsets.second.line-1, end_line_index); auto end=get_buffer()->get_iter_at_line_index(diagnostic.offsets.second.line-1, end_line_index);
@ -1041,12 +1224,17 @@ void Source::ClangViewParse::update_diagnostics() {
auto tooltip_buffer=Gtk::TextBuffer::create(get_buffer()->get_tag_table()); auto tooltip_buffer=Gtk::TextBuffer::create(get_buffer()->get_tag_table());
tooltip_buffer->insert_with_tag(tooltip_buffer->get_insert()->get_iter(), severity_spelling, diagnostic_tag_name); tooltip_buffer->insert_with_tag(tooltip_buffer->get_insert()->get_iter(), severity_spelling, diagnostic_tag_name);
tooltip_buffer->insert_with_tag(tooltip_buffer->get_insert()->get_iter(), ":\n"+spelling, "def:note"); tooltip_buffer->insert_with_tag(tooltip_buffer->get_insert()->get_iter(), ":\n"+spelling, "def:note");
//TODO: Insert newlines to clang_tu->diagnostics[c].spelling (use 80 chars, then newline?)
return tooltip_buffer; return tooltip_buffer;
}; };
diagnostic_tooltips.emplace_back(create_tooltip_buffer, *this, get_buffer()->create_mark(start), get_buffer()->create_mark(end)); diagnostic_tooltips.emplace_back(create_tooltip_buffer, *this, get_buffer()->create_mark(start), get_buffer()->create_mark(end));
get_buffer()->apply_tag_by_name(diagnostic_tag_name+"_underline", start, end); get_buffer()->apply_tag_by_name(diagnostic_tag_name+"_underline", start, end);
auto iter=get_buffer()->get_insert()->get_iter();
if(iter.ends_line()) {
auto next_iter=iter;
if(next_iter.forward_char())
get_buffer()->remove_tag_by_name(diagnostic_tag_name+"_underline", iter, next_iter);
}
} }
} }
} }
@ -1071,9 +1259,13 @@ void Source::ClangViewParse::update_types() {
} }
} }
//Clang indentation //Clang indentation.
//TODO: replace indentation methods with a better implementation or maybe use libclang
bool Source::ClangViewParse::on_key_press_event(GdkEventKey* key) { bool Source::ClangViewParse::on_key_press_event(GdkEventKey* key) {
if(spellcheck_suggestions_dialog_shown) {
if(spellcheck_suggestions_dialog->on_key_press(key))
return true;
}
if(get_buffer()->get_has_selection()) { if(get_buffer()->get_has_selection()) {
return Source::View::on_key_press_event(key); return Source::View::on_key_press_event(key);
} }
@ -1081,67 +1273,82 @@ bool Source::ClangViewParse::on_key_press_event(GdkEventKey* key) {
//Indent depending on if/else/etc and brackets //Indent depending on if/else/etc and brackets
if(key->keyval==GDK_KEY_Return) { if(key->keyval==GDK_KEY_Return) {
string line(get_line_before_insert()); auto iter=get_buffer()->get_insert()->get_iter();
Gtk::TextIter start_of_sentence_iter;
if(find_start_of_closed_expression(iter, start_of_sentence_iter)) {
auto start_line=get_line_before(start_of_sentence_iter);
std::smatch sm; std::smatch sm;
if(std::regex_match(line, sm, bracket_regex)) { std::string tabs;
int line_nr=get_source_buffer()->get_insert()->get_iter().get_line(); if(std::regex_match(start_line, sm, tabs_regex)) {
if((line_nr+1)<get_source_buffer()->get_line_count()) { tabs=sm[1].str();
string next_line=get_line(line_nr+1); }
std::smatch sm2;
if(std::regex_match(next_line, sm2, tabs_regex)) { if(iter.backward_char() && *iter=='{') {
if(sm2[1].str()==sm[1].str()+tab) { auto found_iter=iter;
get_source_buffer()->insert_at_cursor("\n"+sm[1].str()+tab); bool found_right_bracket=find_right_bracket_forward(iter, found_iter);
scroll_to(get_source_buffer()->get_insert());
get_source_buffer()->end_user_action(); bool has_bracket=false;
return true; if(found_right_bracket) {
auto line=get_line_before(found_iter);
if(std::regex_match(line, sm, tabs_regex)) {
if(tabs.size()==sm[1].str().size())
has_bracket=true;
} }
} }
auto next_char=*get_buffer()->get_insert()->get_iter(); if(*get_buffer()->get_insert()->get_iter()=='}') {
auto next_line_with_end_bracket=sm[1].str()+"}"; get_source_buffer()->insert_at_cursor("\n"+tabs+tab+"\n"+tabs);
if(next_char!='}' && next_line.substr(0, next_line_with_end_bracket.size())!=next_line_with_end_bracket) {
get_source_buffer()->insert_at_cursor("\n"+sm[1].str()+tab+"\n"+sm[1].str()+"}");
auto insert_it = get_source_buffer()->get_insert()->get_iter(); auto insert_it = get_source_buffer()->get_insert()->get_iter();
for(size_t c=0;c<sm[1].str().size()+2;c++) if(insert_it.backward_chars(tabs.size()+1)) {
insert_it--;
scroll_to(get_source_buffer()->get_insert()); scroll_to(get_source_buffer()->get_insert());
get_source_buffer()->place_cursor(insert_it); get_source_buffer()->place_cursor(insert_it);
}
get_source_buffer()->end_user_action(); get_source_buffer()->end_user_action();
return true; return true;
} }
else if(next_char=='}') { else if(!has_bracket) {
get_source_buffer()->insert_at_cursor("\n"+sm[1].str()+tab+"\n"+sm[1].str()); //Insert new lines with bracket end
get_source_buffer()->insert_at_cursor("\n"+tabs+tab+"\n"+tabs+"}");
auto insert_it = get_source_buffer()->get_insert()->get_iter(); auto insert_it = get_source_buffer()->get_insert()->get_iter();
for(size_t c=0;c<sm[1].str().size()+1;c++) if(insert_it.backward_chars(tabs.size()+2)) {
insert_it--;
scroll_to(get_source_buffer()->get_insert()); scroll_to(get_source_buffer()->get_insert());
get_source_buffer()->place_cursor(insert_it); get_source_buffer()->place_cursor(insert_it);
}
get_source_buffer()->end_user_action(); get_source_buffer()->end_user_action();
return true; return true;
} }
else { else {
get_source_buffer()->insert_at_cursor("\n"+sm[1].str()+tab); get_source_buffer()->insert_at_cursor("\n"+tabs+tab);
scroll_to(get_source_buffer()->get_insert()); scroll_to(get_buffer()->get_insert());
get_source_buffer()->end_user_action(); get_source_buffer()->end_user_action();
return true; return true;
} }
} }
auto line=get_line_before();
iter=get_buffer()->get_insert()->get_iter();
auto found_iter=iter;
if(find_open_expression_symbol(iter, start_of_sentence_iter, found_iter)) {
auto offset=found_iter.get_line_offset();
tabs.clear();
for(int c=0;c<offset+1;c++)
tabs+=' ';
} }
else if(std::regex_match(line, sm, no_bracket_statement_regex)) { else if(std::regex_match(line, sm, no_bracket_statement_regex)) {
get_source_buffer()->insert_at_cursor("\n"+sm[1].str()+tab); get_source_buffer()->insert_at_cursor("\n"+tabs+tab);
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();
return true; return true;
} }
else if(std::regex_match(line, sm, no_bracket_no_para_statement_regex)) { else if(std::regex_match(line, sm, no_bracket_no_para_statement_regex)) {
get_source_buffer()->insert_at_cursor("\n"+sm[1].str()+tab); get_source_buffer()->insert_at_cursor("\n"+tabs+tab);
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();
return true; return true;
} }
else if(std::regex_match(line, sm, tabs_regex)) { //Indenting after for instance if(...)\n...;\n
else if(iter.backward_char() && *iter==';') {
std::smatch sm2; std::smatch sm2;
size_t line_nr=get_source_buffer()->get_insert()->get_iter().get_line(); size_t line_nr=get_source_buffer()->get_insert()->get_iter().get_line();
if(line_nr>0 && sm[1].str().size()>=tab_size) { if(line_nr>0 && tabs.size()>=tab_size) {
string previous_line=get_line(line_nr-1); string previous_line=get_line(line_nr-1);
if(!std::regex_match(previous_line, sm2, bracket_regex)) { if(!std::regex_match(previous_line, sm2, bracket_regex)) {
if(std::regex_match(previous_line, sm2, no_bracket_statement_regex)) { if(std::regex_match(previous_line, sm2, no_bracket_statement_regex)) {
@ -1159,10 +1366,15 @@ bool Source::ClangViewParse::on_key_press_event(GdkEventKey* key) {
} }
} }
} }
get_source_buffer()->insert_at_cursor("\n"+tabs);
scroll_to(get_source_buffer()->get_insert());
get_source_buffer()->end_user_action();
return true;
}
} }
//Indent left when writing } on a new line //Indent left when writing } on a new line
else if(key->keyval==GDK_KEY_braceright) { else if(key->keyval==GDK_KEY_braceright) {
string line=get_line_before_insert(); string line=get_line_before();
if(line.size()>=tab_size) { if(line.size()>=tab_size) {
for(auto c: line) { for(auto c: line) {
if(c!=tab_char) { if(c!=tab_char) {
@ -1174,9 +1386,7 @@ bool Source::ClangViewParse::on_key_press_event(GdkEventKey* key) {
Gtk::TextIter insert_it = get_source_buffer()->get_insert()->get_iter(); Gtk::TextIter insert_it = get_source_buffer()->get_insert()->get_iter();
Gtk::TextIter line_it = get_source_buffer()->get_iter_at_line(insert_it.get_line()); Gtk::TextIter line_it = get_source_buffer()->get_iter_at_line(insert_it.get_line());
Gtk::TextIter line_plus_it=line_it; Gtk::TextIter line_plus_it=line_it;
for(unsigned c=0;c<tab_size;c++) if(tab_size==0 || line_plus_it.forward_chars(tab_size))
line_plus_it++;
get_source_buffer()->erase(line_it, line_plus_it); get_source_buffer()->erase(line_it, line_plus_it);
} }
get_source_buffer()->insert_at_cursor("}"); get_source_buffer()->insert_at_cursor("}");
@ -1201,10 +1411,9 @@ Source::ClangViewParse(file_path, project_path), autocomplete_cancel_starting(fa
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; autocomplete_cancel_starting=true;
if(completion_dialog_shown) { if(completion_dialog_shown)
completion_dialog->hide(); completion_dialog->hide();
} }
}
}); });
signal_scroll_event().connect([this](GdkEventScroll* event){ signal_scroll_event().connect([this](GdkEventScroll* event){
if(completion_dialog_shown) if(completion_dialog_shown)
@ -1222,9 +1431,8 @@ Source::ClangViewParse(file_path, project_path), autocomplete_cancel_starting(fa
signal_focus_out_event().connect([this](GdkEventFocus* event) { signal_focus_out_event().connect([this](GdkEventFocus* event) {
autocomplete_cancel_starting=true; autocomplete_cancel_starting=true;
if(completion_dialog_shown) { if(completion_dialog_shown)
completion_dialog->hide(); completion_dialog->hide();
}
return false; return false;
}); });
@ -1242,15 +1450,17 @@ bool Source::ClangViewAutocomplete::on_key_press_event(GdkEventKey *key) {
void Source::ClangViewAutocomplete::start_autocomplete() { void Source::ClangViewAutocomplete::start_autocomplete() {
if(!has_focus()) if(!has_focus())
return; return;
auto iter=get_buffer()->get_insert()->get_iter();
if(!((last_keyval>='0' && last_keyval<='9') || if(!((last_keyval>='0' && last_keyval<='9') ||
(last_keyval>='a' && last_keyval<='z') || (last_keyval>='A' && last_keyval<='Z') || (last_keyval>='a' && last_keyval<='z') || (last_keyval>='A' && last_keyval<='Z') ||
last_keyval=='_' || last_keyval=='>' || last_keyval=='.' || last_keyval==':')) { last_keyval=='_' || last_keyval=='.' || last_keyval==':' ||
(last_keyval=='>' && iter.backward_char() && iter.backward_char() && *iter=='-'))) {
autocomplete_cancel_starting=true; autocomplete_cancel_starting=true;
return; return;
} }
std::string line=" "+get_line_before_insert(); std::string line=" "+get_line_before();
if((std::count(line.begin(), line.end(), '\"')%2)!=1 && line.find("//")==std::string::npos) { if((std::count(line.begin(), line.end(), '\"')%2)!=1 && line.find("//")==std::string::npos) {
const std::regex in_specified_namespace("^(.*[a-zA-Z0-9_\\)])(->|\\.|::)([a-zA-Z0-9_]*)$"); const std::regex in_specified_namespace("^(.*[a-zA-Z0-9_\\)\\]\\>])(->|\\.|::)([a-zA-Z0-9_]*)$");
const std::regex within_namespace("^(.*)([^a-zA-Z0-9_]+)([a-zA-Z0-9_]{3,})$"); const std::regex within_namespace("^(.*)([^a-zA-Z0-9_]+)([a-zA-Z0-9_]{3,})$");
std::smatch sm; std::smatch sm;
if(std::regex_match(line, sm, in_specified_namespace)) { if(std::regex_match(line, sm, in_specified_namespace)) {
@ -1282,21 +1492,20 @@ void Source::ClangViewAutocomplete::autocomplete() {
if(!autocomplete_starting) { if(!autocomplete_starting) {
autocomplete_starting=true; autocomplete_starting=true;
autocomplete_cancel_starting=false; autocomplete_cancel_starting=false;
INFO("Source::ClangViewAutocomplete::autocomplete getting autocompletions");
std::shared_ptr<std::vector<Source::AutoCompleteData> > ac_data=std::make_shared<std::vector<Source::AutoCompleteData> >(); std::shared_ptr<std::vector<Source::AutoCompleteData> > ac_data=std::make_shared<std::vector<Source::AutoCompleteData> >();
autocomplete_done_connection.disconnect(); autocomplete_done_connection.disconnect();
autocomplete_done_connection=autocomplete_done.connect([this, ac_data](){ autocomplete_done_connection=autocomplete_done.connect([this, ac_data](){
autocomplete_starting=false; autocomplete_starting=false;
if(!autocomplete_cancel_starting) { if(!autocomplete_cancel_starting) {
auto start_iter=get_buffer()->get_insert()->get_iter(); auto start_iter=get_buffer()->get_insert()->get_iter();
for(size_t c=0;c<prefix.size();c++) if(prefix.size()>0 && !start_iter.backward_chars(prefix.size()))
start_iter--; return;
completion_dialog=std::unique_ptr<CompletionDialog>(new CompletionDialog(*this, get_buffer()->create_mark(start_iter))); completion_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> >(); auto rows=std::make_shared<std::unordered_map<std::string, std::string> >();
completion_dialog->on_hide=[this](){ completion_dialog->on_hide=[this](){
start_reparse();
get_source_buffer()->end_user_action(); get_source_buffer()->end_user_action();
completion_dialog_shown=false; completion_dialog_shown=false;
start_reparse();
}; };
completion_dialog->on_select=[this, rows](const std::string& selected, bool hide_window) { completion_dialog->on_select=[this, rows](const std::string& selected, bool hide_window) {
auto row = rows->at(selected); auto row = rows->at(selected);
@ -1348,6 +1557,7 @@ void Source::ClangViewAutocomplete::autocomplete() {
} }
else { else {
set_status(""); set_status("");
start_reparse();
start_autocomplete(); start_autocomplete();
} }
}); });
@ -1369,6 +1579,7 @@ void Source::ClangViewAutocomplete::autocomplete() {
autocomplete_thread.join(); autocomplete_thread.join();
autocomplete_thread=std::thread([this, ac_data, line_nr, column_nr, buffer_map](){ autocomplete_thread=std::thread([this, ac_data, line_nr, column_nr, buffer_map](){
parsing_mutex.lock(); parsing_mutex.lock();
if(!parse_thread_stop)
*ac_data=move(get_autocomplete_suggestions(line_nr, column_nr, *buffer_map)); *ac_data=move(get_autocomplete_suggestions(line_nr, column_nr, *buffer_map));
autocomplete_done(); autocomplete_done();
parsing_mutex.unlock(); parsing_mutex.unlock();
@ -1378,7 +1589,6 @@ void Source::ClangViewAutocomplete::autocomplete() {
std::vector<Source::AutoCompleteData> Source::ClangViewAutocomplete:: std::vector<Source::AutoCompleteData> Source::ClangViewAutocomplete::
get_autocomplete_suggestions(int line_number, int column, std::map<std::string, std::string>& buffer_map) { get_autocomplete_suggestions(int line_number, int column, std::map<std::string, std::string>& buffer_map) {
INFO("Getting auto complete suggestions");
std::vector<Source::AutoCompleteData> suggestions; std::vector<Source::AutoCompleteData> suggestions;
auto results=clang_tu->get_code_completions(buffer_map, line_number, column); auto results=clang_tu->get_code_completions(buffer_map, line_number, column);
if(!autocomplete_cancel_starting) { if(!autocomplete_cancel_starting) {
@ -1404,8 +1614,6 @@ get_autocomplete_suggestions(int line_number, int column, std::map<std::string,
} }
} }
} }
DEBUG("Number of suggestions");
DEBUG_VAR(suggestions.size());
return suggestions; return suggestions;
} }
@ -1555,6 +1763,10 @@ Source::ClangViewAutocomplete(file_path, project_path) {
}; };
} }
Source::ClangViewRefactor::~ClangViewRefactor() {
delayed_tag_similar_tokens_connection.disconnect();
}
Source::ClangView::ClangView(const boost::filesystem::path &file_path, const boost::filesystem::path& project_path, Glib::RefPtr<Gsv::Language> language): ClangViewRefactor(file_path, project_path) { Source::ClangView::ClangView(const boost::filesystem::path &file_path, const boost::filesystem::path& project_path, Glib::RefPtr<Gsv::Language> language): ClangViewRefactor(file_path, project_path) {
if(language) { if(language) {
get_source_buffer()->set_highlight_syntax(true); get_source_buffer()->set_highlight_syntax(true);
@ -1588,7 +1800,8 @@ void Source::ClangView::async_delete() {
} }
bool Source::ClangView::restart_parse() { bool Source::ClangView::restart_parse() {
if(!restart_parse_running) { if(!restart_parse_running && !parse_thread_stop) {
reparse_needed=false;
restart_parse_running=true; restart_parse_running=true;
parse_thread_stop=true; parse_thread_stop=true;
if(restart_parse_thread.joinable()) if(restart_parse_thread.joinable())

24
src/source.h

@ -87,8 +87,17 @@ namespace Source {
void set_status(const std::string &status); void set_status(const std::string &status);
std::string get_line(size_t line_number); std::string get_line(const Gtk::TextIter &iter);
std::string get_line_before_insert(); std::string get_line(Glib::RefPtr<Gtk::TextBuffer::Mark> mark);
std::string get_line(int line_nr);
std::string get_line();
std::string get_line_before(const Gtk::TextIter &iter);
std::string get_line_before(Glib::RefPtr<Gtk::TextBuffer::Mark> mark);
std::string get_line_before();
bool find_start_of_closed_expression(Gtk::TextIter iter, Gtk::TextIter &found_iter);
bool find_open_expression_symbol(Gtk::TextIter iter, const Gtk::TextIter &until_iter, Gtk::TextIter &found_iter);
bool find_right_bracket_forward(Gtk::TextIter iter, Gtk::TextIter &found_iter);
bool on_key_press_event(GdkEventKey* key); bool on_key_press_event(GdkEventKey* key);
bool on_button_press_event(GdkEventButton *event); bool on_button_press_event(GdkEventButton *event);
@ -100,6 +109,8 @@ namespace Source {
std::regex tabs_regex; std::regex tabs_regex;
bool spellcheck_all=false; bool spellcheck_all=false;
std::unique_ptr<SelectionDialog> spellcheck_suggestions_dialog;
bool spellcheck_suggestions_dialog_shown=false;
private: private:
GtkSourceSearchContext *search_context; GtkSourceSearchContext *search_context;
GtkSourceSearchSettings *search_settings; GtkSourceSearchSettings *search_settings;
@ -108,12 +119,12 @@ namespace Source {
static AspellConfig* spellcheck_config; static AspellConfig* spellcheck_config;
AspellCanHaveError *spellcheck_possible_err; AspellCanHaveError *spellcheck_possible_err;
AspellSpeller *spellcheck_checker; AspellSpeller *spellcheck_checker;
bool is_word_iter(const Gtk::TextIter& iter);
std::pair<Gtk::TextIter, Gtk::TextIter> spellcheck_get_word(Gtk::TextIter iter); std::pair<Gtk::TextIter, Gtk::TextIter> spellcheck_get_word(Gtk::TextIter iter);
void spellcheck_word(const Gtk::TextIter& start, const Gtk::TextIter& end); void spellcheck_word(const Gtk::TextIter& start, const Gtk::TextIter& end);
std::vector<std::string> spellcheck_get_suggestions(const Gtk::TextIter& start, const Gtk::TextIter& end); std::vector<std::string> spellcheck_get_suggestions(const Gtk::TextIter& start, const Gtk::TextIter& end);
std::unique_ptr<SelectionDialog> spellcheck_suggestions_dialog;
bool spellcheck_suggestions_dialog_shown=false;
sigc::connection delayed_spellcheck_suggestions_connection; sigc::connection delayed_spellcheck_suggestions_connection;
bool last_keyval_is_backspace=false;
}; };
class GenericView : public View { class GenericView : public View {
@ -124,9 +135,10 @@ namespace Source {
class ClangViewParse : public View { class ClangViewParse : public View {
public: public:
ClangViewParse(const boost::filesystem::path &file_path, const boost::filesystem::path& project_path); ClangViewParse(const boost::filesystem::path &file_path, const boost::filesystem::path& project_path);
~ClangViewParse();
boost::filesystem::path project_path; boost::filesystem::path project_path;
void start_reparse(); void start_reparse();
bool start_reparse_needed=false; bool reparse_needed=false;
protected: protected:
void init_parse(); void init_parse();
bool on_key_press_event(GdkEventKey* key); bool on_key_press_event(GdkEventKey* key);
@ -157,6 +169,7 @@ namespace Source {
Glib::Dispatcher parse_done; Glib::Dispatcher parse_done;
Glib::Dispatcher parse_start; Glib::Dispatcher parse_start;
Glib::Dispatcher parse_fail;
std::map<std::string, std::string> parse_thread_buffer_map; std::map<std::string, std::string> parse_thread_buffer_map;
std::mutex parse_thread_buffer_map_mutex; std::mutex parse_thread_buffer_map_mutex;
std::atomic<bool> parse_thread_go; std::atomic<bool> parse_thread_go;
@ -187,6 +200,7 @@ namespace Source {
class ClangViewRefactor : public ClangViewAutocomplete { class ClangViewRefactor : public ClangViewAutocomplete {
public: public:
ClangViewRefactor(const boost::filesystem::path &file_path, const boost::filesystem::path& project_path); ClangViewRefactor(const boost::filesystem::path &file_path, const boost::filesystem::path& project_path);
~ClangViewRefactor();
private: private:
Glib::RefPtr<Gtk::TextTag> similar_tokens_tag; Glib::RefPtr<Gtk::TextTag> similar_tokens_tag;
std::string last_similar_tokens_tagged; std::string last_similar_tokens_tagged;

65
src/sourcefile.cc

@ -15,29 +15,58 @@ std::string juci::filesystem::read(const std::string &path) {
return ss.str(); return ss.str();
} }
bool juci::filesystem::read(const std::string &path, Glib::RefPtr<Gtk::TextBuffer> text_buffer) { int juci::filesystem::read(const std::string &path, Glib::RefPtr<Gtk::TextBuffer> text_buffer) {
std::ifstream input(path, std::ofstream::binary); std::ifstream input(path, std::ofstream::binary);
if(input) { if(input) {
std::vector<char> buffer(buffer_size); //need to read the whole file to make this work...
size_t read_length; std::stringstream ss;
std::string buffer_str; ss << input.rdbuf();
while((read_length=input.read(&buffer[0], buffer_size).gcount())>0) { Glib::ustring ustr=std::move(ss.str());
buffer_str+=std::string(&buffer[0], read_length);
if(buffer_str.back()>=0) { bool valid=true;
auto ustr=Glib::ustring(buffer_str); //This was way too slow...
buffer_str=""; /*Glib::ustring::iterator iter;
while(!ustr.validate(iter)) {
auto next_char_iter=iter;
next_char_iter++;
ustr.replace(iter, next_char_iter, "?");
if(valid)
valid=false;
}*/
if(ustr.validate()) if(ustr.validate())
text_buffer->insert_at_cursor(ustr); text_buffer->insert_at_cursor(ustr);
else { else
input.close(); valid=false;
return false;
} //TODO: maybe correct this, emphasis on maybe:
} /*std::vector<char> buffer(buffer_size);
size_t read_length;
while((read_length=input.read(&buffer[0], buffer_size).gcount())>0) {
//auto ustr=Glib::ustring(std::string(&buffer[0], read_length)); //this works...
//this does not work:
Glib::ustring ustr;
ustr.resize(read_length);
ustr.replace(0, read_length, &buffer[0]);
Glib::ustring::iterator iter;
while(!ustr.validate(iter)) {
auto next_char_iter=iter;
next_char_iter++;
ustr.replace(iter, next_char_iter, "?");
} }
text_buffer->insert_at_cursor(ustr); //What if insert happens in the middle of an UTF-8 char???
}*/
input.close(); input.close();
return true; if(valid)
return 1;
else
return -1;
} }
return false; return 0;
} }
//Only use on small files //Only use on small files
@ -70,9 +99,7 @@ bool juci::filesystem::write(const std::string &path, Glib::RefPtr<Gtk::TextBuff
bool end_reached=false; bool end_reached=false;
while(!end_reached) { while(!end_reached) {
for(size_t c=0;c<buffer_size;c++) { for(size_t c=0;c<buffer_size;c++) {
if(end_iter) if(!end_iter.forward_char()) {
end_iter++;
else {
end_reached=true; end_reached=true;
break; break;
} }

4
src/sourcefile.h

@ -10,8 +10,8 @@ namespace juci {
public: public:
static std::string read(const std::string &path); static std::string read(const std::string &path);
static std::string read(const boost::filesystem::path &path) { return read(path.string()); } static std::string read(const boost::filesystem::path &path) { return read(path.string()); }
static bool read(const std::string &path, Glib::RefPtr<Gtk::TextBuffer> text_buffer); static int read(const std::string &path, Glib::RefPtr<Gtk::TextBuffer> text_buffer);
static bool read(const boost::filesystem::path &path, Glib::RefPtr<Gtk::TextBuffer> text_buffer) { return read(path.string(), text_buffer); } static int read(const boost::filesystem::path &path, Glib::RefPtr<Gtk::TextBuffer> text_buffer) { return read(path.string(), text_buffer); }
static std::vector<std::string> read_lines(const std::string &path); static std::vector<std::string> read_lines(const std::string &path);
static std::vector<std::string> read_lines(const boost::filesystem::path &path) { return read_lines(path.string()); }; static std::vector<std::string> read_lines(const boost::filesystem::path &path) { return read_lines(path.string()); };

42
src/terminal.cc

@ -154,6 +154,7 @@ Terminal::Terminal() {
} }
int Terminal::execute(const std::string &command, const boost::filesystem::path &path) { int Terminal::execute(const std::string &command, const boost::filesystem::path &path) {
DEBUG("start");
int stdin_fd, stdout_fd, stderr_fd; int stdin_fd, stdout_fd, stderr_fd;
auto pid=popen3(command, path.string(), &stdin_fd, &stdout_fd, &stderr_fd); auto pid=popen3(command, path.string(), &stdin_fd, &stdout_fd, &stderr_fd);
@ -176,7 +177,6 @@ int Terminal::execute(const std::string &command, const boost::filesystem::path
std::thread stdout_thread([this, stdout_fd](){ std::thread stdout_thread([this, stdout_fd](){
char buffer[1024]; char buffer[1024];
ssize_t n; ssize_t n;
INFO("read");
while ((n=read(stdout_fd, buffer, 1024)) > 0) { while ((n=read(stdout_fd, buffer, 1024)) > 0) {
std::string message; std::string message;
for(ssize_t c=0;c<n;c++) for(ssize_t c=0;c<n;c++)
@ -192,12 +192,14 @@ int Terminal::execute(const std::string &command, const boost::filesystem::path
close(stdout_fd); close(stdout_fd);
close(stderr_fd); close(stderr_fd);
DEBUG("end");
return exit_code; return exit_code;
} }
} }
void Terminal::async_execute(const std::string &command, const boost::filesystem::path &path, std::function<void(int exit_code)> callback) { void Terminal::async_execute(const std::string &command, const boost::filesystem::path &path, std::function<void(int exit_code)> callback) {
std::thread async_execute_thread([this, command, path, callback](){ std::thread async_execute_thread([this, command, path, callback](){
DEBUG("start");
int stdin_fd, stdout_fd, stderr_fd; int stdin_fd, stdout_fd, stderr_fd;
async_executes_mutex.lock(); async_executes_mutex.lock();
stdin_buffer.clear(); stdin_buffer.clear();
@ -225,7 +227,6 @@ void Terminal::async_execute(const std::string &command, const boost::filesystem
std::thread stdout_thread([this, stdout_fd](){ std::thread stdout_thread([this, stdout_fd](){
char buffer[1024]; char buffer[1024];
ssize_t n; ssize_t n;
INFO("read");
while ((n=read(stdout_fd, buffer, 1024)) > 0) { while ((n=read(stdout_fd, buffer, 1024)) > 0) {
std::string message; std::string message;
for(ssize_t c=0;c<n;c++) for(ssize_t c=0;c<n;c++)
@ -252,6 +253,8 @@ void Terminal::async_execute(const std::string &command, const boost::filesystem
if(callback) if(callback)
callback(exit_code); callback(exit_code);
DEBUG("end");
} }
}); });
async_execute_thread.detach(); async_execute_thread.detach();
@ -280,15 +283,22 @@ void Terminal::kill_async_executes(bool force) {
} }
int Terminal::print(const std::string &message, bool bold){ int Terminal::print(const std::string &message, bool bold){
INFO("Terminal: PrintMessage"); Glib::ustring umessage=message;
Glib::ustring::iterator iter;
while(!umessage.validate(iter)) {
auto next_char_iter=iter;
next_char_iter++;
umessage.replace(iter, next_char_iter, "?");
}
if(bold) if(bold)
get_buffer()->insert_with_tag(get_buffer()->end(), message, bold_tag); get_buffer()->insert_with_tag(get_buffer()->end(), umessage, bold_tag);
else else
get_buffer()->insert(get_buffer()->end(), message); get_buffer()->insert(get_buffer()->end(), umessage);
auto iter=get_buffer()->end(); auto end_iter=get_buffer()->end();
if(iter.backward_char()) { if(end_iter.backward_char()) {
auto mark=get_buffer()->create_mark(iter); auto mark=get_buffer()->create_mark(end_iter);
scroll_to(mark, 0.0, 1.0, 1.0); scroll_to(mark, 0.0, 1.0, 1.0);
get_buffer()->delete_mark(mark); get_buffer()->delete_mark(mark);
} }
@ -297,11 +307,17 @@ int Terminal::print(const std::string &message, bool bold){
} }
void Terminal::print(int line_nr, const std::string &message){ void Terminal::print(int line_nr, const std::string &message){
INFO("Terminal: PrintMessage at line " << line_nr); Glib::ustring umessage=message;
auto iter=get_buffer()->get_iter_at_line(line_nr); Glib::ustring::iterator iter;
while(!iter.ends_line()) while(!umessage.validate(iter)) {
iter++; auto next_char_iter=iter;
get_buffer()->insert(iter, message); next_char_iter++;
umessage.replace(iter, next_char_iter, "?");
}
auto end_line_iter=get_buffer()->get_iter_at_line(line_nr);
while(!end_line_iter.ends_line() && end_line_iter.forward_char()) {}
get_buffer()->insert(end_line_iter, umessage);
} }
std::shared_ptr<Terminal::InProgress> Terminal::print_in_progress(std::string start_msg) { std::shared_ptr<Terminal::InProgress> Terminal::print_in_progress(std::string start_msg) {

24
src/tooltips.cc

@ -1,6 +1,9 @@
#include "tooltips.h" #include "tooltips.h"
#include "singletons.h" #include "singletons.h"
#include <iostream>
using namespace std;
namespace sigc { namespace sigc {
#ifndef SIGC_FUNCTORS_DEDUCE_RESULT_TYPE_WITH_DECLTYPE #ifndef SIGC_FUNCTORS_DEDUCE_RESULT_TYPE_WITH_DECLTYPE
template <typename Functor> template <typename Functor>
@ -32,12 +35,10 @@ void Tooltip::update() {
auto end_iter=end_mark->get_iter(); auto end_iter=end_mark->get_iter();
text_view.get_iter_location(iter, activation_rectangle); text_view.get_iter_location(iter, activation_rectangle);
if(iter.get_offset()<end_iter.get_offset()) { if(iter.get_offset()<end_iter.get_offset()) {
iter++; while(iter.forward_char() && iter!=end_iter) {
while(iter!=end_iter) {
Gdk::Rectangle rectangle; Gdk::Rectangle rectangle;
text_view.get_iter_location(iter, rectangle); text_view.get_iter_location(iter, rectangle);
activation_rectangle.join(rectangle); activation_rectangle.join(rectangle);
iter++;
} }
} }
int location_window_x, location_window_y; int location_window_x, location_window_y;
@ -96,7 +97,6 @@ void Tooltip::adjust(bool disregard_drawn) {
} }
void Tooltip::wrap_lines(Glib::RefPtr<Gtk::TextBuffer> text_buffer) { void Tooltip::wrap_lines(Glib::RefPtr<Gtk::TextBuffer> text_buffer) {
INFO("Tooltip::wrap_lines");
auto iter=text_buffer->begin(); auto iter=text_buffer->begin();
while(iter) { while(iter) {
@ -111,28 +111,28 @@ void Tooltip::wrap_lines(Glib::RefPtr<Gtk::TextBuffer> text_buffer) {
last_space=iter; last_space=iter;
if(*iter=='\n') { if(*iter=='\n') {
end=true; end=true;
iter++; iter.forward_char();
break; break;
} }
iter++; iter.forward_char();
} }
if(!end) { if(!end) {
while(!last_space && iter) { //If no space (word longer than 80) while(!last_space && iter) { //If no space (word longer than 80)
iter++; iter.forward_char();
if(iter && *iter==' ') if(iter && *iter==' ')
last_space=iter; last_space=iter;
} }
if(iter && last_space) { if(iter && last_space) {
auto mark=text_buffer->create_mark(last_space); auto mark=text_buffer->create_mark(last_space);
auto iter_mark=text_buffer->create_mark(iter); auto last_space_p=last_space;
auto last_space_p=last_space++; last_space.forward_char();
text_buffer->erase(last_space, last_space_p); text_buffer->erase(last_space_p, last_space);
text_buffer->insert(mark->get_iter(), "\n"); text_buffer->insert(mark->get_iter(), "\n");
iter=iter_mark->get_iter(); iter=mark->get_iter();
iter.forward_char();
text_buffer->delete_mark(mark); text_buffer->delete_mark(mark);
text_buffer->delete_mark(iter_mark);
} }
} }
} }

29
src/window.cc

@ -33,7 +33,7 @@ void Window::generate_keybindings() {
} }
Window::Window() : box(Gtk::ORIENTATION_VERTICAL), notebook(directories), compiling(false) { Window::Window() : box(Gtk::ORIENTATION_VERTICAL), notebook(directories), compiling(false) {
INFO("Create Window"); DEBUG("start");
set_title("juCi++"); set_title("juCi++");
set_default_size(600, 400); set_default_size(600, 400);
set_events(Gdk::POINTER_MOTION_MASK|Gdk::FOCUS_CHANGE_MASK|Gdk::SCROLL_MASK); set_events(Gdk::POINTER_MOTION_MASK|Gdk::FOCUS_CHANGE_MASK|Gdk::SCROLL_MASK);
@ -104,9 +104,9 @@ Window::Window() : box(Gtk::ORIENTATION_VERTICAL), notebook(directories), compil
directories.select(notebook.get_current_view()->file_path); directories.select(notebook.get_current_view()->file_path);
if(auto source_view=dynamic_cast<Source::ClangView*>(notebook.get_current_view())) { if(auto source_view=dynamic_cast<Source::ClangView*>(notebook.get_current_view())) {
if(source_view->start_reparse_needed) { if(source_view->reparse_needed) {
source_view->start_reparse(); source_view->start_reparse();
source_view->start_reparse_needed=false; source_view->reparse_needed=false;
} }
} }
@ -132,11 +132,10 @@ Window::Window() : box(Gtk::ORIENTATION_VERTICAL), notebook(directories), compil
about.set_comments("This is an open source IDE with high-end features to make your programming experience juicy"); about.set_comments("This is an open source IDE with high-end features to make your programming experience juicy");
about.set_license_type(Gtk::License::LICENSE_MIT_X11); about.set_license_type(Gtk::License::LICENSE_MIT_X11);
about.set_transient_for(*this); about.set_transient_for(*this);
INFO("Window created"); DEBUG("end");
} // Window constructor } // Window constructor
void Window::create_menu() { void Window::create_menu() {
INFO("Adding actions to menu");
menu.action_group->add(Gtk::Action::create("FileQuit", "Quit juCi++"), Gtk::AccelKey(menu.key_map["quit"]), [this]() { menu.action_group->add(Gtk::Action::create("FileQuit", "Quit juCi++"), Gtk::AccelKey(menu.key_map["quit"]), [this]() {
hide(); hide();
}); });
@ -189,7 +188,6 @@ void Window::create_menu() {
search_and_replace_entry(); search_and_replace_entry();
}); });
menu.action_group->add(Gtk::Action::create("EditUndo", "Undo"), Gtk::AccelKey(menu.key_map["edit_undo"]), [this]() { menu.action_group->add(Gtk::Action::create("EditUndo", "Undo"), Gtk::AccelKey(menu.key_map["edit_undo"]), [this]() {
INFO("On undo");
if(notebook.get_current_page()!=-1) { if(notebook.get_current_page()!=-1) {
auto undo_manager = notebook.get_current_view()->get_source_buffer()->get_undo_manager(); auto undo_manager = notebook.get_current_view()->get_source_buffer()->get_undo_manager();
if (undo_manager->can_undo()) { if (undo_manager->can_undo()) {
@ -197,10 +195,8 @@ void Window::create_menu() {
notebook.get_current_view()->scroll_to(notebook.get_current_view()->get_buffer()->get_insert()); notebook.get_current_view()->scroll_to(notebook.get_current_view()->get_buffer()->get_insert());
} }
} }
INFO("Done undo");
}); });
menu.action_group->add(Gtk::Action::create("EditRedo", "Redo"), Gtk::AccelKey(menu.key_map["edit_redo"]), [this]() { menu.action_group->add(Gtk::Action::create("EditRedo", "Redo"), Gtk::AccelKey(menu.key_map["edit_redo"]), [this]() {
INFO("On Redo");
if(notebook.get_current_page()!=-1) { if(notebook.get_current_page()!=-1) {
auto undo_manager = notebook.get_current_view()->get_source_buffer()->get_undo_manager(); auto undo_manager = notebook.get_current_view()->get_source_buffer()->get_undo_manager();
if(undo_manager->can_redo()) { if(undo_manager->can_redo()) {
@ -208,7 +204,6 @@ void Window::create_menu() {
notebook.get_current_view()->scroll_to(notebook.get_current_view()->get_buffer()->get_insert()); notebook.get_current_view()->scroll_to(notebook.get_current_view()->get_buffer()->get_insert());
} }
} }
INFO("Done Redo");
}); });
menu.action_group->add(Gtk::Action::create("SourceGotoLine", "Go to Line"), Gtk::AccelKey(menu.key_map["source_goto_line"]), [this]() { menu.action_group->add(Gtk::Action::create("SourceGotoLine", "Go to Line"), Gtk::AccelKey(menu.key_map["source_goto_line"]), [this]() {
@ -218,6 +213,7 @@ void Window::create_menu() {
if(notebook.get_current_page()!=-1) { if(notebook.get_current_page()!=-1) {
while(gtk_events_pending()) while(gtk_events_pending())
gtk_main_iteration(); gtk_main_iteration();
if(notebook.get_current_page()!=-1)
notebook.get_current_view()->scroll_to(notebook.get_current_view()->get_buffer()->get_insert(), 0.0, 1.0, 0.5); notebook.get_current_view()->scroll_to(notebook.get_current_view()->get_buffer()->get_insert(), 0.0, 1.0, 0.5);
} }
}); });
@ -230,6 +226,7 @@ void Window::create_menu() {
notebook.get_current_view()->get_buffer()->place_cursor(notebook.get_current_view()->get_buffer()->get_iter_at_line_index(location.second.line-1, location.second.index-1)); notebook.get_current_view()->get_buffer()->place_cursor(notebook.get_current_view()->get_buffer()->get_iter_at_line_index(location.second.line-1, location.second.index-1));
while(gtk_events_pending()) while(gtk_events_pending())
gtk_main_iteration(); gtk_main_iteration();
if(notebook.get_current_page()!=-1)
notebook.get_current_view()->scroll_to(notebook.get_current_view()->get_buffer()->get_insert(), 0.0, 1.0, 0.5); notebook.get_current_view()->scroll_to(notebook.get_current_view()->get_buffer()->get_insert(), 0.0, 1.0, 0.5);
} }
} }
@ -325,6 +322,10 @@ void Window::create_menu() {
}); });
menu.action_group->add(Gtk::Action::create("WindowCloseTab", "Close Tab"), Gtk::AccelKey(menu.key_map["close_tab"]), [this]() { menu.action_group->add(Gtk::Action::create("WindowCloseTab", "Close Tab"), Gtk::AccelKey(menu.key_map["close_tab"]), [this]() {
notebook.close_current_page(); notebook.close_current_page();
if(notebook.get_current_page()!=-1)
Singleton::status()->set_text(notebook.get_current_view()->status);
else
Singleton::status()->set_text("");
}); });
menu.action_group->add(Gtk::Action::create("HelpAbout", "About"), [this] () { menu.action_group->add(Gtk::Action::create("HelpAbout", "About"), [this] () {
about.show(); about.show();
@ -332,7 +333,6 @@ void Window::create_menu() {
}); });
add_accel_group(menu.ui_manager->get_accel_group()); add_accel_group(menu.ui_manager->get_accel_group());
menu.build(); menu.build();
INFO("Menu build")
} }
bool Window::on_key_press_event(GdkEventKey *event) { bool Window::on_key_press_event(GdkEventKey *event) {
@ -526,7 +526,6 @@ void Window::open_file_dialog() {
void Window::save_file_dialog() { void Window::save_file_dialog() {
if(notebook.get_current_page()==-1) if(notebook.get_current_page()==-1)
return; return;
INFO("Save file dialog");
Gtk::FileChooserDialog dialog(*this, "Please choose a file", Gtk::FILE_CHOOSER_ACTION_SAVE); Gtk::FileChooserDialog dialog(*this, "Please choose a file", Gtk::FILE_CHOOSER_ACTION_SAVE);
gtk_file_chooser_set_filename((GtkFileChooser*)dialog.gobj(), notebook.get_current_view()->file_path.string().c_str()); gtk_file_chooser_set_filename((GtkFileChooser*)dialog.gobj(), notebook.get_current_view()->file_path.string().c_str());
dialog.set_position(Gtk::WindowPosition::WIN_POS_CENTER_ALWAYS); dialog.set_position(Gtk::WindowPosition::WIN_POS_CENTER_ALWAYS);
@ -652,6 +651,7 @@ void Window::goto_line_entry() {
buffer->place_cursor(buffer->get_iter_at_line(line)); buffer->place_cursor(buffer->get_iter_at_line(line));
while(gtk_events_pending()) while(gtk_events_pending())
gtk_main_iteration(); gtk_main_iteration();
if(notebook.get_current_page()!=-1)
notebook.get_current_view()->scroll_to(buffer->get_insert(), 0.0, 1.0, 0.5); notebook.get_current_view()->scroll_to(buffer->get_insert(), 0.0, 1.0, 0.5);
} }
} }
@ -688,10 +688,11 @@ void Window::rename_token_entry() {
entry_box.entries.emplace_back(*token_name, [this, token_name, token](const std::string& content){ entry_box.entries.emplace_back(*token_name, [this, token_name, token](const std::string& content){
if(notebook.get_current_page()!=-1 && content!=*token_name) { if(notebook.get_current_page()!=-1 && content!=*token_name) {
for(int c=0;c<notebook.size();c++) { for(int c=0;c<notebook.size();c++) {
if(notebook.get_view(c)->rename_similar_tokens) { auto view=notebook.get_view(c);
auto number=notebook.get_view(c)->rename_similar_tokens(*token, content); if(view->rename_similar_tokens) {
auto number=view->rename_similar_tokens(*token, content);
if(number>0) { if(number>0) {
Singleton::terminal()->print("Replaced "+boost::lexical_cast<std::string>(number)+" occurrences in file "+notebook.get_view(c)->file_path.string()+"\n"); Singleton::terminal()->print("Replaced "+boost::lexical_cast<std::string>(number)+" occurrences in file "+view->file_path.string()+"\n");
notebook.save(c); notebook.save(c);
} }
} }

Loading…
Cancel
Save