Browse Source

Merged theme and eidheim

merge-requests/365/head
Jørgen Lien Sellæg 10 years ago
parent
commit
10154d6205
  1. 2
      docs/install.md
  2. 4
      src/CMakeLists.txt
  3. 263
      src/cmake.cc
  4. 28
      src/cmake.h
  5. 12
      src/config.cc
  6. 3
      src/config.h
  7. 94
      src/directories.cc
  8. 8
      src/directories.h
  9. 21
      src/files.h
  10. 1
      src/juci.cc
  11. 47
      src/notebook.cc
  12. 7
      src/notebook.h
  13. 1
      src/singletons.cc
  14. 2
      src/singletons.h
  15. 75
      src/source.cc
  16. 10
      src/source.h
  17. 262
      src/terminal.cc
  18. 35
      src/terminal.h
  19. 109
      src/window.cc
  20. 8
      src/window.h

2
docs/install.md

@ -6,7 +6,7 @@ First dependencies:
```sh ```sh
$ sudo apt-get install libboost-python-dev libboost-filesystem-dev libboost-log-dev libboost-test-dev $ sudo apt-get install libboost-python-dev libboost-filesystem-dev libboost-log-dev libboost-test-dev
libboost-thread-dev libboost-system-dev libgtkmm-3.0-dev libgtksourceview2.0-dev libgtksourceviewmm-3.0-dev libboost-thread-dev libboost-system-dev libgtkmm-3.0-dev libgtksourceview2.0-dev libgtksourceviewmm-3.0-dev
libpython-dev libclang-dev make cmake gcc libpython-dev libclang-dev make cmake gcc g++
``` ```
Install the project: Install the project:
```sh ```sh

4
src/CMakeLists.txt

@ -79,7 +79,9 @@ if(${validation})
tooltips.h tooltips.h
tooltips.cc tooltips.cc
singletons.h singletons.h
singletons.cc) singletons.cc
cmake.h
cmake.cc)
add_library(${module} SHARED add_library(${module} SHARED
api api

263
src/cmake.cc

@ -0,0 +1,263 @@
#include "cmake.h"
#include "sourcefile.h"
#include <regex>
#include "singletons.h"
#include <iostream> //TODO: remove
using namespace std; //TODO: remove
CMake::CMake(const boost::filesystem::path &path) {
const auto find_cmake_project=[this](const boost::filesystem::path &cmake_path) {
for(auto &line: juci::filesystem::read_lines(cmake_path)) {
const std::regex project_regex("^ *project *\\(.*$");
std::smatch sm;
if(std::regex_match(line, sm, project_regex)) {
return true;
}
}
return false;
};
auto search_path=boost::filesystem::path(path);
auto search_cmake_path=search_path;
search_cmake_path+="/CMakeLists.txt";
if(boost::filesystem::exists(search_cmake_path))
paths.emplace(paths.begin(), search_cmake_path);
if(find_cmake_project(search_cmake_path))
project_path=search_path;
else {
do {
search_path=search_path.parent_path();
search_cmake_path=search_path;
search_cmake_path+="/CMakeLists.txt";
if(boost::filesystem::exists(search_cmake_path))
paths.emplace(paths.begin(), search_cmake_path);
if(find_cmake_project(search_cmake_path)) {
project_path=search_path;
break;
}
} while(search_path!=search_path.root_directory());
}
if(project_path!="") {
if(boost::filesystem::exists(project_path.string()+"/CMakeLists.txt") && !boost::filesystem::exists(project_path.string()+"/compile_commands.json"))
create_compile_commands(project_path.string());
}
}
bool CMake::create_compile_commands(const std::string &path) {
Singleton::terminal()->print("Creating "+boost::filesystem::path(path+"/compile_commands.json").string()+"\n");
//TODO: Windows...
if(Singleton::terminal()->execute("cmake . -DCMAKE_EXPORT_COMPILE_COMMANDS=ON 2>&1", path)==EXIT_SUCCESS)
return true;
return false;
}
void CMake::read_files() {
for(auto &path: paths)
files.emplace_back(juci::filesystem::read(path));
}
void CMake::remove_tabs() {
for(auto &file: files) {
for(auto &chr: file) {
if(chr=='\t')
chr=' ';
}
}
}
void CMake::remove_comments() {
for(auto &file: files) {
size_t pos=0;
size_t comment_start;
bool inside_comment=false;
while(pos<file.size()) {
if(!inside_comment && file[pos]=='#') {
comment_start=pos;
inside_comment=true;
}
if(inside_comment && file[pos]=='\n') {
file.erase(comment_start, pos-comment_start);
pos-=pos-comment_start;
inside_comment=false;
}
pos++;
}
if(inside_comment)
file.erase(comment_start);
}
}
void CMake::remove_newlines_inside_parentheses() {
for(auto &file: files) {
size_t pos=0;
bool inside_para=false;
bool inside_quote=false;
char last_char=0;
while(pos<file.size()) {
if(!inside_quote && file[pos]=='"' && last_char!='\\')
inside_quote=true;
else if(inside_quote && file[pos]=='"' && last_char!='\\')
inside_quote=false;
else if(!inside_quote && file[pos]=='(')
inside_para=true;
else if(!inside_quote && file[pos]==')')
inside_para=false;
else if(inside_para && file[pos]=='\n')
file.replace(pos, 1, 1, ' ');
last_char=file[pos];
pos++;
}
}
}
void CMake::find_variables() {
for(auto &file: files) {
size_t pos=0;
while(pos<file.size()) {
auto start_line=pos;
auto end_line=file.find('\n', start_line);
if(end_line==std::string::npos)
end_line=file.size();
if(end_line>start_line) {
auto line=file.substr(start_line, end_line-start_line);
const std::regex set_regex("^ *set *\\( *([A-Za-z_][A-Za-z_0-9]*) +(.*)\\) *$");
std::smatch sm;
if(std::regex_match(line, sm, set_regex)) {
std::string data=sm[2];
while(data.size()>0 && data.back()==' ')
data.pop_back();
parse_variable_parameters(data);
variables[sm[1]]=data;
}
}
pos=end_line+1;
}
}
}
void CMake::parse_variable_parameters(std::string &data) {
size_t pos=0;
bool inside_quote=false;
char last_char=0;
while(pos<data.size()) {
if(!inside_quote && data[pos]=='"' && last_char!='\\') {
inside_quote=true;
data.erase(pos, 1); //TODO: instead remove quote-mark if pasted into a quote, for instance: "test${test}test"<-remove quotes from ${test}
pos--;
}
else if(inside_quote && data[pos]=='"' && last_char!='\\') {
inside_quote=false;
data.erase(pos, 1); //TODO: instead remove quote-mark if pasted into a quote, for instance: "test${test}test"<-remove quotes from ${test}
pos--;
}
else if(!inside_quote && data[pos]==' ' && pos+1<data.size() && data[pos+1]==' ') {
data.erase(pos, 1);
pos--;
}
last_char=data[pos];
pos++;
}
for(auto &var: variables) {
auto pos=data.find("${"+var.first+'}');
while(pos!=std::string::npos) {
data.replace(pos, var.first.size()+3, var.second);
pos=data.find("${"+var.first+'}');
}
}
//Remove variables we do not know:
pos=data.find("${");
auto pos_end=data.find("}", pos+2);
while(pos!=std::string::npos && pos_end!=std::string::npos) {
data.erase(pos, pos_end-pos+1);
pos=data.find("${");
pos_end=data.find("}", pos+2);
}
}
void CMake::parse() {
read_files();
remove_tabs();
remove_comments();
remove_newlines_inside_parentheses();
find_variables();
parsed=true;
}
std::vector<std::string> CMake::get_function_parameters(std::string &data) {
std::vector<std::string> parameters;
size_t pos=0;
size_t parameter_pos=0;
bool inside_quote=false;
char last_char=0;
while(pos<data.size()) {
if(!inside_quote && data[pos]=='"' && last_char!='\\') {
inside_quote=true;
data.erase(pos, 1);
pos--;
}
else if(inside_quote && data[pos]=='"' && last_char!='\\') {
inside_quote=false;
data.erase(pos, 1);
pos--;
}
else if(!inside_quote && pos+1<data.size() && data[pos]==' ' && data[pos+1]==' ') {
data.erase(pos, 1);
pos--;
}
else if(!inside_quote && data[pos]==' ') {
parameters.emplace_back(data.substr(parameter_pos, pos-parameter_pos));
if(pos+1<data.size())
parameter_pos=pos+1;
}
last_char=data[pos];
pos++;
}
parameters.emplace_back(data.substr(parameter_pos));
for(auto &var: variables) {
for(auto &parameter: parameters) {
auto pos=parameter.find("${"+var.first+'}');
while(pos!=std::string::npos) {
parameter.replace(pos, var.first.size()+3, var.second);
pos=parameter.find("${"+var.first+'}');
}
}
}
return parameters;
}
std::vector<std::pair<boost::filesystem::path, std::vector<std::string> > > CMake::get_functions_parameters(const std::string &name) {
if(!parsed)
parse();
std::vector<std::pair<boost::filesystem::path, std::vector<std::string> > > functions;
size_t file_c=0;
for(auto &file: files) {
size_t pos=0;
while(pos<file.size()) {
auto start_line=pos;
auto end_line=file.find('\n', start_line);
if(end_line==std::string::npos)
end_line=file.size();
if(end_line>start_line) {
auto line=file.substr(start_line, end_line-start_line);
const std::regex function_regex("^ *"+name+" *\\( *(.*)\\) *$");
std::smatch sm;
if(std::regex_match(line, sm, function_regex)) {
std::string data=sm[1];
while(data.size()>0 && data.back()==' ')
data.pop_back();
auto parameters=get_function_parameters(data);
functions.emplace(functions.begin(), paths[file_c], parameters);
}
}
pos=end_line+1;
}
file_c++;
}
return functions;
}

28
src/cmake.h

@ -0,0 +1,28 @@
#ifndef JUCI_CMAKE_H_
#define JUCI_CMAKE_H_
#include <boost/filesystem.hpp>
#include <vector>
#include <unordered_map>
class CMake {
public:
CMake(const boost::filesystem::path &path);
std::vector<std::pair<boost::filesystem::path, std::vector<std::string> > > get_functions_parameters(const std::string &name);
static bool create_compile_commands(const std::string &path);
std::vector<boost::filesystem::path> paths;
std::vector<std::string> files;
boost::filesystem::path project_path;
std::unordered_map<std::string, std::string> variables;
private:
void read_files();
void remove_tabs();
void remove_comments();
void remove_newlines_inside_parentheses();
void find_variables();
void parse_variable_parameters(std::string &data);
void parse();
std::vector<std::string> get_function_parameters(std::string &data);
bool parsed=false;
};
#endif //JUCI_CMAKE_H_

12
src/config.cc

@ -12,7 +12,6 @@ MainConfig::MainConfig() {
GenerateSource(); GenerateSource();
GenerateTheme(); GenerateTheme();
GenerateDirectoryFilter(); GenerateDirectoryFilter();
GenerateTerminalCommands();
} }
void MainConfig::GenerateTheme() { void MainConfig::GenerateTheme() {
@ -54,17 +53,6 @@ void MainConfig::GenerateSource() {
DEBUG("Source cfg fetched"); DEBUG("Source cfg fetched");
} }
void MainConfig::GenerateTerminalCommands() {
auto terminal_cfg=Singleton::Config::terminal();
boost::property_tree::ptree source_json = cfg.get_child("project");
boost::property_tree::ptree compile_commands_json = source_json.get_child("compile_commands");
boost::property_tree::ptree run_commands_json = source_json.get_child("run_commands");
for (auto &i : compile_commands_json)
terminal_cfg->compile_commands.emplace_back(i.second.get_value<std::string>());
for (auto &i : run_commands_json)
terminal_cfg->run_command=(i.second.get_value<std::string>()); //TODO: run_commands array->one run_command?
}
void MainConfig::GenerateDirectoryFilter() { void MainConfig::GenerateDirectoryFilter() {
auto dir_cfg=Singleton::Config::directories(); auto dir_cfg=Singleton::Config::directories();
DEBUG("Fetching directory filter"); DEBUG("Fetching directory filter");

3
src/config.h

@ -12,10 +12,7 @@ public:
void GenerateSource(); void GenerateSource();
void GenerateDirectoryFilter(); void GenerateDirectoryFilter();
void GenerateTheme(); void GenerateTheme();
void GenerateTerminalCommands();
private: private:
boost::property_tree::ptree cfg; boost::property_tree::ptree cfg;
}; };
#endif #endif

94
src/directories.cc

@ -5,6 +5,9 @@
#include <algorithm> #include <algorithm>
#include "boost/algorithm/string.hpp" #include "boost/algorithm/string.hpp"
#include <iostream> //TODO: remove
using namespace std; //TODO: remove
namespace sigc { namespace sigc {
SIGC_FUNCTORS_DEDUCE_RESULT_TYPE_WITH_DECLTYPE SIGC_FUNCTORS_DEDUCE_RESULT_TYPE_WITH_DECLTYPE
} }
@ -38,21 +41,39 @@ Directories::Directories() {
} }
void Directories::open_folder(const boost::filesystem::path& dir_path) { void Directories::open_folder(const boost::filesystem::path& dir_path) {
auto new_path=dir_path;
INFO("Open folder"); INFO("Open folder");
if(new_path=="") {
if(current_path=="")
return;
new_path=current_path;
}
std::vector<Gtk::TreeModel::Path> expanded_paths; std::vector<Gtk::TreeModel::Path> expanded_paths;
if(last_dir_path==dir_path) { if(current_path==new_path) {
tree_view.map_expanded_rows([&expanded_paths](Gtk::TreeView* tree_view, const Gtk::TreeModel::Path& path){ tree_view.map_expanded_rows([&expanded_paths](Gtk::TreeView* tree_view, const Gtk::TreeModel::Path& path){
expanded_paths.emplace_back(path); expanded_paths.emplace_back(path);
}); });
} }
tree_store->clear(); tree_store->clear();
tree_view.get_column(0)->set_title(get_cmakelists_variable(dir_path, "project"));
add_paths(dir_path, Gtk::TreeModel::Row(), 0); if(dir_path!="")
cmake=std::unique_ptr<CMake>(new CMake(new_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);
for(auto &path: expanded_paths) for(auto &path: expanded_paths)
tree_view.expand_row(path, false); tree_view.expand_row(path, false);
last_dir_path=dir_path;
current_path=new_path;
if(selected_path.size()>0)
select_path(selected_path);
DEBUG("Folder opened"); DEBUG("Folder opened");
} }
@ -62,6 +83,7 @@ void Directories::select_path(const std::string &path) {
auto tree_path=Gtk::TreePath(iter); auto tree_path=Gtk::TreePath(iter);
tree_view.expand_to_path(tree_path); tree_view.expand_to_path(tree_path);
tree_view.set_cursor(tree_path); tree_view.set_cursor(tree_path);
selected_path=path;
return true; return true;
} }
return false; return false;
@ -118,67 +140,3 @@ void Directories::add_paths(const boost::filesystem::path& dir_path, const Gtk::
} }
} }
} }
std::string Directories::get_cmakelists_variable(const boost::filesystem::path& dir_path, std::string command_name) {
INFO("fetches cmake variable value for: "+command_name);
std::string project_name;
std::string project_name_var;
boost::filesystem::directory_iterator end_itr;
for (boost::filesystem::directory_iterator itr( dir_path );itr != end_itr;++itr ) {
if (itr->path().filename().string() == "CMakeLists.txt") {
for (auto &line : juci::filesystem::read_lines(itr->path())) {
if (line.find(command_name+"(", 0) != std::string::npos
|| line.find(command_name+" (", 0) != std::string::npos ) {
size_t variable_start = line.find("{", 0);
size_t variable_end = line.find("}", variable_start);
project_name_var = line.substr(variable_start+1,
(variable_end)-variable_start-1);
boost::algorithm::trim(project_name_var);
if (variable_start == std::string::npos) { // not a variabel
variable_start = line.find("(", 0);
variable_end = line.find(' ', variable_start);
if(variable_end != std::string::npos){
return line.substr(variable_start+1,
(variable_end)-variable_start-1);
}
variable_end = line.find("#", variable_start);
if(variable_end != std::string::npos){
return line.substr(variable_start+1,
(variable_end)-variable_start-1);
}
variable_end = line.find(")", variable_start);
return line.substr(variable_start+1,
(variable_end)-variable_start-1);
if (variable_start == std::string::npos) { // not a variable
variable_start = line.find("(", 0);
variable_end = line.find(")", variable_start);
INFO("Wasn't a variable, returning value");
return line.substr(variable_start+1,
(variable_end)-variable_start-1);
}
break;
}
}
}
for (auto &line : juci::filesystem::read_lines(itr->path())) {
if (line.find("set(", 0) != std::string::npos
|| line.find("set (", 0) != std::string::npos) {
if( line.find(project_name_var, 0) != std::string::npos) {
size_t variable_start = line.find(project_name_var, 0)
+project_name_var.length();
size_t variable_end = line.find(")", variable_start);
project_name = line.substr(variable_start+1,
variable_end-variable_start-1);
boost::algorithm::trim(project_name);
INFO("found variable, returning value");
return project_name;
}
}
}
break;
}
}
INFO("Couldn't find value in CMakeLists.txt");
return "no project name";
}

8
src/directories.h

@ -5,6 +5,7 @@
#include <vector> #include <vector>
#include <string> #include <string>
#include "boost/filesystem.hpp" #include "boost/filesystem.hpp"
#include "cmake.h"
class Directories : public Gtk::ScrolledWindow { class Directories : public Gtk::ScrolledWindow {
public: public:
@ -27,11 +28,12 @@ public:
}; };
Directories(); Directories();
void open_folder(const boost::filesystem::path& dir_path); void open_folder(const boost::filesystem::path& dir_path="");
void select_path(const std::string &path); void select_path(const std::string &path);
std::string get_cmakelists_variable(const boost::filesystem::path& dir_path, std::string command_name);
std::function<void(const std::string &file)> on_row_activated; std::function<void(const std::string &file)> on_row_activated;
std::unique_ptr<CMake> cmake;
boost::filesystem::path current_path;
private: private:
void add_paths(const boost::filesystem::path& dir_path, const Gtk::TreeModel::Row &row, unsigned depth); void add_paths(const boost::filesystem::path& dir_path, const Gtk::TreeModel::Row &row, unsigned depth);
@ -39,7 +41,7 @@ private:
Gtk::TreeView tree_view; Gtk::TreeView tree_view;
Glib::RefPtr<Gtk::TreeStore> tree_store; Glib::RefPtr<Gtk::TreeStore> tree_store;
ColumnRecord column_record; ColumnRecord column_record;
boost::filesystem::path last_dir_path; std::string selected_path;
}; };
#endif // JUCI_DIRECTORIES_H_ #endif // JUCI_DIRECTORIES_H_

21
src/files.h

@ -68,27 +68,6 @@ const std::string configjson =
" \"cmakelists.txt\",\n" " \"cmakelists.txt\",\n"
" \"in-lowercase.pls\"\n" " \"in-lowercase.pls\"\n"
" ]\n" " ]\n"
" },\n"
" \"project\": {\n"
" \"run_commands\": [\n"
" \"./.build/\"\n"
" ],\n"
" \"compile_commands\": [\n"
" \"rm -rf ./.build\",\n"
" \"mkdir ./.build\",\n"
" \"cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -B./.build -H.\",\n"
" \"cd ./.build/; make\",\n"
" \"cp ./.build/compile_commands.json compile_commands.json\"\n"
" ]\n"
" },\n"
" \"example\": {\n"
" \"key\": \"value\",\n"
" \"key2\": [\n"
" \"val1\",\n"
" \"val2\",\n"
" 3\n"
" ],\n"
" \"key3\": \"value\"\n"
" }\n" " }\n"
"}\n"; "}\n";

1
src/juci.cc

@ -37,7 +37,6 @@ void app::on_activate() {
add_window(*window); add_window(*window);
window->show(); window->show();
if(directory!="") { if(directory!="") {
window->notebook.project_path=directory;
window->directories.open_folder(directory); window->directories.open_folder(directory);
} }
for(auto &f: files) for(auto &f: files)

47
src/notebook.cc

@ -3,6 +3,8 @@
#include "sourcefile.h" #include "sourcefile.h"
#include "singletons.h" #include "singletons.h"
#include <fstream> #include <fstream>
#include <regex>
#include "cmake.h"
#include <iostream> //TODO: remove #include <iostream> //TODO: remove
using namespace std; //TODO: remove using namespace std; //TODO: remove
@ -11,7 +13,7 @@ namespace sigc {
SIGC_FUNCTORS_DEDUCE_RESULT_TYPE_WITH_DECLTYPE SIGC_FUNCTORS_DEDUCE_RESULT_TYPE_WITH_DECLTYPE
} }
Notebook::Notebook() : Gtk::Notebook() { Notebook::Notebook(Directories &directories) : Gtk::Notebook(), directories(directories) {
Gsv::init(); Gsv::init();
} }
@ -50,19 +52,26 @@ void Notebook::open(std::string path) {
} }
can_read.close(); can_read.close();
auto tmp_project_path=project_path;
if(tmp_project_path=="") {
tmp_project_path=boost::filesystem::path(path).parent_path().string();
}
auto language=Source::guess_language(path); auto language=Source::guess_language(path);
if(language && (language->get_id()=="chdr" || language->get_id()=="c" || language->get_id()=="cpp" || language->get_id()=="objc")) { if(language && (language->get_id()=="chdr" || language->get_id()=="c" || language->get_id()=="cpp" || language->get_id()=="objc")) {
if(boost::filesystem::exists(tmp_project_path+"/CMakeLists.txt") && !boost::filesystem::exists(tmp_project_path+"/compile_commands.json")) { std::string project_path;
make_compile_commands(); if(directories.cmake && directories.cmake->project_path!="")
project_path=directories.cmake->project_path.string();
else {
auto parent_path=boost::filesystem::path(path).parent_path();
project_path=parent_path.string();
CMake cmake(parent_path);
if(cmake.project_path!="") {
project_path=cmake.project_path.string();
Singleton::terminal()->print("Project path for "+path+" set to "+project_path+"\n");
}
else
Singleton::terminal()->print("Error: could not find project path for "+path+"\n");
} }
source_views.emplace_back(new Source::ClangView(path, tmp_project_path)); source_views.emplace_back(new Source::ClangView(path, project_path));
} }
else else
source_views.emplace_back(new Source::GenericView(path, tmp_project_path, language)); source_views.emplace_back(new Source::GenericView(path, language));
scrolled_windows.emplace_back(new Gtk::ScrolledWindow()); scrolled_windows.emplace_back(new Gtk::ScrolledWindow());
hboxes.emplace_back(new Gtk::HBox()); hboxes.emplace_back(new Gtk::HBox());
@ -111,11 +120,13 @@ bool Notebook::save(int page) {
Singleton::terminal()->print("File saved to: " +view->file_path+"\n"); Singleton::terminal()->print("File saved to: " +view->file_path+"\n");
//If CMakeLists.txt have been modified: //If CMakeLists.txt have been modified:
//TODO: recreate cmake even without directories open?
if(boost::filesystem::path(view->file_path).filename().string()=="CMakeLists.txt") { if(boost::filesystem::path(view->file_path).filename().string()=="CMakeLists.txt") {
if(make_compile_commands()) { if(directories.cmake && directories.cmake->project_path!="" && boost::filesystem::path(view->file_path)>=directories.cmake->project_path && CMake::create_compile_commands(directories.cmake->project_path.string())) {
directories.open_folder();
for(auto source_view: source_views) { for(auto source_view: source_views) {
if(auto source_clang_view=dynamic_cast<Source::ClangView*>(source_view)) { if(auto source_clang_view=dynamic_cast<Source::ClangView*>(source_view)) {
if(project_path==source_view->project_path) { if(directories.cmake->project_path.string()==source_clang_view->project_path) {
if(source_clang_view->restart_parse()) if(source_clang_view->restart_parse())
Singleton::terminal()->print("Reparsing "+source_clang_view->file_path+"\n"); Singleton::terminal()->print("Reparsing "+source_clang_view->file_path+"\n");
else else
@ -133,18 +144,6 @@ bool Notebook::save(int page) {
return false; return false;
} }
bool Notebook::make_compile_commands() {
if(project_path.size()>0) {
Singleton::terminal()->print("Creating "+boost::filesystem::path(project_path+"/compile_commands.json").string()+"\n");
//TODO: Windows...
if(Singleton::terminal()->execute(project_path, "cmake . -DCMAKE_EXPORT_COMPILE_COMMANDS=ON 2>&1")) {
//TODO: refresh directories
return true;
}
}
return false;
}
bool Notebook::save_current() { bool Notebook::save_current() {
INFO("Notebook save current file"); INFO("Notebook save current file");
if(get_current_page()==-1) if(get_current_page()==-1)
@ -168,7 +167,7 @@ bool Notebook::close_current_page() {
scrolled_windows.erase(scrolled_windows.begin()+page); scrolled_windows.erase(scrolled_windows.begin()+page);
hboxes.erase(hboxes.begin()+page); hboxes.erase(hboxes.begin()+page);
if(auto source_clang_view=dynamic_cast<Source::ClangView*>(source_view)) if(auto source_clang_view=dynamic_cast<Source::ClangView*>(source_view))
source_clang_view->delete_object(); source_clang_view->async_delete();
else else
delete source_view; delete source_view;
} }

7
src/notebook.h

@ -8,10 +8,11 @@
#include <type_traits> #include <type_traits>
#include <map> #include <map>
#include <sigc++/sigc++.h> #include <sigc++/sigc++.h>
#include "directories.h"
class Notebook : public Gtk::Notebook { class Notebook : public Gtk::Notebook {
public: public:
Notebook(); Notebook(Directories &directories);
Source::View* get_view(int page); Source::View* get_view(int page);
int size(); int size();
Source::View* get_current_view(); Source::View* get_current_view();
@ -19,11 +20,11 @@ public:
void open(std::string filename); void open(std::string filename);
bool save(int page); bool save(int page);
bool save_current(); bool save_current();
std::string project_path;
private: private:
bool make_compile_commands(); bool make_compile_commands(const std::string &path);
bool save_modified_dialog(); bool save_modified_dialog();
Directories &directories;
std::vector<Source::View*> source_views; //Is NOT freed in destructor, this is intended for quick program exit. std::vector<Source::View*> source_views; //Is NOT freed in destructor, this is intended for quick program exit.
std::vector<std::unique_ptr<Gtk::ScrolledWindow> > scrolled_windows; std::vector<std::unique_ptr<Gtk::ScrolledWindow> > scrolled_windows;
std::vector<std::unique_ptr<Gtk::HBox> > hboxes; std::vector<std::unique_ptr<Gtk::HBox> > hboxes;

1
src/singletons.cc

@ -1,7 +1,6 @@
#include "singletons.h" #include "singletons.h"
std::unique_ptr<Source::Config> Singleton::Config::source_=std::unique_ptr<Source::Config>(new Source::Config()); std::unique_ptr<Source::Config> Singleton::Config::source_=std::unique_ptr<Source::Config>(new Source::Config());
std::unique_ptr<Terminal::Config> Singleton::Config::terminal_=std::unique_ptr<Terminal::Config>(new Terminal::Config());
std::unique_ptr<Directories::Config> Singleton::Config::directories_=std::unique_ptr<Directories::Config>(new Directories::Config()); std::unique_ptr<Directories::Config> Singleton::Config::directories_=std::unique_ptr<Directories::Config>(new Directories::Config());
std::unique_ptr<Terminal> Singleton::terminal_=std::unique_ptr<Terminal>(); std::unique_ptr<Terminal> Singleton::terminal_=std::unique_ptr<Terminal>();
std::unique_ptr<Theme::Config> Singleton::Config::theme_ = std::unique_ptr<Theme::Config>(new Theme::Config()); std::unique_ptr<Theme::Config> Singleton::Config::theme_ = std::unique_ptr<Theme::Config>(new Theme::Config());

2
src/singletons.h

@ -16,7 +16,6 @@ public:
class Config { class Config {
public: public:
static Source::Config *source() {return source_.get();} static Source::Config *source() {return source_.get();}
static Terminal::Config *terminal() {return terminal_.get();}
static Directories::Config *directories() {return directories_.get();} static Directories::Config *directories() {return directories_.get();}
static Theme::Config *theme() { return theme_.get(); } static Theme::Config *theme() { return theme_.get(); }
static Window::Config *window() { return window_.get(); } static Window::Config *window() { return window_.get(); }
@ -24,7 +23,6 @@ public:
static std::unique_ptr<Source::Config> source_; static std::unique_ptr<Source::Config> source_;
static std::unique_ptr<Theme::Config> theme_; static std::unique_ptr<Theme::Config> theme_;
static std::unique_ptr<Window::Config> window_; static std::unique_ptr<Window::Config> window_;
static std::unique_ptr<Terminal::Config> terminal_;
static std::unique_ptr<Directories::Config> directories_; static std::unique_ptr<Directories::Config> directories_;
}; };
static std::string config_dir() { return std::string(getenv("HOME")) + "/.juci/config/"; } static std::string config_dir() { return std::string(getenv("HOME")) + "/.juci/config/"; }

75
src/source.cc

@ -36,9 +36,8 @@ Glib::RefPtr<Gsv::Language> Source::guess_language(const std::string &file_path)
////////////// //////////////
//// View //// //// View ////
////////////// //////////////
Source::View::View(const std::string& file_path, const std::string& project_path): Source::View::View(const std::string& file_path): file_path(file_path) {
file_path(file_path), project_path(project_path) { set_smart_home_end(Gsv::SMART_HOME_END_BEFORE);
set_smart_home_end(Gsv::SMART_HOME_END_BEFORE);
get_source_buffer()->begin_not_undoable_action(); get_source_buffer()->begin_not_undoable_action();
juci::filesystem::read(file_path, get_buffer()); juci::filesystem::read(file_path, get_buffer());
get_source_buffer()->end_not_undoable_action(); get_source_buffer()->end_not_undoable_action();
@ -254,7 +253,7 @@ bool Source::View::on_key_press_event(GdkEventKey* key) {
///////////////////// /////////////////////
//// GenericView //// //// GenericView ////
///////////////////// /////////////////////
Source::GenericView::GenericView(const std::string& file_path, const std::string& project_path, Glib::RefPtr<Gsv::Language> language) : View(file_path, project_path) { Source::GenericView::GenericView(const std::string& file_path, Glib::RefPtr<Gsv::Language> language) : View(file_path) {
if(language) { if(language) {
get_source_buffer()->set_language(language); get_source_buffer()->set_language(language);
Singleton::terminal()->print("Language for file "+file_path+" set to "+language->get_name()+".\n"); Singleton::terminal()->print("Language for file "+file_path+" set to "+language->get_name()+".\n");
@ -273,8 +272,8 @@ Source::GenericView::GenericView(const std::string& file_path, const std::string
//////////////////////// ////////////////////////
clang::Index Source::ClangViewParse::clang_index(0, 0); clang::Index Source::ClangViewParse::clang_index(0, 0);
Source::ClangViewParse::ClangViewParse(const std::string& file_path, const std::string& project_path): Source::ClangViewParse::ClangViewParse(const std::string& file_path, const std::string& project_path) :
Source::View(file_path, project_path) { Source::View(file_path), project_path(project_path) {
INFO("Tagtable beeing filled"); INFO("Tagtable beeing filled");
for (auto &item : Singleton::Config::source()->tags) { for (auto &item : Singleton::Config::source()->tags) {
auto scheme = get_source_buffer()->get_style_scheme(); auto scheme = get_source_buffer()->get_style_scheme();
@ -321,6 +320,34 @@ Source::View(file_path, project_path) {
//TODO: clear tag_class and param_spec? //TODO: clear tag_class and param_spec?
parsing_in_progress=Singleton::terminal()->print_in_progress("Parsing "+file_path); parsing_in_progress=Singleton::terminal()->print_in_progress("Parsing "+file_path);
//GTK-calls must happen in main thread, so the parse_thread
//sends signals to the main thread that it is to call the following functions:
parse_start.connect([this]{
if(parse_thread_buffer_map_mutex.try_lock()) {
parse_thread_buffer_map=get_buffer_map();
parse_thread_mapped=true;
parse_thread_buffer_map_mutex.unlock();
}
parse_thread_go=true;
});
parse_done.connect([this](){
if(parse_thread_mapped) {
if(parsing_mutex.try_lock()) {
INFO("Updating syntax");
update_syntax();
update_diagnostics();
update_types();
clang_readable=true;
set_status("");
parsing_mutex.unlock();
INFO("Syntax updated");
}
parsing_in_progress->done("done");
}
else {
parse_thread_go=true;
}
});
init_parse(); init_parse();
get_buffer()->signal_changed().connect([this]() { get_buffer()->signal_changed().connect([this]() {
@ -362,37 +389,7 @@ void Source::ClangViewParse::init_parse() {
start_offset, start_offset,
end_offset); end_offset);
update_syntax(); update_syntax();
//GTK-calls must happen in main thread, so the parse_thread
//sends signals to the main thread that it is to call the following functions:
parse_start.connect([this]{
if(parse_thread_buffer_map_mutex.try_lock()) {
parse_thread_buffer_map=get_buffer_map();
parse_thread_mapped=true;
parse_thread_buffer_map_mutex.unlock();
}
parse_thread_go=true;
});
parse_done.connect([this](){
if(parse_thread_mapped) {
if(parsing_mutex.try_lock()) {
INFO("Updating syntax");
update_syntax();
update_diagnostics();
update_types();
clang_readable=true;
set_status("");
parsing_mutex.unlock();
INFO("Syntax updated");
}
parsing_in_progress->done("done");
}
else {
parse_thread_go=true;
}
});
set_status("parsing..."); set_status("parsing...");
if(parse_thread.joinable()) if(parse_thread.joinable())
parse_thread.join(); parse_thread.join();
@ -560,7 +557,7 @@ void Source::ClangViewParse::update_types() {
bool Source::ClangViewParse::on_motion_notify_event(GdkEventMotion* event) { bool Source::ClangViewParse::on_motion_notify_event(GdkEventMotion* event) {
delayed_tooltips_connection.disconnect(); delayed_tooltips_connection.disconnect();
if(clang_readable) { if(clang_readable && event->state==0) {
Gdk::Rectangle rectangle(event->x, event->y, 1, 1); Gdk::Rectangle rectangle(event->x, event->y, 1, 1);
Tooltips::init(); Tooltips::init();
type_tooltips.show(rectangle); type_tooltips.show(rectangle);
@ -1082,7 +1079,7 @@ Source::ClangView::ClangView(const std::string& file_path, const std::string& pr
}); });
} }
void Source::ClangView::delete_object() { void Source::ClangView::async_delete() {
parsing_in_progress->cancel("canceled, freeing resources in the background"); parsing_in_progress->cancel("canceled, freeing resources in the background");
parse_thread_stop=true; parse_thread_stop=true;
delete_thread=std::thread([this](){ delete_thread=std::thread([this](){

10
src/source.h

@ -46,7 +46,7 @@ namespace Source {
class View : public Gsv::View { class View : public Gsv::View {
public: public:
View(const std::string& file_path, const std::string& project_path); View(const std::string& file_path);
~View(); ~View();
void search_highlight(const std::string &text, bool case_sensitive, bool regex); void search_highlight(const std::string &text, bool case_sensitive, bool regex);
@ -58,7 +58,6 @@ namespace Source {
void replace_all(const std::string &replacement); void replace_all(const std::string &replacement);
std::string file_path; std::string file_path;
std::string project_path;
std::function<std::pair<std::string, unsigned>()> get_declaration_location; std::function<std::pair<std::string, unsigned>()> get_declaration_location;
std::function<void()> goto_method; std::function<void()> goto_method;
@ -84,12 +83,13 @@ namespace Source {
class GenericView : public View { class GenericView : public View {
public: public:
GenericView(const std::string& file_path, const std::string& project_path, Glib::RefPtr<Gsv::Language> language); GenericView(const std::string& file_path, Glib::RefPtr<Gsv::Language> language);
}; };
class ClangViewParse : public View { class ClangViewParse : public View {
public: public:
ClangViewParse(const std::string& file_path, const std::string& project_path); ClangViewParse(const std::string& file_path, const std::string& project_path);
std::string project_path;
protected: protected:
void init_parse(); void init_parse();
void start_reparse(); void start_reparse();
@ -125,7 +125,7 @@ namespace Source {
bool on_scroll_event(GdkEventScroll* event); bool on_scroll_event(GdkEventScroll* event);
static clang::Index clang_index; static clang::Index clang_index;
std::vector<std::string> get_compilation_commands(); std::vector<std::string> get_compilation_commands();
Glib::Dispatcher parse_done; Glib::Dispatcher parse_done;
Glib::Dispatcher parse_start; Glib::Dispatcher parse_start;
std::map<std::string, std::string> parse_thread_buffer_map; std::map<std::string, std::string> parse_thread_buffer_map;
@ -169,7 +169,7 @@ namespace Source {
class ClangView : public ClangViewRefactor { class ClangView : public ClangViewRefactor {
public: public:
ClangView(const std::string& file_path, const std::string& project_path); ClangView(const std::string& file_path, const std::string& project_path);
void delete_object(); void async_delete();
bool restart_parse(); bool restart_parse();
private: private:
Glib::Dispatcher do_delete_object; Glib::Dispatcher do_delete_object;

262
src/terminal.cc

@ -2,6 +2,84 @@
#include <iostream> #include <iostream>
#include "logging.h" #include "logging.h"
#include "singletons.h" #include "singletons.h"
#include <unistd.h>
#include <sys/wait.h>
#include <unordered_map>
#include <iostream> //TODO: remove
using namespace std; //TODO: remove
int execute_status=-1;
std::mutex async_and_sync_execute_mutex;
std::unordered_map<pid_t, std::vector<int> > async_execute_descriptors;
std::unordered_map<pid_t, int> async_execute_status;
//TODO: Windows...
//Coppied partially from http://www.linuxprogrammingblog.com/code-examples/sigaction
void signal_execl_exit(int sig, siginfo_t *siginfo, void *context) {
async_and_sync_execute_mutex.lock();
if(async_execute_descriptors.find(siginfo->si_pid)!=async_execute_descriptors.end()) {
close(async_execute_descriptors.at(siginfo->si_pid)[0]);
close(async_execute_descriptors.at(siginfo->si_pid)[1]);
close(async_execute_descriptors.at(siginfo->si_pid)[2]);
}
int status;
while (waitpid(siginfo->si_pid, &status, WNOHANG) > 0) {}
if(async_execute_descriptors.find(siginfo->si_pid)!=async_execute_descriptors.end()) {
async_execute_status[siginfo->si_pid]=status;
async_execute_descriptors.erase(siginfo->si_pid);
}
else
execute_status=status;
async_and_sync_execute_mutex.unlock();
}
//TODO: Windows...
//TODO: Someone who knows this stuff see if I have done something stupid.
//Copied partially from http://stackoverflow.com/questions/12778672/killing-process-that-has-been-created-with-popen2
pid_t popen3(const char *command, int *input_descriptor, int *output_descriptor, int *error_descriptor) {
pid_t pid;
int p_stdin[2], p_stdout[2], p_stderr[2];
if (pipe(p_stdin) != 0 || pipe(p_stdout) != 0 || pipe(p_stderr) != 0)
return -1;
pid = fork();
if (pid < 0)
return pid;
else if (pid == 0) {
close(p_stdin[1]);
dup2(p_stdin[0], 0);
close(p_stdout[0]);
dup2(p_stdout[1], 1);
close(p_stderr[0]);
dup2(p_stderr[1], 2);
setpgid(0, 0);
execl("/bin/sh", "sh", "-c", command, NULL);
perror("execl");
exit(1);
}
if (input_descriptor == NULL)
close(p_stdin[1]);
else
*input_descriptor = p_stdin[1];
if (output_descriptor == NULL)
close(p_stdout[0]);
else
*output_descriptor = p_stdout[0];
if (error_descriptor == NULL)
close(p_stderr[0]);
else
*error_descriptor = p_stderr[0];
return pid;
}
Terminal::InProgress::InProgress(const std::string& start_msg): stop(false) { Terminal::InProgress::InProgress(const std::string& start_msg): stop(false) {
waiting_print.connect([this](){ waiting_print.connect([this](){
@ -48,86 +126,154 @@ Terminal::Terminal() {
scrolled_window.add(text_view); scrolled_window.add(text_view);
add(scrolled_window); add(scrolled_window);
change_folder_command = "";
text_view.signal_size_allocate().connect([this](Gtk::Allocation& allocation){ text_view.signal_size_allocate().connect([this](Gtk::Allocation& allocation){
auto end=text_view.get_buffer()->create_mark(text_view.get_buffer()->end()); auto end=text_view.get_buffer()->create_mark(text_view.get_buffer()->end());
text_view.scroll_to(end); text_view.scroll_to(end);
text_view.get_buffer()->delete_mark(end); text_view.get_buffer()->delete_mark(end);
}); });
bold_tag=text_view.get_buffer()->create_tag();
bold_tag->property_weight()=PANGO_WEIGHT_BOLD;
async_print_dispatcher.connect([this](){
async_print_strings_mutex.lock();
if(async_print_strings.size()>0) {
for(auto &string_bold: async_print_strings)
print(string_bold.first, string_bold.second);
async_print_strings.clear();
}
async_print_strings_mutex.unlock();
});
//Coppied from http://www.linuxprogrammingblog.com/code-examples/sigaction
struct sigaction act;
memset (&act, '\0', sizeof(act));
act.sa_sigaction = &signal_execl_exit;
act.sa_flags = SA_SIGINFO;
sigaction(SIGCHLD, &act, NULL);
} }
bool Terminal::execute(const std::string &path, const std::string &command) { int Terminal::execute(const std::string &command, const std::string &path) {
boost::filesystem::path boost_path; boost::filesystem::path boost_path;
if(path=="") std::string cd_path_and_command;
boost_path=boost::filesystem::current_path(); if(path!="") {
else
boost_path=boost::filesystem::path(path); boost_path=boost::filesystem::path(path);
//TODO: Windows... //TODO: Windows...
auto cd_path_and_command="cd "+boost_path.string()+" 2>&1 && "+command; cd_path_and_command="cd "+boost_path.string()+" 2>&1 && "+command;
}
else
cd_path_and_command=command;
FILE* p = NULL; FILE* p;
p = popen(cd_path_and_command.c_str(), "r"); p = popen(cd_path_and_command.c_str(), "r");
if (p == NULL) { if (p == NULL) {
print("Error: Failed to run command" + command + "\n"); print("Error: Failed to run command" + command + "\n");
return false; return -1;
} }
else { else {
char buffer[1028]; char buffer[1024];
while (fgets(buffer, 1028, p) != NULL) { while (fgets(buffer, 1024, p) != NULL) {
print(buffer); print(buffer);
while(gtk_events_pending())
gtk_main_iteration();
} }
async_and_sync_execute_mutex.lock();
int exit_code=pclose(p); int exit_code=pclose(p);
if(exit_code==0) if(exit_code==-1)
return true; exit_code=execute_status;
else async_and_sync_execute_mutex.unlock();
return false; return exit_code;
} }
} }
void Terminal::async_execute(const std::string &path, const std::string &command) { void Terminal::async_execute(const std::string &command, const std::string &path, std::function<void(int exit_code)> callback) {
std::thread async_execute_thread([this, command, path, callback](){
} boost::filesystem::path boost_path;
std::string cd_path_and_command;
void Terminal::set_change_folder_command(boost::filesystem::path CMake_path) { if(path!="") {
INFO("Terminal: set_change_folder_command"); boost_path=boost::filesystem::path(path);
path = CMake_path.string();
change_folder_command = "cd "+ path + "; "; //TODO: Windows...
cd_path_and_command="cd "+boost_path.string()+" && "+command;
}
else
cd_path_and_command=command;
int input_descriptor, output_descriptor, error_descriptor;
async_and_sync_execute_mutex.lock();
auto pid=popen3(cd_path_and_command.c_str(), &input_descriptor, &output_descriptor, &error_descriptor);
async_execute_descriptors[pid]={input_descriptor, output_descriptor, error_descriptor};
async_and_sync_execute_mutex.unlock();
if (pid<=0) {
async_print("Error: Failed to run command: " + command + "\n");
if(callback)
callback(-1);
}
else {
std::thread error_thread([this, error_descriptor](){
char buffer[1024];
ssize_t n;
while ((n=read(error_descriptor, buffer, 1024)) > 0) {
std::string message;
for(int c=0;c<n;c++)
message+=buffer[c];
async_print(message, true);
}
});
error_thread.detach();
char buffer[1024];
ssize_t n;
while ((n=read(output_descriptor, buffer, 1024)) > 0) {
std::string message;
for(int c=0;c<n;c++)
message+=buffer[c];
async_print(message);
}
async_and_sync_execute_mutex.lock();
int exit_code=async_execute_status.at(pid);
async_execute_status.erase(pid);
if(async_execute_descriptors.find(pid)!=async_execute_descriptors.end()) //cleanup in case signal_execl_exit is not called
async_execute_descriptors.erase(pid);
async_and_sync_execute_mutex.unlock();
if(callback)
callback(exit_code);
}
});
async_execute_thread.detach();
} }
void Terminal::compile() { void Terminal::kill_executing() {
INFO("Terminal: compile"); async_and_sync_execute_mutex.lock();
text_view.get_buffer()->set_text(""); int status;
DEBUG("Terminal: compile: running cmake command"); for(auto &pid: async_execute_descriptors) {
std::vector<std::string> commands = Singleton::Config::terminal()->compile_commands; close(async_execute_descriptors.at(pid.first)[0]);
for (size_t it = 0; it < commands.size(); ++it) { close(async_execute_descriptors.at(pid.first)[1]);
execute_command(commands.at(it), "r"); close(async_execute_descriptors.at(pid.first)[2]);
kill(-pid.first, SIGINT); //signal_execl_exit is not always called after kill!
while(waitpid(-pid.first, &status, WNOHANG) > 0) {}
async_execute_status[pid.first]=status;
} }
print("\n"); async_and_sync_execute_mutex.unlock();
DEBUG("Terminal: compile: compile done");
}
void Terminal::run(std::string executable) {
INFO("Terminal: run");
print("juCi++ execute: " + executable + "\n");
DEBUG("Terminal: compile: running run command: ");
DEBUG_VAR(executable);
execute_command("cd "+Singleton::Config::terminal()->run_command + "; ./"+executable, "r");
print("\n");
} }
int Terminal::print(std::string message){ int Terminal::print(const std::string &message, bool bold){
INFO("Terminal: PrintMessage"); INFO("Terminal: PrintMessage");
text_view.get_buffer()->insert(text_view.get_buffer()->end(), "> "+message); if(bold)
text_view.get_buffer()->insert_with_tag(text_view.get_buffer()->end(), message, bold_tag);
else
text_view.get_buffer()->insert(text_view.get_buffer()->end(), message);
return text_view.get_buffer()->end().get_line(); return text_view.get_buffer()->end().get_line();
} }
void Terminal::print(int line_nr, std::string message){ void Terminal::print(int line_nr, const std::string &message, bool bold){
INFO("Terminal: PrintMessage at line " << line_nr); INFO("Terminal: PrintMessage at line " << line_nr);
auto iter=text_view.get_buffer()->get_iter_at_line(line_nr); auto iter=text_view.get_buffer()->get_iter_at_line(line_nr);
while(!iter.ends_line()) while(!iter.ends_line())
iter++; iter++;
text_view.get_buffer()->insert(iter, message); if(bold)
text_view.get_buffer()->insert_with_tag(iter, message, bold_tag);
else
text_view.get_buffer()->insert(iter, message);
} }
std::shared_ptr<Terminal::InProgress> Terminal::print_in_progress(std::string start_msg) { std::shared_ptr<Terminal::InProgress> Terminal::print_in_progress(std::string start_msg) {
@ -135,21 +281,13 @@ std::shared_ptr<Terminal::InProgress> Terminal::print_in_progress(std::string st
return in_progress; return in_progress;
} }
void Terminal::execute_command(std::string command, std::string mode) { void Terminal::async_print(const std::string &message, bool bold) {
INFO("Terminal: execute_command"); async_print_strings_mutex.lock();
command = change_folder_command+command; bool dispatch=true;
DEBUG("Terminal: PrintMessage: running command"); if(async_print_strings.size()>0)
FILE* p = NULL; dispatch=false;
std::cout << command << std::endl; async_print_strings.emplace_back(message, bold);
p = popen(command.c_str(), mode.c_str()); async_print_strings_mutex.unlock();
if (p == NULL) { if(dispatch)
print("juCi++ ERROR: Failed to run command" + command + "\n"); async_print_dispatcher();
}
else {
char buffer[1028];
while (fgets(buffer, 1028, p) != NULL) {
print(buffer);
}
pclose(p);
}
} }

35
src/terminal.h

@ -9,13 +9,7 @@
#include <atomic> #include <atomic>
class Terminal : public Gtk::HBox { class Terminal : public Gtk::HBox {
public: public:
class Config {
public:
std::vector<std::string> compile_commands;
std::string run_command;
};
class InProgress { class InProgress {
public: public:
InProgress(const std::string& start_msg); InProgress(const std::string& start_msg);
@ -31,25 +25,22 @@ public:
}; };
Terminal(); Terminal();
bool execute(const std::string &path, const std::string &command); int execute(const std::string &command, const std::string &path="");
void async_execute(const std::string &path, const std::string &command); void async_execute(const std::string &command, const std::string &path="", std::function<void(int exit_code)> callback=nullptr);
void set_change_folder_command(boost::filesystem::path CMake_path); //TODO: remove void kill_executing();
void run(std::string executable); //TODO: remove
void compile(); //TODO: remove int print(const std::string &message, bool bold=false);
int print(std::string message); void print(int line_nr, const std::string &message, bool bold=false);
void print(int line_nr, std::string message);
std::shared_ptr<InProgress> print_in_progress(std::string start_msg); std::shared_ptr<InProgress> print_in_progress(std::string start_msg);
void async_print(const std::string &message, bool bold=false);
private: private:
void execute_command(std::string command, std::string mode); //TODO: remove
Gtk::TextView text_view; Gtk::TextView text_view;
Gtk::ScrolledWindow scrolled_window; Gtk::ScrolledWindow scrolled_window;
std::string change_folder_command; //TODO: remove Glib::Dispatcher async_print_dispatcher;
std::string path; //TODO: remove std::vector<std::pair<std::string, bool> > async_print_strings;
const std::string cmake_sucsess = "Build files have been written to:"; //TODO: remove std::mutex async_print_strings_mutex;
const std::string make_built = "Built target"; //TODO: remove Glib::RefPtr<Gtk::TextTag> bold_tag;
const std::string make_executable = "Linking CXX executable"; //TODO: remove
}; };
#endif // JUCI_TERMINAL_H_ #endif // JUCI_TERMINAL_H_

109
src/window.cc

@ -21,7 +21,7 @@ void Window::generate_keybindings() {
} }
} }
Window::Window() : box(Gtk::ORIENTATION_VERTICAL) { Window::Window() : box(Gtk::ORIENTATION_VERTICAL), notebook(directories), compiling(false) {
INFO("Create Window"); INFO("Create Window");
set_title("juCi++"); set_title("juCi++");
set_default_size(600, 400); set_default_size(600, 400);
@ -97,6 +97,11 @@ Window::Window() : box(Gtk::ORIENTATION_VERTICAL) {
notebook.signal_page_removed().connect([this](Gtk::Widget* page, guint page_num) { notebook.signal_page_removed().connect([this](Gtk::Widget* page, guint page_num) {
entry_box.hide(); entry_box.hide();
}); });
compile_success.connect([this](){
directories.open_folder();
});
INFO("Window created"); INFO("Window created");
} // Window constructor } // Window constructor
@ -203,41 +208,52 @@ void Window::create_menu() {
}); });
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) if(notebook.get_current_page()==-1 || compiling)
return; return;
notebook.save_current(); CMake cmake(notebook.get_current_view()->file_path);
if (running.try_lock()) { directories.open_folder();
std::thread execute([this]() { auto executables = cmake.get_functions_parameters("add_executable");
std::string path = notebook.get_current_view()->file_path; std::string executable;
size_t pos = path.find_last_of("/\\"); boost::filesystem::path path;
if(pos != std::string::npos) { if(executables.size()>0 && executables[0].second.size()>0) {
path.erase(path.begin()+pos,path.end()); executable=executables[0].second[0];
Singleton::terminal()->set_change_folder_command(path); path=executables[0].first.parent_path();
} path+="/"+executables[0].second[0];
Singleton::terminal()->compile(); }
std::string executable = directories.get_cmakelists_variable(path,"add_executable"); if(cmake.project_path!="") {
Singleton::terminal()->run(executable); if(path!="") {
running.unlock(); compiling=true;
}); Singleton::terminal()->print("Compiling and executing "+path.string()+"\n");
execute.detach(); //TODO: Windows...
Singleton::terminal()->async_execute("make", cmake.project_path.string(), [this, path](int exit_code){
compiling=false;
if(exit_code==EXIT_SUCCESS) {
compile_success();
//TODO: Windows...
Singleton::terminal()->async_execute(path.string(), path.parent_path().string(), [this, path](int exit_code){
Singleton::terminal()->async_print(path.string()+" returned: "+std::to_string(exit_code)+'\n');
});
}
});
}
else
Singleton::terminal()->print("Could not find an executable, please use add_executable in CMakeLists.txt\n");
} }
}); });
menu.action_group->add(Gtk::Action::create("ProjectCompile", "Compile"), Gtk::AccelKey(menu.key_map["compile"]), [this]() { menu.action_group->add(Gtk::Action::create("ProjectCompile", "Compile"), Gtk::AccelKey(menu.key_map["compile"]), [this]() {
if(notebook.get_current_page()==-1) if(notebook.get_current_page()==-1 || compiling)
return; return;
notebook.save_current(); CMake cmake(notebook.get_current_view()->file_path);
if (running.try_lock()) { directories.open_folder();
std::thread execute([this]() { if(cmake.project_path!="") {
std::string path = notebook.get_current_view()->file_path; compiling=true;
size_t pos = path.find_last_of("/\\"); Singleton::terminal()->print("Compiling project "+cmake.project_path.string()+"\n");
if(pos != std::string::npos){ //TODO: Windows...
path.erase(path.begin()+pos,path.end()); Singleton::terminal()->async_execute("make 2>&1", cmake.project_path.string(), [this](int exit_code){
Singleton::terminal()->set_change_folder_command(path); compiling=false;
} if(exit_code==EXIT_SUCCESS)
Singleton::terminal()->compile(); compile_success();
running.unlock();
}); });
execute.detach();
} }
}); });
@ -250,8 +266,11 @@ void Window::create_menu() {
} }
bool Window::on_key_press_event(GdkEventKey *event) { bool Window::on_key_press_event(GdkEventKey *event) {
if(event->keyval==GDK_KEY_Escape) if(event->keyval==GDK_KEY_Escape) {
if(entry_box.entries.size()==0)
Singleton::terminal()->kill_executing();
entry_box.hide(); entry_box.hide();
}
#ifdef __APPLE__ //For Apple's Command-left, right, up, down keys #ifdef __APPLE__ //For Apple's Command-left, right, up, down keys
else if((event->state & GDK_META_MASK)>0) { else if((event->state & GDK_META_MASK)>0) {
if(event->keyval==GDK_KEY_Left) { if(event->keyval==GDK_KEY_Left) {
@ -293,6 +312,7 @@ void Window::hide() {
if(!notebook.close_current_page()) if(!notebook.close_current_page())
return; return;
} }
Singleton::terminal()->kill_executing();
Gtk::Window::hide(); Gtk::Window::hide();
} }
@ -301,18 +321,18 @@ void Window::new_file_entry() {
entry_box.entries.emplace_back("untitled", [this](const std::string& content){ entry_box.entries.emplace_back("untitled", [this](const std::string& content){
std::string filename=content; std::string filename=content;
if(filename!="") { if(filename!="") {
if(notebook.project_path!="" && !boost::filesystem::path(filename).is_absolute()) if(directories.current_path!="" && !boost::filesystem::path(filename).is_absolute())
filename=notebook.project_path+"/"+filename; filename=directories.current_path.string()+"/"+filename;
boost::filesystem::path p(filename); boost::filesystem::path p(filename);
if(boost::filesystem::exists(p)) { if(boost::filesystem::exists(p)) {
Singleton::terminal()->print("Error: "+p.string()+" already exists.\n"); Singleton::terminal()->print("Error: "+p.string()+" already exists.\n");
} }
else { else {
if(juci::filesystem::write(p)) { if(juci::filesystem::write(p)) {
if(notebook.project_path!="") if(directories.current_path!="")
directories.open_folder(notebook.project_path); directories.open_folder();
notebook.open(boost::filesystem::canonical(p).string()); notebook.open(boost::filesystem::canonical(p).string());
Singleton::terminal()->print("New file "+p.string()+" created.\n"); Singleton::terminal()->print("New file "+p.string()+" created.\n");
} }
else else
Singleton::terminal()->print("Error: could not create new file "+p.string()+".\n"); Singleton::terminal()->print("Error: could not create new file "+p.string()+".\n");
@ -329,8 +349,8 @@ void Window::new_file_entry() {
void Window::open_folder_dialog() { void Window::open_folder_dialog() {
Gtk::FileChooserDialog dialog("Please choose a folder", Gtk::FILE_CHOOSER_ACTION_SELECT_FOLDER); Gtk::FileChooserDialog dialog("Please choose a folder", Gtk::FILE_CHOOSER_ACTION_SELECT_FOLDER);
if(notebook.project_path.size()>0) if(directories.current_path!="")
gtk_file_chooser_set_current_folder((GtkFileChooser*)dialog.gobj(), notebook.project_path.c_str()); gtk_file_chooser_set_current_folder((GtkFileChooser*)dialog.gobj(), directories.current_path.string().c_str());
else else
gtk_file_chooser_set_current_folder((GtkFileChooser*)dialog.gobj(), boost::filesystem::current_path().string().c_str()); gtk_file_chooser_set_current_folder((GtkFileChooser*)dialog.gobj(), boost::filesystem::current_path().string().c_str());
dialog.set_transient_for(*this); dialog.set_transient_for(*this);
@ -342,17 +362,14 @@ void Window::open_folder_dialog() {
if(result==Gtk::RESPONSE_OK) { if(result==Gtk::RESPONSE_OK) {
std::string project_path=dialog.get_filename(); std::string project_path=dialog.get_filename();
notebook.project_path=project_path;
directories.open_folder(project_path); directories.open_folder(project_path);
if(notebook.get_current_page()!=-1)
directories.select_path(notebook.get_current_view()->file_path);
} }
} }
void Window::open_file_dialog() { void Window::open_file_dialog() {
Gtk::FileChooserDialog dialog("Please choose a file", Gtk::FILE_CHOOSER_ACTION_OPEN); Gtk::FileChooserDialog dialog("Please choose a file", Gtk::FILE_CHOOSER_ACTION_OPEN);
if(notebook.project_path.size()>0) if(directories.current_path!="")
gtk_file_chooser_set_current_folder((GtkFileChooser*)dialog.gobj(), notebook.project_path.c_str()); gtk_file_chooser_set_current_folder((GtkFileChooser*)dialog.gobj(), directories.current_path.string().c_str());
else else
gtk_file_chooser_set_current_folder((GtkFileChooser*)dialog.gobj(), boost::filesystem::current_path().string().c_str()); gtk_file_chooser_set_current_folder((GtkFileChooser*)dialog.gobj(), boost::filesystem::current_path().string().c_str());
dialog.set_transient_for(*this); dialog.set_transient_for(*this);
@ -406,8 +423,8 @@ void Window::save_file_dialog() {
if(file) { if(file) {
file << notebook.get_current_view()->get_buffer()->get_text(); file << notebook.get_current_view()->get_buffer()->get_text();
file.close(); file.close();
if(notebook.project_path!="") if(directories.current_path!="")
directories.open_folder(notebook.project_path); directories.open_folder();
notebook.open(path); notebook.open(path);
Singleton::terminal()->print("File saved to: " + notebook.get_current_view()->file_path+"\n"); Singleton::terminal()->print("File saved to: " + notebook.get_current_view()->file_path+"\n");
} }

8
src/window.h

@ -7,19 +7,22 @@
#include "notebook.h" #include "notebook.h"
#include "menu.h" #include "menu.h"
#include <boost/property_tree/json_parser.hpp> #include <boost/property_tree/json_parser.hpp>
#include <atomic>
class Window : public Gtk::Window { class Window : public Gtk::Window {
public: public:
Window(); Window();
Notebook notebook;
Directories directories; Directories directories;
Notebook notebook;
class Config { class Config {
public: public:
boost::property_tree::ptree keybindings; boost::property_tree::ptree keybindings;
}; };
protected: protected:
bool on_key_press_event(GdkEventKey *event); bool on_key_press_event(GdkEventKey *event);
bool on_delete_event (GdkEventAny *event); bool on_delete_event (GdkEventAny *event);
private: private:
Gtk::Box box; Gtk::Box box;
Gtk::VPaned vpaned; Gtk::VPaned vpaned;
@ -28,8 +31,9 @@ private:
Gtk::VBox terminal_vbox; Gtk::VBox terminal_vbox;
Gtk::HBox status_hbox; Gtk::HBox status_hbox;
EntryBox entry_box; EntryBox entry_box;
std::mutex running;
Menu menu; Menu menu;
std::atomic<bool> compiling;
Glib::Dispatcher compile_success;
void create_menu(); void create_menu();
void hide(); void hide();

Loading…
Cancel
Save