1555 lines
58 KiB
C++
1555 lines
58 KiB
C++
/*
|
|
4coder_project_commands.cpp - commands for loading and using a project.
|
|
*/
|
|
|
|
// TOP
|
|
|
|
static Project current_project = {};
|
|
static Partition current_project_arena = {};
|
|
|
|
///////////////////////////////
|
|
|
|
static Project_File_Pattern_Array
|
|
get_pattern_array_from_cstring_array(Partition *arena, CString_Array list){
|
|
Project_File_Pattern_Array array = {};
|
|
i32 count = list.count;
|
|
array.patterns = push_array(arena, Project_File_Pattern, count);
|
|
array.count = count;
|
|
for (i32 i = 0; i < count; ++i){
|
|
String base_str = make_string_slowly(list.strings[i]);
|
|
String str = string_push(arena, base_str.size + 3);
|
|
append(&str, "*.");
|
|
append(&str, base_str);
|
|
terminate_with_null(&str);
|
|
get_absolutes(str, &array.patterns[i].absolutes, false, false);
|
|
}
|
|
return(array);
|
|
}
|
|
|
|
static Project_File_Pattern_Array
|
|
get_pattern_array_from_extension_list(Partition *arena, Extension_List extension_list){
|
|
CString_Array list = {};
|
|
list.strings = extension_list.exts;
|
|
list.count = extension_list.count;
|
|
return(get_pattern_array_from_cstring_array(arena, list));
|
|
}
|
|
|
|
///////////////////////////////
|
|
|
|
static void
|
|
close_all_files_with_extension(Application_Links *app, Partition *scratch_part,
|
|
CString_Array extension_array){
|
|
Temp_Memory temp = begin_temp_memory(scratch_part);
|
|
|
|
i32 buffers_to_close_max = part_remaining(scratch_part)/sizeof(i32);
|
|
i32 *buffers_to_close = push_array(scratch_part, i32, buffers_to_close_max);
|
|
|
|
i32 buffers_to_close_count = 0;
|
|
b32 do_repeat = 0;
|
|
do{
|
|
buffers_to_close_count = 0;
|
|
do_repeat = 0;
|
|
|
|
u32 access = AccessAll;
|
|
Buffer_Summary buffer = {};
|
|
for (buffer = get_buffer_first(app, access);
|
|
buffer.exists;
|
|
get_buffer_next(app, &buffer, access)){
|
|
|
|
b32 is_match = 1;
|
|
if (extension_array.count > 0){
|
|
is_match = 0;
|
|
if (buffer.file_name != 0){
|
|
String extension = file_extension(make_string(buffer.file_name, buffer.file_name_len));
|
|
for (i32 i = 0; i < extension_array.count; ++i){
|
|
if (match(extension, extension_array.strings[i])){
|
|
is_match = 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (is_match){
|
|
if (buffers_to_close_count >= buffers_to_close_max){
|
|
do_repeat = 1;
|
|
break;
|
|
}
|
|
buffers_to_close[buffers_to_close_count++] = buffer.buffer_id;
|
|
}
|
|
}
|
|
|
|
for (i32 i = 0; i < buffers_to_close_count; ++i){
|
|
kill_buffer(app, buffer_identifier(buffers_to_close[i]), true, 0);
|
|
}
|
|
}while(do_repeat);
|
|
|
|
end_temp_memory(temp);
|
|
}
|
|
|
|
static b32
|
|
match_in_pattern_array(char *str, Project_File_Pattern_Array array){
|
|
b32 found_match = false;
|
|
Project_File_Pattern *pattern = array.patterns;
|
|
for (i32 i = 0; i < array.count; ++i, ++pattern){
|
|
if (wildcard_match_c(&pattern->absolutes, str, true)){
|
|
found_match = true;
|
|
break;
|
|
}
|
|
}
|
|
return(found_match);
|
|
}
|
|
|
|
static void
|
|
open_all_files_in_directory_pattern_match__inner(Application_Links *app, String space,
|
|
Project_File_Pattern_Array whitelist,
|
|
Project_File_Pattern_Array blacklist,
|
|
u32 flags){
|
|
File_List list = get_file_list(app, space.str, space.size);
|
|
i32 dir_size = space.size;
|
|
|
|
File_Info *info = list.infos;
|
|
for (u32 i = 0; i < list.count; ++i, ++info){
|
|
if (info->folder){
|
|
if ((flags & OpenAllFilesFlag_Recursive) == 0){
|
|
continue;
|
|
}
|
|
if (match_in_pattern_array(info->filename, blacklist)){
|
|
continue;
|
|
}
|
|
|
|
space.size = dir_size;
|
|
String str = make_string(info->filename, info->filename_len);
|
|
append(&space, str);
|
|
append(&space, "/");
|
|
open_all_files_in_directory_pattern_match__inner(app, space, whitelist, blacklist, flags);
|
|
}
|
|
else{
|
|
if (!match_in_pattern_array(info->filename, whitelist)){
|
|
continue;
|
|
}
|
|
if (match_in_pattern_array(info->filename, blacklist)){
|
|
continue;
|
|
}
|
|
|
|
space.size = dir_size;
|
|
String str = make_string(info->filename, info->filename_len);
|
|
append(&space, str);
|
|
create_buffer(app, space.str, space.size, 0);
|
|
}
|
|
}
|
|
|
|
free_file_list(app, list);
|
|
}
|
|
|
|
static Project_File_Pattern_Array
|
|
get_standard_blacklist(Partition *arena){
|
|
static char *dot_str = ".*";
|
|
CString_Array black_array = {};
|
|
black_array.strings = &dot_str;
|
|
black_array.count = 1;
|
|
return(get_pattern_array_from_cstring_array(arena, black_array));
|
|
}
|
|
|
|
static void
|
|
open_all_files_in_directory_pattern_match(Application_Links *app, Partition *scratch,
|
|
String dir,
|
|
Project_File_Pattern_Array whitelist,
|
|
Project_File_Pattern_Array blacklist,
|
|
u32 flags){
|
|
Temp_Memory temp = begin_temp_memory(scratch);
|
|
i32 size = 32 << 10;
|
|
char *mem = push_array(scratch, char, size);
|
|
String space = make_string_cap(mem, 0, size);
|
|
append(&space, dir);
|
|
if (space.size == 0 || !char_is_slash(space.str[space.size - 1])){
|
|
append(&space, '/');
|
|
}
|
|
open_all_files_in_directory_pattern_match__inner(app, space, whitelist, blacklist, flags);
|
|
end_temp_memory(temp);
|
|
}
|
|
|
|
static void
|
|
open_all_files_in_directory_with_extension(Application_Links *app, Partition *scratch,
|
|
String dir, CString_Array array,
|
|
u32 flags){
|
|
Temp_Memory temp = begin_temp_memory(scratch);
|
|
Project_File_Pattern_Array whitelist = get_pattern_array_from_cstring_array(scratch, array);
|
|
Project_File_Pattern_Array blacklist = get_standard_blacklist(scratch);
|
|
open_all_files_in_directory_pattern_match(app, scratch, dir, whitelist, blacklist, flags);
|
|
end_temp_memory(temp);
|
|
}
|
|
|
|
static void
|
|
open_all_files_in_hot_with_extension(Application_Links *app, Partition *scratch,
|
|
CString_Array array,
|
|
u32 flags){
|
|
Temp_Memory temp = begin_temp_memory(scratch);
|
|
i32 size = 32 << 10;
|
|
char *mem = push_array(scratch, char, size);
|
|
String space = make_string_cap(mem, 0, size);
|
|
space.size = directory_get_hot(app, space.str, space.memory_size);
|
|
if (space.size == 0 || !char_is_slash(space.str[space.size - 1])){
|
|
append(&space, '/');
|
|
}
|
|
Project_File_Pattern_Array whitelist = get_pattern_array_from_cstring_array(scratch, array);
|
|
Project_File_Pattern_Array blacklist = get_standard_blacklist(scratch);
|
|
open_all_files_in_directory_pattern_match__inner(app, space, whitelist, blacklist, flags);
|
|
end_temp_memory(temp);
|
|
}
|
|
|
|
///////////////////////////////
|
|
|
|
#if defined(IS_WINDOWS)
|
|
#define PlatformName "win"
|
|
#elif defined(IS_LINUX)
|
|
#define PlatformName "linux"
|
|
#elif defined(IS_MAC)
|
|
#define PlatformName "mac"
|
|
#else
|
|
# error no project configuration names for this platform
|
|
#endif
|
|
|
|
static Project*
|
|
parse_project__config_data__version_0(Partition *arena, String file_dir, Config *parsed){
|
|
Project *project = push_array(arena, Project, 1);
|
|
memset(project, 0, sizeof(*project));
|
|
|
|
// Set new project directory
|
|
{
|
|
i32 cap = file_dir.size + 1;
|
|
char *mem = push_array(arena, char, cap);
|
|
project->dir = make_string(mem, 0, cap);
|
|
copy(&project->dir, file_dir);
|
|
terminate_with_null(&project->dir);
|
|
|
|
project->load_path_array.paths = push_array(arena, Project_File_Load_Path, 1);
|
|
project->load_path_array.count = 1;
|
|
|
|
project->load_path_array.paths[0].path = project->dir;
|
|
project->load_path_array.paths[0].recursive = false;
|
|
project->load_path_array.paths[0].relative = false;
|
|
|
|
project->name = project->dir;
|
|
}
|
|
|
|
// Read the settings from project.4coder
|
|
String str = {};
|
|
if (config_string_var(parsed, "extensions", 0, &str)){
|
|
Extension_List extension_list;
|
|
parse_extension_line_to_extension_list(str, &extension_list);
|
|
project->pattern_array = get_pattern_array_from_extension_list(arena, extension_list);
|
|
project->blacklist_pattern_array = get_standard_blacklist(arena);
|
|
}
|
|
|
|
b32 open_recursively = false;
|
|
if (config_bool_var(parsed, "open_recursively", 0, &open_recursively)){
|
|
project->load_path_array.paths[0].recursive = open_recursively;
|
|
}
|
|
|
|
char fkey_command_name[] = "fkey_command_" PlatformName;
|
|
|
|
project->command_array.commands = push_array(arena, Project_Command, 16);
|
|
project->command_array.count = 16;
|
|
|
|
Project_Command *command = project->command_array.commands;
|
|
for (i32 j = 1; j <= 16; ++j, ++command){
|
|
project->fkey_commands[j - 1] = j - 1;
|
|
memset(command, 0, sizeof(*command));
|
|
|
|
Config_Compound *compound = 0;
|
|
if (config_compound_var(parsed, fkey_command_name, j, &compound)){
|
|
command->name = string_push(arena, 4);
|
|
append_int_to_str(&command->name, j);
|
|
terminate_with_null(&command->name);
|
|
|
|
String cmd = {};
|
|
if (config_compound_string_member(parsed, compound, "cmd", 0, &cmd)){
|
|
command->cmd = string_push_copy(arena, cmd);
|
|
}
|
|
|
|
String out = {};
|
|
if (config_compound_string_member(parsed, compound, "out", 1, &out)){
|
|
command->out = string_push_copy(arena, out);
|
|
}
|
|
|
|
b32 footer_panel = false;
|
|
if (config_compound_bool_member(parsed, compound, "footer_panel", 2, &footer_panel)){
|
|
command->footer_panel = footer_panel;
|
|
}
|
|
|
|
b32 save_dirty_files = false;
|
|
if (config_compound_bool_member(parsed, compound, "save_dirty_files", 3, &save_dirty_files)){
|
|
command->save_dirty_files = save_dirty_files;
|
|
}
|
|
}
|
|
else{
|
|
command->name = string_push(arena, 4);
|
|
append_int_to_str(&command->name, j);
|
|
terminate_with_null(&command->name);
|
|
}
|
|
}
|
|
|
|
project->loaded = true;
|
|
return(project);
|
|
}
|
|
|
|
static void
|
|
parse_project__extract_pattern_array(Partition *arena, Config *parsed,
|
|
char *root_variable_name,
|
|
Project_File_Pattern_Array *array_out){
|
|
Config_Compound *compound = 0;
|
|
if (config_compound_var(parsed, root_variable_name, 0, &compound)){
|
|
Config_Get_Result_List list = typed_string_array_reference_list(arena, parsed, compound);
|
|
|
|
array_out->patterns = push_array(arena, Project_File_Pattern, list.count);
|
|
array_out->count = list.count;
|
|
|
|
i32 i = 0;
|
|
for (Config_Get_Result_Node *node = list.first;
|
|
node != 0;
|
|
node = node->next, i += 1){
|
|
String str = string_push_copy(arena, node->result.string);
|
|
get_absolutes(str, &array_out->patterns[i].absolutes, false, false);
|
|
}
|
|
}
|
|
}
|
|
|
|
static Project_OS_Match_Level
|
|
parse_project__version_1__os_match(String str, String this_os_str){
|
|
if (match(str, this_os_str)){
|
|
return(ProjectOSMatchLevel_ActiveMatch);
|
|
}
|
|
else if (match(str, make_lit_string("all"))){
|
|
return(ProjectOSMatchLevel_ActiveMatch);
|
|
}
|
|
else if (match(str, make_lit_string("default"))){
|
|
return(ProjectOSMatchLevel_PassiveMatch);
|
|
}
|
|
return(ProjectOSMatchLevel_NoMatch);
|
|
}
|
|
|
|
static Project*
|
|
parse_project__config_data__version_1(Partition *arena, String root_dir, Config *parsed){
|
|
Project *project = push_array(arena, Project, 1);
|
|
memset(project, 0, sizeof(*project));
|
|
|
|
// Set new project directory
|
|
{
|
|
i32 cap = root_dir.size + 1;
|
|
project->dir = string_push(arena, cap);
|
|
copy(&project->dir, root_dir);
|
|
terminate_with_null(&project->dir);
|
|
}
|
|
|
|
// project_name
|
|
{
|
|
String str = {};
|
|
if (config_string_var(parsed, "project_name", 0, &str)){
|
|
project->name = string_push_copy(arena, str);
|
|
}
|
|
else{
|
|
project->name = make_lit_string("");
|
|
}
|
|
}
|
|
|
|
// patterns
|
|
parse_project__extract_pattern_array(arena, parsed, "patterns", &project->pattern_array);
|
|
|
|
// blacklist_patterns
|
|
parse_project__extract_pattern_array(arena, parsed, "blacklist_patterns", &project->blacklist_pattern_array);
|
|
|
|
// load_paths
|
|
{
|
|
Config_Compound *compound = 0;
|
|
if (config_compound_var(parsed, "load_paths", 0, &compound)){
|
|
b32 found_match = false;
|
|
Config_Compound *best_paths = 0;
|
|
|
|
for (i32 i = 0;; ++i){
|
|
Config_Iteration_Step_Result result = typed_array_iteration_step(parsed, compound, ConfigRValueType_Compound, i);
|
|
if (result.step == Iteration_Skip){
|
|
continue;
|
|
}
|
|
else if (result.step == Iteration_Quit){
|
|
break;
|
|
}
|
|
Config_Compound *paths_option = result.get.compound;
|
|
|
|
Config_Compound *paths = 0;
|
|
if (config_compound_compound_member(parsed, paths_option, "paths", 0, &paths)){
|
|
String str = {};
|
|
if (config_compound_string_member(parsed, paths_option, "os", 1, &str)){
|
|
Project_OS_Match_Level r = parse_project__version_1__os_match(str, make_lit_string(PlatformName));
|
|
if (r == ProjectOSMatchLevel_ActiveMatch){
|
|
found_match = true;
|
|
best_paths = paths;
|
|
break;
|
|
}
|
|
else if (r == ProjectOSMatchLevel_PassiveMatch){
|
|
if (!found_match){
|
|
found_match = true;
|
|
best_paths = paths;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (found_match){
|
|
Config_Get_Result_List list = typed_compound_array_reference_list(arena, parsed, best_paths);
|
|
|
|
project->load_path_array.paths = push_array(arena, Project_File_Load_Path, list.count);
|
|
project->load_path_array.count = list.count;
|
|
|
|
Project_File_Load_Path *dst = project->load_path_array.paths;
|
|
for (Config_Get_Result_Node *node = list.first;
|
|
node != 0;
|
|
node = node->next, ++dst){
|
|
Config_Compound *src = node->result.compound;
|
|
memset(dst, 0, sizeof(*dst));
|
|
dst->recursive = true;
|
|
dst->relative = true;
|
|
|
|
String str = {};
|
|
if (config_compound_string_member(parsed, src, "path", 0, &str)){
|
|
dst->path = string_push_copy(arena, str);
|
|
}
|
|
|
|
config_compound_bool_member(parsed, src, "recursive", 1, &dst->recursive);
|
|
config_compound_bool_member(parsed, src, "relative", 2, &dst->relative);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// command_list
|
|
{
|
|
Config_Compound *compound = 0;
|
|
if (config_compound_var(parsed, "command_list", 0, &compound)){
|
|
Config_Get_Result_List list = typed_compound_array_reference_list(arena, parsed, compound);
|
|
|
|
project->command_array.commands = push_array(arena, Project_Command, list.count);
|
|
project->command_array.count = list.count;
|
|
|
|
Project_Command *dst = project->command_array.commands;
|
|
for (Config_Get_Result_Node *node = list.first;
|
|
node != 0;
|
|
node = node->next, ++dst){
|
|
char *pos = node->result.pos;
|
|
Config_Compound *src = node->result.compound;
|
|
memset(dst, 0, sizeof(*dst));
|
|
|
|
b32 can_emit_command = true;
|
|
|
|
String name = {};
|
|
Config_Get_Result cmd_result = {};
|
|
Config_Compound *cmd_set = 0;
|
|
char *cmd_pos = 0;
|
|
String cmd_str = {};
|
|
String out = {};
|
|
b32 footer_panel = false;
|
|
b32 save_dirty_files = true;
|
|
b32 cursor_at_end = false;
|
|
|
|
if (!config_compound_string_member(parsed, src, "name", 0, &name)){
|
|
can_emit_command = false;
|
|
config_add_error(arena, parsed, pos, "a command must have a string type name member");
|
|
goto finish_command;
|
|
}
|
|
|
|
cmd_result = config_compound_member(parsed, src,
|
|
make_lit_string("cmd"), 1);
|
|
if (cmd_result.success){
|
|
cmd_set = cmd_result.compound;
|
|
cmd_pos = cmd_result.pos;
|
|
}
|
|
else{
|
|
can_emit_command = false;
|
|
config_add_error(arena, parsed, pos, "a command must have an array type cmd member");
|
|
goto finish_command;
|
|
}
|
|
|
|
can_emit_command = false;
|
|
for (i32 j = 0;; ++j){
|
|
Config_Iteration_Step_Result result = typed_array_iteration_step(parsed, cmd_set, ConfigRValueType_Compound, j);
|
|
if (result.step == Iteration_Skip){
|
|
continue;
|
|
}
|
|
else if (result.step == Iteration_Quit){
|
|
break;
|
|
}
|
|
Config_Compound *cmd_option = result.get.compound;
|
|
|
|
String cmd = {};
|
|
if (config_compound_string_member(parsed, cmd_option, "cmd", 0, &cmd)){
|
|
String str = {};
|
|
if (config_compound_string_member(parsed, cmd_option, "os", 1, &str)){
|
|
Project_OS_Match_Level r = parse_project__version_1__os_match(str, make_lit_string(PlatformName));
|
|
if (r == ProjectOSMatchLevel_ActiveMatch){
|
|
can_emit_command = true;
|
|
cmd_str = cmd;
|
|
break;
|
|
}
|
|
else if (r == ProjectOSMatchLevel_PassiveMatch){
|
|
if (!can_emit_command){
|
|
can_emit_command = true;
|
|
cmd_str = cmd;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!can_emit_command){
|
|
config_add_error(arena, parsed, cmd_pos, "no usable command strings found in cmd");
|
|
goto finish_command;
|
|
}
|
|
|
|
config_compound_string_member(parsed, src, "out", 2, &out);
|
|
config_compound_bool_member(parsed, src, "footer_panel", 3, &footer_panel);
|
|
config_compound_bool_member(parsed, src, "save_dirty_files", 4,
|
|
&save_dirty_files);
|
|
config_compound_bool_member(parsed, src, "cursor_at_end", 5,
|
|
&cursor_at_end);
|
|
|
|
dst->name = string_push_copy(arena, name);
|
|
dst->cmd = string_push_copy(arena, cmd_str);
|
|
dst->out = string_push_copy(arena, out);
|
|
dst->footer_panel = footer_panel;
|
|
dst->save_dirty_files = save_dirty_files;
|
|
dst->cursor_at_end = cursor_at_end;
|
|
|
|
finish_command:;
|
|
}
|
|
}
|
|
}
|
|
|
|
// fkey_command
|
|
{
|
|
for (i32 i = 1; i <= 16; ++i){
|
|
String name = {};
|
|
i32 index = -1;
|
|
if (config_string_var(parsed, "fkey_command", i, &name)){
|
|
i32 count = project->command_array.count;
|
|
Project_Command *command_ptr = project->command_array.commands;
|
|
for (i32 j = 0; j < count; ++j, ++command_ptr){
|
|
if (match(command_ptr->name, name)){
|
|
index = j;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
project->fkey_commands[i - 1] = index;
|
|
}
|
|
}
|
|
|
|
project->loaded = true;
|
|
return(project);
|
|
}
|
|
|
|
static Project*
|
|
parse_project__config_data(Partition *arena, String file_dir, Config *parsed){
|
|
i32 version = 0;
|
|
if (parsed->version != 0){
|
|
version = *parsed->version;
|
|
}
|
|
|
|
switch (version){
|
|
case 0:
|
|
{
|
|
return(parse_project__config_data__version_0(arena, file_dir, parsed));
|
|
}break;
|
|
|
|
case 1:
|
|
{
|
|
return(parse_project__config_data__version_1(arena, file_dir, parsed));
|
|
}break;
|
|
|
|
default:
|
|
{
|
|
return(0);
|
|
}break;
|
|
}
|
|
}
|
|
|
|
static Project_Parse_Result
|
|
parse_project__data(Partition *arena, String file_name, String data, String file_dir){
|
|
Project_Parse_Result result = {};
|
|
Cpp_Token_Array array = text_data_to_token_array(arena, data);
|
|
if (array.tokens != 0){
|
|
result.parsed = text_data_and_token_array_to_parse_data(arena, file_name, data, array);
|
|
if (result.parsed != 0){
|
|
result.project = parse_project__config_data(arena, file_dir, result.parsed);
|
|
}
|
|
}
|
|
return(result);
|
|
}
|
|
|
|
static Project_Parse_Result
|
|
parse_project__nearest_file(Application_Links *app, Partition *arena){
|
|
Project_Parse_Result result = {};
|
|
|
|
Temp_Memory restore_point = begin_temp_memory(arena);
|
|
String project_path = {};
|
|
i32 size = 32 << 10;
|
|
char *space = push_array(arena, char, size);
|
|
if (space != 0){
|
|
project_path = make_string_cap(space, 0, size);
|
|
project_path.size = directory_get_hot(app, project_path.str, project_path.memory_size);
|
|
end_temp_memory(restore_point);
|
|
push_array(arena, char, project_path.size);
|
|
project_path.memory_size = project_path.size;
|
|
if (project_path.size == 0){
|
|
print_message(app, literal("The hot directory is empty, cannot search for a project.\n"));
|
|
}
|
|
else{
|
|
File_Name_Path_Data dump = dump_file_search_up_path(arena, project_path, make_lit_string("project.4coder"));
|
|
if (dump.data.str != 0){
|
|
String project_root = dump.path;
|
|
remove_last_folder(&project_root);
|
|
result = parse_project__data(arena, dump.file_name, dump.data, project_root);
|
|
}
|
|
else{
|
|
char message_space[512];
|
|
String message = make_fixed_width_string(message_space);
|
|
append(&message, "Did not find project.4coder. ");
|
|
if (current_project.loaded){
|
|
if (current_project.name.size > 0){
|
|
append(&message, "Continuing with: ");
|
|
append(&message, current_project.name);
|
|
}
|
|
else{
|
|
append(&message, "Continuing with: ");
|
|
append(&message, current_project.dir);
|
|
}
|
|
}
|
|
else{
|
|
append(&message, "Continuing without a project");
|
|
}
|
|
append(&message, '\n');
|
|
print_message(app, message.str, message.size);
|
|
}
|
|
}
|
|
}
|
|
|
|
return(result);
|
|
}
|
|
|
|
static b32
|
|
project_deep_copy__pattern_array(Partition *arena, Project_File_Pattern_Array *src_array,
|
|
Project_File_Pattern_Array *dst_array){
|
|
i32 pattern_count = src_array->count;
|
|
dst_array->patterns = push_array(arena, Project_File_Pattern, pattern_count);
|
|
if (dst_array->patterns == 0){
|
|
return(false);
|
|
}
|
|
dst_array->count = pattern_count;
|
|
|
|
Project_File_Pattern *dst = dst_array->patterns;
|
|
Project_File_Pattern *src = src_array->patterns;
|
|
for (i32 i = 0; i < pattern_count; ++i, ++dst, ++src){
|
|
i32 abs_count = src->absolutes.count;
|
|
dst->absolutes.count = abs_count;
|
|
for (i32 j = 0; j < abs_count; ++j){
|
|
dst->absolutes.a[j] = string_push_copy(arena, src->absolutes.a[j]);
|
|
if (dst->absolutes.a[j].str == 0){
|
|
return(false);
|
|
}
|
|
}
|
|
}
|
|
|
|
return(true);
|
|
}
|
|
|
|
static Project
|
|
project_deep_copy__inner(Partition *arena, Project *project){
|
|
Project result = {};
|
|
|
|
result.dir = string_push_copy(arena, project->dir);
|
|
if (result.dir.str == 0){
|
|
return(result);
|
|
}
|
|
|
|
result.name = string_push_copy(arena, project->name);
|
|
if (result.name.str == 0){
|
|
return(result);
|
|
}
|
|
|
|
if (!project_deep_copy__pattern_array(arena, &project->pattern_array, &result.pattern_array)){
|
|
return(result);
|
|
}
|
|
if (!project_deep_copy__pattern_array(arena, &project->blacklist_pattern_array, &result.blacklist_pattern_array)){
|
|
return(result);
|
|
}
|
|
|
|
{
|
|
i32 path_count = project->load_path_array.count;
|
|
result.load_path_array.paths = push_array(arena, Project_File_Load_Path, path_count);
|
|
if (result.load_path_array.paths == 0){
|
|
return(result);
|
|
}
|
|
result.load_path_array.count = path_count;
|
|
|
|
Project_File_Load_Path *dst = result.load_path_array.paths;
|
|
Project_File_Load_Path *src = project->load_path_array.paths;
|
|
for (i32 i = 0; i < path_count; ++i, ++dst, ++src){
|
|
dst->path = string_push_copy(arena, src->path);
|
|
if (dst->path.str == 0){
|
|
return(result);
|
|
}
|
|
dst->recursive = src->recursive;
|
|
dst->relative = src->relative;
|
|
}
|
|
}
|
|
|
|
{
|
|
i32 command_count = project->command_array.count;
|
|
result.command_array.commands = push_array(arena, Project_Command, command_count);
|
|
if (result.command_array.commands == 0){
|
|
return(result);
|
|
}
|
|
result.command_array.count = command_count;
|
|
|
|
Project_Command *dst = result.command_array.commands;
|
|
Project_Command *src = project->command_array.commands;
|
|
for (i32 i = 0; i < command_count; ++i, ++dst, ++src){
|
|
memset(dst, 0, sizeof(*dst));
|
|
if (src->name.str != 0){
|
|
dst->name = string_push_copy(arena, src->name);
|
|
if (dst->name.str == 0){
|
|
return(result);
|
|
}
|
|
}
|
|
if (src->cmd.str != 0){
|
|
dst->cmd = string_push_copy(arena, src->cmd);
|
|
if (dst->cmd.str == 0){
|
|
return(result);
|
|
}
|
|
}
|
|
if (src->out.str != 0){
|
|
dst->out = string_push_copy(arena, src->out);
|
|
if (dst->out.str == 0){
|
|
return(result);
|
|
}
|
|
}
|
|
dst->footer_panel = src->footer_panel;
|
|
dst->save_dirty_files = src->save_dirty_files;
|
|
dst->cursor_at_end = src->cursor_at_end;
|
|
}
|
|
}
|
|
|
|
{
|
|
memcpy(result.fkey_commands, project->fkey_commands, sizeof(result.fkey_commands));
|
|
}
|
|
|
|
result.loaded = true;
|
|
return(result);
|
|
}
|
|
|
|
static Project
|
|
project_deep_copy(Partition *arena, Project *project){
|
|
Temp_Memory temp = begin_temp_memory(arena);
|
|
Project result = project_deep_copy__inner(arena, project);
|
|
if (!result.loaded){
|
|
end_temp_memory(temp);
|
|
memset(&result, 0, sizeof(result));
|
|
}
|
|
return(result);
|
|
}
|
|
|
|
static void
|
|
config_feedback_file_pattern_array(String *space, char *name, Project_File_Pattern_Array *array){
|
|
append(space, name);
|
|
append(space, " = {\n");
|
|
Project_File_Pattern *pattern = array->patterns;
|
|
for (i32 i = 0; i < array->count; ++i, ++pattern){
|
|
append(space, "\"");
|
|
String *absolute = pattern->absolutes.a;
|
|
for (i32 j = 0; j < pattern->absolutes.count; ++j, ++absolute){
|
|
if (j != 0){
|
|
append(space, "*");
|
|
}
|
|
append(space, *absolute);
|
|
}
|
|
append(space, "\",\n");
|
|
}
|
|
append(space, "};\n");
|
|
}
|
|
|
|
static void
|
|
config_feedback_file_load_path_array(String *space, char *name, Project_File_Load_Path_Array *array){
|
|
append(space, name);
|
|
append(space, " = {\n");
|
|
Project_File_Load_Path *path = array->paths;
|
|
for (i32 i = 0; i < array->count; ++i, ++path){
|
|
append(space, "{ ");
|
|
append(space, ".path = \"");
|
|
append(space, path->path);
|
|
append(space, "\", ");
|
|
append(space, ".recursive = ");
|
|
append(space, (char*)(path->recursive?"true":"false"));
|
|
append(space, ", ");
|
|
append(space, ".relative = ");
|
|
append(space, (char*)(path->relative?"true":"false"));
|
|
append(space, ", ");
|
|
append(space, "},\n");
|
|
}
|
|
append(space, "};\n");
|
|
}
|
|
|
|
static void
|
|
config_feedback_command_array(String *space, char *name, Project_Command_Array *array){
|
|
append(space, name);
|
|
append(space, " = {\n");
|
|
Project_Command *command = array->commands;
|
|
for (i32 i = 0; i < array->count; ++i, ++command){
|
|
append(space, "{ ");
|
|
append(space, ".name = \"");
|
|
append(space, command->name);
|
|
append(space, "\", ");
|
|
append(space, ".cmd = \"");
|
|
append(space, command->cmd);
|
|
append(space, "\", ");
|
|
append(space, ".out = \"");
|
|
append(space, command->out);
|
|
append(space, "\", ");
|
|
append(space, ".footer_panel = ");
|
|
append(space, (char*)(command->footer_panel?"true":"false"));
|
|
append(space, ", ");
|
|
append(space, ".save_dirty_files = ");
|
|
append(space, (char*)(command->save_dirty_files?"true":"false"));
|
|
append(space, ", ");
|
|
append(space, ".cursor_at_end = ");
|
|
append(space, (char*)(command->cursor_at_end?"true":"false"));
|
|
append(space, ", ");
|
|
append(space, "},\n");
|
|
}
|
|
append(space, "};\n");
|
|
}
|
|
|
|
static void
|
|
set_current_project(Application_Links *app, Partition *scratch, Project *project, Config *parsed){
|
|
b32 print_feedback = false;
|
|
|
|
if (parsed != 0 && project != 0){
|
|
if (current_project_arena.base == 0){
|
|
i32 project_mem_size = (1 << 20);
|
|
void *project_mem = memory_allocate(app, project_mem_size);
|
|
current_project_arena = make_part(project_mem, project_mem_size);
|
|
}
|
|
|
|
// Copy project to current_project
|
|
current_project_arena.pos = 0;
|
|
Project new_project = project_deep_copy(¤t_project_arena, project);
|
|
if (new_project.loaded){
|
|
current_project = new_project;
|
|
|
|
print_feedback = true;
|
|
|
|
// Open all project files
|
|
for (i32 i = 0; i < current_project.load_path_array.count; ++i){
|
|
Project_File_Load_Path *load_path = ¤t_project.load_path_array.paths[i];
|
|
u32 flags = 0;
|
|
if (load_path->recursive){
|
|
flags |= OpenAllFilesFlag_Recursive;
|
|
}
|
|
|
|
Temp_Memory temp = begin_temp_memory(scratch);
|
|
String path_str = load_path->path;
|
|
String file_dir = path_str;
|
|
if (load_path->relative){
|
|
String project_dir = current_project.dir;
|
|
i32 cap = path_str.size + project_dir.size + 2;
|
|
char *mem = push_array(scratch, char, cap);
|
|
push_align(scratch, 8);
|
|
if (mem != 0){
|
|
file_dir = make_string_cap(mem, 0, cap);
|
|
append(&file_dir, project_dir);
|
|
if (file_dir.size == 0 || file_dir.str[file_dir.size - 1] != '/'){
|
|
append(&file_dir, "/");
|
|
}
|
|
append(&file_dir, path_str);
|
|
if (file_dir.size == 0 || file_dir.str[file_dir.size - 1] != '/'){
|
|
append(&file_dir, "/");
|
|
}
|
|
terminate_with_null(&file_dir);
|
|
}
|
|
}
|
|
|
|
Project_File_Pattern_Array whitelist = current_project.pattern_array;
|
|
Project_File_Pattern_Array blacklist = current_project.blacklist_pattern_array;
|
|
open_all_files_in_directory_pattern_match(app, scratch, file_dir,
|
|
whitelist, blacklist,
|
|
flags);
|
|
|
|
end_temp_memory(temp);
|
|
}
|
|
|
|
// Set window title
|
|
if (project->name.size > 0){
|
|
char space[1024];
|
|
String builder = make_fixed_width_string(space);
|
|
append(&builder, "4coder project: ");
|
|
append(&builder, project->name);
|
|
terminate_with_null(&builder);
|
|
set_window_title(app, builder.str);
|
|
}
|
|
}
|
|
else{
|
|
#define M "Failed to initialize new project; need more memory dedicated to the project system.\n"
|
|
print_message(app, literal(M));
|
|
#undef M
|
|
}
|
|
}
|
|
else if (parsed != 0){
|
|
print_feedback = true;
|
|
}
|
|
|
|
if (print_feedback){
|
|
Temp_Memory temp = begin_temp_memory(scratch);
|
|
|
|
// Top
|
|
print_message(app, literal("Loaded project file:\n"));
|
|
|
|
// Errors
|
|
String error_text = config_stringize_errors(scratch, parsed);
|
|
print_message(app, error_text.str, error_text.size);
|
|
|
|
// Values
|
|
if (project == 0){
|
|
print_message(app, literal("Could not instantiate project\n"));
|
|
}
|
|
else{
|
|
Temp_Memory temp2 = begin_temp_memory(scratch);
|
|
String space = string_push(scratch, part_remaining(scratch));
|
|
|
|
{
|
|
config_feedback_string(&space, "'root_directory'", project->dir);
|
|
config_feedback_string(&space, "project_name", project->name);
|
|
|
|
config_feedback_file_pattern_array(&space, "patterns", &project->pattern_array);
|
|
config_feedback_file_pattern_array(&space, "blacklist_patterns", &project->blacklist_pattern_array);
|
|
config_feedback_file_load_path_array(&space, "load_paths", &project->load_path_array);
|
|
config_feedback_command_array(&space, "command_list", &project->command_array);
|
|
}
|
|
|
|
append(&space, "\n");
|
|
print_message(app, space.str, space.size);
|
|
end_temp_memory(temp2);
|
|
}
|
|
|
|
end_temp_memory(temp);
|
|
}
|
|
}
|
|
|
|
static void
|
|
set_current_project_from_data(Application_Links *app, Partition *scratch,
|
|
String file_name, String data, String file_dir){
|
|
Temp_Memory temp = begin_temp_memory(scratch);
|
|
Project_Parse_Result project_parse = parse_project__data(scratch, file_name, data, file_dir);
|
|
set_current_project(app, scratch, project_parse.project, project_parse.parsed);
|
|
end_temp_memory(temp);
|
|
}
|
|
|
|
static void
|
|
set_current_project_from_nearest_project_file(Application_Links *app, Partition *scratch){
|
|
Temp_Memory temp = begin_temp_memory(scratch);
|
|
Project_Parse_Result project_parse = parse_project__nearest_file(app, scratch);
|
|
set_current_project(app, scratch, project_parse.project, project_parse.parsed);
|
|
end_temp_memory(temp);
|
|
}
|
|
|
|
static void
|
|
exec_project_command(Application_Links *app, Project_Command *command){
|
|
if (command->cmd.size > 0){
|
|
b32 footer_panel = command->footer_panel;
|
|
b32 save_dirty_files = command->save_dirty_files;
|
|
b32 cursor_at_end = command->cursor_at_end;
|
|
|
|
if (save_dirty_files){
|
|
save_all_dirty_buffers(app);
|
|
}
|
|
|
|
View_Summary view = {};
|
|
View_Summary *view_ptr = 0;
|
|
Buffer_Identifier buffer_id = {};
|
|
u32 flags = CLI_OverlapWithConflict;
|
|
if (cursor_at_end){
|
|
flags |= CLI_CursorAtEnd;
|
|
}
|
|
|
|
b32 set_fancy_font = false;
|
|
if (command->out.size > 0){
|
|
buffer_id = buffer_identifier(command->out.str, command->out.size);
|
|
|
|
if (footer_panel){
|
|
view = get_or_open_build_panel(app);
|
|
if (match(command->out, "*compilation*")){
|
|
set_fancy_font = true;
|
|
}
|
|
}
|
|
else{
|
|
view = get_active_view(app, AccessAll);
|
|
}
|
|
view_ptr = &view;
|
|
|
|
memset(&prev_location, 0, sizeof(prev_location));
|
|
lock_jump_buffer(command->out.str, command->out.size);
|
|
}
|
|
else{
|
|
// TODO(allen): fix the exec_system_command call so it can take a null buffer_id.
|
|
buffer_id = buffer_identifier(literal("*dump*"));
|
|
}
|
|
|
|
String dir = current_project.dir;
|
|
String cmd = command->cmd;
|
|
exec_system_command(app, view_ptr, buffer_id, dir.str, dir.size, cmd.str, cmd.size, flags);
|
|
if (set_fancy_font){
|
|
set_fancy_compilation_buffer_font(app);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
exec_project_command_by_index(Application_Links *app, i32 command_index){
|
|
if (!current_project.loaded){
|
|
return;
|
|
}
|
|
if (command_index < 0 || command_index >= current_project.command_array.count){
|
|
return;
|
|
}
|
|
Project_Command *command = ¤t_project.command_array.commands[command_index];
|
|
exec_project_command(app, command);
|
|
}
|
|
|
|
static void
|
|
exec_project_fkey_command(Application_Links *app, i32 fkey_index){
|
|
if (!current_project.loaded){
|
|
return;
|
|
}
|
|
i32 command_index = current_project.fkey_commands[fkey_index];
|
|
if (command_index < 0 || command_index >= current_project.command_array.count){
|
|
return;
|
|
}
|
|
Project_Command *command = ¤t_project.command_array.commands[command_index];
|
|
exec_project_command(app, command);
|
|
}
|
|
|
|
static void
|
|
exec_project_command_by_name(Application_Links *app, String name){
|
|
if (!current_project.loaded){
|
|
return;
|
|
}
|
|
Project_Command *command = current_project.command_array.commands;
|
|
for (i32 i = 0; i < current_project.command_array.count; ++i, ++command){
|
|
if (match(command->name, name)){
|
|
exec_project_command(app, command);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
exec_project_command_by_name(Application_Links *app, char *name){
|
|
exec_project_command_by_name(app, make_string_slowly(name));
|
|
}
|
|
|
|
////////////////////////////////
|
|
|
|
CUSTOM_COMMAND_SIG(close_all_code)
|
|
CUSTOM_DOC("Closes any buffer with a filename ending with an extension configured to be recognized as a code file type.")
|
|
{
|
|
CString_Array extensions = get_code_extensions(&global_config.code_exts);
|
|
close_all_files_with_extension(app, &global_part, extensions);
|
|
}
|
|
|
|
CUSTOM_COMMAND_SIG(open_all_code)
|
|
CUSTOM_DOC("Open all code in the current directory. File types are determined by extensions. An extension is considered code based on the extensions specified in 4coder.config.")
|
|
{
|
|
CString_Array extensions = get_code_extensions(&global_config.code_exts);
|
|
open_all_files_in_hot_with_extension(app, &global_part, extensions, 0);
|
|
}
|
|
|
|
CUSTOM_COMMAND_SIG(open_all_code_recursive)
|
|
CUSTOM_DOC("Works as open_all_code but also runs in all subdirectories.")
|
|
{
|
|
CString_Array extensions = get_code_extensions(&global_config.code_exts);
|
|
open_all_files_in_hot_with_extension(app, &global_part, extensions, OpenAllFilesFlag_Recursive);
|
|
}
|
|
|
|
///////////////////////////////
|
|
|
|
CUSTOM_COMMAND_SIG(load_project)
|
|
CUSTOM_DOC("Looks for a project.4coder file in the current directory and tries to load it. Looks in parent directories until a project file is found or there are no more parents.")
|
|
{
|
|
save_all_dirty_buffers(app);
|
|
set_current_project_from_nearest_project_file(app, &global_part);
|
|
}
|
|
|
|
CUSTOM_COMMAND_SIG(project_fkey_command)
|
|
CUSTOM_DOC("Run an 'fkey command' configured in a project.4coder file. Determines the index of the 'fkey command' by which function key or numeric key was pressed to trigger the command.")
|
|
{
|
|
User_Input input = get_command_input(app);
|
|
b32 got_ind = false;
|
|
i32 ind = 0;
|
|
if (input.key.keycode >= key_f1 && input.key.keycode <= key_f16){
|
|
ind = (input.key.keycode - key_f1);
|
|
got_ind = true;
|
|
}
|
|
else if (input.key.character_no_caps_lock >= '1' && input.key.character_no_caps_lock >= '9'){
|
|
ind = (input.key.character_no_caps_lock - '1');
|
|
got_ind = true;
|
|
}
|
|
else if (input.key.character_no_caps_lock == '0'){
|
|
ind = 9;
|
|
got_ind = true;
|
|
}
|
|
if (got_ind){
|
|
exec_project_fkey_command(app, ind);
|
|
}
|
|
}
|
|
|
|
CUSTOM_COMMAND_SIG(project_go_to_root_directory)
|
|
CUSTOM_DOC("Changes 4coder's hot directory to the root directory of the currently loaded project. With no loaded project nothing hapepns.")
|
|
{
|
|
if (current_project.loaded){
|
|
String dir = current_project.dir;
|
|
directory_set_hot(app, dir.str, dir.size);
|
|
}
|
|
}
|
|
|
|
///////////////////////////////
|
|
|
|
static Project_Setup_Status
|
|
project_is_setup(Application_Links *app, Partition *scratch, String script_path, String script_file){
|
|
Project_Setup_Status result = {};
|
|
|
|
Temp_Memory temp = begin_temp_memory(scratch);
|
|
|
|
static i32 needed_extra_space = 15;
|
|
char *space = push_array(&global_part, char, script_path.size + needed_extra_space);
|
|
String str = make_string_cap(space, 0, script_path.size + needed_extra_space);
|
|
copy(&str, script_path);
|
|
|
|
i32 dir_len = str.size;
|
|
append(&str, "/");
|
|
append(&str, script_file);
|
|
append(&str, ".bat");
|
|
result.bat_exists = file_exists(app, str.str, str.size);
|
|
|
|
str.size = dir_len;
|
|
append(&str, "/");
|
|
append(&str, script_file);
|
|
append(&str, ".sh");
|
|
result.sh_exists = file_exists(app, str.str, str.size);
|
|
|
|
str.size = dir_len;
|
|
append(&str, "/project.4coder");
|
|
result.project_exists = file_exists(app, str.str, str.size);
|
|
|
|
result.everything_exists = result.bat_exists && result.sh_exists && result.project_exists;
|
|
|
|
end_temp_memory(temp);
|
|
|
|
return(result);
|
|
}
|
|
|
|
static Project_Key_Strings
|
|
project_key_strings_query_user(Application_Links *app,
|
|
b32 get_script_file, b32 get_code_file,
|
|
char *script_file_space, i32 script_file_cap,
|
|
char *code_file_space, i32 code_file_cap,
|
|
char *output_dir_space, i32 output_dir_cap,
|
|
char *binary_file_space, i32 binary_file_cap){
|
|
Project_Key_Strings keys = {};
|
|
|
|
Query_Bar script_file_bar = {};
|
|
Query_Bar code_file_bar = {};
|
|
Query_Bar output_dir_bar = {};
|
|
Query_Bar binary_file_bar = {};
|
|
|
|
if (get_script_file){
|
|
script_file_bar.prompt = make_lit_string("Script Name: ");
|
|
script_file_bar.string = make_string_cap(script_file_space, 0, script_file_cap);
|
|
|
|
if (!query_user_string(app, &script_file_bar)) return(keys);
|
|
if (script_file_bar.string.size == 0) return(keys);
|
|
}
|
|
|
|
if (get_code_file){
|
|
code_file_bar.prompt = make_lit_string("Build Target: ");
|
|
code_file_bar.string = make_string_cap(code_file_space, 0, code_file_cap);
|
|
|
|
if (!query_user_string(app, &code_file_bar)) return(keys);
|
|
if (code_file_bar.string.size == 0) return(keys);
|
|
}
|
|
|
|
output_dir_bar.prompt = make_lit_string("Output Directory: ");
|
|
output_dir_bar.string = make_string_cap(output_dir_space, 0, output_dir_cap);
|
|
|
|
if (!query_user_string(app, &output_dir_bar)) return(keys);
|
|
if (output_dir_bar.string.size == 0){
|
|
copy(&output_dir_bar.string, ".");
|
|
}
|
|
|
|
|
|
binary_file_bar.prompt = make_lit_string("Binary Output: ");
|
|
binary_file_bar.string = make_string_cap(binary_file_space, 0, binary_file_cap);
|
|
|
|
if (!query_user_string(app, &binary_file_bar)) return(keys);
|
|
if (binary_file_bar.string.size == 0) return(keys);
|
|
|
|
|
|
keys.success = true;
|
|
keys.script_file = script_file_bar.string;
|
|
keys.code_file = code_file_bar.string;
|
|
keys.output_dir = output_dir_bar.string;
|
|
keys.binary_file = binary_file_bar.string;
|
|
|
|
return(keys);
|
|
}
|
|
|
|
static b32
|
|
project_generate_bat_script(Partition *scratch, String opts, String compiler,
|
|
String script_path, String script_file,
|
|
String code_file, String output_dir, String binary_file){
|
|
b32 success = false;
|
|
|
|
Temp_Memory temp = begin_temp_memory(scratch);
|
|
|
|
String cf = string_push_copy(scratch, code_file);
|
|
String od = string_push_copy(scratch, output_dir);
|
|
String bf = string_push_copy(scratch, binary_file);
|
|
|
|
replace_char(&cf, '/', '\\');
|
|
replace_char(&od, '/', '\\');
|
|
replace_char(&bf, '/', '\\');
|
|
|
|
i32 space_cap = part_remaining(scratch);
|
|
char *space = push_array(scratch, char, space_cap);
|
|
String file_name = make_string_cap(space, 0, space_cap);
|
|
append(&file_name, script_path);
|
|
append(&file_name, "/");
|
|
append(&file_name, script_file);
|
|
append(&file_name, ".bat");
|
|
terminate_with_null(&file_name);
|
|
|
|
FILE *bat_script = fopen(file_name.str, "wb");
|
|
if (bat_script != 0){
|
|
fprintf(bat_script, "@echo off\n\n");
|
|
fprintf(bat_script, "set opts=%.*s\n", opts.size, opts.str);
|
|
fprintf(bat_script, "set code=%%cd%%\n");
|
|
|
|
fprintf(bat_script, "pushd %.*s\n", od.size, od.str);
|
|
fprintf(bat_script, "%.*s %%opts%% %%code%%\\%.*s -Fe%.*s\n",
|
|
compiler.size, compiler.str, cf.size, cf.str, bf.size, bf.str);
|
|
fprintf(bat_script, "popd\n");
|
|
|
|
fclose(bat_script);
|
|
success = true;
|
|
}
|
|
|
|
end_temp_memory(temp);
|
|
|
|
return(success);
|
|
}
|
|
|
|
static b32
|
|
project_generate_sh_script(Partition *scratch, String opts, String compiler,
|
|
String script_path, String script_file,
|
|
String code_file, String output_dir, String binary_file){
|
|
b32 success = false;
|
|
|
|
Temp_Memory temp = begin_temp_memory(scratch);
|
|
|
|
String cf = code_file;
|
|
String od = output_dir;
|
|
String bf = binary_file;
|
|
|
|
i32 space_cap = part_remaining(scratch);
|
|
char *space = push_array(scratch, char, space_cap);
|
|
String file_name = make_string_cap(space, 0, space_cap);
|
|
append(&file_name, script_path);
|
|
append(&file_name, "/");
|
|
append(&file_name, script_file);
|
|
append(&file_name, ".sh");
|
|
terminate_with_null(&file_name);
|
|
|
|
FILE *sh_script = fopen(file_name.str, "wb");
|
|
if (sh_script != 0){
|
|
fprintf(sh_script, "#!/bin/bash\n\n");
|
|
|
|
fprintf(sh_script, "code=\"$PWD\"\n");
|
|
|
|
fprintf(sh_script, "opts=%.*s\n", opts.size, opts.str);
|
|
|
|
fprintf(sh_script, "cd %.*s > /dev/null\n", od.size, od.str);
|
|
fprintf(sh_script, "%.*s $opts $code/%.*s -o %.*s\n",
|
|
compiler.size, compiler.str, cf.size, cf.str, bf.size, bf.str);
|
|
fprintf(sh_script, "cd $code > /dev/null\n");
|
|
|
|
fclose(sh_script);
|
|
success = true;
|
|
}
|
|
|
|
end_temp_memory(temp);
|
|
|
|
return(success);
|
|
}
|
|
|
|
static b32
|
|
project_generate_project_4coder_file(Partition *scratch,
|
|
String script_path, String script_file,
|
|
String output_dir, String binary_file){
|
|
b32 success = false;
|
|
|
|
Temp_Memory temp = begin_temp_memory(scratch);
|
|
|
|
String od = output_dir;
|
|
String bf = binary_file;
|
|
|
|
String od_win = string_push(scratch, od.size*2);
|
|
String bf_win = string_push(scratch, bf.size*2);
|
|
|
|
append(&od_win, od);
|
|
append(&bf_win, bf);
|
|
|
|
replace_str(&od_win, "/", "\\\\");
|
|
replace_str(&bf_win, "/", "\\\\");
|
|
|
|
i32 space_cap = part_remaining(scratch);
|
|
char *space = push_array(scratch, char, space_cap);
|
|
String file_name = make_string_cap(space, 0, space_cap);
|
|
append(&file_name, script_path);
|
|
append(&file_name, "/project.4coder");
|
|
terminate_with_null(&file_name);
|
|
|
|
FILE *out = fopen(file_name.str, "wb");
|
|
if (out != 0){
|
|
fprintf(out, "version(1);\n");
|
|
fprintf(out, "project_name = \"%.*s\";\n", binary_file.size, binary_file.str);
|
|
fprintf(out, "patterns = {\n");
|
|
fprintf(out, "\"*.c\",\n");
|
|
fprintf(out, "\"*.cpp\",\n");
|
|
fprintf(out, "\"*.h\",\n");
|
|
fprintf(out, "\"*.m\",\n");
|
|
fprintf(out, "\"*.bat\",\n");
|
|
fprintf(out, "\"*.sh\",\n");
|
|
fprintf(out, "\"*.4coder\",\n");
|
|
fprintf(out, "};\n");
|
|
fprintf(out, "blacklist_patterns = {\n");
|
|
fprintf(out, "\".*\",\n");
|
|
fprintf(out, "};\n");
|
|
fprintf(out, "load_paths_base = {\n");
|
|
fprintf(out, " { \".\", .relative = true, .recursive = true, },\n");
|
|
fprintf(out, "};\n");
|
|
fprintf(out, "load_paths = {\n");
|
|
fprintf(out, " { load_paths_base, .os = \"win\", },\n");
|
|
fprintf(out, " { load_paths_base, .os = \"linux\", },\n");
|
|
fprintf(out, " { load_paths_base, .os = \"mac\", },\n");
|
|
fprintf(out, "};\n");
|
|
|
|
fprintf(out, "\n");
|
|
|
|
fprintf(out, "command_list = {\n");
|
|
fprintf(out, " { .name = \"build\",\n");
|
|
fprintf(out, " .out = \"*compilation*\", .footer_panel = true, .save_dirty_files = true,\n");
|
|
fprintf(out, " .cmd = { { \"%.*s.bat\" , .os = \"win\" },\n",
|
|
script_file.size, script_file.str);
|
|
fprintf(out, " { \"./%.*s.sh\", .os = \"linux\" },\n",
|
|
script_file.size, script_file.str);
|
|
fprintf(out, " { \"./%.*s.sh\", .os = \"mac\" }, }, },\n",
|
|
script_file.size, script_file.str);
|
|
fprintf(out, " { .name = \"run\",\n");
|
|
fprintf(out, " .out = \"*run*\", .footer_panel = false, .save_dirty_files = false,\n");
|
|
fprintf(out, " .cmd = { { \"%.*s\\\\%.*s\", .os = \"win\" },\n",
|
|
od_win.size, od_win.str, bf_win.size, bf_win.str);
|
|
fprintf(out, " { \"%.*s/%.*s\" , .os = \"linux\" },\n",
|
|
od.size, od.str, bf.size, bf.str);
|
|
fprintf(out, " { \"%.*s/%.*s\" , .os = \"mac\" }, }, },\n",
|
|
od.size, od.str, bf.size, bf.str);
|
|
fprintf(out, "};\n");
|
|
|
|
fprintf(out, "fkey_command[1] = \"build\";\n");
|
|
fprintf(out, "fkey_command[2] = \"run\";\n");
|
|
|
|
fclose(out);
|
|
success = true;
|
|
}
|
|
|
|
end_temp_memory(temp);
|
|
|
|
return(success);
|
|
}
|
|
|
|
static void
|
|
project_setup_scripts__generic(Application_Links *app, Partition *scratch,
|
|
b32 do_project_file,
|
|
b32 do_bat_script,
|
|
b32 do_sh_script){
|
|
Temp_Memory temp = begin_temp_memory(scratch);
|
|
String script_path = get_hot_directory(app, scratch);
|
|
|
|
b32 needs_to_do_work = false;
|
|
Project_Setup_Status status = {};
|
|
if (do_project_file){
|
|
status = project_is_setup(app, scratch, script_path, make_lit_string("build"));
|
|
needs_to_do_work =
|
|
!status.project_exists ||
|
|
(do_bat_script && !status.bat_exists) ||
|
|
(do_sh_script && !status.sh_exists);
|
|
}
|
|
else{
|
|
needs_to_do_work = true;
|
|
}
|
|
|
|
if (needs_to_do_work){
|
|
// Query the User for Key File Names
|
|
char script_file_space[1024];
|
|
char code_file_space [1024];
|
|
char output_dir_space [1024];
|
|
char binary_file_space[1024];
|
|
|
|
b32 get_script_file = !do_project_file;
|
|
b32 get_code_file =
|
|
(do_bat_script && !status.bat_exists) ||
|
|
(do_sh_script && !status.sh_exists);
|
|
|
|
Project_Key_Strings keys =
|
|
project_key_strings_query_user(app,
|
|
get_script_file, get_code_file,
|
|
script_file_space, sizeof(script_file_space),
|
|
code_file_space, sizeof(code_file_space),
|
|
output_dir_space, sizeof(output_dir_space),
|
|
binary_file_space, sizeof(binary_file_space));
|
|
|
|
if (!keys.success){
|
|
return;
|
|
}
|
|
|
|
if (do_project_file){
|
|
keys.script_file = make_lit_string("build");
|
|
}
|
|
|
|
if (!do_project_file){
|
|
status = project_is_setup(app, scratch, script_path, keys.script_file);
|
|
}
|
|
|
|
// Generate Scripts
|
|
if (do_bat_script){
|
|
if (!status.bat_exists){
|
|
if (!project_generate_bat_script(scratch,
|
|
global_config.default_flags_bat,
|
|
global_config.default_compiler_bat,
|
|
script_path, keys.script_file,
|
|
keys.code_file, keys.output_dir, keys.binary_file)){
|
|
print_message(app, literal("could not create build.bat for new project\n"));
|
|
}
|
|
}
|
|
else{
|
|
print_message(app, literal("the batch script already exists, no changes made to it\n"));
|
|
}
|
|
}
|
|
|
|
if (do_sh_script){
|
|
if (!status.bat_exists){
|
|
if (!project_generate_sh_script(scratch,
|
|
global_config.default_flags_sh,
|
|
global_config.default_compiler_sh,
|
|
script_path, keys.script_file,
|
|
keys.code_file, keys.output_dir, keys.binary_file)){
|
|
print_message(app, literal("could not create build.sh for new project\n"));
|
|
}
|
|
}
|
|
else{
|
|
print_message(app, literal("the shell script already exists, no changes made to it\n"));
|
|
}
|
|
}
|
|
|
|
if (do_project_file){
|
|
if (!status.project_exists){
|
|
if (!project_generate_project_4coder_file(scratch, script_path, keys.script_file,
|
|
keys.output_dir, keys.binary_file)){
|
|
print_message(app, literal("could not create project.4coder for new project\n"));
|
|
}
|
|
}
|
|
else{
|
|
print_message(app, literal("project.4coder already exists, no changes made to it\n"));
|
|
}
|
|
}
|
|
}
|
|
else{
|
|
if (do_project_file){
|
|
print_message(app, literal("project already setup, no changes made\n"));
|
|
}
|
|
}
|
|
|
|
end_temp_memory(temp);
|
|
}
|
|
|
|
CUSTOM_COMMAND_SIG(setup_new_project)
|
|
CUSTOM_DOC("Queries the user for several configuration options and initializes a new 4coder project with build scripts for every OS.")
|
|
{
|
|
project_setup_scripts__generic(app, &global_part, true, true, true);
|
|
load_project(app);
|
|
}
|
|
|
|
CUSTOM_COMMAND_SIG(setup_build_bat)
|
|
CUSTOM_DOC("Queries the user for several configuration options and initializes a new build batch script.")
|
|
{
|
|
project_setup_scripts__generic(app, &global_part, false, true, false);
|
|
}
|
|
|
|
CUSTOM_COMMAND_SIG(setup_build_sh)
|
|
CUSTOM_DOC("Queries the user for several configuration options and initializes a new build shell script.")
|
|
{
|
|
project_setup_scripts__generic(app, &global_part, false, false, true);
|
|
}
|
|
|
|
CUSTOM_COMMAND_SIG(setup_build_bat_and_sh)
|
|
CUSTOM_DOC("Queries the user for several configuration options and initializes a new build batch script.")
|
|
{
|
|
project_setup_scripts__generic(app, &global_part, false, true, true);
|
|
}
|
|
|
|
///////////////////////////////
|
|
|
|
static void
|
|
activate_project_command(Application_Links *app, Partition *scratch, Heap *heap,
|
|
View_Summary *view, Lister_State *state,
|
|
String text_field, void *user_data, b32 activated_by_mouse){
|
|
i32 command_index = (i32)PtrAsInt(user_data);
|
|
exec_project_command_by_index(app, command_index);
|
|
lister_default(app, scratch, heap, view, state, ListerActivation_Finished);
|
|
}
|
|
|
|
CUSTOM_COMMAND_SIG(project_command_lister)
|
|
CUSTOM_DOC("Open a lister of all commands in the currently loaded project.")
|
|
{
|
|
if (!current_project.loaded){
|
|
return;
|
|
}
|
|
|
|
Partition *arena = &global_part;
|
|
|
|
View_Summary view = get_active_view(app, AccessAll);
|
|
view_end_ui_mode(app, &view);
|
|
Temp_Memory temp = begin_temp_memory(arena);
|
|
i32 option_count = current_project.command_array.count;
|
|
Lister_Option *options = push_array(arena, Lister_Option, option_count);
|
|
for (i32 i = 0; i < current_project.command_array.count; i += 1){
|
|
options[i].string = string_push_copy(arena, current_project.command_array.commands[i].name);
|
|
options[i].status = string_push_copy(arena, current_project.command_array.commands[i].cmd);
|
|
options[i].user_data = IntAsPtr(i);
|
|
}
|
|
begin_integrated_lister__basic_list(app, "Command:", activate_project_command, 0, 0,
|
|
options, option_count,
|
|
0,
|
|
&view);
|
|
end_temp_memory(temp);
|
|
}
|
|
|
|
// BOTTOM
|
|
|