Browse Source

Added Prettier tooltips, and cleanup of src/source* files

merge-requests/382/head
eidheim 8 years ago
parent
commit
9693bcea34
  1. 2
      README.md
  2. 2
      libclangmm
  3. 9
      src/notebook.cc
  4. 818
      src/source.cc
  5. 12
      src/source.h
  6. 188
      src/source_base.cc
  7. 23
      src/source_base.h
  8. 50
      src/source_clang.cc
  9. 1
      src/source_clang.h
  10. 1
      src/source_diff.h
  11. 57
      src/source_language_protocol.cc
  12. 2
      src/source_language_protocol.h

2
README.md

@ -39,7 +39,7 @@ See [language-server-protocol/specification.md](https://github.com/Microsoft/lan
* Regex search and replace
* Smart paste, keys and indentation
* Multiple cursors
* Auto-indentation of C++ file buffers through [clang-format](http://clang.llvm.org/docs/ClangFormat.html)
* Auto-indentation through [clang-format](http://clang.llvm.org/docs/ClangFormat.html) or [Prettier](https://github.com/prettier/prettier) if installed
* Source minimap
* Split view
* Multiple cursors

2
libclangmm

@ -1 +1 @@
Subproject commit 997d02a8de78ff879577a3522e1de5ebec9802e7
Subproject commit e8f8a04f98b2259468d50843759caac3c5922bfa

9
src/notebook.cc

@ -69,13 +69,13 @@ Notebook::Notebook() : Gtk::Paned(), notebooks(2) {
});
auto provider = Gtk::CssProvider::create();
//GtkNotebook-tab-overlap got removed in gtk 3.20, but margin works in 3.20
//GtkNotebook-tab-overlap got removed in gtk 3.20, but margin works in 3.20
#if GTK_VERSION_GE(3, 20)
provider->load_from_data("tab {border-radius: 5px 5px 0 0; padding: 0 4px; margin: 0;}");
provider->load_from_data("tab {border-radius: 5px 5px 0 0; padding: 0 4px; margin: 0;}");
#else
provider->load_from_data(".notebook {-GtkNotebook-tab-overlap: 0px;} tab {border-radius: 5px 5px 0 0; padding: 4px 4px;}");
provider->load_from_data(".notebook {-GtkNotebook-tab-overlap: 0px;} tab {border-radius: 5px 5px 0 0; padding: 4px 4px;}");
#endif
notebook.get_style_context()->add_provider(provider, GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
notebook.get_style_context()->add_provider(provider, GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
}
pack1(notebooks[0], true, true);
}
@ -120,6 +120,7 @@ void Notebook::open(const boost::filesystem::path &file_path_, size_t notebook_i
if(notebook_index==1 && !split)
toggle_split();
// Use canonical path to follow symbolic links
boost::system::error_code ec;
auto canonical_file_path=boost::filesystem::canonical(file_path, ec);
if(ec)

818
src/source.cc

@ -121,7 +121,7 @@ std::string Source::FixIt::string(Glib::RefPtr<Gtk::TextBuffer> buffer) {
std::unordered_set<Source::View*> Source::View::non_deleted_views;
std::unordered_set<Source::View*> Source::View::views;
Source::View::View(const boost::filesystem::path &file_path, Glib::RefPtr<Gsv::Language> language): BaseView(file_path, language), SpellCheckView(file_path, language), DiffView(file_path, language) {
Source::View::View(const boost::filesystem::path &file_path, Glib::RefPtr<Gsv::Language> language, bool is_generic_view): BaseView(file_path, language), SpellCheckView(file_path, language), DiffView(file_path, language) {
non_deleted_views.emplace(this);
views.emplace(this);
@ -184,344 +184,8 @@ Source::View::View(const boost::filesystem::path &file_path, Glib::RefPtr<Gsv::L
}
});
set_tooltip_and_dialog_events();
static auto prettier = filesystem::find_executable("prettier");
if(!prettier.empty() && language &&
(language->get_id()=="js" || language->get_id()=="json" || language->get_id()=="css")) {
format_style=[this](bool continue_without_style_file) {
auto command=prettier.string()+" --cursor-offset "+std::to_string(get_buffer()->get_insert()->get_iter().get_offset());
command+=" --stdin-filepath "+this->file_path.string();
if(get_buffer()->get_has_selection()) {
Gtk::TextIter start, end;
get_buffer()->get_selection_bounds(start, end);
command+=" --range-start "+std::to_string(start.get_offset());
command+=" --range-end "+std::to_string(end.get_offset());
}
if(!continue_without_style_file) {
bool has_style_file=false;
auto style_file_search_path=this->file_path.parent_path();
while(true) {
if(boost::filesystem::exists(style_file_search_path/".prettierrc") ||
boost::filesystem::exists(style_file_search_path/"prettier.config")) {
has_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(!has_style_file && !continue_without_style_file)
return;
}
std::stringstream stdin_stream(get_buffer()->get_text()), stdout_stream, stderr_stream;
auto exit_status=Terminal::get().process(stdin_stream, stdout_stream, command, this->file_path.parent_path(), &stderr_stream);
if(exit_status==0) {
replace_text(stdout_stream.str());
std::string line;
std::getline(stderr_stream, line);
if(line!="NaN") {
try {
auto offset=atoi(line.c_str());
if(offset<get_buffer()->size()) {
get_buffer()->place_cursor(get_buffer()->get_iter_at_offset(offset));
hide_tooltips();
}
}
catch(...) {}
}
}
else {
// static std::regex regex("^\\[error\\] stdin: (.*) \\(([0-9]*):([0-9]*)\\)$");
// std::string line;
// std::getline(stderr_stream, line);
// std::smatch sm;
// if(std::regex_match(line, sm, regex)) {
// auto line=std::min(atoi(sm[1].str().c_str()), get_buffer()->get_line_count()-1); // TODO: add try
// if(line<0)
// line=0;
// auto iter=get_iter_at_line_end(line);
// auto pos=std::min(atoi(sm[2].str().c_str()), iter.get_line_offset()); // TODO: add try
// if(pos<0)
// pos=0;
// auto start=get_buffer()->get_iter_at_line_offset(line, pos);
// auto end=start;
// end.forward_char();
// if(start==end)
// start.forward_char();
// std::string diagnostic_tag_name="def:error";
// std::string severity_spelling="Error";
// auto spelling=sm[1].str();
// auto create_tooltip_buffer=[this, spelling, severity_spelling, diagnostic_tag_name]() {
// 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(), ":\n"+spelling, "def:note");
// return tooltip_buffer;
// };
// 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);
// }
Terminal::get().print("Prettier:\n"+stderr_stream.str()+'\n', true); // TODO: consider using the above WiP code instead
}
};
}
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" || language->get_id()=="html" || language->get_id()=="cuda" ||
language->get_id()=="php" || language->get_id()=="rust" || language->get_id()=="swift" ||
language->get_id()=="go" || language->get_id()=="scala" || language->get_id()=="opencl")) {
is_bracket_language=true;
format_style=[this](bool continue_without_style_file) {
static auto clang_format_command = filesystem::get_executable("clang-format").string();
auto command=clang_format_command+" -output-replacements-xml -assume-filename="+filesystem::escape_argument(this->file_path.string());
if(get_buffer()->get_has_selection()) {
Gtk::TextIter start, end;
get_buffer()->get_selection_bounds(start, end);
command+=" -lines="+std::to_string(start.get_line()+1)+':'+std::to_string(end.get_line()+1);
}
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 {
if(!continue_without_style_file)
return;
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) {
// The following code is complex due to clang-format returning offsets in byte offsets instead of char offsets
// Create bytes_in_lines cache to significantly speed up the processing of finding iterators from byte offsets
std::vector<size_t> bytes_in_lines;
auto line_count=get_buffer()->get_line_count();
for(int line_nr=0;line_nr<line_count;++line_nr) {
auto iter=get_buffer()->get_iter_at_line(line_nr);
bytes_in_lines.emplace_back(iter.get_bytes_in_line());
}
get_buffer()->begin_user_action();
try {
boost::property_tree::ptree pt;
boost::property_tree::xml_parser::read_xml(stdout_stream, pt);
auto replacements_pt=pt.get_child("replacements");
for(auto it=replacements_pt.rbegin();it!=replacements_pt.rend();++it) {
if(it->first=="replacement") {
auto offset=it->second.get<size_t>("<xmlattr>.offset");
auto length=it->second.get<size_t>("<xmlattr>.length");
auto replacement_str=it->second.get<std::string>("");
size_t bytes=0;
for(size_t c=0;c<bytes_in_lines.size();++c) {
auto previous_bytes=bytes;
bytes+=bytes_in_lines[c];
if(offset<bytes || (c==bytes_in_lines.size()-1 && offset==bytes)) {
std::pair<size_t, size_t> line_index(c, offset-previous_bytes);
auto start=get_buffer()->get_iter_at_line_index(line_index.first, line_index.second);
// Use left gravity insert to avoid moving cursor from end of line
bool left_gravity_insert=false;
if(get_buffer()->get_insert()->get_iter()==start) {
auto iter=start;
do {
if(*iter!=' ' && *iter!='\t') {
left_gravity_insert=iter.ends_line();
break;
}
} while(iter.forward_char());
}
if(length>0) {
auto offset_end=offset+length;
size_t bytes=0;
for(size_t c=0;c<bytes_in_lines.size();++c) {
auto previous_bytes=bytes;
bytes+=bytes_in_lines[c];
if(offset_end<bytes || (c==bytes_in_lines.size()-1 && offset_end==bytes)) {
auto end=get_buffer()->get_iter_at_line_index(c, offset_end-previous_bytes);
get_buffer()->erase(start, end);
start=get_buffer()->get_iter_at_line_index(line_index.first, line_index.second);
break;
}
}
}
if(left_gravity_insert) {
auto mark=get_buffer()->create_mark(start);
get_buffer()->insert(start, replacement_str);
get_buffer()->place_cursor(mark->get_iter());
get_buffer()->delete_mark(mark);
}
else
get_buffer()->insert(start, replacement_str);
break;
}
}
}
}
}
catch(const std::exception &e) {
Terminal::get().print(std::string("Error: error parsing clang-format output: ")+e.what()+'\n', true);
}
get_buffer()->end_user_action();
}
};
}
else if(language && language->get_id()=="markdown") {
// The style file currently has no options, but checking if it exists
format_style=[this](bool continue_without_style_file) {
bool has_style_file=false;
auto style_file_search_path=this->file_path.parent_path();
while(true) {
if(boost::filesystem::exists(style_file_search_path/".markdown-format")) {
has_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(!has_style_file && !continue_without_style_file)
return;
auto special_character=[](Gtk::TextIter iter) {
if(*iter=='*' || *iter=='#' || *iter=='<' || *iter=='>' || *iter==' ' || *iter=='=' || *iter=='`' || *iter=='-')
return true;
// Tests if a line starts with for instance: 2.
if(*iter>='0' && *iter<='9' && iter.forward_char() &&
*iter=='.' && iter.forward_char() &&
*iter==' ')
return true;
return false;
};
get_buffer()->begin_user_action();
disable_spellcheck=true;
cleanup_whitespace_characters();
auto iter=get_buffer()->begin();
size_t last_space_offset=-1;
bool headline=false;
bool monospace=false;
bool script=false;
bool html_tag=false;
int square_brackets=0;
do {
if(iter.starts_line()) {
last_space_offset=-1;
auto next_line_iter=iter;
if(*iter=='#' || (next_line_iter.forward_line() && *next_line_iter=='='))
headline=true;
else
headline=false;
auto test_iter=iter;
if(*test_iter=='`' && test_iter.forward_char() &&
*test_iter=='`' && test_iter.forward_char() &&
*test_iter=='`') {
script=!script;
iter.forward_chars(3);
continue;
}
}
if(!script && *iter=='`')
monospace=!monospace;
if(!script && !monospace) {
if(*iter=='<')
html_tag=true;
else if(*iter=='>')
html_tag=false;
else if(*iter=='[')
++square_brackets;
else if(*iter==']')
--square_brackets;
}
if(!headline && !script && !monospace && !html_tag && square_brackets==0) {
if(*iter==' ' && iter.get_line_offset()<=80)
last_space_offset=iter.get_offset();
// Insert newline on long lines
else if((*iter==' ' || iter.ends_line()) && iter.get_line_offset()>80 && last_space_offset!=static_cast<size_t>(-1)) {
auto stored_iter=iter;
iter=get_buffer()->get_iter_at_offset(last_space_offset);
auto next_iter=iter;
next_iter.forward_char();
// Do not add newline if the next iter is a special character
if(special_character(next_iter)) {
iter=stored_iter;
if(*iter==' ')
last_space_offset=iter.get_offset();
continue;
}
iter=get_buffer()->erase(iter, next_iter);
iter=get_buffer()->insert(iter, "\n");
iter.backward_char();
}
// Remove newline on short lines
else if(iter.ends_line() && !iter.starts_line() && iter.get_line_offset()<=80) {
auto next_line_iter=iter;
// Do not remove newline if the next line for instance is a header
if(next_line_iter.forward_char() && !next_line_iter.ends_line() && !special_character(next_line_iter)) {
auto end_word_iter=next_line_iter;
// Do not remove newline if the word on the next line is too long
size_t diff=0;
while(*end_word_iter!=' ' && !end_word_iter.ends_line() && end_word_iter.forward_char())
++diff;
if(iter.get_line_offset()+diff+1<=80) {
iter=get_buffer()->erase(iter, next_line_iter);
iter=get_buffer()->insert(iter, " ");
iter.backward_char();
if(iter.get_line_offset()<=80)
last_space_offset=iter.get_offset();
}
}
}
}
} while(iter.forward_char());
disable_spellcheck=false;
get_buffer()->end_user_action();
};
}
setup_tooltip_and_dialog_events();
setup_format_style(is_generic_view);
#ifndef __APPLE__
set_tab_width(4); //Visual size of a \t hardcoded to be equal to visual size of 4 spaces. Buggy on OS X
@ -737,6 +401,8 @@ bool Source::View::save() {
last_write_time=boost::filesystem::last_write_time(file_path, ec);
if(ec)
last_write_time=static_cast<std::time_t>(-1);
// Remonitor file in case it did not exist before
monitor_file();
get_buffer()->set_modified(false);
Directories::get().on_save_file(file_path);
return true;
@ -847,7 +513,7 @@ void Source::View::configure() {
}
}
void Source::View::set_tooltip_and_dialog_events() {
void Source::View::setup_tooltip_and_dialog_events() {
get_buffer()->signal_changed().connect([this] {
hide_tooltips();
});
@ -868,61 +534,398 @@ void Source::View::set_tooltip_and_dialog_events() {
return false;
}, 100);
}
type_tooltips.hide();
diagnostic_tooltips.hide();
}
on_motion_last_x=event->x;
on_motion_last_y=event->y;
return false;
});
type_tooltips.hide();
diagnostic_tooltips.hide();
}
on_motion_last_x=event->x;
on_motion_last_y=event->y;
return false;
});
get_buffer()->signal_mark_set().connect([this](const Gtk::TextBuffer::iterator& iterator, const Glib::RefPtr<Gtk::TextBuffer::Mark>& mark) {
if(get_buffer()->get_has_selection() && mark->get_name()=="selection_bound")
delayed_tooltips_connection.disconnect();
if(mark->get_name()=="insert") {
hide_tooltips();
delayed_tooltips_connection=Glib::signal_timeout().connect([this]() {
Tooltips::init();
Gdk::Rectangle rectangle;
get_iter_location(get_buffer()->get_insert()->get_iter(), rectangle);
int location_window_x, location_window_y;
buffer_to_window_coords(Gtk::TextWindowType::TEXT_WINDOW_TEXT, rectangle.get_x(), rectangle.get_y(), location_window_x, location_window_y);
rectangle.set_x(location_window_x-2);
rectangle.set_y(location_window_y);
rectangle.set_width(5);
if(parsed) {
show_type_tooltips(rectangle);
show_diagnostic_tooltips(rectangle);
}
return false;
}, 500);
if(SelectionDialog::get())
SelectionDialog::get()->hide();
if(CompletionDialog::get())
CompletionDialog::get()->hide();
if(update_status_location)
update_status_location(this);
}
});
signal_scroll_event().connect([this](GdkEventScroll* event) {
hide_tooltips();
hide_dialogs();
return false;
});
signal_focus_out_event().connect([this](GdkEventFocus* event) {
hide_tooltips();
return false;
});
signal_leave_notify_event().connect([this](GdkEventCrossing*) {
delayed_tooltips_connection.disconnect();
return false;
});
}
void Source::View::setup_format_style(bool is_generic_view) {
static auto prettier = filesystem::find_executable("prettier");
if(!prettier.empty() && language &&
(language->get_id()=="js" || language->get_id()=="json" || language->get_id()=="css")) {
if(is_generic_view) {
goto_next_diagnostic=[this] {
place_cursor_at_next_diagnostic();
};
get_buffer()->signal_changed().connect([this] {
clear_diagnostic_tooltips();
status_diagnostics=std::make_tuple<size_t, size_t, size_t>(0, 0, 0);
if(update_status_diagnostics)
update_status_diagnostics(this);
});
}
format_style=[this, is_generic_view](bool continue_without_style_file) {
auto command=prettier.string();
if(!continue_without_style_file) {
std::stringstream stdin_stream, stdout_stream;
auto exit_status=Terminal::get().process(stdin_stream, stdout_stream, command+" --find-config-path "+this->file_path.string());
if(exit_status==0) {
if(stdout_stream.tellp()==0)
return;
}
else
return;
}
command+=" --stdin-filepath "+this->file_path.string()+" --print-width 120 --config-precedence prefer-file";
if(get_buffer()->get_has_selection()) { // Cannot be used together with --cursor-offset
Gtk::TextIter start, end;
get_buffer()->get_selection_bounds(start, end);
command+=" --range-start "+std::to_string(start.get_offset());
command+=" --range-end "+std::to_string(end.get_offset());
}
else
command+=" --cursor-offset "+std::to_string(get_buffer()->get_insert()->get_iter().get_offset());
size_t num_warnings=0, num_errors=0, num_fix_its=0;
if(is_generic_view)
clear_diagnostic_tooltips();
std::stringstream stdin_stream(get_buffer()->get_text()), stdout_stream, stderr_stream;
auto exit_status=Terminal::get().process(stdin_stream, stdout_stream, command, this->file_path.parent_path(), &stderr_stream);
if(exit_status==0) {
replace_text(stdout_stream.str());
std::string line;
std::getline(stderr_stream, line);
if(!line.empty() && line!="NaN") {
try {
auto offset=atoi(line.c_str());
if(offset<get_buffer()->size()) {
get_buffer()->place_cursor(get_buffer()->get_iter_at_offset(offset));
hide_tooltips();
}
}
catch(...) {}
}
}
else if(is_generic_view) {
static std::regex regex("^\\[error\\] stdin: (.*) \\(([0-9]*):([0-9]*)\\)$");
std::string line;
std::getline(stderr_stream, line);
std::smatch sm;
if(std::regex_match(line, sm, regex)) {
try {
auto start=get_iter_at_line_offset(atoi(sm[2].str().c_str())-1, atoi(sm[3].str().c_str())-1);
++num_errors;
if(start.ends_line())
start.backward_char();
auto end=start;
end.forward_char();
if(start==end)
start.forward_char();
add_diagnostic_tooltip(start, end, sm[1].str(), true);
}
catch(...) {}
}
}
if(is_generic_view) {
status_diagnostics=std::make_tuple(num_warnings, num_errors, num_fix_its);
if(update_status_diagnostics)
update_status_diagnostics(this);
}
};
}
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" || language->get_id()=="html" || language->get_id()=="cuda" ||
language->get_id()=="php" || language->get_id()=="rust" || language->get_id()=="swift" ||
language->get_id()=="go" || language->get_id()=="scala" || language->get_id()=="opencl")) {
is_bracket_language=true;
format_style=[this](bool continue_without_style_file) {
static auto clang_format_command = filesystem::get_executable("clang-format").string();
auto command=clang_format_command+" -output-replacements-xml -assume-filename="+filesystem::escape_argument(this->file_path.string());
if(get_buffer()->get_has_selection()) {
Gtk::TextIter start, end;
get_buffer()->get_selection_bounds(start, end);
command+=" -lines="+std::to_string(start.get_line()+1)+':'+std::to_string(end.get_line()+1);
}
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 {
if(!continue_without_style_file)
return;
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+="}\"";
}
get_buffer()->signal_mark_set().connect([this](const Gtk::TextBuffer::iterator& iterator, const Glib::RefPtr<Gtk::TextBuffer::Mark>& mark) {
if(get_buffer()->get_has_selection() && mark->get_name()=="selection_bound")
delayed_tooltips_connection.disconnect();
std::stringstream stdin_stream(get_buffer()->get_text()), stdout_stream;
if(mark->get_name()=="insert") {
hide_tooltips();
delayed_tooltips_connection=Glib::signal_timeout().connect([this]() {
Tooltips::init();
Gdk::Rectangle rectangle;
get_iter_location(get_buffer()->get_insert()->get_iter(), rectangle);
int location_window_x, location_window_y;
buffer_to_window_coords(Gtk::TextWindowType::TEXT_WINDOW_TEXT, rectangle.get_x(), rectangle.get_y(), location_window_x, location_window_y);
rectangle.set_x(location_window_x-2);
rectangle.set_y(location_window_y);
rectangle.set_width(5);
if(parsed) {
show_type_tooltips(rectangle);
show_diagnostic_tooltips(rectangle);
auto exit_status=Terminal::get().process(stdin_stream, stdout_stream, command, this->file_path.parent_path());
if(exit_status==0) {
// The following code is complex due to clang-format returning offsets in byte offsets instead of char offsets
// Create bytes_in_lines cache to significantly speed up the processing of finding iterators from byte offsets
std::vector<size_t> bytes_in_lines;
auto line_count=get_buffer()->get_line_count();
for(int line_nr=0;line_nr<line_count;++line_nr) {
auto iter=get_buffer()->get_iter_at_line(line_nr);
bytes_in_lines.emplace_back(iter.get_bytes_in_line());
}
return false;
}, 500);
if(SelectionDialog::get())
SelectionDialog::get()->hide();
if(CompletionDialog::get())
CompletionDialog::get()->hide();
get_buffer()->begin_user_action();
try {
boost::property_tree::ptree pt;
boost::property_tree::xml_parser::read_xml(stdout_stream, pt);
auto replacements_pt=pt.get_child("replacements");
for(auto it=replacements_pt.rbegin();it!=replacements_pt.rend();++it) {
if(it->first=="replacement") {
auto offset=it->second.get<size_t>("<xmlattr>.offset");
auto length=it->second.get<size_t>("<xmlattr>.length");
auto replacement_str=it->second.get<std::string>("");
if(update_status_location)
update_status_location(this);
}
});
size_t bytes=0;
for(size_t c=0;c<bytes_in_lines.size();++c) {
auto previous_bytes=bytes;
bytes+=bytes_in_lines[c];
if(offset<bytes || (c==bytes_in_lines.size()-1 && offset==bytes)) {
std::pair<size_t, size_t> line_index(c, offset-previous_bytes);
auto start=get_buffer()->get_iter_at_line_index(line_index.first, line_index.second);
signal_scroll_event().connect([this](GdkEventScroll* event) {
hide_tooltips();
hide_dialogs();
return false;
});
// Use left gravity insert to avoid moving cursor from end of line
bool left_gravity_insert=false;
if(get_buffer()->get_insert()->get_iter()==start) {
auto iter=start;
do {
if(*iter!=' ' && *iter!='\t') {
left_gravity_insert=iter.ends_line();
break;
}
} while(iter.forward_char());
}
signal_focus_out_event().connect([this](GdkEventFocus* event) {
hide_tooltips();
return false;
});
if(length>0) {
auto offset_end=offset+length;
size_t bytes=0;
for(size_t c=0;c<bytes_in_lines.size();++c) {
auto previous_bytes=bytes;
bytes+=bytes_in_lines[c];
if(offset_end<bytes || (c==bytes_in_lines.size()-1 && offset_end==bytes)) {
auto end=get_buffer()->get_iter_at_line_index(c, offset_end-previous_bytes);
get_buffer()->erase(start, end);
start=get_buffer()->get_iter_at_line_index(line_index.first, line_index.second);
break;
}
}
}
if(left_gravity_insert) {
auto mark=get_buffer()->create_mark(start);
get_buffer()->insert(start, replacement_str);
get_buffer()->place_cursor(mark->get_iter());
get_buffer()->delete_mark(mark);
}
else
get_buffer()->insert(start, replacement_str);
break;
}
}
}
}
}
catch(const std::exception &e) {
Terminal::get().print(std::string("Error: error parsing clang-format output: ")+e.what()+'\n', true);
}
get_buffer()->end_user_action();
}
};
}
else if(language && language->get_id()=="markdown") {
// The style file currently has no options, but checking if it exists
format_style=[this](bool continue_without_style_file) {
bool has_style_file=false;
auto style_file_search_path=this->file_path.parent_path();
while(true) {
if(boost::filesystem::exists(style_file_search_path/".markdown-format")) {
has_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(!has_style_file && !continue_without_style_file)
return;
signal_leave_notify_event().connect([this](GdkEventCrossing*) {
delayed_tooltips_connection.disconnect();
return false;
});
auto special_character=[](Gtk::TextIter iter) {
if(*iter=='*' || *iter=='#' || *iter=='<' || *iter=='>' || *iter==' ' || *iter=='=' || *iter=='`' || *iter=='-')
return true;
// Tests if a line starts with for instance: 2.
if(*iter>='0' && *iter<='9' && iter.forward_char() &&
*iter=='.' && iter.forward_char() &&
*iter==' ')
return true;
return false;
};
get_buffer()->begin_user_action();
disable_spellcheck=true;
cleanup_whitespace_characters();
auto iter=get_buffer()->begin();
size_t last_space_offset=-1;
bool headline=false;
bool monospace=false;
bool script=false;
bool html_tag=false;
int square_brackets=0;
do {
if(iter.starts_line()) {
last_space_offset=-1;
auto next_line_iter=iter;
if(*iter=='#' || (next_line_iter.forward_line() && *next_line_iter=='='))
headline=true;
else
headline=false;
auto test_iter=iter;
if(*test_iter=='`' && test_iter.forward_char() &&
*test_iter=='`' && test_iter.forward_char() &&
*test_iter=='`') {
script=!script;
iter.forward_chars(3);
continue;
}
}
if(!script && *iter=='`')
monospace=!monospace;
if(!script && !monospace) {
if(*iter=='<')
html_tag=true;
else if(*iter=='>')
html_tag=false;
else if(*iter=='[')
++square_brackets;
else if(*iter==']')
--square_brackets;
}
if(!headline && !script && !monospace && !html_tag && square_brackets==0) {
if(*iter==' ' && iter.get_line_offset()<=80)
last_space_offset=iter.get_offset();
// Insert newline on long lines
else if((*iter==' ' || iter.ends_line()) && iter.get_line_offset()>80 && last_space_offset!=static_cast<size_t>(-1)) {
auto stored_iter=iter;
iter=get_buffer()->get_iter_at_offset(last_space_offset);
auto next_iter=iter;
next_iter.forward_char();
// Do not add newline if the next iter is a special character
if(special_character(next_iter)) {
iter=stored_iter;
if(*iter==' ')
last_space_offset=iter.get_offset();
continue;
}
iter=get_buffer()->erase(iter, next_iter);
iter=get_buffer()->insert(iter, "\n");
iter.backward_char();
}
// Remove newline on short lines
else if(iter.ends_line() && !iter.starts_line() && iter.get_line_offset()<=80) {
auto next_line_iter=iter;
// Do not remove newline if the next line for instance is a header
if(next_line_iter.forward_char() && !next_line_iter.ends_line() && !special_character(next_line_iter)) {
auto end_word_iter=next_line_iter;
// Do not remove newline if the word on the next line is too long
size_t diff=0;
while(*end_word_iter!=' ' && !end_word_iter.ends_line() && end_word_iter.forward_char())
++diff;
if(iter.get_line_offset()+diff+1<=80) {
iter=get_buffer()->erase(iter, next_line_iter);
iter=get_buffer()->insert(iter, " ");
iter.backward_char();
if(iter.get_line_offset()<=80)
last_space_offset=iter.get_offset();
}
}
}
}
} while(iter.forward_char());
disable_spellcheck=false;
get_buffer()->end_user_action();
};
}
}
void Source::View::search_occurrences_updated(GtkWidget* widget, GParamSpec* property, gpointer data) {
@ -1169,28 +1172,35 @@ void Source::View::paste() {
scroll_to_cursor_delayed(this, false, false);
}
Gtk::TextIter Source::View::get_iter_for_dialog() {
auto iter=get_buffer()->get_insert()->get_iter();
Gdk::Rectangle visible_rect;
get_visible_rect(visible_rect);
Gdk::Rectangle iter_rect;
get_iter_location(iter, iter_rect);
iter_rect.set_width(1);
if(iter.get_line_offset()>=80) {
get_iter_at_location(iter, visible_rect.get_x(), iter_rect.get_y());
get_iter_location(iter, iter_rect);
}
if(!visible_rect.intersects(iter_rect))
get_iter_at_location(iter, visible_rect.get_x(), visible_rect.get_y()+visible_rect.get_height()/3);
return iter;
}
void Source::View::hide_tooltips() {
delayed_tooltips_connection.disconnect();
type_tooltips.hide();
diagnostic_tooltips.hide();
}
void Source::View::add_diagnostic_tooltip(const Gtk::TextIter &start, const Gtk::TextIter &end, std::string spelling, bool error) {
diagnostic_offsets.emplace(start.get_offset());
std::string severity_tag_name = error ? "def:error" : "def:warning";
auto create_tooltip_buffer=[this, spelling=std::move(spelling), error, severity_tag_name]() {
auto tooltip_buffer=Gtk::TextBuffer::create(get_buffer()->get_tag_table());
tooltip_buffer->insert_with_tag(tooltip_buffer->get_insert()->get_iter(), error ? "Error" : "Warning", severity_tag_name);
tooltip_buffer->insert_with_tag(tooltip_buffer->get_insert()->get_iter(), ":\n"+spelling, "def:note");
return tooltip_buffer;
};
diagnostic_tooltips.emplace_back(create_tooltip_buffer, this, get_buffer()->create_mark(start), get_buffer()->create_mark(end));
get_buffer()->apply_tag_by_name(severity_tag_name+"_underline", start, end);
}
void Source::View::clear_diagnostic_tooltips() {
diagnostic_offsets.clear();
diagnostic_tooltips.clear();
get_buffer()->remove_tag_by_name("def:warning_underline", get_buffer()->begin(), get_buffer()->end());
get_buffer()->remove_tag_by_name("def:error_underline", get_buffer()->begin(), get_buffer()->end());
}
void Source::View::hide_dialogs() {
SpellCheckView::hide_dialogs();
if(SelectionDialog::get())
@ -2887,7 +2897,7 @@ std::pair<char, unsigned> Source::View::find_tab_char_and_size() {
/////////////////////
//// GenericView ////
/////////////////////
Source::GenericView::GenericView(const boost::filesystem::path &file_path, Glib::RefPtr<Gsv::Language> language) : BaseView(file_path, language), View(file_path, language) {
Source::GenericView::GenericView(const boost::filesystem::path &file_path, Glib::RefPtr<Gsv::Language> language) : BaseView(file_path, language), View(file_path, language, true) {
configure();
spellcheck_all=true;

12
src/source.h

@ -42,7 +42,7 @@ namespace Source {
static std::unordered_set<View*> non_deleted_views;
static std::unordered_set<View*> views;
View(const boost::filesystem::path &file_path, Glib::RefPtr<Gsv::Language> language);
View(const boost::filesystem::path &file_path, Glib::RefPtr<Gsv::Language> language, bool is_generic_view=false);
~View();
bool save() override;
@ -77,8 +77,6 @@ namespace Source {
std::function<std::tuple<Source::Offset, std::string, size_t>()> get_documentation_template;
std::function<void(int)> toggle_breakpoint;
Gtk::TextIter get_iter_for_dialog();
void hide_tooltips() override;
void hide_dialogs() override;
@ -90,15 +88,16 @@ namespace Source {
virtual void soft_reparse(bool delayed=false) {soft_reparse_needed=false;}
virtual void full_reparse() {full_reparse_needed=false;}
protected:
bool parsed=false;
bool parsed=true;
Tooltips diagnostic_tooltips;
Tooltips type_tooltips;
sigc::connection delayed_tooltips_connection;
virtual void show_diagnostic_tooltips(const Gdk::Rectangle &rectangle) { diagnostic_tooltips.show(rectangle); }
void add_diagnostic_tooltip(const Gtk::TextIter &start, const Gtk::TextIter &end, std::string spelling, bool error);
void clear_diagnostic_tooltips();
virtual void show_type_tooltips(const Gdk::Rectangle &rectangle) {}
gdouble on_motion_last_x=0.0;
gdouble on_motion_last_y=0.0;
void set_tooltip_and_dialog_events();
/// Usually returns at start of line, but not always
Gtk::TextIter find_start_of_sentence(Gtk::TextIter iter);
@ -130,6 +129,9 @@ namespace Source {
guint previous_non_modifier_keyval=0;
private:
void setup_tooltip_and_dialog_events();
void setup_format_style(bool is_generic_view);
void cleanup_whitespace_characters();
Gsv::DrawSpacesFlags parse_show_whitespace_characters(const std::string &text);

188
src/source_base.cc

@ -15,39 +15,7 @@ Source::BaseView::BaseView(const boost::filesystem::path &file_path, Glib::RefPt
return false;
});
#ifdef __APPLE__ // TODO: Gio file monitor is bugged on MacOS
class Recursive {
public:
static void f(BaseView *view, std::time_t last_write_time_) {
view->delayed_monitor_changed_connection.disconnect();
view->delayed_monitor_changed_connection=Glib::signal_timeout().connect([view, last_write_time_]() {
boost::system::error_code ec;
auto last_write_time=boost::filesystem::last_write_time(view->file_path, ec);
if(last_write_time!=last_write_time_)
view->check_last_write_time(last_write_time);
Recursive::f(view, last_write_time);
return false;
}, 1000);
}
};
if(this->last_write_time!=static_cast<std::time_t>(-1))
Recursive::f(this, last_write_time);
#else
if(this->last_write_time!=static_cast<std::time_t>(-1)) {
monitor=Gio::File::create_for_path(file_path.string())->monitor_file(Gio::FileMonitorFlags::FILE_MONITOR_NONE);
monitor_changed_connection=monitor->signal_changed().connect([this](const Glib::RefPtr<Gio::File> &file,
const Glib::RefPtr<Gio::File>&,
Gio::FileMonitorEvent monitor_event) {
if(monitor_event!=Gio::FileMonitorEvent::FILE_MONITOR_EVENT_CHANGES_DONE_HINT) {
delayed_monitor_changed_connection.disconnect();
delayed_monitor_changed_connection=Glib::signal_timeout().connect([this]() {
check_last_write_time();
return false;
}, 500);
}
});
}
#endif
monitor_file();
}
Source::BaseView::~BaseView() {
@ -204,6 +172,92 @@ void Source::BaseView::rename(const boost::filesystem::path &path) {
update_tab_label(this);
}
void Source::BaseView::monitor_file() {
#ifdef __APPLE__ // TODO: Gio file monitor is bugged on MacOS
class Recursive {
public:
static void f(BaseView *view, std::time_t last_write_time_) {
view->delayed_monitor_changed_connection.disconnect();
view->delayed_monitor_changed_connection=Glib::signal_timeout().connect([view, last_write_time_]() {
boost::system::error_code ec;
auto last_write_time=boost::filesystem::last_write_time(view->file_path, ec);
if(last_write_time!=last_write_time_)
view->check_last_write_time(last_write_time);
Recursive::f(view, last_write_time);
return false;
}, 1000);
}
};
delayed_monitor_changed_connection.disconnect();
if(this->last_write_time!=static_cast<std::time_t>(-1))
Recursive::f(this, last_write_time);
#else
if(this->last_write_time!=static_cast<std::time_t>(-1)) {
monitor=Gio::File::create_for_path(file_path.string())->monitor_file(Gio::FileMonitorFlags::FILE_MONITOR_NONE);
monitor_changed_connection.disconnect();
monitor_changed_connection=monitor->signal_changed().connect([this](const Glib::RefPtr<Gio::File> &file,
const Glib::RefPtr<Gio::File>&,
Gio::FileMonitorEvent monitor_event) {
if(monitor_event!=Gio::FileMonitorEvent::FILE_MONITOR_EVENT_CHANGES_DONE_HINT) {
delayed_monitor_changed_connection.disconnect();
delayed_monitor_changed_connection=Glib::signal_timeout().connect([this]() {
check_last_write_time();
return false;
}, 500);
}
});
}
#endif
}
void Source::BaseView::check_last_write_time(std::time_t last_write_time_) {
if(this->last_write_time==static_cast<std::time_t>(-1))
return;
if(Config::get().source.auto_reload_changed_files && !get_buffer()->get_modified()) {
boost::system::error_code ec;
auto last_write_time=last_write_time_!=static_cast<std::time_t>(-1) ? last_write_time_ : boost::filesystem::last_write_time(file_path, ec);
if(!ec && last_write_time!=this->last_write_time) {
if(load()) {
get_buffer()->set_modified(false);
return;
}
}
}
else if(has_focus()) {
boost::system::error_code ec;
auto last_write_time=last_write_time_!=static_cast<std::time_t>(-1) ? last_write_time_ : boost::filesystem::last_write_time(file_path, ec);
if(!ec && last_write_time!=this->last_write_time)
Info::get().print("Caution: " + file_path.filename().string() + " was changed outside of juCi++");
}
}
Gtk::TextIter Source::BaseView::get_iter_at_line_pos(int line, int pos) {
return get_iter_at_line_index(line, pos);
}
Gtk::TextIter Source::BaseView::get_iter_at_line_offset(int line, int offset) {
line=std::min(line, get_buffer()->get_line_count()-1);
if(line<0)
line=0;
auto iter=get_iter_at_line_end(line);
offset=std::min(offset, iter.get_line_offset());
if(offset<0)
offset=0;
return get_buffer()->get_iter_at_line_offset(line, offset);
}
Gtk::TextIter Source::BaseView::get_iter_at_line_index(int line, int index) {
line=std::min(line, get_buffer()->get_line_count()-1);
if(line<0)
line=0;
auto iter=get_iter_at_line_end(line);
index=std::min(index, iter.get_line_index());
if(index<0)
index=0;
return get_buffer()->get_iter_at_line_index(line, index);
}
Gtk::TextIter Source::BaseView::get_iter_at_line_end(int line_nr) {
if(line_nr>=get_buffer()->get_line_count())
return get_buffer()->end();
@ -221,15 +275,20 @@ Gtk::TextIter Source::BaseView::get_iter_at_line_end(int line_nr) {
}
}
Gtk::TextIter Source::BaseView::get_iter_at_line_pos(int line, int pos) {
line=std::min(line, get_buffer()->get_line_count()-1);
if(line<0)
line=0;
auto iter=get_iter_at_line_end(line);
pos=std::min(pos, iter.get_line_index());
if(pos<0)
pos=0;
return get_buffer()->get_iter_at_line_index(line, pos);
Gtk::TextIter Source::BaseView::get_iter_for_dialog() {
auto iter=get_buffer()->get_insert()->get_iter();
Gdk::Rectangle visible_rect;
get_visible_rect(visible_rect);
Gdk::Rectangle iter_rect;
get_iter_location(iter, iter_rect);
iter_rect.set_width(1);
if(iter.get_line_offset()>=80) {
get_iter_at_location(iter, visible_rect.get_x(), iter_rect.get_y());
get_iter_location(iter, iter_rect);
}
if(!visible_rect.intersects(iter_rect))
get_iter_at_location(iter, visible_rect.get_x(), visible_rect.get_y()+visible_rect.get_height()/3);
return iter;
}
void Source::BaseView::place_cursor_at_line_pos(int line, int pos) {
@ -237,21 +296,11 @@ void Source::BaseView::place_cursor_at_line_pos(int line, int pos) {
}
void Source::BaseView::place_cursor_at_line_offset(int line, int offset) {
line=std::min(line, get_buffer()->get_line_count()-1);
if(line<0)
line=0;
auto iter=get_iter_at_line_end(line);
offset=std::min(offset, iter.get_line_offset());
get_buffer()->place_cursor(get_buffer()->get_iter_at_line_offset(line, offset));
get_buffer()->place_cursor(get_iter_at_line_offset(line, offset));
}
void Source::BaseView::place_cursor_at_line_index(int line, int index) {
line=std::min(line, get_buffer()->get_line_count()-1);
if(line<0)
line=0;
auto iter=get_iter_at_line_end(line);
index=std::min(index, iter.get_line_index());
get_buffer()->place_cursor(get_buffer()->get_iter_at_line_index(line, index));
get_buffer()->place_cursor(get_iter_at_line_index(line, index));
}
Gtk::TextIter Source::BaseView::get_smart_home_iter(const Gtk::TextIter &iter) {
@ -266,6 +315,7 @@ Gtk::TextIter Source::BaseView::get_smart_home_iter(const Gtk::TextIter &iter) {
else
return start_line_iter;
}
Gtk::TextIter Source::BaseView::get_smart_end_iter(const Gtk::TextIter &iter) {
auto end_line_iter=get_iter_at_line_end(iter.get_line());
auto end_sentence_iter=end_line_iter;
@ -324,24 +374,20 @@ Gtk::TextIter Source::BaseView::get_tabs_end_iter() {
return get_tabs_end_iter(get_buffer()->get_insert());
}
void Source::BaseView::check_last_write_time(std::time_t last_write_time_) {
if(this->last_write_time==static_cast<std::time_t>(-1))
return;
if(Config::get().source.auto_reload_changed_files && !get_buffer()->get_modified()) {
boost::system::error_code ec;
auto last_write_time=last_write_time_!=static_cast<std::time_t>(-1) ? last_write_time_ : boost::filesystem::last_write_time(file_path, ec);
if(!ec && last_write_time!=this->last_write_time) {
if(load()) {
get_buffer()->set_modified(false);
return;
}
void Source::BaseView::place_cursor_at_next_diagnostic() {
auto insert_offset=get_buffer()->get_insert()->get_iter().get_offset();
for(auto offset: diagnostic_offsets) {
if(offset>insert_offset) {
get_buffer()->place_cursor(get_buffer()->get_iter_at_offset(offset));
scroll_to(get_buffer()->get_insert(), 0.0, 1.0, 0.5);
return;
}
}
else if(has_focus()) {
boost::system::error_code ec;
auto last_write_time=last_write_time_!=static_cast<std::time_t>(-1) ? last_write_time_ : boost::filesystem::last_write_time(file_path, ec);
if(!ec && last_write_time!=this->last_write_time)
Info::get().print("Caution: " + file_path.filename().string() + " was changed outside of juCi++");
if(diagnostic_offsets.size()==0)
Info::get().print("No diagnostics found in current buffer");
else {
auto iter=get_buffer()->get_iter_at_offset(*diagnostic_offsets.begin());
get_buffer()->place_cursor(iter);
scroll_to(get_buffer()->get_insert(), 0.0, 1.0, 0.5);
}
}

23
src/source_base.h

@ -2,6 +2,7 @@
#include <gtksourceviewmm.h>
#include <mutex>
#include <set>
#include <boost/filesystem.hpp>
namespace Source {
@ -29,10 +30,16 @@ namespace Source {
std::function<void(BaseView* view, bool center, bool show_tooltips)> scroll_to_cursor_delayed=[](BaseView* view, bool center, bool show_tooltips) {};
/// Safely returns iter given line and an offset using either byte index or character offset. Defaults to using byte index.
virtual Gtk::TextIter get_iter_at_line_pos(int line, int pos);
/// Safely returns iter given line and character offset
Gtk::TextIter get_iter_at_line_offset(int line, int offset);
/// Safely returns iter given line and byte index
Gtk::TextIter get_iter_at_line_index(int line, int index);
Gtk::TextIter get_iter_at_line_end(int line_nr);
Gtk::TextIter get_iter_for_dialog();
/// Safely returns iter at a line at an offset using either byte index or character offset. Defaults to using byte index.
virtual Gtk::TextIter get_iter_at_line_pos(int line, int pos);
/// Safely places cursor at line using get_iter_at_line_pos.
void place_cursor_at_line_pos(int line, int pos);
/// Safely places cursor at line offset
@ -40,6 +47,11 @@ namespace Source {
/// Safely places cursor at line index
void place_cursor_at_line_index(int line, int index);
protected:
std::time_t last_write_time;
void monitor_file();
void check_last_write_time(std::time_t last_write_time_=static_cast<std::time_t>(-1));
/// Move iter to line start. Depending on iter position, before or after indentation.
/// Works with wrapped lines.
Gtk::TextIter get_smart_home_iter(const Gtk::TextIter &iter);
@ -60,6 +72,9 @@ namespace Source {
Gtk::TextIter get_tabs_end_iter(int line_nr);
Gtk::TextIter get_tabs_end_iter();
std::set<int> diagnostic_offsets;
void place_cursor_at_next_diagnostic();
public:
std::function<void(BaseView *view)> update_tab_label;
std::function<void(BaseView *view)> update_status_location;
std::function<void(BaseView *view)> update_status_file_path;
@ -71,9 +86,5 @@ namespace Source {
std::string status_branch;
bool disable_spellcheck=false;
protected:
std::time_t last_write_time;
void check_last_write_time(std::time_t last_write_time_=static_cast<std::time_t>(-1));
};
}

50
src/source_clang.cc

@ -265,11 +265,8 @@ void Source::ClangViewParse::update_syntax() {
}
void Source::ClangViewParse::update_diagnostics() {
diagnostic_offsets.clear();
diagnostic_tooltips.clear();
clear_diagnostic_tooltips();
fix_its.clear();
get_buffer()->remove_tag_by_name("def:warning_underline", get_buffer()->begin(), get_buffer()->end());
get_buffer()->remove_tag_by_name("def:error_underline", get_buffer()->begin(), get_buffer()->end());
size_t num_warnings=0;
size_t num_errors=0;
size_t num_fix_its=0;
@ -296,19 +293,18 @@ void Source::ClangViewParse::update_diagnostics() {
if(index>=0 && index<end.get_line_index())
end=get_buffer()->get_iter_at_line_index(line, index);
std::string diagnostic_tag_name;
if(diagnostic.severity<=CXDiagnostic_Warning) {
diagnostic_tag_name="def:warning";
bool error=false;
std::string severity_tag_name;
if(diagnostic.severity<=clangmm::Diagnostic::Severity::Warning) {
severity_tag_name="def:warning";
num_warnings++;
}
else {
diagnostic_tag_name="def:error";
severity_tag_name="def:error";
num_errors++;
error=true;
}
auto spelling=diagnostic.spelling;
auto severity_spelling=diagnostic.severity_spelling;
std::string fix_its_string;
unsigned fix_its_count=0;
for(auto &fix_it: diagnostic.fix_its) {
@ -333,23 +329,15 @@ void Source::ClangViewParse::update_diagnostics() {
else if(fix_its_count>1)
fix_its_string.insert(0, "Fix-its:\n");
auto create_tooltip_buffer=[this, spelling, severity_spelling, diagnostic_tag_name, fix_its_string]() {
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(), ":\n"+spelling, "def:note");
if(fix_its_string.size()>0) {
tooltip_buffer->insert_with_tag(tooltip_buffer->get_insert()->get_iter(), ":\n\n"+fix_its_string, "def:note");
}
return tooltip_buffer;
};
diagnostic_tooltips.emplace_back(create_tooltip_buffer, this, get_buffer()->create_mark(start), get_buffer()->create_mark(end));
if(!fix_its_string.empty())
diagnostic.spelling+="\n\n"+fix_its_string;
add_diagnostic_tooltip(start, end, diagnostic.spelling, error);
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);
get_buffer()->remove_tag_by_name(severity_tag_name+"_underline", iter, next_iter);
}
}
}
@ -1616,21 +1604,7 @@ Source::ClangViewRefactor::ClangViewRefactor(const boost::filesystem::path &file
Info::get().print("Buffer is parsing");
return;
}
auto insert_offset=get_buffer()->get_insert()->get_iter().get_offset();
for(auto offset: diagnostic_offsets) {
if(offset>insert_offset) {
get_buffer()->place_cursor(get_buffer()->get_iter_at_offset(offset));
scroll_to(get_buffer()->get_insert(), 0.0, 1.0, 0.5);
return;
}
}
if(diagnostic_offsets.size()==0)
Info::get().print("No diagnostics found in current buffer");
else {
auto iter=get_buffer()->get_iter_at_offset(*diagnostic_offsets.begin());
get_buffer()->place_cursor(iter);
scroll_to(get_buffer()->get_insert(), 0.0, 1.0, 0.5);
}
place_cursor_at_next_diagnostic();
};
get_fix_its=[this]() {

1
src/source_clang.h

@ -32,7 +32,6 @@ namespace Source {
void show_type_tooltips(const Gdk::Rectangle &rectangle) override;
std::set<int> diagnostic_offsets;
std::vector<FixIt> fix_its;
std::thread parse_thread;

1
src/source_diff.h

@ -39,6 +39,7 @@ namespace Source {
void git_goto_next_diff();
std::string git_get_diff_details();
/// Use canonical path to follow symbolic links
boost::filesystem::path canonical_file_path;
private:
std::mutex canonical_file_path_mutex;

57
src/source_language_protocol.cc

@ -322,7 +322,6 @@ Source::LanguageProtocolView::LanguageProtocolView(const boost::filesystem::path
configure();
get_source_buffer()->set_language(language);
get_source_buffer()->set_highlight_syntax(true);
parsed=true;
similar_symbol_tag=get_buffer()->create_tag();
similar_symbol_tag->property_weight()=Pango::WEIGHT_ULTRAHEAVY;
@ -829,21 +828,7 @@ void Source::LanguageProtocolView::setup_navigation_and_refactoring() {
}
goto_next_diagnostic=[this]() {
auto insert_offset=get_buffer()->get_insert()->get_iter().get_offset();
for(auto offset: diagnostic_offsets) {
if(offset>insert_offset) {
get_buffer()->place_cursor(get_buffer()->get_iter_at_offset(offset));
scroll_to(get_buffer()->get_insert(), 0.0, 1.0, 0.5);
return;
}
}
if(diagnostic_offsets.size()==0)
Info::get().print("No diagnostics found in current buffer");
else {
auto iter=get_buffer()->get_iter_at_offset(*diagnostic_offsets.begin());
get_buffer()->place_cursor(iter);
scroll_to(get_buffer()->get_insert(), 0.0, 1.0, 0.5);
}
place_cursor_at_next_diagnostic();
};
}
@ -879,10 +864,7 @@ void Source::LanguageProtocolView::unescape_text(std::string &text) {
void Source::LanguageProtocolView::update_diagnostics(std::vector<LanguageProtocol::Diagnostic> &&diagnostics) {
dispatcher.post([this, diagnostics=std::move(diagnostics)] {
diagnostic_offsets.clear();
diagnostic_tooltips.clear();
get_buffer()->remove_tag_by_name("def:warning_underline", get_buffer()->begin(), get_buffer()->end());
get_buffer()->remove_tag_by_name("def:error_underline", get_buffer()->begin(), get_buffer()->end());
clear_diagnostic_tooltips();
size_t num_warnings=0;
size_t num_errors=0;
size_t num_fix_its=0;
@ -898,37 +880,25 @@ void Source::LanguageProtocolView::update_diagnostics(std::vector<LanguageProtoc
start.forward_char();
}
diagnostic_offsets.emplace(start.get_offset());
std::string diagnostic_tag_name;
std::string severity_spelling;
bool error=false;
std::string severity_tag_name;
if(diagnostic.severity>=2) {
severity_spelling="Warning";
diagnostic_tag_name="def:warning";
severity_tag_name="def:warning";
num_warnings++;
}
else {
severity_spelling="Error";
diagnostic_tag_name="def:error";
severity_tag_name="def:error";
num_errors++;
error=true;
}
auto spelling=diagnostic.spelling;
auto create_tooltip_buffer=[this, spelling, severity_spelling, diagnostic_tag_name]() {
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(), ":\n"+spelling, "def:note");
return tooltip_buffer;
};
diagnostic_tooltips.emplace_back(create_tooltip_buffer, this, get_buffer()->create_mark(start), get_buffer()->create_mark(end));
add_diagnostic_tooltip(start, end, diagnostic.spelling, error);
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);
get_buffer()->remove_tag_by_name(severity_tag_name+"_underline", iter, next_iter);
}
}
}
@ -939,14 +909,7 @@ void Source::LanguageProtocolView::update_diagnostics(std::vector<LanguageProtoc
}
Gtk::TextIter Source::LanguageProtocolView::get_iter_at_line_pos(int line, int pos) {
line=std::min(line, get_buffer()->get_line_count()-1);
if(line<0)
line=0;
auto iter=get_iter_at_line_end(line);
pos=std::min(pos, iter.get_line_offset());
if(pos<0)
pos=0;
return get_buffer()->get_iter_at_line_offset(line, pos);
return get_iter_at_line_offset(line, pos);
}
void Source::LanguageProtocolView::show_type_tooltips(const Gdk::Rectangle &rectangle) {

2
src/source_language_protocol.h

@ -111,8 +111,6 @@ namespace Source {
void escape_text(std::string &text);
void unescape_text(std::string &text);
std::set<int> diagnostic_offsets;
Glib::RefPtr<Gtk::TextTag> similar_symbol_tag;
sigc::connection delayed_tag_similar_symbols_connection;
void tag_similar_symbols();

Loading…
Cancel
Save