@ -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 ;