Browse Source

Merge pull request #166 from eidheim/master

Auto indentation and bracket based smart indentation for Java, JavaScript and TypeScript
merge-requests/365/head
Ole Christian Eidheim 10 years ago
parent
commit
dbf63dff1c
  1. 2
      README.md
  2. 2
      src/directories.cc
  3. 2
      src/dispatcher.cc
  4. 2
      src/dispatcher.h
  5. 12
      src/notebook.cc
  6. 4
      src/project.cc
  7. 315
      src/source.cc
  8. 12
      src/source.h
  9. 300
      src/source_clang.cc
  10. 10
      src/source_clang.h
  11. 4
      src/terminal.cc
  12. 22
      src/window.cc

2
README.md

@ -1,5 +1,5 @@
# juCi++ # juCi++
###### a lightweight platform independent C++-IDE with support for C++11, C++14, and experimental C++17 features depending on libclang version. ###### a lightweight, platform independent C++-IDE with support for C++11, C++14, and experimental C++17 features depending on libclang version.
<!--<img src="https://github.com/cppit/jucipp/blob/master/docs/images/screenshot3.png"/>--> <!--<img src="https://github.com/cppit/jucipp/blob/master/docs/images/screenshot3.png"/>-->
## About ## About
Current IDEs struggle with C++ support due to the complexity of Current IDEs struggle with C++ support due to the complexity of

2
src/directories.cc

@ -98,7 +98,7 @@ Directories::Directories() : Gtk::TreeView(), stop_update_thread(false) {
it=last_write_times.erase(it); it=last_write_times.erase(it);
} }
if(update_paths.size()>0) { if(update_paths.size()>0) {
dispatcher.push([this] { dispatcher.post([this] {
update_mutex.lock(); update_mutex.lock();
for(auto &path: update_paths) { for(auto &path: update_paths) {
if(last_write_times.count(path)>0) if(last_write_times.count(path)>0)

2
src/dispatcher.cc

@ -18,7 +18,7 @@ Dispatcher::~Dispatcher() {
functions_mutex.unlock(); functions_mutex.unlock();
} }
void Dispatcher::push(std::function<void()> &&function) { void Dispatcher::post(std::function<void()> &&function) {
functions_mutex.lock(); functions_mutex.lock();
functions.emplace_back(function); functions.emplace_back(function);
functions_mutex.unlock(); functions_mutex.unlock();

2
src/dispatcher.h

@ -13,7 +13,7 @@ private:
public: public:
Dispatcher(); Dispatcher();
~Dispatcher(); ~Dispatcher();
void push(std::function<void()> &&function); void post(std::function<void()> &&function);
void disconnect(); void disconnect();
}; };

12
src/notebook.cc

@ -116,6 +116,18 @@ void Notebook::open(const boost::filesystem::path &file_path) {
else else
source_views.emplace_back(new Source::GenericView(file_path, project_path, language)); source_views.emplace_back(new Source::GenericView(file_path, project_path, language));
source_views.back()->scroll_to_cursor_delayed=[this](Source::View* view, bool center, bool show_tooltips) {
while(g_main_context_pending(NULL))
g_main_context_iteration(NULL, false);
if(get_current_page()!=-1 && get_current_view()==view) {
if(center)
view->scroll_to(view->get_buffer()->get_insert(), 0.0, 1.0, 0.5);
else
view->scroll_to(view->get_buffer()->get_insert());
if(!show_tooltips)
view->delayed_tooltips_connection.disconnect();
}
};
source_views.back()->on_update_status=[this](Source::View* view, const std::string &status_text) { source_views.back()->on_update_status=[this](Source::View* view, const std::string &status_text) {
if(get_current_page()!=-1 && get_current_view()==view) if(get_current_page()!=-1 && get_current_view()==view)
status.set_text(status_text+" "); status.set_text(status_text+" ");

4
src/project.cc

@ -268,11 +268,11 @@ void Project::Clang::debug_start() {
debugging=false; debugging=false;
Terminal::get().async_print(run_arguments+" returned: "+std::to_string(exit_status)+'\n'); Terminal::get().async_print(run_arguments+" returned: "+std::to_string(exit_status)+'\n');
}, [this](const std::string &status) { }, [this](const std::string &status) {
dispatcher.push([this, status] { dispatcher.post([this, status] {
debug_update_status(status); debug_update_status(status);
}); });
}, [this](const boost::filesystem::path &file_path, int line_nr, int line_index) { }, [this](const boost::filesystem::path &file_path, int line_nr, int line_index) {
dispatcher.push([this, file_path, line_nr, line_index] { dispatcher.post([this, file_path, line_nr, line_index] {
Project::debug_stop.first=file_path; Project::debug_stop.first=file_path;
Project::debug_stop.second.first=line_nr; Project::debug_stop.second.first=line_nr;
Project::debug_stop.second.second=line_index; Project::debug_stop.second.second=line_index;

315
src/source.cc

@ -151,12 +151,12 @@ Source::View::View(const boost::filesystem::path &file_path, const boost::filesy
if(ends_line || *iter=='/' || *iter=='*') //iter_has_context_class is sadly bugged if(ends_line || *iter=='/' || *iter=='*') //iter_has_context_class is sadly bugged
backward_success=context_iter.backward_char(); backward_success=context_iter.backward_char();
if(backward_success) { if(backward_success) {
if(last_keyval_is_backspace && !is_word_iter(iter) && iter.forward_char()) {} //backspace fix if(last_keyval==GDK_KEY_BackSpace && !is_word_iter(iter) && iter.forward_char()) {} //backspace fix
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(!is_word_iter(iter) || last_keyval_is_return) { //Might have used space or - to split two words if(!is_word_iter(iter) || last_keyval==GDK_KEY_Return) { //Might have used space or - to split two words
auto first=iter; auto first=iter;
auto second=iter; auto second=iter;
if(last_keyval_is_return) { if(last_keyval==GDK_KEY_Return) {
while(first && !first.ends_line() && first.backward_char()) {} while(first && !first.ends_line() && 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);
@ -292,6 +292,79 @@ Source::View::View(const boost::filesystem::path &file_path, const boost::filesy
} }
} }
set_tab_char_and_size(tab_char, tab_size); set_tab_char_and_size(tab_char, tab_size);
bracket_regex=boost::regex("^([ \\t]*).*\\{ *$");
no_bracket_statement_regex=boost::regex("^([ \\t]*)(if|for|else if|while) *\\(.*[^;}] *$");
no_bracket_no_para_statement_regex=boost::regex("^([ \\t]*)(else) *$");
if(language && (language->get_id()=="chdr" || language->get_id()=="cpphdr" || language->get_id()=="c" ||
language->get_id()=="cpp" || language->get_id()=="objc" || language->get_id()=="java" ||
language->get_id()=="js" || language->get_id()=="ts" || language->get_id()=="proto" ||
language->get_id()=="c-sharp")) {
is_bracket_language=true;
auto_indent=[this]() {
auto command=Config::get().terminal.clang_format_command;
bool use_style_file=false;
auto style_file_search_path=this->file_path.parent_path();
while(true) {
if(boost::filesystem::exists(style_file_search_path/".clang-format") || boost::filesystem::exists(style_file_search_path/"_clang-format")) {
use_style_file=true;
break;
}
if(style_file_search_path==style_file_search_path.root_directory())
break;
style_file_search_path=style_file_search_path.parent_path();
}
if(use_style_file)
command+=" -style=file";
else {
unsigned indent_width;
std::string tab_style;
if(tab_char=='\t') {
indent_width=tab_size*8;
tab_style="UseTab: Always";
}
else {
indent_width=tab_size;
tab_style="UseTab: Never";
}
command+=" -style=\"{IndentWidth: "+std::to_string(indent_width);
command+=", "+tab_style;
command+=", "+std::string("AccessModifierOffset: -")+std::to_string(indent_width);
if(Config::get().source.clang_format_style!="")
command+=", "+Config::get().source.clang_format_style;
command+="}\"";
}
std::stringstream stdin_stream(get_buffer()->get_text()), stdout_stream;
auto exit_status=Terminal::get().process(stdin_stream, stdout_stream, command, this->file_path.parent_path());
if(exit_status==0) {
get_source_buffer()->begin_user_action();
auto iter=get_buffer()->get_insert()->get_iter();
auto cursor_line_nr=iter.get_line();
auto cursor_line_offset=iter.get_line_offset();
get_buffer()->set_text(stdout_stream.str());
get_source_buffer()->end_user_action();
cursor_line_nr=std::min(cursor_line_nr, get_buffer()->get_line_count()-1);
if(cursor_line_nr>=0) {
iter=get_buffer()->get_iter_at_line(cursor_line_nr);
for(int c=0;c<cursor_line_offset;c++) {
if(iter.ends_line())
break;
iter.forward_char();
}
get_buffer()->place_cursor(iter);
scroll_to_cursor_delayed(this, true, false);
}
}
};
}
} }
void Source::View::set_tab_char_and_size(char tab_char, unsigned tab_size) { void Source::View::set_tab_char_and_size(char tab_char, unsigned tab_size) {
@ -673,12 +746,13 @@ void Source::View::paste() {
} }
} }
get_buffer()->place_cursor(get_buffer()->get_insert()->get_iter()); get_buffer()->place_cursor(get_buffer()->get_insert()->get_iter());
scroll_to(get_buffer()->get_insert());
get_source_buffer()->end_user_action(); get_source_buffer()->end_user_action();
scroll_to_cursor_delayed(this, false, false);
} }
else { else {
Gtk::Clipboard::get()->set_text(text); Gtk::Clipboard::get()->set_text(text);
get_buffer()->paste_clipboard(Gtk::Clipboard::get()); get_buffer()->paste_clipboard(Gtk::Clipboard::get());
scroll_to_cursor_delayed(this, false, false);
} }
} }
@ -973,22 +1047,34 @@ bool Source::View::find_left_bracket_backward(Gtk::TextIter iter, Gtk::TextIter
return false; return false;
} }
//Basic indentation
bool Source::View::on_key_press_event(GdkEventKey* key) { bool Source::View::on_key_press_event(GdkEventKey* key) {
if(spellcheck_suggestions_dialog && spellcheck_suggestions_dialog->shown) { if(spellcheck_suggestions_dialog && spellcheck_suggestions_dialog->shown) {
if(spellcheck_suggestions_dialog->on_key_press(key)) if(spellcheck_suggestions_dialog->on_key_press(key))
return true; return true;
} }
if(key->keyval==GDK_KEY_BackSpace)
last_keyval_is_backspace=true;
else
last_keyval_is_backspace=false;
if(key->keyval==GDK_KEY_Return)
last_keyval_is_return=true;
else
last_keyval_is_return=false;
if(autocomplete_dialog && autocomplete_dialog->shown) {
if(autocomplete_dialog->on_key_press(key))
return true;
}
last_keyval=key->keyval;
if(get_buffer()->get_has_selection())
return on_key_press_event_basic(key);
auto iter=get_buffer()->get_insert()->get_iter();
if(iter.backward_char() && (get_source_buffer()->iter_has_context_class(iter, "comment") || get_source_buffer()->iter_has_context_class(iter, "string")))
return on_key_press_event_basic(key);
if(is_bracket_language)
return on_key_press_event_bracket_language(key);
else
return on_key_press_event_basic(key);
}
//Basic indentation
bool Source::View::on_key_press_event_basic(GdkEventKey* key) {
get_source_buffer()->begin_user_action(); get_source_buffer()->begin_user_action();
auto iter=get_buffer()->get_insert()->get_iter(); auto iter=get_buffer()->get_insert()->get_iter();
//Indent as in next or previous line //Indent as in next or previous line
@ -1202,6 +1288,203 @@ bool Source::View::on_key_press_event(GdkEventKey* key) {
return stop; return stop;
} }
//Bracket language indentation
bool Source::View::on_key_press_event_bracket_language(GdkEventKey* key) {
get_source_buffer()->begin_user_action();
auto iter=get_buffer()->get_insert()->get_iter();
//Indent depending on if/else/etc and brackets
if(key->keyval==GDK_KEY_Return && !iter.starts_line()) {
//First remove spaces or tabs around cursor
auto start_blank_iter=iter;
auto end_blank_iter=iter;
while((*end_blank_iter==' ' || *end_blank_iter=='\t') &&
!end_blank_iter.ends_line() && end_blank_iter.forward_char()) {}
start_blank_iter.backward_char();
while((*start_blank_iter==' ' || *start_blank_iter=='\t' || start_blank_iter.ends_line()) &&
!start_blank_iter.starts_line() && start_blank_iter.backward_char()) {}
if(!start_blank_iter.starts_line()) {
start_blank_iter.forward_char();
get_buffer()->erase(start_blank_iter, end_blank_iter);
}
else
get_buffer()->erase(iter, end_blank_iter);
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_sentence_tabs_end_iter=get_tabs_end_iter(start_of_sentence_iter);
auto tabs=get_line_before(start_sentence_tabs_end_iter);
boost::smatch sm;
if(iter.backward_char() && *iter=='{') {
auto found_iter=iter;
bool found_right_bracket=find_right_bracket_forward(iter, found_iter);
bool has_bracket=false;
if(found_right_bracket) {
auto tabs_end_iter=get_tabs_end_iter(found_iter);
auto line_tabs=get_line_before(tabs_end_iter);
if(tabs.size()==line_tabs.size())
has_bracket=true;
}
if(*get_buffer()->get_insert()->get_iter()=='}') {
get_source_buffer()->insert_at_cursor("\n"+tabs+tab+"\n"+tabs);
auto insert_it = get_source_buffer()->get_insert()->get_iter();
if(insert_it.backward_chars(tabs.size()+1)) {
scroll_to(get_source_buffer()->get_insert());
get_source_buffer()->place_cursor(insert_it);
}
get_source_buffer()->end_user_action();
return true;
}
else if(!has_bracket) {
//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();
if(insert_it.backward_chars(tabs.size()+2)) {
scroll_to(get_source_buffer()->get_insert());
get_source_buffer()->place_cursor(insert_it);
}
get_source_buffer()->end_user_action();
return true;
}
else {
get_source_buffer()->insert_at_cursor("\n"+tabs+tab);
scroll_to(get_buffer()->get_insert());
get_source_buffer()->end_user_action();
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 tabs_end_iter=get_tabs_end_iter(found_iter);
tabs=get_line_before(tabs_end_iter);
auto iter=tabs_end_iter;
while(iter<=found_iter) {
tabs+=' ';
iter.forward_char();
}
}
else if(boost::regex_match(line, sm, no_bracket_statement_regex)) {
get_source_buffer()->insert_at_cursor("\n"+tabs+tab);
scroll_to(get_source_buffer()->get_insert());
get_source_buffer()->end_user_action();
return true;
}
else if(boost::regex_match(line, sm, no_bracket_no_para_statement_regex)) {
get_source_buffer()->insert_at_cursor("\n"+tabs+tab);
scroll_to(get_source_buffer()->get_insert());
get_source_buffer()->end_user_action();
return true;
}
//Indenting after for instance if(...)\n...;\n
else if(iter.backward_char() && *iter==';') {
boost::smatch sm2;
size_t line_nr=get_source_buffer()->get_insert()->get_iter().get_line();
if(line_nr>0 && tabs.size()>=tab_size) {
std::string previous_line=get_line(line_nr-1);
if(!boost::regex_match(previous_line, sm2, bracket_regex)) {
if(boost::regex_match(previous_line, sm2, no_bracket_statement_regex)) {
get_source_buffer()->insert_at_cursor("\n"+sm2[1].str());
scroll_to(get_source_buffer()->get_insert());
get_source_buffer()->end_user_action();
return true;
}
else if(boost::regex_match(previous_line, sm2, no_bracket_no_para_statement_regex)) {
get_source_buffer()->insert_at_cursor("\n"+sm2[1].str());
scroll_to(get_source_buffer()->get_insert());
get_source_buffer()->end_user_action();
return true;
}
}
}
}
//Indenting after ':'
else if(*iter==':') {
Gtk::TextIter left_bracket_iter;
if(find_left_bracket_backward(iter, left_bracket_iter)) {
if(!left_bracket_iter.ends_line())
left_bracket_iter.forward_char();
Gtk::TextIter start_of_left_bracket_sentence_iter;
if(find_start_of_closed_expression(left_bracket_iter, start_of_left_bracket_sentence_iter)) {
boost::smatch sm;
auto tabs_end_iter=get_tabs_end_iter(start_of_left_bracket_sentence_iter);
auto tabs_start_of_sentence=get_line_before(tabs_end_iter);
if(tabs.size()==(tabs_start_of_sentence.size()+tab_size)) {
auto start_line_iter=get_buffer()->get_iter_at_line(iter.get_line());
auto start_line_plus_tab_size=start_line_iter;
for(size_t c=0;c<tab_size;c++)
start_line_plus_tab_size.forward_char();
get_buffer()->erase(start_line_iter, start_line_plus_tab_size);
}
else {
get_source_buffer()->insert_at_cursor("\n"+tabs+tab);
scroll_to(get_source_buffer()->get_insert());
get_source_buffer()->end_user_action();
return true;
}
}
}
}
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
else if(key->keyval==GDK_KEY_braceright) {
std::string line=get_line_before();
if(line.size()>=tab_size) {
for(auto c: line) {
if(c!=tab_char) {
get_source_buffer()->insert_at_cursor("}");
get_source_buffer()->end_user_action();
return true;
}
}
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_plus_it=line_it;
line_plus_it.forward_chars(tab_size);
get_source_buffer()->erase(line_it, line_plus_it);
}
get_source_buffer()->insert_at_cursor("}");
get_source_buffer()->end_user_action();
return true;
}
//Indent left when writing { on a new line after for instance if(...)\n...
else if(key->keyval==GDK_KEY_braceleft) {
auto iter=get_buffer()->get_insert()->get_iter();
auto tabs_end_iter=get_tabs_end_iter();
auto tabs=get_line_before(tabs_end_iter);
size_t line_nr=iter.get_line();
if(line_nr>0 && tabs.size()>=tab_size && iter==tabs_end_iter) {
std::string previous_line=get_line(line_nr-1);
boost::smatch sm;
if(!boost::regex_match(previous_line, sm, bracket_regex)) {
auto start_iter=iter;
start_iter.backward_chars(tab_size);
if(boost::regex_match(previous_line, sm, no_bracket_statement_regex) ||
boost::regex_match(previous_line, sm, no_bracket_no_para_statement_regex)) {
if((tabs.size()-tab_size)==sm[1].str().size()) {
get_buffer()->erase(start_iter, iter);
get_buffer()->insert_at_cursor("{");
scroll_to(get_buffer()->get_insert());
get_buffer()->end_user_action();
return true;
}
}
}
}
}
get_source_buffer()->end_user_action();
return on_key_press_event_basic(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;
@ -1445,10 +1728,8 @@ Source::GenericView::GenericView(const boost::filesystem::path &file_path, const
configure(); configure();
spellcheck_all=true; spellcheck_all=true;
if(language) { if(language)
get_source_buffer()->set_language(language); get_source_buffer()->set_language(language);
Terminal::get().print("Language for file "+file_path.string()+" set to "+language->get_name()+".\n");
}
auto completion=get_completion(); auto completion=get_completion();
completion->property_show_headers()=false; completion->property_show_headers()=false;

12
src/source.h

@ -7,6 +7,7 @@
#include <string> #include <string>
#include <unordered_map> #include <unordered_map>
#include <vector> #include <vector>
#include <boost/regex.hpp>
#include "selectiondialog.h" #include "selectiondialog.h"
#include "tooltips.h" #include "tooltips.h"
@ -91,6 +92,7 @@ namespace Source {
Gtk::TextIter get_iter_for_dialog(); Gtk::TextIter get_iter_for_dialog();
sigc::connection delayed_tooltips_connection; sigc::connection delayed_tooltips_connection;
std::function<void(View* view, bool center, bool show_tooltips)> scroll_to_cursor_delayed=[](View* view, bool center, bool show_tooltips) {};
std::function<void(View* view, const std::string &status_text)> on_update_status; std::function<void(View* view, const std::string &status_text)> on_update_status;
std::function<void(View* view, const std::string &info_text)> on_update_info; std::function<void(View* view, const std::string &info_text)> on_update_info;
void set_status(const std::string &status); void set_status(const std::string &status);
@ -136,7 +138,14 @@ 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);
boost::regex bracket_regex;
boost::regex no_bracket_statement_regex;
boost::regex no_bracket_no_para_statement_regex;
bool on_key_press_event(GdkEventKey* key) override; bool on_key_press_event(GdkEventKey* key) override;
bool on_key_press_event_basic(GdkEventKey* key);
bool on_key_press_event_bracket_language(GdkEventKey* key);
bool is_bracket_language=false;
bool on_button_press_event(GdkEventButton *event) override; 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();
@ -146,8 +155,7 @@ namespace Source {
bool spellcheck_all=false; bool spellcheck_all=false;
std::unique_ptr<SelectionDialog> spellcheck_suggestions_dialog; std::unique_ptr<SelectionDialog> spellcheck_suggestions_dialog;
bool last_keyval_is_backspace=false; guint last_keyval=0;
bool last_keyval_is_return=false;
private: private:
GtkSourceSearchContext *search_context; GtkSourceSearchContext *search_context;
GtkSourceSearchSettings *search_settings; GtkSourceSearchSettings *search_settings;

300
src/source_clang.cc

@ -70,10 +70,6 @@ void Source::ClangViewParse::configure() {
JINFO("Style " + item.second + " not found in " + scheme->get_name()); JINFO("Style " + item.second + " not found in " + scheme->get_name());
} }
} }
bracket_regex=boost::regex("^([ \\t]*).*\\{ *$");
no_bracket_statement_regex=boost::regex("^([ \\t]*)(if|for|else if|while) *\\(.*[^;}] *$");
no_bracket_no_para_statement_regex=boost::regex("^([ \\t]*)(else) *$");
} }
void Source::ClangViewParse::parse_initialize() { void Source::ClangViewParse::parse_initialize() {
@ -111,7 +107,7 @@ void Source::ClangViewParse::parse_initialize() {
break; break;
auto expected=ParseProcessState::STARTING; auto expected=ParseProcessState::STARTING;
if(parse_process_state.compare_exchange_strong(expected, ParseProcessState::PREPROCESSING)) { if(parse_process_state.compare_exchange_strong(expected, ParseProcessState::PREPROCESSING)) {
dispatcher.push([this] { dispatcher.post([this] {
auto expected=ParseProcessState::PREPROCESSING; auto expected=ParseProcessState::PREPROCESSING;
if(parse_mutex.try_lock()) { if(parse_mutex.try_lock()) {
if(parse_process_state.compare_exchange_strong(expected, ParseProcessState::PROCESSING)) if(parse_process_state.compare_exchange_strong(expected, ParseProcessState::PROCESSING))
@ -130,7 +126,7 @@ void Source::ClangViewParse::parse_initialize() {
if(parse_process_state.compare_exchange_strong(expected, ParseProcessState::POSTPROCESSING)) { if(parse_process_state.compare_exchange_strong(expected, ParseProcessState::POSTPROCESSING)) {
clang_tokens=clang_tu->get_tokens(0, parse_thread_buffer.bytes()-1); clang_tokens=clang_tu->get_tokens(0, parse_thread_buffer.bytes()-1);
parse_mutex.unlock(); parse_mutex.unlock();
dispatcher.push([this] { dispatcher.post([this] {
if(parse_mutex.try_lock()) { if(parse_mutex.try_lock()) {
auto expected=ParseProcessState::POSTPROCESSING; auto expected=ParseProcessState::POSTPROCESSING;
if(parse_process_state.compare_exchange_strong(expected, ParseProcessState::IDLE)) { if(parse_process_state.compare_exchange_strong(expected, ParseProcessState::IDLE)) {
@ -149,7 +145,7 @@ void Source::ClangViewParse::parse_initialize() {
else { else {
parse_state=ParseState::STOP; parse_state=ParseState::STOP;
parse_mutex.unlock(); parse_mutex.unlock();
dispatcher.push([this] { dispatcher.post([this] {
Terminal::get().print("Error: failed to reparse "+this->file_path.string()+".\n", true); Terminal::get().print("Error: failed to reparse "+this->file_path.string()+".\n", true);
set_status(""); set_status("");
set_info(""); set_info("");
@ -451,215 +447,6 @@ void Source::ClangViewParse::show_type_tooltips(const Gdk::Rectangle &rectangle)
//type_tooltips.show(rectangle); //type_tooltips.show(rectangle);
} }
//Clang indentation.
bool Source::ClangViewParse::on_key_press_event(GdkEventKey* key) {
if(spellcheck_suggestions_dialog && spellcheck_suggestions_dialog->shown) {
if(spellcheck_suggestions_dialog->on_key_press(key))
return true;
}
auto iter=get_buffer()->get_insert()->get_iter();
if(iter.backward_char() && (get_source_buffer()->iter_has_context_class(iter, "comment") || get_source_buffer()->iter_has_context_class(iter, "string")))
return Source::View::on_key_press_event(key);
if(get_buffer()->get_has_selection()) {
return Source::View::on_key_press_event(key);
}
get_source_buffer()->begin_user_action();
iter=get_buffer()->get_insert()->get_iter();
//Indent depending on if/else/etc and brackets
if(key->keyval==GDK_KEY_Return && !iter.starts_line()) {
//First remove spaces or tabs around cursor
auto start_blank_iter=iter;
auto end_blank_iter=iter;
while((*end_blank_iter==' ' || *end_blank_iter=='\t') &&
!end_blank_iter.ends_line() && end_blank_iter.forward_char()) {}
start_blank_iter.backward_char();
while((*start_blank_iter==' ' || *start_blank_iter=='\t' || start_blank_iter.ends_line()) &&
!start_blank_iter.starts_line() && start_blank_iter.backward_char()) {}
if(!start_blank_iter.starts_line()) {
start_blank_iter.forward_char();
get_buffer()->erase(start_blank_iter, end_blank_iter);
}
else
get_buffer()->erase(iter, end_blank_iter);
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_sentence_tabs_end_iter=get_tabs_end_iter(start_of_sentence_iter);
auto tabs=get_line_before(start_sentence_tabs_end_iter);
boost::smatch sm;
if(iter.backward_char() && *iter=='{') {
auto found_iter=iter;
bool found_right_bracket=find_right_bracket_forward(iter, found_iter);
bool has_bracket=false;
if(found_right_bracket) {
auto tabs_end_iter=get_tabs_end_iter(found_iter);
auto line_tabs=get_line_before(tabs_end_iter);
if(tabs.size()==line_tabs.size())
has_bracket=true;
}
if(*get_buffer()->get_insert()->get_iter()=='}') {
get_source_buffer()->insert_at_cursor("\n"+tabs+tab+"\n"+tabs);
auto insert_it = get_source_buffer()->get_insert()->get_iter();
if(insert_it.backward_chars(tabs.size()+1)) {
scroll_to(get_source_buffer()->get_insert());
get_source_buffer()->place_cursor(insert_it);
}
get_source_buffer()->end_user_action();
return true;
}
else if(!has_bracket) {
//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();
if(insert_it.backward_chars(tabs.size()+2)) {
scroll_to(get_source_buffer()->get_insert());
get_source_buffer()->place_cursor(insert_it);
}
get_source_buffer()->end_user_action();
return true;
}
else {
get_source_buffer()->insert_at_cursor("\n"+tabs+tab);
scroll_to(get_buffer()->get_insert());
get_source_buffer()->end_user_action();
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 tabs_end_iter=get_tabs_end_iter(found_iter);
tabs=get_line_before(tabs_end_iter);
auto iter=tabs_end_iter;
while(iter<=found_iter) {
tabs+=' ';
iter.forward_char();
}
}
else if(boost::regex_match(line, sm, no_bracket_statement_regex)) {
get_source_buffer()->insert_at_cursor("\n"+tabs+tab);
scroll_to(get_source_buffer()->get_insert());
get_source_buffer()->end_user_action();
return true;
}
else if(boost::regex_match(line, sm, no_bracket_no_para_statement_regex)) {
get_source_buffer()->insert_at_cursor("\n"+tabs+tab);
scroll_to(get_source_buffer()->get_insert());
get_source_buffer()->end_user_action();
return true;
}
//Indenting after for instance if(...)\n...;\n
else if(iter.backward_char() && *iter==';') {
boost::smatch sm2;
size_t line_nr=get_source_buffer()->get_insert()->get_iter().get_line();
if(line_nr>0 && tabs.size()>=tab_size) {
std::string previous_line=get_line(line_nr-1);
if(!boost::regex_match(previous_line, sm2, bracket_regex)) {
if(boost::regex_match(previous_line, sm2, no_bracket_statement_regex)) {
get_source_buffer()->insert_at_cursor("\n"+sm2[1].str());
scroll_to(get_source_buffer()->get_insert());
get_source_buffer()->end_user_action();
return true;
}
else if(boost::regex_match(previous_line, sm2, no_bracket_no_para_statement_regex)) {
get_source_buffer()->insert_at_cursor("\n"+sm2[1].str());
scroll_to(get_source_buffer()->get_insert());
get_source_buffer()->end_user_action();
return true;
}
}
}
}
//Indenting after ':'
else if(*iter==':') {
Gtk::TextIter left_bracket_iter;
if(find_left_bracket_backward(iter, left_bracket_iter)) {
if(!left_bracket_iter.ends_line())
left_bracket_iter.forward_char();
Gtk::TextIter start_of_left_bracket_sentence_iter;
if(find_start_of_closed_expression(left_bracket_iter, start_of_left_bracket_sentence_iter)) {
boost::smatch sm;
auto tabs_end_iter=get_tabs_end_iter(start_of_left_bracket_sentence_iter);
auto tabs_start_of_sentence=get_line_before(tabs_end_iter);
if(tabs.size()==(tabs_start_of_sentence.size()+tab_size)) {
auto start_line_iter=get_buffer()->get_iter_at_line(iter.get_line());
auto start_line_plus_tab_size=start_line_iter;
for(size_t c=0;c<tab_size;c++)
start_line_plus_tab_size.forward_char();
get_buffer()->erase(start_line_iter, start_line_plus_tab_size);
}
else {
get_source_buffer()->insert_at_cursor("\n"+tabs+tab);
scroll_to(get_source_buffer()->get_insert());
get_source_buffer()->end_user_action();
return true;
}
}
}
}
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
else if(key->keyval==GDK_KEY_braceright) {
std::string line=get_line_before();
if(line.size()>=tab_size) {
for(auto c: line) {
if(c!=tab_char) {
get_source_buffer()->insert_at_cursor("}");
get_source_buffer()->end_user_action();
return true;
}
}
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_plus_it=line_it;
line_plus_it.forward_chars(tab_size);
get_source_buffer()->erase(line_it, line_plus_it);
}
get_source_buffer()->insert_at_cursor("}");
get_source_buffer()->end_user_action();
return true;
}
//Indent left when writing { on a new line after for instance if(...)\n...
else if(key->keyval==GDK_KEY_braceleft) {
auto iter=get_buffer()->get_insert()->get_iter();
auto tabs_end_iter=get_tabs_end_iter();
auto tabs=get_line_before(tabs_end_iter);
size_t line_nr=iter.get_line();
if(line_nr>0 && tabs.size()>=tab_size && iter==tabs_end_iter) {
std::string previous_line=get_line(line_nr-1);
boost::smatch sm;
if(!boost::regex_match(previous_line, sm, bracket_regex)) {
auto start_iter=iter;
start_iter.backward_chars(tab_size);
if(boost::regex_match(previous_line, sm, no_bracket_statement_regex) ||
boost::regex_match(previous_line, sm, no_bracket_no_para_statement_regex)) {
if((tabs.size()-tab_size)==sm[1].str().size()) {
get_buffer()->erase(start_iter, iter);
get_buffer()->insert_at_cursor("{");
scroll_to(get_buffer()->get_insert());
get_buffer()->end_user_action();
return true;
}
}
}
}
}
get_source_buffer()->end_user_action();
return Source::View::on_key_press_event(key);
}
////////////////////////////// //////////////////////////////
//// ClangViewAutocomplete /// //// ClangViewAutocomplete ///
////////////////////////////// //////////////////////////////
@ -714,15 +501,6 @@ Source::ClangViewParse(file_path, project_path, language), autocomplete_state(Au
}); });
} }
bool Source::ClangViewAutocomplete::on_key_press_event(GdkEventKey *key) {
last_keyval=key->keyval;
if(autocomplete_dialog && autocomplete_dialog->shown) {
if(autocomplete_dialog->on_key_press(key))
return true;
}
return ClangViewParse::on_key_press_event(key);
}
void Source::ClangViewAutocomplete::autocomplete_dialog_setup() { void Source::ClangViewAutocomplete::autocomplete_dialog_setup() {
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()))
@ -874,7 +652,7 @@ void Source::ClangViewAutocomplete::autocomplete() {
auto autocomplete_data=std::make_shared<std::vector<AutoCompleteData> >(autocomplete_get_suggestions(buffer->raw(), line_nr, column_nr)); auto autocomplete_data=std::make_shared<std::vector<AutoCompleteData> >(autocomplete_get_suggestions(buffer->raw(), line_nr, column_nr));
if(parse_state==ParseState::PROCESSING) { if(parse_state==ParseState::PROCESSING) {
dispatcher.push([this, autocomplete_data] { dispatcher.post([this, autocomplete_data] {
if(autocomplete_state==AutocompleteState::CANCELED) { if(autocomplete_state==AutocompleteState::CANCELED) {
set_status(""); set_status("");
soft_reparse(); soft_reparse();
@ -920,7 +698,7 @@ void Source::ClangViewAutocomplete::autocomplete() {
}); });
} }
else { else {
dispatcher.push([this] { dispatcher.post([this] {
Terminal::get().print("Error: autocomplete failed, reparsing "+this->file_path.string()+"\n", true); Terminal::get().print("Error: autocomplete failed, reparsing "+this->file_path.string()+"\n", true);
autocomplete_state=AutocompleteState::CANCELED; autocomplete_state=AutocompleteState::CANCELED;
full_reparse(); full_reparse();
@ -1000,7 +778,7 @@ bool Source::ClangViewAutocomplete::full_reparse() {
parse_thread.join(); parse_thread.join();
if(autocomplete_thread.joinable()) if(autocomplete_thread.joinable())
autocomplete_thread.join(); autocomplete_thread.join();
dispatcher.push([this] { dispatcher.post([this] {
parse_initialize(); parse_initialize();
full_reparse_running=false; full_reparse_running=false;
}); });
@ -1030,72 +808,6 @@ Source::ClangViewAutocomplete(file_path, project_path, language) {
} }
}); });
auto_indent=[this]() {
auto command=Config::get().terminal.clang_format_command;
bool use_style_file=false;
auto style_file_search_path=this->file_path.parent_path();
while(true) {
auto style_file=style_file_search_path/"CMakeLists.txt";
if(boost::filesystem::exists(style_file_search_path/".clang-format") || boost::filesystem::exists(style_file_search_path/"_clang-format")) {
use_style_file=true;
break;
}
if(style_file_search_path==style_file_search_path.root_directory())
break;
style_file_search_path=style_file_search_path.parent_path();
}
if(use_style_file)
command+=" -style=file";
else {
unsigned indent_width;
std::string tab_style;
if(tab_char=='\t') {
indent_width=tab_size*8;
tab_style="UseTab: Always";
}
else {
indent_width=tab_size;
tab_style="UseTab: Never";
}
command+=" -style=\"{IndentWidth: "+std::to_string(indent_width);
command+=", "+tab_style;
command+=", "+std::string("AccessModifierOffset: -")+std::to_string(indent_width);
if(Config::get().source.clang_format_style!="")
command+=", "+Config::get().source.clang_format_style;
command+="}\"";
}
std::stringstream stdin_stream(get_buffer()->get_text()), stdout_stream;
auto exit_status=Terminal::get().process(stdin_stream, stdout_stream, command, this->file_path.parent_path());
if(exit_status==0) {
get_source_buffer()->begin_user_action();
auto iter=get_buffer()->get_insert()->get_iter();
auto cursor_line_nr=iter.get_line();
auto cursor_line_offset=iter.get_line_offset();
get_buffer()->erase(get_buffer()->begin(), get_buffer()->end());
get_buffer()->insert(get_buffer()->begin(), stdout_stream.str());
cursor_line_nr=std::min(cursor_line_nr, get_buffer()->get_line_count()-1);
if(cursor_line_nr>=0) {
iter=get_buffer()->get_iter_at_line(cursor_line_nr);
for(int c=0;c<cursor_line_offset;c++) {
if(iter.ends_line())
break;
iter.forward_char();
}
get_buffer()->place_cursor(iter);
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);
scroll_to(get_buffer()->get_insert(), 0.0, 1.0, 0.5);
}
get_source_buffer()->end_user_action();
}
};
get_token=[this]() -> Token { get_token=[this]() -> Token {
if(parsed) { if(parsed) {
auto iter=get_buffer()->get_insert()->get_iter(); auto iter=get_buffer()->get_insert()->get_iter();

10
src/source_clang.h

@ -5,7 +5,6 @@
#include <atomic> #include <atomic>
#include <mutex> #include <mutex>
#include <set> #include <set>
#include <boost/regex.hpp>
#include "clangmm.h" #include "clangmm.h"
#include "source.h" #include "source.h"
#include "terminal.h" #include "terminal.h"
@ -42,12 +41,6 @@ namespace Source {
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 no_bracket_statement_regex;
boost::regex no_bracket_no_para_statement_regex;
std::set<int> diagnostic_offsets; std::set<int> diagnostic_offsets;
std::vector<FixIt> fix_its; std::vector<FixIt> fix_its;
@ -83,8 +76,6 @@ namespace Source {
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;
private: private:
std::atomic<AutocompleteState> autocomplete_state; std::atomic<AutocompleteState> autocomplete_state;
@ -94,7 +85,6 @@ namespace Source {
std::unordered_map<std::string, std::pair<std::string, std::string> > autocomplete_dialog_rows; std::unordered_map<std::string, std::pair<std::string, std::string> > autocomplete_dialog_rows;
std::vector<AutoCompleteData> autocomplete_get_suggestions(const std::string &buffer, int line_number, int column); std::vector<AutoCompleteData> autocomplete_get_suggestions(const std::string &buffer, int line_number, int column);
Tooltips autocomplete_tooltips; Tooltips autocomplete_tooltips;
guint last_keyval=0;
std::string prefix; std::string prefix;
std::mutex prefix_mutex; std::mutex prefix_mutex;

4
src/terminal.cc

@ -210,13 +210,13 @@ std::shared_ptr<Terminal::InProgress> Terminal::print_in_progress(std::string st
} }
void Terminal::async_print(const std::string &message, bool bold) { void Terminal::async_print(const std::string &message, bool bold) {
dispatcher.push([this, message, bold] { dispatcher.post([this, message, bold] {
Terminal::get().print(message, bold); Terminal::get().print(message, bold);
}); });
} }
void Terminal::async_print(size_t line_nr, const std::string &message) { void Terminal::async_print(size_t line_nr, const std::string &message) {
dispatcher.push([this, line_nr, message] { dispatcher.post([this, line_nr, message] {
if(line_nr<deleted_lines) if(line_nr<deleted_lines)
return; return;

22
src/window.cc

@ -344,10 +344,7 @@ void Window::set_menu_actions() {
if(notebook.get_current_page()!=-1) { if(notebook.get_current_page()!=-1) {
auto view=notebook.get_current_view(); auto view=notebook.get_current_view();
while(g_main_context_pending(NULL)) view->scroll_to_cursor_delayed(view, true, false);
g_main_context_iteration(NULL, false);
if(notebook.get_current_page()!=-1 && notebook.get_current_view()==view)
view->scroll_to(view->get_buffer()->get_insert(), 0.0, 1.0, 0.5);
} }
}); });
@ -415,12 +412,7 @@ void Window::set_menu_actions() {
index=std::min(index, end_line_index); index=std::min(index, end_line_index);
view->get_buffer()->place_cursor(view->get_buffer()->get_iter_at_line_index(line, index)); view->get_buffer()->place_cursor(view->get_buffer()->get_iter_at_line_index(line, index));
while(g_main_context_pending(NULL)) view->scroll_to_cursor_delayed(view, true, false);
g_main_context_iteration(NULL, false);
if(notebook.get_current_page()!=-1 && notebook.get_current_view()==view) {
view->scroll_to(view->get_buffer()->get_insert(), 0.0, 1.0, 0.5);
view->delayed_tooltips_connection.disconnect();
}
} }
} }
} }
@ -709,10 +701,7 @@ void Window::set_menu_actions() {
line_index=0; line_index=0;
view->get_buffer()->place_cursor(view->get_buffer()->get_iter_at_line_index(line_nr, line_index)); view->get_buffer()->place_cursor(view->get_buffer()->get_iter_at_line_index(line_nr, line_index));
while(g_main_context_pending(NULL)) view->scroll_to_cursor_delayed(view, true, true);
g_main_context_iteration(NULL, false);
if(notebook.get_current_page()!=-1 && notebook.get_current_view()==view)
view->scroll_to(view->get_buffer()->get_insert(), 0.0, 1.0, 0.5);
} }
Project::debug_update_stop(); Project::debug_update_stop();
} }
@ -984,10 +973,7 @@ void Window::goto_line_entry() {
line--; line--;
view->get_buffer()->place_cursor(view->get_buffer()->get_iter_at_line(line)); view->get_buffer()->place_cursor(view->get_buffer()->get_iter_at_line(line));
while(g_main_context_pending(NULL)) view->scroll_to_cursor_delayed(view, true, false);
g_main_context_iteration(NULL, false);
if(notebook.get_current_page()!=-1 && notebook.get_current_view()==view)
view->scroll_to(view->get_buffer()->get_insert(), 0.0, 1.0, 0.5);
} }
} }
catch(const std::exception &e) {} catch(const std::exception &e) {}

Loading…
Cancel
Save