Browse Source

Merge branch 'master' of http://github.com/eidheim/jucipp

merge-requests/365/head
Jørgen Lien Sellæg 10 years ago
parent
commit
2b404be313
  1. 7
      src/config.cc
  2. 246
      src/directories.cc
  3. 24
      src/directories.h
  4. 16
      src/files.h
  5. 2
      src/juci.cc
  6. 1
      src/notebook.cc
  7. 164
      src/source.cc
  8. 17
      src/source.h
  9. 153
      src/window.cc
  10. 4
      src/window.h

7
src/config.cc

@ -45,10 +45,9 @@ void MainConfig::GenerateSource() {
auto source_cfg = Singleton::Config::source();
auto source_json = cfg.get_child("source");
source_cfg->tab_size = source_json.get<unsigned>("tab_size");
source_cfg->tab_char = source_json.get<char>("tab_char");
for(unsigned c=0;c<source_cfg->tab_size;c++)
source_cfg->tab+=source_cfg->tab_char;
source_cfg->default_tab_char = source_json.get<char>("default_tab_char");
source_cfg->default_tab_size = source_json.get<unsigned>("default_tab_size");
source_cfg->auto_tab_char_and_size = source_json.get<bool>("auto_tab_char_and_size");
source_cfg->highlight_current_line = source_json.get_value<bool>("highlight_current_line");
source_cfg->show_line_numbers = source_json.get_value<bool>("show_line_numbers");

246
src/directories.cc

@ -3,7 +3,7 @@
#include "logging.h"
#include "singletons.h"
#include <algorithm>
#include "boost/algorithm/string.hpp"
#include <unordered_set>
#include <iostream> //TODO: remove
using namespace std; //TODO: remove
@ -12,78 +12,169 @@ namespace sigc {
SIGC_FUNCTORS_DEDUCE_RESULT_TYPE_WITH_DECLTYPE
}
Directories::Directories() {
Directories::Directories() : stop_update_thread(false) {
DEBUG("adding treeview to scrolledwindow");
add(tree_view);
set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
tree_store = Gtk::TreeStore::create(column_record);
tree_view.set_model(tree_store);
tree_view.append_column("", column_record.name);
tree_store->set_sort_column(0, Gtk::SortType::SORT_ASCENDING);
tree_store->set_sort_column(column_record.id, Gtk::SortType::SORT_ASCENDING);
tree_view.set_enable_search(true); //TODO: why does this not work in OS X?
tree_view.set_search_column(column_record.name);
tree_view.signal_row_activated().connect([this](const Gtk::TreeModel::Path& path, Gtk::TreeViewColumn* column){
INFO("Directory navigation");
auto iter = tree_store->get_iter(path);
if (iter) {
Gtk::TreeModel::Row row = *iter;
std::string upath = Glib::ustring(row[column_record.path]);
boost::filesystem::path fs_path(upath);
if (boost::filesystem::is_directory(fs_path)) {
tree_view.row_expanded(path) ? tree_view.collapse_row(path) : tree_view.expand_row(path, false);
} else {
std::stringstream sstm;
sstm << row[column_record.path];
if(on_row_activated)
on_row_activated(sstm.str());
auto path_str=iter->get_value(column_record.path);
if(path_str!="") {
if (boost::filesystem::is_directory(boost::filesystem::path(path_str))) {
tree_view.row_expanded(path) ? tree_view.collapse_row(path) : tree_view.expand_row(path, false);
} else {
if(on_row_activated)
on_row_activated(path_str);
}
}
}
});
tree_view.signal_test_expand_row().connect([this](const Gtk::TreeModel::iterator& iter, const Gtk::TreeModel::Path& path){
if(iter->children().begin()->get_value(column_record.path)=="") {
update_mutex.lock();
add_path(iter->get_value(column_record.path), *iter);
update_mutex.unlock();
}
return false;
});
tree_view.signal_row_collapsed().connect([this](const Gtk::TreeModel::iterator& iter, const Gtk::TreeModel::Path& path){
update_mutex.lock();
last_write_times.erase(iter->get_value(column_record.path));
update_mutex.unlock();
auto children=iter->children();
if(children) {
while(children) {
tree_store->erase(children.begin());
}
auto child=tree_store->append(iter->children());
child->set_value(column_record.name, std::string("(empty)"));
}
});
update_dispatcher.connect([this](){
update_mutex.lock();
for(auto &path: update_paths) {
if(last_write_times.count(path)>0)
add_path(path, last_write_times.at(path).first);
}
update_paths.clear();
update_mutex.unlock();
});
update_thread=std::thread([this](){
while(!stop_update_thread) {
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
update_mutex.lock();
if(update_paths.size()==0) {
for(auto it=last_write_times.begin();it!=last_write_times.end();) {
try {
if(boost::filesystem::exists(it->first)) { //Added for older boost versions (no exception thrown)
if(it->second.second<boost::filesystem::last_write_time(it->first)) {
update_paths.emplace_back(it->first);
}
it++;
}
else
it=last_write_times.erase(it);
}
catch(const std::exception &e) {
it=last_write_times.erase(it);
}
}
if(update_paths.size()>0)
update_dispatcher();
}
update_mutex.unlock();
}
});
}
void Directories::open_folder(const boost::filesystem::path& dir_path) {
auto new_path=dir_path;
INFO("Open folder");
if(new_path=="") {
if(current_path=="")
return;
new_path=current_path;
}
Directories::~Directories() {
stop_update_thread=true;
update_thread.join();
}
std::vector<Gtk::TreeModel::Path> expanded_paths;
if(current_path==new_path) {
tree_view.map_expanded_rows([&expanded_paths](Gtk::TreeView* tree_view, const Gtk::TreeModel::Path& path){
expanded_paths.emplace_back(path);
});
}
void Directories::open(const boost::filesystem::path& dir_path) {
if(dir_path=="")
return;
INFO("Open folder");
tree_store->clear();
update_mutex.lock();
last_write_times.clear();
update_paths.clear();
update_mutex.unlock();
if(dir_path!="")
cmake=std::unique_ptr<CMake>(new CMake(new_path));
cmake=std::unique_ptr<CMake>(new CMake(dir_path));
auto project=cmake->get_functions_parameters("project");
if(project.size()>0 && project[0].second.size()>0)
tree_view.get_column(0)->set_title(project[0].second[0]);
else
tree_view.get_column(0)->set_title("");
add_paths(new_path, Gtk::TreeModel::Row(), 0);
update_mutex.lock();
add_path(dir_path, Gtk::TreeModel::Row());
update_mutex.unlock();
for(auto &path: expanded_paths)
tree_view.expand_row(path, false);
current_path=dir_path;
current_path=new_path;
if(selected_path!="")
select_path(selected_path);
DEBUG("Folder opened");
}
void Directories::select_path(const boost::filesystem::path &path) {
void Directories::update() {
update_mutex.lock();
for(auto &last_write_time: last_write_times) {
add_path(last_write_time.first, last_write_time.second.first);
}
update_mutex.unlock();
}
void Directories::select(const boost::filesystem::path &path) {
if(current_path=="")
return;
if(path.string().substr(0, current_path.string().size())!=current_path.string())
return;
std::list<boost::filesystem::path> paths;
boost::filesystem::path parent_path;
if(boost::filesystem::is_directory(path))
parent_path=path;
else
parent_path=path.parent_path();
paths.emplace_front(parent_path);
while(parent_path!=current_path) {
parent_path=parent_path.parent_path();
paths.emplace_front(parent_path);
}
for(auto &a_path: paths) {
tree_store->foreach_iter([this, &a_path](const Gtk::TreeModel::iterator& iter){
if(iter->get_value(column_record.path)==a_path.string()) {
update_mutex.lock();
add_path(a_path, *iter);
update_mutex.unlock();
return true;
}
return false;
});
}
tree_store->foreach_iter([this, &path](const Gtk::TreeModel::iterator& iter){
if(iter->get_value(column_record.path)==path.string()) {
auto tree_path=Gtk::TreePath(iter);
tree_view.expand_to_path(tree_path);
tree_view.set_cursor(tree_path);
selected_path=path;
return true;
}
return false;
@ -106,37 +197,58 @@ bool Directories::ignored(std::string path) {
return false;
}
void Directories::add_paths(const boost::filesystem::path& dir_path, const Gtk::TreeModel::Row &parent, unsigned row_id) {
boost::filesystem::directory_iterator end_itr;
Gtk::TreeModel::Row child;
Gtk::TreeModel::Row row;
DEBUG("");
// Fill the treeview
for(boost::filesystem::directory_iterator itr(dir_path);itr != end_itr;++itr) {
if (!ignored(itr->path().filename().string())) {
if (boost::filesystem::is_directory(itr->status())) {
if (boost::filesystem::canonical(itr->path()) > boost::filesystem::canonical(dir_path)) { // is child
child = *(tree_store->append(parent.children()));
std::string col_id("a"+itr->path().filename().string());
child[column_record.id] = col_id;
child[column_record.name] = itr->path().filename().string();
child[column_record.path] = itr->path().string();
add_paths(itr->path(), child, row_id);
} else {
row = *(tree_store->append());
std::string col_id("a"+itr->path().filename().string());
row[column_record.path] = itr->path().string();
row[column_record.id] = col_id;
row[column_record.name] = itr->path().filename().string();
add_paths(itr->path(), parent, row_id);
void Directories::add_path(const boost::filesystem::path& dir_path, const Gtk::TreeModel::Row &parent) {
last_write_times[dir_path.string()]={parent, boost::filesystem::last_write_time(dir_path)};
std::unique_ptr<Gtk::TreeNodeChildren> children; //Gtk::TreeNodeChildren is missing default constructor...
if(parent)
children=std::unique_ptr<Gtk::TreeNodeChildren>(new Gtk::TreeNodeChildren(parent.children()));
else
children=std::unique_ptr<Gtk::TreeNodeChildren>(new Gtk::TreeNodeChildren(tree_store->children()));
if(*children) {
if(children->begin()->get_value(column_record.path)=="")
tree_store->erase(children->begin());
}
std::unordered_set<std::string> not_deleted;
boost::filesystem::directory_iterator end_it;
for(boost::filesystem::directory_iterator it(dir_path);it!=end_it;it++) {
auto filename=it->path().filename().string();
if (!ignored(filename)) {
bool already_added=false;
if(*children) {
for(auto &child: *children) {
if(child.get_value(column_record.name)==filename) {
not_deleted.emplace(filename);
already_added=true;
break;
}
}
} else { // is a file
child = *(tree_store->append(parent.children()));
std::string col_id("b"+itr->path().filename().string());
child[column_record.id] = col_id;
child[column_record.name] = itr->path().filename().string();
child[column_record.path] = itr->path().string();
}
if(!already_added) {
auto child = tree_store->append(*children);
not_deleted.emplace(filename);
child->set_value(column_record.name, filename);
child->set_value(column_record.path, it->path().string());
if (boost::filesystem::is_directory(*it)) {
child->set_value(column_record.id, "a"+filename);
auto grandchild=tree_store->append(child->children());
grandchild->set_value(column_record.name, std::string("(empty)"));
}
else
child->set_value(column_record.id, "b"+filename);
}
}
}
if(*children) {
for(auto it=children->begin();it!=children->end();) {
if(not_deleted.count(it->get_value(column_record.name))==0) {
it=tree_store->erase(it);
}
else
it++;
}
}
if(!*children) {
auto child=tree_store->append(*children);
child->set_value(column_record.name, std::string("(empty)"));
}
}

24
src/directories.h

@ -6,6 +6,9 @@
#include <string>
#include "boost/filesystem.hpp"
#include "cmake.h"
#include <thread>
#include <mutex>
#include <atomic>
class Directories : public Gtk::ScrolledWindow {
public:
@ -22,26 +25,33 @@ public:
add(name);
add(path);
}
Gtk::TreeModelColumn<Glib::ustring> id;
Gtk::TreeModelColumn<Glib::ustring> name;
Gtk::TreeModelColumn<Glib::ustring> path;
Gtk::TreeModelColumn<std::string> id;
Gtk::TreeModelColumn<std::string> name;
Gtk::TreeModelColumn<std::string> path;
};
Directories();
void open_folder(const boost::filesystem::path& dir_path="");
void select_path(const boost::filesystem::path &path);
~Directories();
void open(const boost::filesystem::path& dir_path="");
void update();
void select(const boost::filesystem::path &path);
std::function<void(const std::string &file)> on_row_activated;
std::unique_ptr<CMake> cmake;
boost::filesystem::path current_path;
private:
void add_paths(const boost::filesystem::path& dir_path, const Gtk::TreeModel::Row &row, unsigned depth);
void add_path(const boost::filesystem::path& dir_path, const Gtk::TreeModel::Row &row);
bool ignored(std::string path);
Gtk::TreeView tree_view;
Glib::RefPtr<Gtk::TreeStore> tree_store;
ColumnRecord column_record;
boost::filesystem::path selected_path;
std::unordered_map<std::string, std::pair<Gtk::TreeModel::Row, std::time_t> > last_write_times;
std::mutex update_mutex;
std::thread update_thread;
std::atomic<bool> stop_update_thread;
Glib::Dispatcher update_dispatcher;
std::vector<std::string> update_paths;
};
#endif // JUCI_DIRECTORIES_H_

16
src/files.h

@ -7,7 +7,12 @@ const std::string configjson =
" },\n"
" \"source\": {\n"
" \"style\": \"juci-light\", //Use \"\" for default style, and for instance juci-dark together with dark gtk_theme variant. Styles from normal gtksourceview install: classic, cobalt, kate, oblivion, solarized-dark, solarized-light, tango\n"
" \"font\": \"Monospace\", //Use \"\" for default font, and for instance \"Monospace 12\" to also set size.\n"
#ifdef __APPLE__
" \"font\": \"Menlo 11\", "
#else
" \"font\": \"Monospace\", "
#endif
"//Use \"\" for default font, and for instance \"Monospace 12\" to also set size.\n"
" \"clang_types\": {\n"
" \"8\": \"def:function\",\n"
" \"21\": \"def:function\",\n"
@ -23,14 +28,16 @@ const std::string configjson =
" \"702\": \"def:statement\",\n"
" \"705\": \"def:comment\"\n"
" },\n"
" \"tab_size\": 2,\n"
" \"tab_char\": \" \", //Use \"\\t\" for regular tab\n"
" \"auto_tab_char_and_size\": true, //Use false to always use default tab char and size\n"
" \"default_tab_char\": \" \", //Use \"\\t\" for regular tab\n"
" \"default_tab_size\": 2,\n"
" \"highlight_current_line\": true,\n"
" \"show_line_numbers\": true\n"
" },\n"
" \"keybindings\": {\n"
" \"new_file\": \"<primary>n\",\n"
" \"open_folder\": \"<primary><alt>o\",\n"
" \"new_folder\": \"<primary><shift>n\",\n"
" \"open_folder\": \"<primary><shift>o\",\n"
" \"open_file\": \"<primary>o\",\n"
" \"save\": \"<primary>s\",\n"
" \"save_as\": \"<primary><shift>s\",\n"
@ -76,6 +83,7 @@ const std::string menuxml =
" <menubar name=\"MenuBar\">\n"
" <menu action=\"FileMenu\">\n"
" <menuitem action=\"FileNewFile\"/>\n"
" <menuitem action=\"FileNewFolder\"/>\n"
" <menu action=\"FileNewProject\">\n"
" <menuitem action=\"FileNewProjectCpp\"/>\n"
" </menu>\n"

2
src/juci.cc

@ -45,7 +45,7 @@ void app::on_activate() {
bool first_directory=true;
for(auto &directory: directories) {
if(first_directory) {
window->directories.open_folder(directory);
window->directories.open(directory);
first_directory=false;
}
else {

1
src/notebook.cc

@ -120,7 +120,6 @@ bool Notebook::save(int page) {
//TODO: recreate cmake even without directories open?
if(view->file_path.filename()=="CMakeLists.txt") {
if(directories.cmake && directories.cmake->project_path!="" && view->file_path.string().substr(0, directories.cmake->project_path.string().size())==directories.cmake->project_path.string() && CMake::create_compile_commands(directories.cmake->project_path)) {
directories.open_folder();
for(auto source_view: source_views) {
if(auto source_clang_view=dynamic_cast<Source::ClangView*>(source_view)) {
if(directories.cmake->project_path.string()==source_clang_view->project_path) {

164
src/source.cc

@ -4,7 +4,6 @@
#include <boost/timer/timer.hpp>
#include "logging.h"
#include <algorithm>
#include <regex>
#include "singletons.h"
#include <gtksourceview/gtksource.h>
#include <boost/lexical_cast.hpp>
@ -75,6 +74,29 @@ Source::View::View(const boost::filesystem::path &file_path): file_path(file_pat
property_show_line_numbers() = Singleton::Config::source()->show_line_numbers;
if(Singleton::Config::source()->font.size()>0)
override_font(Pango::FontDescription(Singleton::Config::source()->font));
tab_char=Singleton::Config::source()->default_tab_char;
tab_size=Singleton::Config::source()->default_tab_size;
if(Singleton::Config::source()->auto_tab_char_and_size) {
auto tab_char_and_size=find_tab_char_and_size();
if(tab_char_and_size.first!=0) {
if(tab_char!=tab_char_and_size.first || tab_size!=tab_char_and_size.second) {
std::string tab_str;
if(tab_char_and_size.first==' ')
tab_str="<space>";
else
tab_str="<tab>";
Singleton::terminal()->print("Tab char and size for file "+file_path.string()+" set to: "+tab_str+", "+boost::lexical_cast<std::string>(tab_char_and_size.second)+".\n");
}
tab_char=tab_char_and_size.first;
tab_size=tab_char_and_size.second;
}
}
for(unsigned c=0;c<tab_size;c++)
tab+=tab_char;
tabs_regex=std::regex(std::string("^(")+tab_char+"*)(.*)$");
}
void Source::View::search_occurrences_updated(GtkWidget* widget, GParamSpec* property, gpointer data) {
@ -151,11 +173,10 @@ void Source::View::replace_all(const std::string &replacement) {
void Source::View::paste() {
Gtk::Clipboard::get()->request_text([this](const Glib::ustring& text){
const std::regex spaces_regex(std::string("^(")+Singleton::Config::source()->tab_char+"*)(.*)$");
auto line=get_line_before_insert();
std::smatch sm;
std::string prefix_tabs;
if(!get_buffer()->get_has_selection() && std::regex_match(line, sm, spaces_regex) && sm[2].str().size()==0) {
if(!get_buffer()->get_has_selection() && std::regex_match(line, sm, tabs_regex) && sm[2].str().size()==0) {
prefix_tabs=sm[1].str();
Glib::ustring::size_type start_line=0;
@ -177,7 +198,7 @@ void Source::View::paste() {
std::string line=text.substr(start_line, end_line-start_line);
size_t tabs=0;
for(auto chr: line) {
if(chr==Singleton::Config::source()->tab_char)
if(chr==tab_char)
tabs++;
else
break;
@ -260,21 +281,19 @@ string Source::View::get_line_before_insert() {
//Basic indentation
bool Source::View::on_key_press_event(GdkEventKey* key) {
get_source_buffer()->begin_user_action();
auto config=Singleton::Config::source();
const std::regex spaces_regex(std::string("^(")+config->tab_char+"*).*$");
//Indent as in next or previous line
if(key->keyval==GDK_KEY_Return && key->state==0 && !get_buffer()->get_has_selection()) {
auto insert_it=get_buffer()->get_insert()->get_iter();
int line_nr=insert_it.get_line();
auto line=get_line_before_insert();
std::smatch sm;
if(std::regex_match(line, sm, spaces_regex)) {
if(std::regex_match(line, sm, tabs_regex)) {
if((line_nr+1)<get_buffer()->get_line_count()) {
string next_line=get_line(line_nr+1);
auto line_end_iter=get_buffer()->get_iter_at_line(line_nr+1);
line_end_iter--;
std::smatch sm2;
if(insert_it==line_end_iter && std::regex_match(next_line, sm2, spaces_regex)) {
if(insert_it==line_end_iter && std::regex_match(next_line, sm2, tabs_regex)) {
if(sm2[1].str().size()>sm[1].str().size()) {
get_source_buffer()->insert_at_cursor("\n"+sm2[1].str());
scroll_to(get_source_buffer()->get_insert());
@ -297,7 +316,7 @@ bool Source::View::on_key_press_event(GdkEventKey* key) {
int line_end=selection_end.get_line();
for(int line=line_start;line<=line_end;line++) {
Gtk::TextIter line_it = get_source_buffer()->get_iter_at_line(line);
get_source_buffer()->insert(line_it, config->tab);
get_source_buffer()->insert(line_it, tab);
}
get_source_buffer()->end_user_action();
return true;
@ -309,11 +328,11 @@ bool Source::View::on_key_press_event(GdkEventKey* key) {
int line_start=selection_start.get_line();
int line_end=selection_end.get_line();
unsigned indent_left_steps=config->tab_size;
unsigned indent_left_steps=tab_size;
for(int line_nr=line_start;line_nr<=line_end;line_nr++) {
string line=get_line(line_nr);
std::smatch sm;
if(std::regex_match(line, sm, spaces_regex) && sm[1].str().size()>0) {
if(std::regex_match(line, sm, tabs_regex) && sm[1].str().size()>0) {
indent_left_steps=std::min(indent_left_steps, (unsigned)sm[1].str().size());
}
else {
@ -339,12 +358,12 @@ bool Source::View::on_key_press_event(GdkEventKey* key) {
int line_nr=insert_it.get_line();
auto line=get_line_before_insert();
std::smatch sm;
if(std::regex_match(line, sm, spaces_regex) && sm[1].str().size()==line.size()) {
if(std::regex_match(line, sm, tabs_regex) && sm[1].str().size()==line.size()) {
if((line_nr-1)>=0) {
string previous_line=get_line(line_nr-1);
std::smatch sm2;
if(std::regex_match(previous_line, sm2, spaces_regex)) {
if(line.size()==sm2[1].str().size() || line.size()==sm2[1].str().size()+config->tab_size || line.size()==sm2[1].str().size()-config->tab_size) {
if(std::regex_match(previous_line, sm2, tabs_regex)) {
if(line.size()==sm2[1].str().size() || line.size()==sm2[1].str().size()+tab_size || line.size()==sm2[1].str().size()-tab_size) {
auto previous_line_end_it=insert_it;
for(unsigned c=0;c<line.size();c++)
previous_line_end_it--;
@ -355,9 +374,9 @@ bool Source::View::on_key_press_event(GdkEventKey* key) {
}
}
}
if(line.size()>=config->tab_size) {
if(line.size()>=tab_size) {
auto insert_minus_tab_it=insert_it;
for(unsigned c=0;c<config->tab_size;c++)
for(unsigned c=0;c<tab_size;c++)
insert_minus_tab_it--;
get_source_buffer()->erase(insert_minus_tab_it, insert_it);
get_source_buffer()->end_user_action();
@ -371,6 +390,58 @@ bool Source::View::on_key_press_event(GdkEventKey* key) {
return stop;
}
std::pair<char, unsigned> Source::View::find_tab_char_and_size() {
const std::regex indent_regex("^([ \t]+).*$");
auto size=get_buffer()->get_line_count();
std::unordered_map<char, size_t> tab_chars;
std::unordered_map<unsigned, size_t> tab_sizes;
unsigned last_tab_size=0;
for(int c=0;c<size;c++) {
auto line=get_line(c);
std::smatch sm;
if(std::regex_match(line, sm, indent_regex)) {
auto str=sm[1].str();
long tab_diff=abs(static_cast<long>(str.size()-last_tab_size));
if(tab_diff>0) {
unsigned tab_diff_unsigned=static_cast<unsigned>(tab_diff);
auto it_size=tab_sizes.find(tab_diff_unsigned);
if(it_size!=tab_sizes.end())
it_size->second++;
else
tab_sizes[tab_diff_unsigned]=1;
}
last_tab_size=str.size();
if(str.size()>0) {
auto it_char=tab_chars.find(str[0]);
if(it_char!=tab_chars.end())
it_char->second++;
else
tab_chars[str[0]]=1;
}
}
}
char found_tab_char=0;
size_t occurences=0;
for(auto &tab_char: tab_chars) {
if(tab_char.second>occurences) {
found_tab_char=tab_char.first;
occurences=tab_char.second;
}
}
unsigned found_tab_size=0;
occurences=0;
for(auto &tab_size: tab_sizes) {
if(tab_size.second>occurences) {
found_tab_size=tab_size.first;
occurences=tab_size.second;
}
}
return {found_tab_char, found_tab_size};
}
/////////////////////
//// GenericView ////
/////////////////////
@ -509,6 +580,10 @@ Source::View(file_path), project_path(project_path) {
});
get_buffer()->signal_mark_set().connect(sigc::mem_fun(*this, &Source::ClangViewParse::on_mark_set), false);
bracket_regex=std::regex(std::string("^(")+tab_char+"*).*\\{ *$");
no_bracket_statement_regex=std::regex(std::string("^(")+tab_char+"*)(if|for|else if|catch|while) *\\(.*[^;}] *$");
no_bracket_no_para_statement_regex=std::regex(std::string("^(")+tab_char+"*)(else|try|do) *$");
}
void Source::ClangViewParse::init_parse() {
@ -658,8 +733,24 @@ void Source::ClangViewParse::update_diagnostics() {
auto diagnostics=clang_tu->get_diagnostics();
for(auto &diagnostic: diagnostics) {
if(diagnostic.path==file_path.string()) {
auto start=get_buffer()->get_iter_at_line_index(diagnostic.offsets.first.line-1, diagnostic.offsets.first.index-1);
auto end=get_buffer()->get_iter_at_line_index(diagnostic.offsets.second.line-1, diagnostic.offsets.second.index-1);
auto start_line=get_line(diagnostic.offsets.first.line-1); //index is sometimes off the line
auto start_line_index=diagnostic.offsets.first.index-1;
if(start_line_index>=start_line.size()) {
if(start_line.size()==0)
start_line_index=0;
else
start_line_index=start_line.size()-1;
}
auto end_line=get_line(diagnostic.offsets.second.line-1); //index is sometimes off the line
auto end_line_index=diagnostic.offsets.second.index-1;
if(end_line_index>=end_line.size()) {
if(end_line.size()==0)
end_line_index=0;
else
end_line_index=end_line.size()-1;
}
auto start=get_buffer()->get_iter_at_line_index(diagnostic.offsets.first.line-1, start_line_index);
auto end=get_buffer()->get_iter_at_line_index(diagnostic.offsets.second.line-1, end_line_index);
std::string diagnostic_tag_name;
if(diagnostic.severity<=CXDiagnostic_Warning)
diagnostic_tag_name="def:warning";
@ -765,11 +856,6 @@ bool Source::ClangViewParse::on_key_press_event(GdkEventKey* key) {
return Source::View::on_key_press_event(key);
}
get_source_buffer()->begin_user_action();
auto config=Singleton::Config::source();
const std::regex bracket_regex(std::string("^(")+config->tab_char+"*).*\\{ *$");
const std::regex no_bracket_statement_regex(std::string("^(")+config->tab_char+"*)(if|for|else if|catch|while) *\\(.*[^;}] *$");
const std::regex no_bracket_no_para_statement_regex(std::string("^(")+config->tab_char+"*)(else|try|do) *$");
const std::regex spaces_regex(std::string("^(")+config->tab_char+"*).*$");
//Indent depending on if/else/etc and brackets
if(key->keyval==GDK_KEY_Return && key->state==0) {
@ -780,18 +866,18 @@ bool Source::ClangViewParse::on_key_press_event(GdkEventKey* key) {
if((line_nr+1)<get_source_buffer()->get_line_count()) {
string next_line=get_line(line_nr+1);
std::smatch sm2;
if(std::regex_match(next_line, sm2, spaces_regex)) {
if(sm2[1].str()==sm[1].str()+config->tab) {
get_source_buffer()->insert_at_cursor("\n"+sm[1].str()+config->tab);
if(std::regex_match(next_line, sm2, tabs_regex)) {
if(sm2[1].str()==sm[1].str()+tab) {
get_source_buffer()->insert_at_cursor("\n"+sm[1].str()+tab);
scroll_to(get_source_buffer()->get_insert());
get_source_buffer()->end_user_action();
return true;
}
}
if(next_line!=sm[1].str()+"}") {
get_source_buffer()->insert_at_cursor("\n"+sm[1].str()+config->tab+"\n"+sm[1].str()+"}");
get_source_buffer()->insert_at_cursor("\n"+sm[1].str()+tab+"\n"+sm[1].str()+"}");
auto insert_it = get_source_buffer()->get_insert()->get_iter();
for(size_t c=0;c<config->tab_size+sm[1].str().size();c++)
for(size_t c=0;c<sm[1].str().size()+2;c++)
insert_it--;
scroll_to(get_source_buffer()->get_insert());
get_source_buffer()->place_cursor(insert_it);
@ -799,7 +885,7 @@ bool Source::ClangViewParse::on_key_press_event(GdkEventKey* key) {
return true;
}
else {
get_source_buffer()->insert_at_cursor("\n"+sm[1].str()+config->tab);
get_source_buffer()->insert_at_cursor("\n"+sm[1].str()+tab);
scroll_to(get_source_buffer()->get_insert());
get_source_buffer()->end_user_action();
return true;
@ -807,21 +893,21 @@ bool Source::ClangViewParse::on_key_press_event(GdkEventKey* key) {
}
}
else if(std::regex_match(line, sm, no_bracket_statement_regex)) {
get_source_buffer()->insert_at_cursor("\n"+sm[1].str()+config->tab);
get_source_buffer()->insert_at_cursor("\n"+sm[1].str()+tab);
scroll_to(get_source_buffer()->get_insert());
get_source_buffer()->end_user_action();
return true;
}
else if(std::regex_match(line, sm, no_bracket_no_para_statement_regex)) {
get_source_buffer()->insert_at_cursor("\n"+sm[1].str()+config->tab);
get_source_buffer()->insert_at_cursor("\n"+sm[1].str()+tab);
scroll_to(get_source_buffer()->get_insert());
get_source_buffer()->end_user_action();
return true;
}
else if(std::regex_match(line, sm, spaces_regex)) {
else if(std::regex_match(line, sm, tabs_regex)) {
std::smatch sm2;
size_t line_nr=get_source_buffer()->get_insert()->get_iter().get_line();
if(line_nr>0 && sm[1].str().size()>=config->tab_size) {
if(line_nr>0 && sm[1].str().size()>=tab_size) {
string previous_line=get_line(line_nr-1);
if(!std::regex_match(previous_line, sm2, bracket_regex)) {
if(std::regex_match(previous_line, sm2, no_bracket_statement_regex)) {
@ -843,23 +929,25 @@ bool Source::ClangViewParse::on_key_press_event(GdkEventKey* key) {
//Indent left when writing } on a new line
else if(key->keyval==GDK_KEY_braceright) {
string line=get_line_before_insert();
if(line.size()>=config->tab_size) {
if(line.size()>=tab_size) {
for(auto c: line) {
if(c!=config->tab_char) {
if(c!=tab_char) {
get_source_buffer()->insert_at_cursor("}");
get_source_buffer()->end_user_action();
return Source::View::on_key_press_event(key);
return true;
}
}
Gtk::TextIter insert_it = get_source_buffer()->get_insert()->get_iter();
Gtk::TextIter line_it = get_source_buffer()->get_iter_at_line(insert_it.get_line());
Gtk::TextIter line_plus_it=line_it;
for(unsigned c=0;c<config->tab_size;c++)
for(unsigned c=0;c<tab_size;c++)
line_plus_it++;
get_source_buffer()->erase(line_it, line_plus_it);
}
get_source_buffer()->insert_at_cursor("}");
get_source_buffer()->end_user_action();
return Source::View::on_key_press_event(key);
return true;
}
get_source_buffer()->end_user_action();

17
src/source.h

@ -14,6 +14,7 @@
#include "tooltips.h"
#include "selectiondialog.h"
#include <set>
#include <regex>
namespace Source {
Glib::RefPtr<Gsv::Language> guess_language(const boost::filesystem::path &file_path);
@ -22,9 +23,9 @@ namespace Source {
public:
std::string style;
std::string font;
unsigned tab_size;
char tab_char;
std::string tab;
bool auto_tab_char_and_size;
char default_tab_char;
unsigned default_tab_size;
bool highlight_current_line;
bool show_line_numbers;
std::unordered_map<std::string, std::string> clang_types;
@ -79,6 +80,12 @@ namespace Source {
std::string get_line_before_insert();
bool on_key_press_event(GdkEventKey* key);
std::pair<char, unsigned> find_tab_char_and_size();
unsigned tab_size;
char tab_char;
std::string tab;
std::regex tabs_regex;
private:
GtkSourceSearchContext *search_context;
GtkSourceSearchSettings *search_settings;
@ -109,6 +116,10 @@ namespace Source {
std::shared_ptr<Terminal::InProgress> parsing_in_progress;
std::thread parse_thread;
std::atomic<bool> parse_thread_stop;
std::regex bracket_regex;
std::regex no_bracket_statement_regex;
std::regex no_bracket_no_para_statement_regex;
private:
std::map<std::string, std::string> get_buffer_map() const;
// inits the syntax highligthing on file open

153
src/window.cc

@ -91,7 +91,7 @@ Window::Window() : box(Gtk::ORIENTATION_VERTICAL), notebook(directories), compil
if(auto menu_item=dynamic_cast<Gtk::MenuItem*>(menu.ui_manager->get_widget("/MenuBar/SourceMenu/SourceRename")))
menu_item->set_sensitive((bool)notebook.get_current_view()->rename_similar_tokens);
directories.select_path(notebook.get_current_view()->file_path);
directories.select(notebook.get_current_view()->file_path);
Singleton::status()->set_text(notebook.get_current_view()->status);
}
@ -100,10 +100,6 @@ Window::Window() : box(Gtk::ORIENTATION_VERTICAL), notebook(directories), compil
entry_box.hide();
});
compile_success.connect([this](){
directories.open_folder();
});
INFO("Window created");
} // Window constructor
@ -112,20 +108,23 @@ void Window::create_menu() {
menu.action_group->add(Gtk::Action::create("FileQuit", "Quit juCi++"), Gtk::AccelKey(menu.key_map["quit"]), [this]() {
hide();
});
menu.action_group->add(Gtk::Action::create("FileNewFile", "New file"), Gtk::AccelKey(menu.key_map["new_file"]), [this]() {
new_file_entry();
menu.action_group->add(Gtk::Action::create("FileNewFile", "New File"), Gtk::AccelKey(menu.key_map["new_file"]), [this]() {
new_file_dialog();
});
menu.action_group->add(Gtk::Action::create("FileNewFolder", "New Folder"), Gtk::AccelKey(menu.key_map["new_folder"]), [this]() {
new_folder_dialog();
});
menu.action_group->add(Gtk::Action::create("FileNewProject", "New Project"));
menu.action_group->add(Gtk::Action::create("FileNewProjectCpp", "C++"), [this]() {
new_cpp_project_dialog();
});
menu.action_group->add(Gtk::Action::create("FileOpenFile", "Open file"), Gtk::AccelKey(menu.key_map["open_file"]), [this]() {
menu.action_group->add(Gtk::Action::create("FileOpenFile", "Open File"), Gtk::AccelKey(menu.key_map["open_file"]), [this]() {
open_file_dialog();
});
menu.action_group->add(Gtk::Action::create("FileOpenFolder", "Open folder"), Gtk::AccelKey(menu.key_map["open_folder"]), [this]() {
menu.action_group->add(Gtk::Action::create("FileOpenFolder", "Open Folder"), Gtk::AccelKey(menu.key_map["open_folder"]), [this]() {
open_folder_dialog();
});
menu.action_group->add(Gtk::Action::create("FileSaveAs", "Save as"), Gtk::AccelKey(menu.key_map["save_as"]), [this]() {
menu.action_group->add(Gtk::Action::create("FileSaveAs", "Save As"), Gtk::AccelKey(menu.key_map["save_as"]), [this]() {
save_file_dialog();
});
@ -178,17 +177,17 @@ void Window::create_menu() {
INFO("Done Redo");
});
menu.action_group->add(Gtk::Action::create("SourceGotoLine", "Go to line"), Gtk::AccelKey(menu.key_map["source_goto_line"]), [this]() {
menu.action_group->add(Gtk::Action::create("SourceGotoLine", "Go to Line"), Gtk::AccelKey(menu.key_map["source_goto_line"]), [this]() {
goto_line_entry();
});
menu.action_group->add(Gtk::Action::create("SourceCenterCursor", "Center cursor"), Gtk::AccelKey(menu.key_map["source_center_cursor"]), [this]() {
menu.action_group->add(Gtk::Action::create("SourceCenterCursor", "Center Cursor"), Gtk::AccelKey(menu.key_map["source_center_cursor"]), [this]() {
if(notebook.get_current_page()!=-1) {
while(gtk_events_pending())
gtk_main_iteration();
notebook.get_current_view()->scroll_to(notebook.get_current_view()->get_buffer()->get_insert(), 0.0, 1.0, 0.5);
}
});
menu.action_group->add(Gtk::Action::create("SourceGotoDeclaration", "Go to declaration"), Gtk::AccelKey(menu.key_map["source_goto_declaration"]), [this]() {
menu.action_group->add(Gtk::Action::create("SourceGotoDeclaration", "Go to Declaration"), Gtk::AccelKey(menu.key_map["source_goto_declaration"]), [this]() {
if(notebook.get_current_page()!=-1) {
if(notebook.get_current_view()->get_declaration_location) {
auto location=notebook.get_current_view()->get_declaration_location();
@ -202,7 +201,7 @@ void Window::create_menu() {
}
}
});
menu.action_group->add(Gtk::Action::create("SourceGotoMethod", "Go to method"), Gtk::AccelKey(menu.key_map["source_goto_method"]), [this]() {
menu.action_group->add(Gtk::Action::create("SourceGotoMethod", "Go to Method"), Gtk::AccelKey(menu.key_map["source_goto_method"]), [this]() {
if(notebook.get_current_page()!=-1) {
if(notebook.get_current_view()->goto_method) {
notebook.get_current_view()->goto_method();
@ -213,11 +212,10 @@ void Window::create_menu() {
rename_token_entry();
});
menu.action_group->add(Gtk::Action::create("ProjectCompileAndRun", "Compile And Run"), Gtk::AccelKey(menu.key_map["compile_and_run"]), [this]() {
menu.action_group->add(Gtk::Action::create("ProjectCompileAndRun", "Compile and Run"), Gtk::AccelKey(menu.key_map["compile_and_run"]), [this]() {
if(notebook.get_current_page()==-1 || compiling)
return;
CMake cmake(notebook.get_current_view()->file_path);
directories.open_folder();
auto executables = cmake.get_functions_parameters("add_executable");
boost::filesystem::path executable_path;
if(executables.size()>0 && executables[0].second.size()>0) {
@ -233,7 +231,6 @@ void Window::create_menu() {
Singleton::terminal()->async_execute(Singleton::Config::terminal()->make_command, cmake.project_path, [this, executable_path, project_path](int exit_code){
compiling=false;
if(exit_code==EXIT_SUCCESS) {
compile_success();
//TODO: Windows...
auto executable_path_spaces_fixed=executable_path.string();
char last_char=0;
@ -261,15 +258,12 @@ void Window::create_menu() {
if(notebook.get_current_page()==-1 || compiling)
return;
CMake cmake(notebook.get_current_view()->file_path);
directories.open_folder();
if(cmake.project_path!="") {
compiling=true;
Singleton::terminal()->print("Compiling project "+cmake.project_path.string()+"\n");
//TODO: Windows...
Singleton::terminal()->async_execute(Singleton::Config::terminal()->make_command, cmake.project_path, [this](int exit_code){
compiling=false;
if(exit_code==EXIT_SUCCESS)
compile_success();
});
}
});
@ -298,7 +292,7 @@ void Window::create_menu() {
Singleton::terminal()->kill_last_async_execute(true);
});
menu.action_group->add(Gtk::Action::create("WindowCloseTab", "Close tab"), Gtk::AccelKey(menu.key_map["close_tab"]), [this]() {
menu.action_group->add(Gtk::Action::create("WindowCloseTab", "Close Tab"), Gtk::AccelKey(menu.key_map["close_tab"]), [this]() {
notebook.close_current_page();
});
add_accel_group(menu.ui_manager->get_accel_group());
@ -355,35 +349,62 @@ void Window::hide() {
Gtk::Window::hide();
}
void Window::new_file_entry() {
entry_box.clear();
entry_box.entries.emplace_back("untitled", [this](const std::string& content){
std::string filename=content;
if(filename!="") {
if(directories.current_path!="" && !boost::filesystem::path(filename).is_absolute())
filename=directories.current_path.string()+"/"+filename;
boost::filesystem::path p(filename);
if(boost::filesystem::exists(p)) {
Singleton::terminal()->print("Error: "+p.string()+" already exists.\n");
void Window::new_file_dialog() {
Gtk::FileChooserDialog dialog("Please create a new file", Gtk::FILE_CHOOSER_ACTION_SAVE);
if(directories.current_path!="")
gtk_file_chooser_set_current_folder((GtkFileChooser*)dialog.gobj(), directories.current_path.string().c_str());
else
gtk_file_chooser_set_current_folder((GtkFileChooser*)dialog.gobj(), boost::filesystem::current_path().string().c_str());
dialog.set_transient_for(*this);
dialog.set_position(Gtk::WindowPosition::WIN_POS_CENTER_ALWAYS);
dialog.add_button("Cancel", Gtk::RESPONSE_CANCEL);
dialog.add_button("Save", Gtk::RESPONSE_OK);
int result = dialog.run();
if(result==Gtk::RESPONSE_OK) {
boost::filesystem::path path = dialog.get_filename();
if(path!="") {
if(boost::filesystem::exists(path)) {
Singleton::terminal()->print("Error: "+path.string()+" already exists.\n");
}
else {
if(juci::filesystem::write(p)) {
if(juci::filesystem::write(path)) {
if(directories.current_path!="")
directories.open_folder();
notebook.open(boost::filesystem::canonical(p).string());
Singleton::terminal()->print("New file "+p.string()+" created.\n");
directories.update();
notebook.open(path.string());
Singleton::terminal()->print("New file "+path.string()+" created.\n");
}
else
Singleton::terminal()->print("Error: could not create new file "+p.string()+".\n");
Singleton::terminal()->print("Error: could not create new file "+path.string()+".\n");
}
}
entry_box.hide();
});
auto entry_it=entry_box.entries.begin();
entry_box.buttons.emplace_back("Create file", [this, entry_it](){
entry_it->activate();
});
entry_box.show();
}
}
void Window::new_folder_dialog() {
auto time_now=std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
Gtk::FileChooserDialog dialog("Please create a new folder", Gtk::FILE_CHOOSER_ACTION_CREATE_FOLDER);
if(directories.current_path!="")
gtk_file_chooser_set_current_folder((GtkFileChooser*)dialog.gobj(), directories.current_path.string().c_str());
else
gtk_file_chooser_set_current_folder((GtkFileChooser*)dialog.gobj(), boost::filesystem::current_path().string().c_str());
dialog.set_transient_for(*this);
dialog.set_position(Gtk::WindowPosition::WIN_POS_CENTER_ALWAYS);
dialog.add_button("Cancel", Gtk::RESPONSE_CANCEL);
dialog.add_button("Create", Gtk::RESPONSE_OK);
int result = dialog.run();
if(result==Gtk::RESPONSE_OK) {
boost::filesystem::path path=dialog.get_filename();
if(boost::filesystem::last_write_time(path)>=time_now) {
if(directories.current_path!="")
directories.update();
Singleton::terminal()->print("New folder "+path.string()+" created.\n");
}
else
Singleton::terminal()->print("Error: "+path.string()+" already exists.\n");
directories.select(path);
}
}
void Window::new_cpp_project_dialog() {
@ -393,9 +414,9 @@ void Window::new_cpp_project_dialog() {
else
gtk_file_chooser_set_current_folder((GtkFileChooser*)dialog.gobj(), boost::filesystem::current_path().string().c_str());
dialog.set_transient_for(*this);
dialog.add_button("_Cancel", Gtk::RESPONSE_CANCEL);
dialog.add_button("Select", Gtk::RESPONSE_OK);
dialog.set_position(Gtk::WindowPosition::WIN_POS_CENTER_ALWAYS);
dialog.add_button("Cancel", Gtk::RESPONSE_CANCEL);
dialog.add_button("Create", Gtk::RESPONSE_OK);
int result = dialog.run();
if(result==Gtk::RESPONSE_OK) {
@ -420,7 +441,7 @@ void Window::new_cpp_project_dialog() {
std::string cmakelists="cmake_minimum_required(VERSION 2.8)\n\nproject("+project_name+")\n\nset(CMAKE_CXX_FLAGS \"${CMAKE_CXX_FLAGS} -std=c++1y -Wall\")\n\nadd_executable("+project_name+" main.cpp)\n";
std::string cpp_main="#include <iostream>\n\nusing namespace std;\n\nint main() {\n cout << \"Hello World!\" << endl;\n\n return 0;\n}\n";
if(juci::filesystem::write(cmakelists_path, cmakelists) && juci::filesystem::write(cpp_main_path, cpp_main)) {
directories.open_folder(project_path);
directories.open(project_path);
notebook.open(cpp_main_path);
Singleton::terminal()->print("C++ project "+project_name+" created.\n");
}
@ -436,15 +457,15 @@ void Window::open_folder_dialog() {
else
gtk_file_chooser_set_current_folder((GtkFileChooser*)dialog.gobj(), boost::filesystem::current_path().string().c_str());
dialog.set_transient_for(*this);
//Add response buttons the the dialog:
dialog.add_button("_Cancel", Gtk::RESPONSE_CANCEL);
dialog.add_button("Select", Gtk::RESPONSE_OK);
dialog.set_position(Gtk::WindowPosition::WIN_POS_CENTER_ALWAYS);
dialog.add_button("Cancel", Gtk::RESPONSE_CANCEL);
dialog.add_button("Open", Gtk::RESPONSE_OK);
int result = dialog.run();
if(result==Gtk::RESPONSE_OK) {
std::string project_path=dialog.get_filename();
directories.open_folder(project_path);
directories.open(project_path);
}
}
@ -456,28 +477,8 @@ void Window::open_file_dialog() {
gtk_file_chooser_set_current_folder((GtkFileChooser*)dialog.gobj(), boost::filesystem::current_path().string().c_str());
dialog.set_transient_for(*this);
dialog.set_position(Gtk::WindowPosition::WIN_POS_CENTER_ALWAYS);
//Add response buttons the the dialog:
dialog.add_button("_Cancel", Gtk::RESPONSE_CANCEL);
dialog.add_button("_Open", Gtk::RESPONSE_OK);
//Add filters, so that only certain file types can be selected:
Glib::RefPtr<Gtk::FileFilter> filter_text = Gtk::FileFilter::create();
filter_text->set_name("Text files");
filter_text->add_mime_type("text/plain");
dialog.add_filter(filter_text);
Glib::RefPtr<Gtk::FileFilter> filter_cpp = Gtk::FileFilter::create();
filter_cpp->set_name("C/C++ files");
filter_cpp->add_mime_type("text/x-c");
filter_cpp->add_mime_type("text/x-c++");
filter_cpp->add_mime_type("text/x-c-header");
dialog.add_filter(filter_cpp);
Glib::RefPtr<Gtk::FileFilter> filter_any = Gtk::FileFilter::create();
filter_any->set_name("Any files");
filter_any->add_pattern("*");
dialog.add_filter(filter_any);
dialog.add_button("Cancel", Gtk::RESPONSE_CANCEL);
dialog.add_button("Open", Gtk::RESPONSE_OK);
int result = dialog.run();
@ -494,8 +495,8 @@ void Window::save_file_dialog() {
Gtk::FileChooserDialog dialog(*this, "Please choose a file", Gtk::FILE_CHOOSER_ACTION_SAVE);
gtk_file_chooser_set_filename((GtkFileChooser*)dialog.gobj(), notebook.get_current_view()->file_path.c_str());
dialog.set_position(Gtk::WindowPosition::WIN_POS_CENTER_ALWAYS);
dialog.add_button("_Cancel", Gtk::RESPONSE_CANCEL);
dialog.add_button("_Save", Gtk::RESPONSE_OK);
dialog.add_button("Cancel", Gtk::RESPONSE_CANCEL);
dialog.add_button("Save", Gtk::RESPONSE_OK);
int result = dialog.run();
if(result==Gtk::RESPONSE_OK) {
@ -506,7 +507,7 @@ void Window::save_file_dialog() {
file << notebook.get_current_view()->get_buffer()->get_text();
file.close();
if(directories.current_path!="")
directories.open_folder();
directories.update();
notebook.open(path);
Singleton::terminal()->print("File saved to: " + notebook.get_current_view()->file_path.string()+"\n");
}

4
src/window.h

@ -36,11 +36,11 @@ private:
EntryBox entry_box;
Menu menu;
std::atomic<bool> compiling;
Glib::Dispatcher compile_success;
void create_menu();
void hide();
void new_file_entry();
void new_file_dialog();
void new_folder_dialog();
void new_cpp_project_dialog();
void open_folder_dialog();
void open_file_dialog();

Loading…
Cancel
Save