Browse Source

Terminal links cleanup, and added links for a assert error messages. Also added terminal_test.cc

merge-requests/365/head
eidheim 8 years ago
parent
commit
7516d1d9b0
  1. 2
      src/CMakeLists.txt
  2. 113
      src/terminal.cc
  3. 4
      src/terminal.h
  4. 8
      tests/CMakeLists.txt
  5. 9
      tests/stubs/notebook.cc
  6. 3
      tests/stubs/project.cc
  7. 33
      tests/stubs/terminal.cc
  8. 41
      tests/terminal_test.cc

2
src/CMakeLists.txt

@ -13,7 +13,6 @@ set(project_files
notebook.cc
project.cc
selection_dialog.cc
terminal.cc
tooltips.cc
window.cc
)
@ -33,6 +32,7 @@ set(project_shared_files
source_clang.cc
source_diff.cc
source_spellcheck.cc
terminal.cc
${PROJECT_SOURCE_DIR}/libclangmm/src/CodeCompleteResults.cc
${PROJECT_SOURCE_DIR}/libclangmm/src/CompilationDatabase.cc

113
src/terminal.cc

@ -43,8 +43,6 @@ void Terminal::InProgress::cancel(const std::string& msg) {
Terminal::get().async_print(line_nr-1, msg);
}
const std::regex Terminal::link_regex("^([A-Z]:)?([^:]+):([0-9]+):([0-9]+)$");
Terminal::Terminal() {
bold_tag=get_buffer()->create_tag();
bold_tag->property_weight()=PANGO_WEIGHT_BOLD;
@ -177,39 +175,70 @@ bool Terminal::on_motion_notify_event(GdkEventMotion *motion_event) {
return Gtk::TextView::on_motion_notify_event(motion_event);
}
std::tuple<size_t, size_t, std::string, std::string, std::string> Terminal::find_link(const std::string &line) {
const static std::regex link_regex("^([A-Z]:)?([^:]+):([0-9]+):([0-9]+): .*$|" //compile warning/error
"^Assertion failed: .*file ([A-Z]:)?([^:]+), line ([0-9]+)\\.$|" //clang assert()
"^[^:]*: ([A-Z]:)?([^:]+):([0-9]+): .* Assertion .* failed\\.$|" //gcc assert()
"^ERROR:([A-Z]:)?([^:]+):([0-9]+):.*$"); //g_assert (glib.h)
size_t start_position=-1, end_position=-1;
std::string path, line_number, line_offset;
std::smatch sm;
if(std::regex_match(line, sm, link_regex)) {
for(size_t sub=1;sub<link_regex.mark_count();) {
size_t subs=sub==1?4:3;
if(sm.length(sub+1)) {
start_position=sm.position(sub+1)-sm.length(sub);
end_position=sm.position(sub+subs-1)+sm.length(sub+subs-1);
if(sm.length(sub))
path+=sm[sub].str();
path+=sm[sub+1].str();
line_number=sm[sub+2].str();
line_offset=subs==4?sm[sub+3].str():"1";
break;
}
sub+=subs;
}
}
return std::make_tuple(start_position, end_position, path, line_number, line_offset);
}
void Terminal::apply_link_tags(Gtk::TextIter start_iter, Gtk::TextIter end_iter) {
auto iter=start_iter;
int offset=0;
size_t colons=0;
Gtk::TextIter start_path_iter;
bool possible_path=false;
//Search for path with line and index
//Simple implementation. Not sure if it is work the effort to make it work 100% on all platforms.
Gtk::TextIter line_start;
bool line_start_set=false;
bool delimiter_found=false;
bool dot_found=false;
bool number_found=false;
do {
if(iter.starts_line()) {
offset=0;
colons=0;
start_path_iter=iter;
possible_path=true;
line_start=iter;
line_start_set=true;
delimiter_found=false;
dot_found=false;
number_found=false;
}
if(possible_path) {
if(*iter==' ' || *iter=='\t' || iter.ends_line())
possible_path=false;
else {
++offset;
if(*iter==':') {
#ifdef _WIN32
if(offset!=2)
#endif
++colons;
if(colons==3 && possible_path) {
std::smatch sm;
if(std::regex_match(get_buffer()->get_text(start_path_iter, iter).raw(), sm, link_regex))
get_buffer()->apply_tag(link_tag, start_path_iter, iter);
possible_path=false;
}
}
if(line_start_set && (*iter=='\\' || *iter=='/'))
delimiter_found=true;
else if(line_start_set && *iter=='.')
dot_found=true;
else if(line_start_set && (*iter>='0' && *iter<='9'))
number_found=true;
else if(line_start_set && delimiter_found && dot_found && number_found && iter.ends_line()) {
auto line=get_buffer()->get_text(line_start, iter);
//Convert to ascii for std::regex and Gtk::Iter::forward_chars
for(size_t c=0;c<line.size();++c) {
if(line[c]>127)
line.replace(c, 1, "a");
}
auto link=find_link(line.raw());
if(std::get<0>(link)!=static_cast<size_t>(-1)) {
auto link_start=line_start;
auto link_end=line_start;
link_start.forward_chars(std::get<0>(link));
link_end.forward_chars(std::get<1>(link));
get_buffer()->apply_tag(link_tag, link_start, link_end);
}
line_start_set=false;
}
} while(iter.forward_char() && iter!=end_iter);
}
@ -348,14 +377,16 @@ bool Terminal::on_button_press_event(GdkEventButton* button_event) {
int location_x, location_y;
window_to_buffer_coords(Gtk::TextWindowType::TEXT_WINDOW_TEXT, button_event->x, button_event->y, location_x, location_y);
get_iter_at_location(iter, location_x, location_y);
auto start_iter=iter;
auto end_iter=iter;
if(iter.has_tag(link_tag) &&
start_iter.backward_to_tag_toggle(link_tag) && end_iter.forward_to_tag_toggle(link_tag)) {
std::string path_str=get_buffer()->get_text(start_iter, end_iter);
std::smatch sm;
if(std::regex_match(path_str, sm, link_regex)) {
auto path=boost::filesystem::path(sm[1].str()+sm[2].str());
if(iter.has_tag(link_tag)) {
auto start_iter=get_buffer()->get_iter_at_line(iter.get_line());
auto end_iter=start_iter;
while(!end_iter.ends_line() && end_iter.forward_char()) {}
auto link=find_link(get_buffer()->get_text(start_iter, end_iter).raw());
if(std::get<0>(link)!=static_cast<size_t>(-1)) {
boost::filesystem::path path=std::get<2>(link);
std::string line=std::get<3>(link);
std::string index=std::get<4>(link);
if(path.is_relative()) {
if(Project::current) {
auto absolute_path=Project::current->build->get_default_path()/path;
@ -371,13 +402,13 @@ bool Terminal::on_button_press_event(GdkEventButton* button_event) {
Notebook::get().open(path);
if(auto view=Notebook::get().get_current_view()) {
try {
int line = std::stoi(sm[3].str())-1;
int index = std::stoi(sm[4].str())-1;
view->place_cursor_at_line_index(line, index);
int line_int = std::stoi(line)-1;
int index_int = std::stoi(index)-1;
view->place_cursor_at_line_index(line_int, index_int);
view->scroll_to_cursor_delayed(view, true, true);
return true;
}
catch(const std::exception &) {}
catch(...) {}
}
}
}

4
src/terminal.h

@ -12,6 +12,7 @@
#include "dispatcher.h"
#include <unordered_set>
#include <regex>
#include <tuple>
class Terminal : public Gtk::TextView {
public:
@ -64,7 +65,8 @@ private:
Glib::RefPtr<Gdk::Cursor> link_mouse_cursor;
Glib::RefPtr<Gdk::Cursor> default_mouse_cursor;
size_t deleted_lines=0;
const static std::regex link_regex;
std::tuple<size_t, size_t, std::string, std::string, std::string> find_link(const std::string &line);
void apply_link_tags(Gtk::TextIter start_iter, Gtk::TextIter end_iter);
std::vector<std::shared_ptr<TinyProcessLib::Process>> processes;

8
tests/CMakeLists.txt

@ -5,8 +5,9 @@ set(stub_files
stubs/dialogs.cc
stubs/directories.cc
stubs/info.cc
stubs/notebook.cc
stubs/project.cc
stubs/selection_dialog.cc
stubs/terminal.cc
stubs/tooltips.cc
)
@ -54,6 +55,11 @@ add_executable(source_key_test source_key_test.cc
target_link_libraries(source_key_test ${global_libraries})
add_test(source_key_test source_key_test)
add_executable(terminal_test terminal_test.cc
$<TARGET_OBJECTS:project_shared> $<TARGET_OBJECTS:stubs>)
target_link_libraries(terminal_test ${global_libraries})
add_test(terminal_test terminal_test)
if(LIBLLDB_FOUND)
add_executable(lldb_test lldb_test.cc
$<TARGET_OBJECTS:project_shared> $<TARGET_OBJECTS:stubs>)

9
tests/stubs/notebook.cc

@ -0,0 +1,9 @@
#include "notebook.h"
Notebook::Notebook() {}
Source::View *Notebook::get_current_view() {
return nullptr;
}
void Notebook::open(const boost::filesystem::path &file_path, size_t notebook_index) {}

3
tests/stubs/project.cc

@ -0,0 +1,3 @@
#include "project.h"
std::unique_ptr<Project::Base> Project::current;

33
tests/stubs/terminal.cc

@ -1,33 +0,0 @@
#include "terminal.h"
Terminal::InProgress::InProgress(const std::string& start_msg): stop(false) {}
Terminal::InProgress::~InProgress() {}
std::shared_ptr<Terminal::InProgress> Terminal::print_in_progress(std::string start_msg) {
return std::make_shared<Terminal::InProgress>("");
}
void Terminal::InProgress::done(const std::string& msg) {}
void Terminal::InProgress::cancel(const std::string &msg) {}
Terminal::Terminal() {}
bool Terminal::on_motion_notify_event(GdkEventMotion* motion_event) {return false;}
bool Terminal::on_button_press_event(GdkEventButton* button_event) {return false;}
bool Terminal::on_key_press_event(GdkEventKey *event) {return false;}
int Terminal::process(const std::string &command, const boost::filesystem::path &path, bool use_pipes) {
return 0;
}
int Terminal::process(std::istream &stdin_stream, std::ostream &stdout_stream, const std::string &command, const boost::filesystem::path &path) {
return 0;
}
size_t Terminal::print(const std::string &message, bool bold) {
return 0;
}
void Terminal::async_print(const std::string &message, bool bold) {}

41
tests/terminal_test.cc

@ -0,0 +1,41 @@
#include "terminal.h"
#include <glib.h>
//Requires display server to work
//However, it is possible to use the Broadway backend if the test is run in a pure terminal environment:
//broadwayd&
//make test
int main() {
auto app = Gtk::Application::create();
{
auto link=Terminal::get().find_link("~/test/test.cc:7:41: error: expected ';' after expression.");
assert(std::get<0>(link)==0);
assert(std::get<1>(link)==19);
assert(std::get<2>(link)=="~/test/test.cc");
assert(std::get<3>(link)=="7");
assert(std::get<4>(link)=="41");
}
{
auto link=Terminal::get().find_link("Assertion failed: (false), function main, file ~/test/test.cc, line 15.");
assert(std::get<0>(link)==47);
assert(std::get<1>(link)==70);
assert(std::get<2>(link)=="~/test/test.cc");
assert(std::get<3>(link)=="15");
}
{
auto link=Terminal::get().find_link("test: ~/examples/main.cpp:17: int main(int, char**): Assertion `false' failed.");
assert(std::get<0>(link)==6);
assert(std::get<1>(link)==28);
assert(std::get<2>(link)=="~/examples/main.cpp");
assert(std::get<3>(link)=="17");
}
{
auto link=Terminal::get().find_link("ERROR:~/test/test.cc:36:int main(): assertion failed: (false)");
assert(std::get<0>(link)==6);
assert(std::get<1>(link)==23);
assert(std::get<2>(link)=="~/test/test.cc");
assert(std::get<3>(link)=="36");
}
}
Loading…
Cancel
Save