Browse Source

Bracket based auto indentation added for Java, JavaScript, TypeScript

merge-requests/365/head
eidheim 10 years ago
parent
commit
755747bbba
  1. 224
      src/source.cc
  2. 8
      src/source.h
  3. 215
      src/source_clang.cc
  4. 7
      src/source_clang.h

224
src/source.cc

@ -293,9 +293,15 @@ Source::View::View(const boost::filesystem::path &file_path, const boost::filesy
}
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")) {
is_bracket_language=true;
auto_indent=[this]() {
auto command=Config::get().terminal.clang_format_command;
bool use_style_file=false;
@ -1041,8 +1047,15 @@ bool Source::View::find_left_bracket_backward(Gtk::TextIter iter, Gtk::TextIter
return false;
}
//Basic indentation
bool Source::View::on_key_press_event(GdkEventKey* 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) {
if(spellcheck_suggestions_dialog && spellcheck_suggestions_dialog->shown) {
if(spellcheck_suggestions_dialog->on_key_press(key))
return true;
@ -1270,6 +1283,215 @@ bool Source::View::on_key_press_event(GdkEventKey* key) {
return stop;
}
//Bracket language indentation
bool Source::View::on_key_press_event_bracket_language(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 on_key_press_event_basic(key);
if(get_buffer()->get_has_selection()) {
return on_key_press_event_basic(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 on_key_press_event_basic(key);
}
bool Source::View::on_button_press_event(GdkEventButton *event) {
if(event->type==GDK_2BUTTON_PRESS) {
Gtk::TextIter start, end;

8
src/source.h

@ -7,6 +7,7 @@
#include <string>
#include <unordered_map>
#include <vector>
#include <boost/regex.hpp>
#include "selectiondialog.h"
#include "tooltips.h"
@ -136,7 +137,14 @@ namespace Source {
bool find_right_bracket_forward(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_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;
std::pair<char, unsigned> find_tab_char_and_size();

215
src/source_clang.cc

@ -70,10 +70,6 @@ void Source::ClangViewParse::configure() {
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() {
@ -451,215 +447,6 @@ void Source::ClangViewParse::show_type_tooltips(const Gdk::Rectangle &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 ///
//////////////////////////////
@ -720,7 +507,7 @@ bool Source::ClangViewAutocomplete::on_key_press_event(GdkEventKey *key) {
if(autocomplete_dialog->on_key_press(key))
return true;
}
return ClangViewParse::on_key_press_event(key);
return View::on_key_press_event(key);
}
void Source::ClangViewAutocomplete::autocomplete_dialog_setup() {

7
src/source_clang.h

@ -5,7 +5,6 @@
#include <atomic>
#include <mutex>
#include <set>
#include <boost/regex.hpp>
#include "clangmm.h"
#include "source.h"
#include "terminal.h"
@ -42,12 +41,6 @@ namespace Source {
void show_diagnostic_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::vector<FixIt> fix_its;

Loading…
Cancel
Save