1702 lines
60 KiB
C++
1702 lines
60 KiB
C++
|
|
#ifndef FCODER_DEFAULT_INCLUDE
|
|
#define FCODER_DEFAULT_INCLUDE
|
|
|
|
#include "4coder_API/custom.h"
|
|
|
|
|
|
#include "4coder_jump_parsing.cpp"
|
|
|
|
#include "4coder_default_framework.h"
|
|
#include "4coder_base_commands.cpp"
|
|
#include "4coder_auto_indent.cpp"
|
|
#include "4coder_search.cpp"
|
|
|
|
#include "4coder_helper/4coder_bind_helper.h"
|
|
#include "4coder_helper/4coder_helper.h"
|
|
#include "4coder_helper/4coder_streaming.h"
|
|
#include "4coder_helper/4coder_long_seek.h"
|
|
|
|
#define FSTRING_IMPLEMENTATION
|
|
#include "4coder_lib/4coder_string.h"
|
|
#include "4coder_lib/4coder_table.h"
|
|
#include "4coder_lib/4coder_mem.h"
|
|
|
|
#include "4cpp/4cpp_lexer.h"
|
|
|
|
#include <assert.h>
|
|
|
|
//
|
|
// Seeks Using Default Framework Memory
|
|
//
|
|
|
|
static int32_t
|
|
buffer_boundary_seek(Application_Links *app, Buffer_Summary *buffer, int32_t start_pos, bool32 seek_forward, Seek_Boundary_Flag flags){
|
|
int32_t result = buffer_boundary_seek(app, buffer, &global_part, start_pos, seek_forward, flags);
|
|
return(result);
|
|
}
|
|
|
|
static void
|
|
basic_seek(Application_Links *app, int32_t seek_type, uint32_t flags){
|
|
uint32_t access = AccessProtected;
|
|
View_Summary view = get_active_view(app, access);
|
|
Buffer_Summary buffer = get_buffer(app, view.buffer_id, access);
|
|
int32_t pos = buffer_boundary_seek(app, &buffer, view.cursor.pos, seek_type, flags);
|
|
view_set_cursor(app, &view, seek_pos(pos), true);
|
|
}
|
|
|
|
#define seek_command(n, dir, flags) CUSTOM_COMMAND_SIG(seek_##n##_##dir){ basic_seek(app, dir, flags); }
|
|
|
|
#define right true
|
|
#define left false
|
|
|
|
seek_command(whitespace, right, BoundaryWhitespace)
|
|
seek_command(whitespace, left, BoundaryWhitespace)
|
|
seek_command(token, right, BoundaryToken)
|
|
seek_command(token, left, BoundaryToken)
|
|
seek_command(white_or_token, right, BoundaryToken | BoundaryWhitespace)
|
|
seek_command(white_or_token, left, BoundaryToken | BoundaryWhitespace)
|
|
seek_command(alphanumeric, right, BoundaryAlphanumeric)
|
|
seek_command(alphanumeric, left, BoundaryAlphanumeric)
|
|
seek_command(alphanumeric_or_camel, right, BoundaryAlphanumeric | BoundaryCamelCase)
|
|
seek_command(alphanumeric_or_camel, left, BoundaryAlphanumeric | BoundaryCamelCase)
|
|
|
|
#undef right
|
|
#undef left
|
|
|
|
//
|
|
// Clipboard
|
|
//
|
|
|
|
static bool32
|
|
clipboard_copy(Application_Links *app, int32_t start, int32_t end, Buffer_Summary *buffer_out,
|
|
uint32_t access){
|
|
View_Summary view = get_active_view(app, access);
|
|
Buffer_Summary buffer = get_buffer(app, view.buffer_id, access);
|
|
bool32 result = 0;
|
|
|
|
if (buffer.exists){
|
|
if (0 <= start && start <= end && end <= buffer.size){
|
|
int32_t size = (end - start);
|
|
char *str = (char*)app->memory;
|
|
|
|
if (size <= app->memory_size){
|
|
buffer_read_range(app, &buffer, start, end, str);
|
|
clipboard_post(app, 0, str, size);
|
|
if (buffer_out){*buffer_out = buffer;}
|
|
result = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
return(result);
|
|
}
|
|
|
|
static int32_t
|
|
clipboard_cut(Application_Links *app, int32_t start, int32_t end, Buffer_Summary *buffer_out, uint32_t access){
|
|
Buffer_Summary buffer = {0};
|
|
int32_t result = false;
|
|
|
|
if (clipboard_copy(app, start, end, &buffer, access)){
|
|
buffer_replace_range(app, &buffer, start, end, 0, 0);
|
|
if (buffer_out){*buffer_out = buffer;}
|
|
}
|
|
|
|
return(result);
|
|
}
|
|
|
|
CUSTOM_COMMAND_SIG(copy){
|
|
uint32_t access = AccessProtected;
|
|
View_Summary view = get_active_view(app, access);
|
|
Range range = get_range(&view);
|
|
clipboard_copy(app, range.min, range.max, 0, access);
|
|
}
|
|
|
|
CUSTOM_COMMAND_SIG(cut){
|
|
uint32_t access = AccessOpen;
|
|
View_Summary view = get_active_view(app, access);
|
|
Range range = get_range(&view);
|
|
clipboard_cut(app, range.min, range.max, 0, access);
|
|
}
|
|
|
|
CUSTOM_COMMAND_SIG(paste){
|
|
uint32_t access = AccessOpen;
|
|
int32_t count = clipboard_count(app, 0);
|
|
if (count > 0){
|
|
View_Summary view = get_active_view(app, access);
|
|
|
|
view_paste_index[view.view_id].next_rewrite = RewritePaste;
|
|
|
|
int32_t paste_index = 0;
|
|
view_paste_index[view.view_id].index = paste_index;
|
|
|
|
int32_t len = clipboard_index(app, 0, paste_index, 0, 0);
|
|
char *str = 0;
|
|
|
|
if (len <= app->memory_size){
|
|
str = (char*)app->memory;
|
|
}
|
|
|
|
if (str){
|
|
clipboard_index(app, 0, paste_index, str, len);
|
|
|
|
Buffer_Summary buffer = get_buffer(app, view.buffer_id, access);
|
|
int32_t pos = view.cursor.pos;
|
|
buffer_replace_range(app, &buffer, pos, pos, str, len);
|
|
view_set_mark(app, &view, seek_pos(pos));
|
|
view_set_cursor(app, &view, seek_pos(pos + len), true);
|
|
|
|
// TODO(allen): Send this to all views.
|
|
Theme_Color paste;
|
|
paste.tag = Stag_Paste;
|
|
get_theme_colors(app, &paste, 1);
|
|
view_post_fade(app, &view, 0.667f, pos, pos + len, paste.color);
|
|
}
|
|
}
|
|
}
|
|
|
|
CUSTOM_COMMAND_SIG(paste_next){
|
|
uint32_t access = AccessOpen;
|
|
int32_t count = clipboard_count(app, 0);
|
|
if (count > 0){
|
|
View_Summary view = get_active_view(app, access);
|
|
|
|
if (view_paste_index[view.view_id].rewrite == RewritePaste){
|
|
view_paste_index[view.view_id].next_rewrite = RewritePaste;
|
|
|
|
int32_t paste_index = view_paste_index[view.view_id].index + 1;
|
|
view_paste_index[view.view_id].index = paste_index;
|
|
|
|
int32_t len = clipboard_index(app, 0, paste_index, 0, 0);
|
|
char *str = 0;
|
|
|
|
if (len <= app->memory_size){
|
|
str = (char*)app->memory;
|
|
}
|
|
|
|
if (str){
|
|
clipboard_index(app, 0, paste_index, str, len);
|
|
|
|
Buffer_Summary buffer = get_buffer(app, view.buffer_id, access);
|
|
Range range = get_range(&view);
|
|
int32_t pos = range.min;
|
|
|
|
buffer_replace_range(app, &buffer, range.min, range.max, str, len);
|
|
view_set_cursor(app, &view, seek_pos(pos + len), true);
|
|
|
|
// TODO(allen): Send this to all views.
|
|
Theme_Color paste;
|
|
paste.tag = Stag_Paste;
|
|
get_theme_colors(app, &paste, 1);
|
|
view_post_fade(app, &view, 0.667f, pos, pos + len, paste.color);
|
|
}
|
|
}
|
|
else{
|
|
exec_command(app, paste);
|
|
}
|
|
}
|
|
}
|
|
|
|
CUSTOM_COMMAND_SIG(paste_and_indent){
|
|
exec_command(app, paste);
|
|
exec_command(app, auto_tab_range);
|
|
}
|
|
|
|
CUSTOM_COMMAND_SIG(paste_next_and_indent){
|
|
exec_command(app, paste_next);
|
|
exec_command(app, auto_tab_range);
|
|
}
|
|
|
|
//////////////////////////
|
|
|
|
static void
|
|
write_string(Application_Links *app, View_Summary *view, Buffer_Summary *buffer, String string){
|
|
buffer_replace_range(app, buffer, view->cursor.pos, view->cursor.pos, string.str, string.size);
|
|
view_set_cursor(app, view, seek_pos(view->cursor.pos + string.size), 1);
|
|
}
|
|
|
|
static void
|
|
write_string(Application_Links *app, String string){
|
|
uint32_t access = AccessOpen;
|
|
View_Summary view = get_active_view(app, access);
|
|
Buffer_Summary buffer = get_buffer(app, view.buffer_id, access);
|
|
write_string(app, &view, &buffer, string);
|
|
}
|
|
|
|
static void
|
|
long_braces(Application_Links *app, char *text, int32_t size){
|
|
uint32_t access = AccessOpen;
|
|
View_Summary view = get_active_view(app, access);
|
|
Buffer_Summary buffer = get_buffer(app, view.buffer_id, access);
|
|
int32_t pos = view.cursor.pos;
|
|
|
|
buffer_replace_range(app, &buffer, pos, pos, text, size);
|
|
view_set_cursor(app, &view, seek_pos(pos + 2), true);
|
|
|
|
buffer_auto_indent(app, &buffer, pos, pos + size, DEF_TAB_WIDTH, DEFAULT_INDENT_FLAGS | AutoIndent_FullTokens);
|
|
move_past_lead_whitespace(app, &view, &buffer);
|
|
}
|
|
|
|
CUSTOM_COMMAND_SIG(open_long_braces){
|
|
char text[] = "{\n\n}";
|
|
int32_t size = sizeof(text) - 1;
|
|
long_braces(app, text, size);
|
|
}
|
|
|
|
CUSTOM_COMMAND_SIG(open_long_braces_semicolon){
|
|
char text[] = "{\n\n};";
|
|
int32_t size = sizeof(text) - 1;
|
|
long_braces(app, text, size);
|
|
}
|
|
|
|
CUSTOM_COMMAND_SIG(open_long_braces_break){
|
|
char text[] = "{\n\n}break;";
|
|
int32_t size = sizeof(text) - 1;
|
|
long_braces(app, text, size);
|
|
}
|
|
|
|
CUSTOM_COMMAND_SIG(if0_off){
|
|
char text1[] = "\n#if 0";
|
|
int32_t size1 = sizeof(text1) - 1;
|
|
|
|
char text2[] = "#endif\n";
|
|
int32_t size2 = sizeof(text2) - 1;
|
|
|
|
View_Summary view = get_active_view(app, AccessOpen);
|
|
Buffer_Summary buffer = get_buffer(app, view.buffer_id, AccessOpen);
|
|
|
|
Range range = get_range(&view);
|
|
|
|
if (range.min < range.max){
|
|
Buffer_Edit edits[2];
|
|
char *str = 0;
|
|
char *base = (char*)partition_current(&global_part);
|
|
|
|
str = push_array(&global_part, char, size1);
|
|
memcpy(str, text1, size1);
|
|
edits[0].str_start = (int32_t)(str - base);
|
|
edits[0].len = size1;
|
|
edits[0].start = range.min;
|
|
edits[0].end = range.min;
|
|
|
|
str = push_array(&global_part, char, size2);
|
|
memcpy(str, text2, size2);
|
|
edits[1].str_start = (int32_t)(str - base);
|
|
edits[1].len = size2;
|
|
edits[1].start = range.max;
|
|
edits[1].end = range.max;
|
|
|
|
buffer_batch_edit(app, &buffer, base, global_part.pos, edits, ArrayCount(edits), BatchEdit_Normal);
|
|
|
|
view = get_view(app, view.view_id, AccessAll);
|
|
if (view.cursor.pos > view.mark.pos){
|
|
view_set_cursor(app, &view, seek_line_char(view.cursor.line+1, view.cursor.character), 1);
|
|
}
|
|
else{
|
|
view_set_mark(app, &view, seek_line_char(view.mark.line+1, view.mark.character));
|
|
}
|
|
|
|
range = get_range(&view);
|
|
buffer_auto_indent(app, &buffer, range.min, range.max, DEF_TAB_WIDTH, DEFAULT_INDENT_FLAGS | AutoIndent_FullTokens);
|
|
move_past_lead_whitespace(app, &view, &buffer);
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Fast Deletes
|
|
//
|
|
|
|
CUSTOM_COMMAND_SIG(backspace_word){
|
|
uint32_t access = AccessOpen;
|
|
|
|
View_Summary view = get_active_view(app, access);
|
|
Buffer_Summary buffer = get_buffer(app, view.buffer_id, access);
|
|
|
|
if (buffer.exists){
|
|
int32_t pos2 = 0, pos1 = 0;
|
|
|
|
pos2 = view.cursor.pos;
|
|
exec_command(app, seek_alphanumeric_left);
|
|
refresh_view(app, &view);
|
|
pos1 = view.cursor.pos;
|
|
|
|
buffer_replace_range(app, &buffer, pos1, pos2, 0, 0);
|
|
}
|
|
}
|
|
|
|
CUSTOM_COMMAND_SIG(delete_word){
|
|
uint32_t access = AccessOpen;
|
|
|
|
View_Summary view = get_active_view(app, access);
|
|
Buffer_Summary buffer = get_buffer(app, view.buffer_id, access);
|
|
|
|
if (buffer.exists){
|
|
int32_t pos2 = 0, pos1 = 0;
|
|
|
|
pos1 = view.cursor.pos;
|
|
exec_command(app, seek_alphanumeric_right);
|
|
refresh_view(app, &view);
|
|
pos2 = view.cursor.pos;
|
|
|
|
buffer_replace_range(app, &buffer, pos1, pos2, 0, 0);
|
|
}
|
|
}
|
|
|
|
CUSTOM_COMMAND_SIG(snipe_token_or_word){
|
|
uint32_t access = AccessOpen;
|
|
|
|
View_Summary view = get_active_view(app, access);
|
|
Buffer_Summary buffer = get_buffer(app, view.buffer_id, access);
|
|
|
|
int32_t pos1 = buffer_boundary_seek(app, &buffer, view.cursor.pos, 0, BoundaryToken | BoundaryWhitespace);
|
|
int32_t pos2 = buffer_boundary_seek(app, &buffer, pos1, 1, BoundaryToken | BoundaryWhitespace);
|
|
|
|
Range range = make_range(pos1, pos2);
|
|
buffer_replace_range(app, &buffer, range.start, range.end, 0, 0);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Open File In Quotes
|
|
//
|
|
|
|
static int32_t
|
|
file_name_in_quotes(Application_Links *app, String *file_name){
|
|
int32_t result = false;
|
|
uint32_t access = AccessProtected;
|
|
|
|
View_Summary view;
|
|
Buffer_Summary buffer;
|
|
char short_file_name[128];
|
|
int32_t pos, start, end, size;
|
|
|
|
view = get_active_view(app, access);
|
|
buffer = get_buffer(app, view.buffer_id, access);
|
|
pos = view.cursor.pos;
|
|
buffer_seek_delimiter_forward(app, &buffer, pos, '"', &end);
|
|
buffer_seek_delimiter_backward(app, &buffer, pos, '"', &start);
|
|
|
|
++start;
|
|
size = end - start;
|
|
|
|
// NOTE(allen): This check is necessary because buffer_read_range
|
|
// requiers that the output buffer you provide is at least (end - start) bytes long.
|
|
if (size < sizeof(short_file_name)){
|
|
if (buffer_read_range(app, &buffer, start, end, short_file_name)){
|
|
result = true;
|
|
copy_ss(file_name, make_string(buffer.file_name, buffer.file_name_len));
|
|
remove_last_folder(file_name);
|
|
append_ss(file_name, make_string(short_file_name, size));
|
|
}
|
|
}
|
|
|
|
return(result);
|
|
}
|
|
|
|
CUSTOM_COMMAND_SIG(open_file_in_quotes){
|
|
char file_name_[256];
|
|
String file_name = make_fixed_width_string(file_name_);
|
|
|
|
if (file_name_in_quotes(app, &file_name)){
|
|
exec_command(app, change_active_panel);
|
|
View_Summary view = get_active_view(app, AccessAll);
|
|
view_open_file(app, &view, expand_str(file_name), true);
|
|
}
|
|
}
|
|
|
|
CUSTOM_COMMAND_SIG(open_in_other){
|
|
exec_command(app, change_active_panel);
|
|
exec_command(app, interactive_open);
|
|
}
|
|
|
|
|
|
//
|
|
// System Commands
|
|
//
|
|
|
|
static char out_buffer_space[1024];
|
|
static char command_space[1024];
|
|
static char hot_directory_space[1024];
|
|
|
|
CUSTOM_COMMAND_SIG(execute_any_cli){
|
|
Query_Bar bar_out = {0};
|
|
Query_Bar bar_cmd = {0};
|
|
|
|
bar_out.prompt = make_lit_string("Output Buffer: ");
|
|
bar_out.string = make_fixed_width_string(out_buffer_space);
|
|
if (!query_user_string(app, &bar_out)) return;
|
|
|
|
bar_cmd.prompt = make_lit_string("Command: ");
|
|
bar_cmd.string = make_fixed_width_string(command_space);
|
|
if (!query_user_string(app, &bar_cmd)) return;
|
|
|
|
String hot_directory = make_fixed_width_string(hot_directory_space);
|
|
hot_directory.size = directory_get_hot(app, hot_directory.str, hot_directory.memory_size);
|
|
|
|
uint32_t access = AccessAll;
|
|
View_Summary view = get_active_view(app, access);
|
|
|
|
exec_system_command(app, &view, buffer_identifier(bar_out.string.str, bar_out.string.size), hot_directory.str, hot_directory.size, bar_cmd.string.str, bar_cmd.string.size, CLI_OverlapWithConflict | CLI_CursorAtEnd);
|
|
}
|
|
|
|
CUSTOM_COMMAND_SIG(execute_previous_cli){
|
|
String out_buffer = make_string_slowly(out_buffer_space);
|
|
String cmd = make_string_slowly(command_space);
|
|
String hot_directory = make_string_slowly(hot_directory_space);
|
|
|
|
if (out_buffer.size > 0 && cmd.size > 0 && hot_directory.size > 0){
|
|
uint32_t access = AccessAll;
|
|
View_Summary view = get_active_view(app, access);
|
|
|
|
exec_system_command(app, &view, buffer_identifier(out_buffer.str, out_buffer.size), hot_directory.str, hot_directory.size, cmd.str, cmd.size, CLI_OverlapWithConflict | CLI_CursorAtEnd);
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Default Building Stuff
|
|
//
|
|
|
|
// NOTE(allen|a4.0.9): This is provided to establish a default method of getting
|
|
// a "build directory". This function tries to setup the build directory in the
|
|
// directory of the given buffer, if it cannot get that information it get's the
|
|
// 4coder hot directory.
|
|
//
|
|
// There is no requirement that a custom build system in 4coder actually use the
|
|
// directory given by this function.
|
|
enum Get_Build_Directory_Result{
|
|
BuildDir_None,
|
|
BuildDir_AtFile,
|
|
BuildDir_AtHot
|
|
};
|
|
|
|
static int32_t
|
|
get_build_directory(Application_Links *app, Buffer_Summary *buffer, String *dir_out){
|
|
int32_t result = BuildDir_None;
|
|
|
|
if (buffer && buffer->file_name){
|
|
if (!match_cc(buffer->file_name, buffer->buffer_name)){
|
|
String dir = make_string_cap(buffer->file_name,
|
|
buffer->file_name_len,
|
|
buffer->file_name_len+1);
|
|
remove_last_folder(&dir);
|
|
append_ss(dir_out, dir);
|
|
result = BuildDir_AtFile;
|
|
}
|
|
}
|
|
|
|
if (!result){
|
|
int32_t len = directory_get_hot(app, dir_out->str,
|
|
dir_out->memory_size - dir_out->size);
|
|
if (len + dir_out->size < dir_out->memory_size){
|
|
dir_out->size += len;
|
|
result = BuildDir_AtHot;
|
|
}
|
|
}
|
|
|
|
return(result);
|
|
}
|
|
|
|
// TODO(allen): Better names for the "standard build search" family.
|
|
static int32_t
|
|
standard_build_search(Application_Links *app, View_Summary *view, Buffer_Summary *active_buffer, String *dir, String *command, int32_t perform_backup, int32_t use_path_in_command, String filename, String commandname){
|
|
int32_t result = false;
|
|
|
|
for(;;){
|
|
int32_t old_size = dir->size;
|
|
append_ss(dir, filename);
|
|
|
|
if (file_exists(app, dir->str, dir->size)){
|
|
dir->size = old_size;
|
|
|
|
if (use_path_in_command){
|
|
append_s_char(command, '"');
|
|
append_ss(command, *dir);
|
|
append_ss(command, commandname);
|
|
append_s_char(command, '"');
|
|
}
|
|
else{
|
|
append_ss(command, commandname);
|
|
}
|
|
|
|
char space[512];
|
|
String message = make_fixed_width_string(space);
|
|
append_ss(&message, make_lit_string("Building with: "));
|
|
append_ss(&message, *command);
|
|
append_s_char(&message, '\n');
|
|
print_message(app, message.str, message.size);
|
|
|
|
exec_system_command(app, view, buffer_identifier(literal("*compilation*")), dir->str, dir->size, command->str, command->size, CLI_OverlapWithConflict);
|
|
result = true;
|
|
break;
|
|
}
|
|
dir->size = old_size;
|
|
|
|
if (directory_cd(app, dir->str, &dir->size, dir->memory_size, literal("..")) == 0){
|
|
if (perform_backup){
|
|
dir->size = directory_get_hot(app, dir->str, dir->memory_size);
|
|
char backup_space[256];
|
|
String backup_command = make_fixed_width_string(backup_space);
|
|
append_ss(&backup_command, make_lit_string("echo could not find "));
|
|
append_ss(&backup_command, filename);
|
|
exec_system_command(app, view, buffer_identifier(literal("*compilation*")), dir->str, dir->size, backup_command.str, backup_command.size, CLI_OverlapWithConflict);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
return(result);
|
|
}
|
|
|
|
#if defined(_WIN32)
|
|
|
|
// NOTE(allen): Build search rule for windows.
|
|
static int32_t
|
|
execute_standard_build_search(Application_Links *app, View_Summary *view,
|
|
Buffer_Summary *active_buffer,
|
|
String *dir, String *command, int32_t perform_backup){
|
|
int32_t result = standard_build_search(app, view, active_buffer, dir, command, perform_backup, true, make_lit_string("build.bat"), make_lit_string("build"));
|
|
return(result);
|
|
}
|
|
|
|
#elif defined(__linux__)
|
|
|
|
// NOTE(allen): Build search rule for linux.
|
|
static int32_t
|
|
execute_standard_build_search(Application_Links *app, View_Summary *view, Buffer_Summary *active_buffer, String *dir, String *command, bool32 perform_backup){
|
|
char dir_space[512];
|
|
String dir_copy = make_fixed_width_string(dir_space);
|
|
copy(&dir_copy, *dir);
|
|
|
|
int32_t result = standard_build_search(app, view, active_buffer, dir, command, 0, 1, make_lit_string("build.sh"), make_lit_string("build.sh"));
|
|
|
|
if (!result){
|
|
result = standard_build_search(app, view, active_buffer, &dir_copy, command, perform_backup, 0, make_lit_string("Makefile"), make_lit_string("make"));
|
|
}
|
|
|
|
return(result);
|
|
}
|
|
|
|
#else
|
|
# error No build search rule for this platform.
|
|
#endif
|
|
|
|
|
|
// NOTE(allen): This searches first using the active file's directory,
|
|
// then if no build script is found, it searches from 4coders hot directory.
|
|
static void
|
|
execute_standard_build(Application_Links *app, View_Summary *view, Buffer_Summary *active_buffer){
|
|
char dir_space[512];
|
|
String dir = make_fixed_width_string(dir_space);
|
|
|
|
char command_str_space[512];
|
|
String command = make_fixed_width_string(command_str_space);
|
|
|
|
int32_t build_dir_type = get_build_directory(app, active_buffer, &dir);
|
|
|
|
if (build_dir_type == BuildDir_AtFile){
|
|
if (!execute_standard_build_search(app, view, active_buffer, &dir, &command, false)){
|
|
dir.size = 0;
|
|
command.size = 0;
|
|
build_dir_type = get_build_directory(app, 0, &dir);
|
|
}
|
|
}
|
|
|
|
if (build_dir_type == BuildDir_AtHot){
|
|
execute_standard_build_search(app, view, active_buffer, &dir, &command, true);
|
|
}
|
|
}
|
|
|
|
CUSTOM_COMMAND_SIG(build_search){
|
|
uint32_t access = AccessAll;
|
|
View_Summary view = get_active_view(app, access);
|
|
Buffer_Summary buffer = get_buffer(app, view.buffer_id, access);
|
|
execute_standard_build(app, &view, &buffer);
|
|
prev_location = null_location;
|
|
lock_jump_buffer(literal("*compilation*"));
|
|
}
|
|
|
|
#define GET_COMP_BUFFER(app) get_buffer_by_name(app, literal("*compilation*"), AccessAll)
|
|
|
|
static View_Summary
|
|
get_or_open_build_panel(Application_Links *app){
|
|
View_Summary view = {0};
|
|
|
|
Buffer_Summary buffer = GET_COMP_BUFFER(app);
|
|
if (buffer.exists){
|
|
view = get_first_view_with_buffer(app, buffer.buffer_id);
|
|
}
|
|
if (!view.exists){
|
|
view = open_special_note_view(app);
|
|
}
|
|
|
|
return(view);
|
|
}
|
|
|
|
static void
|
|
set_fancy_compilation_buffer_font(Application_Links *app){
|
|
Buffer_Summary comp_buffer = get_buffer_by_name(app, literal("*compilation*"), AccessAll);
|
|
buffer_set_font(app, &comp_buffer, literal("Inconsolata"));
|
|
}
|
|
|
|
CUSTOM_COMMAND_SIG(build_in_build_panel){
|
|
uint32_t access = AccessAll;
|
|
View_Summary view = get_active_view(app, access);
|
|
Buffer_Summary buffer = get_buffer(app, view.buffer_id, access);
|
|
|
|
View_Summary build_view = get_or_open_build_panel(app);
|
|
|
|
execute_standard_build(app, &build_view, &buffer);
|
|
set_fancy_compilation_buffer_font(app);
|
|
|
|
prev_location = null_location;
|
|
lock_jump_buffer(literal("*compilation*"));
|
|
}
|
|
|
|
CUSTOM_COMMAND_SIG(close_build_panel){
|
|
close_special_note_view(app);
|
|
}
|
|
|
|
CUSTOM_COMMAND_SIG(change_to_build_panel){
|
|
View_Summary view = open_special_note_view(app, false);
|
|
|
|
if (!view.exists){
|
|
Buffer_Summary buffer = GET_COMP_BUFFER(app);
|
|
if (buffer.exists){
|
|
view = open_special_note_view(app);
|
|
view_set_buffer(app, &view, buffer.buffer_id, 0);
|
|
}
|
|
}
|
|
|
|
if (view.exists){
|
|
set_active_view(app, &view);
|
|
}
|
|
}
|
|
|
|
// NOTE(allen|a4): scroll rule information
|
|
//
|
|
// The parameters:
|
|
// target_x, target_y
|
|
// This is where the view would like to be for the purpose of
|
|
// following the cursor, doing mouse wheel work, etc.
|
|
//
|
|
// scroll_x, scroll_y
|
|
// These are pointers to where the scrolling actually is. If you bind
|
|
// the scroll rule it is you have to update these in some way to move
|
|
// the actual location of the scrolling.
|
|
//
|
|
// view_id
|
|
// This corresponds to which view is computing it's new scrolling position.
|
|
// This id DOES correspond to the views that View_Summary contains.
|
|
// This will always be between 1 and 16 (0 is a null id).
|
|
// See below for an example of having state that carries across scroll udpates.
|
|
//
|
|
// is_new_target
|
|
// If the target of the view is different from the last target in either x or y
|
|
// this is true, otherwise it is false.
|
|
//
|
|
// The return:
|
|
// Should be true if and only if scroll_x or scroll_y are changed.
|
|
//
|
|
// Don't try to use the app pointer in a scroll rule, you're asking for trouble.
|
|
//
|
|
// If you don't bind scroll_rule, nothing bad will happen, yo will get default
|
|
// 4coder scrolling behavior.
|
|
//
|
|
|
|
struct Scroll_Velocity{
|
|
float x, y;
|
|
};
|
|
|
|
Scroll_Velocity scroll_velocity_[16] = {0};
|
|
Scroll_Velocity *scroll_velocity = scroll_velocity_ - 1;
|
|
|
|
static int32_t
|
|
smooth_camera_step(float target, float *current, float *vel, float S, float T){
|
|
int32_t result = 0;
|
|
float curr = *current;
|
|
float v = *vel;
|
|
if (curr != target){
|
|
if (curr > target - .1f && curr < target + .1f){
|
|
curr = target;
|
|
v = 1.f;
|
|
}
|
|
else{
|
|
float L = curr + T*(target - curr);
|
|
|
|
int32_t sign = (target > curr) - (target < curr);
|
|
float V = curr + sign*v;
|
|
|
|
if (sign > 0) curr = (L<V)?(L):(V);
|
|
else curr = (L>V)?(L):(V);
|
|
|
|
if (curr == V){
|
|
v *= S;
|
|
}
|
|
}
|
|
|
|
*current = curr;
|
|
*vel = v;
|
|
result = 1;
|
|
}
|
|
return(result);
|
|
}
|
|
|
|
SCROLL_RULE_SIG(smooth_scroll_rule){
|
|
Scroll_Velocity *velocity = scroll_velocity + view_id;
|
|
int32_t result = 0;
|
|
if (velocity->x == 0.f){
|
|
velocity->x = 1.f;
|
|
velocity->y = 1.f;
|
|
}
|
|
|
|
if (smooth_camera_step(target_y, scroll_y, &velocity->y, 80.f, 1.f/2.f)){
|
|
result = 1;
|
|
}
|
|
if (smooth_camera_step(target_x, scroll_x, &velocity->x, 80.f, 1.f/2.f)){
|
|
result = 1;
|
|
}
|
|
|
|
return(result);
|
|
}
|
|
|
|
// NOTE(allen|a4.0.9): All command calls can now go through this hook
|
|
// If this hook is not implemented a default behavior of calling the
|
|
// command is used. It is important to note that paste_next does not
|
|
// work without this hook.
|
|
// NOTE(allen|a4.0.10): As of this version the word_complete command
|
|
// also relies on this particular command caller hook.
|
|
COMMAND_CALLER_HOOK(default_command_caller){
|
|
View_Summary view = get_active_view(app, AccessAll);
|
|
|
|
view_paste_index[view.view_id].next_rewrite = 0;
|
|
exec_command(app, cmd);
|
|
view_paste_index[view.view_id].rewrite = view_paste_index[view.view_id].next_rewrite;
|
|
|
|
return(0);
|
|
}
|
|
|
|
struct Config_Line{
|
|
Cpp_Token id_token;
|
|
Cpp_Token subscript_token;
|
|
Cpp_Token eq_token;
|
|
Cpp_Token val_token;
|
|
int32_t val_array_start;
|
|
int32_t val_array_end;
|
|
int32_t val_array_count;
|
|
bool32 read_success;
|
|
};
|
|
|
|
struct Config_Item{
|
|
Config_Line line;
|
|
Cpp_Token_Array array;
|
|
char *mem;
|
|
String id;
|
|
int32_t subscript_index;
|
|
bool32 has_subscript;
|
|
};
|
|
|
|
struct Config_Array_Reader{
|
|
Cpp_Token_Array array;
|
|
char *mem;
|
|
int32_t i;
|
|
int32_t val_array_end;
|
|
bool32 good;
|
|
};
|
|
|
|
static Cpp_Token
|
|
read_config_token(Cpp_Token_Array array, int32_t *i_ptr){
|
|
Cpp_Token token = {0};
|
|
|
|
int32_t i = *i_ptr;
|
|
|
|
for (; i < array.count; ++i){
|
|
Cpp_Token comment_token = array.tokens[i];
|
|
if (comment_token.type != CPP_TOKEN_COMMENT){
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i < array.count){
|
|
token = array.tokens[i];
|
|
}
|
|
|
|
*i_ptr = i;
|
|
|
|
return(token);
|
|
}
|
|
|
|
static Config_Line
|
|
read_config_line(Cpp_Token_Array array, int32_t *i_ptr){
|
|
Config_Line config_line = {0};
|
|
|
|
int32_t i = *i_ptr;
|
|
|
|
config_line.id_token = read_config_token(array, &i);
|
|
if (config_line.id_token.type == CPP_TOKEN_IDENTIFIER){
|
|
++i;
|
|
if (i < array.count){
|
|
Cpp_Token token = read_config_token(array, &i);
|
|
|
|
bool32 subscript_success = 1;
|
|
if (token.type == CPP_TOKEN_BRACKET_OPEN){
|
|
subscript_success = 0;
|
|
++i;
|
|
if (i < array.count){
|
|
config_line.subscript_token = read_config_token(array, &i);
|
|
if (config_line.subscript_token.type == CPP_TOKEN_INTEGER_CONSTANT){
|
|
++i;
|
|
if (i < array.count){
|
|
token = read_config_token(array, &i);
|
|
if (token.type == CPP_TOKEN_BRACKET_CLOSE){
|
|
++i;
|
|
if (i < array.count){
|
|
token = read_config_token(array, &i);
|
|
subscript_success = 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (subscript_success){
|
|
if (token.type == CPP_TOKEN_EQ){
|
|
config_line.eq_token = read_config_token(array, &i);
|
|
++i;
|
|
if (i < array.count){
|
|
Cpp_Token val_token = read_config_token(array, &i);
|
|
|
|
bool32 array_success = 1;
|
|
if (val_token.type == CPP_TOKEN_BRACE_OPEN){
|
|
array_success = 0;
|
|
++i;
|
|
if (i < array.count){
|
|
config_line.val_array_start = i;
|
|
|
|
bool32 expecting_array_item = 1;
|
|
for (; i < array.count; ++i){
|
|
Cpp_Token array_token = read_config_token(array, &i);
|
|
if (array_token.size == 0){
|
|
break;
|
|
}
|
|
if (array_token.type == CPP_TOKEN_BRACE_CLOSE){
|
|
config_line.val_array_end = i;
|
|
array_success = 1;
|
|
break;
|
|
}
|
|
else{
|
|
if (array_token.type == CPP_TOKEN_COMMA){
|
|
if (!expecting_array_item){
|
|
expecting_array_item = 1;
|
|
}
|
|
else{
|
|
break;
|
|
}
|
|
}
|
|
else{
|
|
if (expecting_array_item){
|
|
expecting_array_item = 0;
|
|
++config_line.val_array_count;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (array_success){
|
|
config_line.val_token = val_token;
|
|
++i;
|
|
if (i < array.count){
|
|
Cpp_Token semicolon_token = read_config_token(array, &i);
|
|
if (semicolon_token.type == CPP_TOKEN_SEMICOLON){
|
|
config_line.read_success = 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!config_line.read_success){
|
|
for (; i < array.count; ++i){
|
|
Cpp_Token token = read_config_token(array, &i);
|
|
if (token.type == CPP_TOKEN_SEMICOLON){
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
*i_ptr = i;
|
|
|
|
return(config_line);
|
|
}
|
|
|
|
static Config_Item
|
|
get_config_item(Config_Line line, char *mem, Cpp_Token_Array array){
|
|
Config_Item item = {0};
|
|
item.line = line;
|
|
item.array = array;
|
|
item.mem = mem;
|
|
if (line.id_token.size != 0){
|
|
item.id = make_string(mem + line.id_token.start, line.id_token.size);
|
|
}
|
|
|
|
if (line.subscript_token.size != 0){
|
|
String subscript_str = make_string(mem + line.subscript_token.start,line.subscript_token.size);
|
|
item.subscript_index = str_to_int_s(subscript_str);
|
|
item.has_subscript = 1;
|
|
}
|
|
|
|
return(item);
|
|
}
|
|
|
|
static bool32
|
|
config_var(Config_Item item, char *var_name, int32_t *subscript, uint32_t token_type, void *var_out){
|
|
bool32 result = 0;
|
|
bool32 subscript_succes = 1;
|
|
if (item.line.val_token.type == token_type){
|
|
if ((var_name == 0 && item.id.size == 0) || match(item.id, var_name)){
|
|
if (subscript){
|
|
if (item.has_subscript){
|
|
*subscript = item.subscript_index;
|
|
}
|
|
else{
|
|
subscript_succes = 0;
|
|
}
|
|
}
|
|
|
|
if (subscript_succes){
|
|
if (var_out){
|
|
switch (token_type){
|
|
case CPP_TOKEN_BOOLEAN_CONSTANT:
|
|
{
|
|
*(bool32*)var_out = (item.mem[item.line.val_token.start] == 't');
|
|
}break;
|
|
|
|
case CPP_TOKEN_INTEGER_CONSTANT:
|
|
{
|
|
String val = make_string(item.mem + item.line.val_token.start, item.line.val_token.size);
|
|
*(int32_t*)var_out = str_to_int(val);
|
|
}break;
|
|
|
|
case CPP_TOKEN_STRING_CONSTANT:
|
|
{
|
|
*(String*)var_out = make_string(item.mem + item.line.val_token.start + 1,item.line.val_token.size - 2);
|
|
}break;
|
|
|
|
case CPP_TOKEN_BRACE_OPEN:
|
|
{
|
|
Config_Array_Reader *array_reader = (Config_Array_Reader*)var_out;
|
|
array_reader->array = item.array;
|
|
array_reader->mem = item.mem;
|
|
array_reader->i = item.line.val_array_start;
|
|
array_reader->val_array_end = item.line.val_array_end;
|
|
array_reader->good = 1;
|
|
}break;
|
|
}
|
|
}
|
|
result = 1;
|
|
}
|
|
}
|
|
}
|
|
return(result);
|
|
}
|
|
|
|
static bool32
|
|
config_bool_var(Config_Item item, char *var_name, int32_t *subscript, bool32 *var_out){
|
|
bool32 result = config_var(item, var_name, subscript, CPP_TOKEN_BOOLEAN_CONSTANT, var_out);
|
|
return(result);
|
|
}
|
|
|
|
static bool32
|
|
config_int_var(Config_Item item, char *var_name, int32_t *subscript, int32_t *var_out){
|
|
bool32 result = config_var(item, var_name, subscript, CPP_TOKEN_INTEGER_CONSTANT, var_out);
|
|
return(result);
|
|
}
|
|
|
|
static bool32
|
|
config_string_var(Config_Item item, char *var_name, int32_t *subscript, String *var_out){
|
|
bool32 result = config_var(item, var_name, subscript, CPP_TOKEN_STRING_CONSTANT, var_out);
|
|
return(result);
|
|
}
|
|
|
|
static bool32
|
|
config_array_var(Config_Item item, char *var_name, int32_t *subscript, Config_Array_Reader *array_reader){
|
|
bool32 result = config_var(item, var_name, subscript, CPP_TOKEN_BRACE_OPEN, array_reader);
|
|
return(result);
|
|
}
|
|
|
|
static bool32
|
|
config_array_next_item(Config_Array_Reader *array_reader, Config_Item *item){
|
|
bool32 result = 0;
|
|
|
|
for (;array_reader->i < array_reader->val_array_end;
|
|
++array_reader->i){
|
|
Cpp_Token array_token = read_config_token(array_reader->array, &array_reader->i);
|
|
if (array_token.size == 0 || array_reader->i >= array_reader->val_array_end){
|
|
break;
|
|
}
|
|
|
|
if (array_token.type == CPP_TOKEN_BRACE_CLOSE){
|
|
break;
|
|
}
|
|
|
|
switch (array_token.type){
|
|
case CPP_TOKEN_BOOLEAN_CONSTANT:
|
|
case CPP_TOKEN_INTEGER_CONSTANT:
|
|
case CPP_TOKEN_STRING_CONSTANT:
|
|
{
|
|
Config_Line line = {0};
|
|
line.val_token = array_token;
|
|
line.read_success = 1;
|
|
*item = get_config_item(line, array_reader->mem, array_reader->array);
|
|
result = 1;
|
|
++array_reader->i;
|
|
goto doublebreak;
|
|
}break;
|
|
}
|
|
}
|
|
doublebreak:;
|
|
|
|
array_reader->good = result;
|
|
return(result);
|
|
}
|
|
|
|
static bool32
|
|
config_array_good(Config_Array_Reader *array_reader){
|
|
bool32 result = (array_reader->good);
|
|
return(result);
|
|
}
|
|
|
|
// NOTE(allen|a4.0.12): A primordial config system (actually really hate this but it seems best at least right now... arg)
|
|
|
|
static bool32 enable_code_wrapping = 1;
|
|
static bool32 automatically_adjust_wrapping = 1;
|
|
static int32_t default_wrap_width = 672;
|
|
static int32_t default_min_base_width = 550;
|
|
static bool32 automatically_indent_text_on_save = 1;
|
|
|
|
static String default_theme_name = make_lit_string("4coder");
|
|
static String default_font_name = make_lit_string("Liberation Sans");
|
|
|
|
#include <stdio.h>
|
|
|
|
static void
|
|
adjust_all_buffer_wrap_widths(Application_Links *app, int32_t wrap_widths, int32_t min_base_width){
|
|
for (Buffer_Summary buffer = get_buffer_first(app, AccessAll);
|
|
buffer.exists;
|
|
get_buffer_next(app, &buffer, AccessAll)){
|
|
buffer_set_setting(app, &buffer, BufferSetting_WrapPosition, wrap_widths);
|
|
buffer_set_setting(app, &buffer, BufferSetting_MinimumBaseWrapPosition, min_base_width);
|
|
}
|
|
default_wrap_width = wrap_widths;
|
|
default_min_base_width = min_base_width;
|
|
}
|
|
|
|
static bool32
|
|
file_handle_dump(Partition *part, FILE *file, char **mem_ptr, int32_t *size_ptr){
|
|
bool32 success = 0;
|
|
|
|
fseek(file, 0, SEEK_END);
|
|
int32_t size = ftell(file);
|
|
char *mem = (char*)push_block(part, size+1);
|
|
fseek(file, 0, SEEK_SET);
|
|
int32_t check_size = (int32_t)fread(mem, 1, size, file);
|
|
if (check_size == size){
|
|
mem[size] = 0;
|
|
success = 1;
|
|
}
|
|
|
|
*mem_ptr = mem;
|
|
*size_ptr = size;
|
|
|
|
return(success);
|
|
}
|
|
|
|
static void
|
|
process_config_file(Application_Links *app){
|
|
Partition *part = &global_part;
|
|
FILE *file = fopen("config.4coder", "rb");
|
|
|
|
if (!file){
|
|
char space[256];
|
|
int32_t size = get_4ed_path(app, space, sizeof(space));
|
|
String str = make_string_cap(space, size, sizeof(space));
|
|
append_sc(&str, "/config.4coder");
|
|
terminate_with_null(&str);
|
|
file = fopen(str.str, "rb");
|
|
}
|
|
|
|
if (file){
|
|
Temp_Memory temp = begin_temp_memory(part);
|
|
|
|
char *mem = 0;
|
|
int32_t size = 0;
|
|
bool32 file_read_success = file_handle_dump(part, file, &mem, &size);
|
|
|
|
if (file_read_success){
|
|
fclose(file);
|
|
|
|
Cpp_Token_Array array;
|
|
array.count = 0;
|
|
array.max_count = (1 << 20)/sizeof(Cpp_Token);
|
|
array.tokens = push_array(&global_part, Cpp_Token, array.max_count);
|
|
|
|
Cpp_Lex_Data S = cpp_lex_data_init();
|
|
Cpp_Lex_Result result = cpp_lex_step(&S, mem, size+1, HAS_NULL_TERM, &array, NO_OUT_LIMIT);
|
|
|
|
if (result == LexResult_Finished){
|
|
int32_t new_wrap_width = default_wrap_width;
|
|
int32_t new_min_base_width = default_min_base_width;
|
|
|
|
for (int32_t i = 0; i < array.count; ++i){
|
|
Config_Line config_line = read_config_line(array, &i);
|
|
|
|
if (config_line.read_success){
|
|
Config_Item item = get_config_item(config_line, mem, array);
|
|
|
|
config_bool_var(item, "enable_code_wrapping", 0, &enable_code_wrapping);
|
|
config_bool_var(item, "automatically_adjust_wrapping", 0, &automatically_adjust_wrapping);
|
|
config_bool_var(item, "automatically_indent_text_on_save", 0, &automatically_indent_text_on_save);
|
|
|
|
config_int_var(item, "default_wrap_width", 0, &new_wrap_width);
|
|
config_int_var(item, "default_min_base_width", 0, &new_min_base_width);
|
|
|
|
config_string_var(item, "default_theme_name", 0, &default_theme_name);
|
|
config_string_var(item, "default_font_name", 0, &default_font_name);
|
|
}
|
|
}
|
|
adjust_all_buffer_wrap_widths(app, new_wrap_width, new_min_base_width);
|
|
}
|
|
}
|
|
|
|
end_temp_memory(temp);
|
|
}
|
|
else{
|
|
print_message(app, literal("Did not find config.4coder, using default settings\n"));
|
|
}
|
|
}
|
|
|
|
// NOTE(allen): Project system setup
|
|
|
|
static char *default_extensions[] = {
|
|
"cpp",
|
|
"hpp",
|
|
"c",
|
|
"h",
|
|
"cc"
|
|
};
|
|
|
|
struct Fkey_Command{
|
|
char command[128];
|
|
char out[128];
|
|
bool32 use_build_panel;
|
|
};
|
|
|
|
struct Project{
|
|
char dir_space[256];
|
|
char *dir;
|
|
int32_t dir_len;
|
|
|
|
char extension_space[256];
|
|
char *extensions[94];
|
|
int32_t extension_count;
|
|
|
|
Fkey_Command fkey_commands[16];
|
|
|
|
bool32 close_all_code_when_this_project_closes;
|
|
bool32 close_all_files_when_project_opens;
|
|
};
|
|
|
|
static Project null_project = {};
|
|
static Project current_project = {};
|
|
|
|
static void
|
|
set_project_extensions(Project *project, String src){
|
|
int32_t mode = 0;
|
|
int32_t j = 0, k = 0;
|
|
for (int32_t i = 0; i < src.size; ++i){
|
|
switch (mode){
|
|
case 0:
|
|
{
|
|
if (src.str[i] == '.'){
|
|
mode = 1;
|
|
project->extensions[k++] = &project->extension_space[j];
|
|
}
|
|
}break;
|
|
|
|
case 1:
|
|
{
|
|
if (src.str[i] == '.'){
|
|
project->extension_space[j++] = 0;
|
|
project->extensions[k++] = &project->extension_space[j];
|
|
}
|
|
else{
|
|
project->extension_space[j++] = src.str[i];
|
|
}
|
|
}break;
|
|
}
|
|
}
|
|
project->extension_space[j++] = 0;
|
|
project->extension_count = k;
|
|
}
|
|
|
|
// TODO(allen): make this a string operation or a lexer operation or something
|
|
static void
|
|
interpret_escaped_string(char *dst, String src){
|
|
int32_t mode = 0;
|
|
int32_t j = 0;
|
|
for (int32_t i = 0; i < src.size; ++i){
|
|
switch (mode){
|
|
case 0:
|
|
{
|
|
if (src.str[i] == '\\'){
|
|
mode = 1;
|
|
}
|
|
else{
|
|
dst[j++] = src.str[i];
|
|
}
|
|
}break;
|
|
|
|
case 1:
|
|
{
|
|
switch (src.str[i]){
|
|
case '\\':{dst[j++] = '\\'; mode = 0;}break;
|
|
case 'n': {dst[j++] = '\n'; mode = 0;}break;
|
|
case 't': {dst[j++] = '\t'; mode = 0;}break;
|
|
case '"': {dst[j++] = '"'; mode = 0;}break;
|
|
case '0': {dst[j++] = '\0'; mode = 0;}break;
|
|
}
|
|
}break;
|
|
}
|
|
}
|
|
dst[j] = 0;
|
|
}
|
|
|
|
static void
|
|
close_all_files_with_extension(Application_Links *app, Partition *scratch_part, char **extension_list, int32_t extension_count){
|
|
Temp_Memory temp = begin_temp_memory(scratch_part);
|
|
|
|
int32_t buffers_to_close_max = partition_remaining(scratch_part)/sizeof(int32_t);
|
|
int32_t *buffers_to_close = push_array(scratch_part, int32_t, buffers_to_close_max);
|
|
|
|
int32_t buffers_to_close_count = 0;
|
|
bool32 do_repeat = 0;
|
|
do{
|
|
buffers_to_close_count = 0;
|
|
do_repeat = 0;
|
|
|
|
uint32_t access = AccessAll;
|
|
Buffer_Summary buffer = {0};
|
|
for (buffer = get_buffer_first(app, access);
|
|
buffer.exists;
|
|
get_buffer_next(app, &buffer, access)){
|
|
|
|
bool32 is_match = 1;
|
|
if (extension_count > 0){
|
|
String extension = file_extension(make_string(buffer.file_name, buffer.file_name_len));
|
|
is_match = 0;
|
|
for (int32_t i = 0; i < extension_count; ++i){
|
|
if (match(extension, extension_list[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 (int32_t 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 void
|
|
open_all_files_with_extension(Application_Links *app, Partition *scratch_part, char **extension_list, int32_t extension_count){
|
|
Temp_Memory temp = begin_temp_memory(scratch_part);
|
|
|
|
int32_t max_size = partition_remaining(scratch_part);
|
|
char *memory = push_array(scratch_part, char, max_size);
|
|
|
|
String dir = make_string_cap(memory, 0, max_size);
|
|
dir.size = directory_get_hot(app, dir.str, dir.memory_size);
|
|
int32_t dir_size = dir.size;
|
|
|
|
// NOTE(allen|a3.4.4): Here we get the list of files in this directory.
|
|
// Notice that we free_file_list at the end.
|
|
File_List list = get_file_list(app, dir.str, dir.size);
|
|
|
|
for (int32_t i = 0; i < list.count; ++i){
|
|
File_Info *info = list.infos + i;
|
|
if (!info->folder){
|
|
bool32 is_match = 1;
|
|
|
|
if (extension_count > 0){
|
|
is_match = 0;
|
|
|
|
String extension = make_string_cap(info->filename, info->filename_len, info->filename_len+1);
|
|
extension = file_extension(extension);
|
|
for (int32_t j = 0; j < extension_count; ++j){
|
|
if (match(extension, extension_list[j])){
|
|
is_match = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (is_match){
|
|
// NOTE(allen): There's no way in the 4coder API to use relative
|
|
// paths at the moment, so everything should be full paths. Which is
|
|
// managable. Here simply set the dir string size back to where it
|
|
// was originally, so that new appends overwrite old ones.
|
|
dir.size = dir_size;
|
|
append_sc(&dir, info->filename);
|
|
create_buffer(app, dir.str, dir.size, 0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
free_file_list(app, list);
|
|
|
|
end_temp_memory(temp);
|
|
}
|
|
|
|
static char**
|
|
get_standard_code_extensions(int32_t *extension_count_out){
|
|
char **extension_list = default_extensions;
|
|
int32_t extension_count = ArrayCount(default_extensions);
|
|
if (current_project.dir != 0){
|
|
extension_list = current_project.extensions;
|
|
extension_count = current_project.extension_count;
|
|
}
|
|
*extension_count_out = extension_count;
|
|
return(extension_list);
|
|
}
|
|
|
|
// NOTE(allen|a4.0.14): open_all_code and close_all_code now use the extensions set in the loaded project. If there is no project loaded the extensions ".cpp.hpp.c.h.cc" are used.
|
|
CUSTOM_COMMAND_SIG(open_all_code){
|
|
int32_t extension_count = 0;
|
|
char **extension_list = get_standard_code_extensions(&extension_count);
|
|
open_all_files_with_extension(app, &global_part, extension_list, extension_count);
|
|
}
|
|
|
|
CUSTOM_COMMAND_SIG(close_all_code){
|
|
int32_t extension_count = 0;
|
|
char **extension_list = get_standard_code_extensions(&extension_count);
|
|
close_all_files_with_extension(app, &global_part, extension_list, extension_count);
|
|
}
|
|
|
|
CUSTOM_COMMAND_SIG(load_project){
|
|
Partition *part = &global_part;
|
|
|
|
char project_file_space[512];
|
|
String project_name = make_fixed_width_string(project_file_space);
|
|
project_name.size = directory_get_hot(app, project_name.str, project_name.memory_size);
|
|
if (project_name.size >= project_name.memory_size){
|
|
project_name.size = 0;
|
|
}
|
|
|
|
if (project_name.size != 0){
|
|
int32_t original_size = project_name.size;
|
|
append_sc(&project_name, "project.4coder");
|
|
terminate_with_null(&project_name);
|
|
|
|
// TODO(allen): make sure we do nothing when this project is already open
|
|
|
|
FILE *file = fopen(project_name.str, "rb");
|
|
if (file){
|
|
project_name.size = original_size;
|
|
terminate_with_null(&project_name);
|
|
|
|
Temp_Memory temp = begin_temp_memory(part);
|
|
|
|
char *mem = 0;
|
|
int32_t size = 0;
|
|
bool32 file_read_success = file_handle_dump(part, file, &mem, &size);
|
|
if (file_read_success){
|
|
fclose(file);
|
|
|
|
Cpp_Token_Array array;
|
|
array.count = 0;
|
|
array.max_count = (1 << 20)/sizeof(Cpp_Token);
|
|
array.tokens = push_array(&global_part, Cpp_Token, array.max_count);
|
|
|
|
Cpp_Lex_Data S = cpp_lex_data_init();
|
|
Cpp_Lex_Result result = cpp_lex_step(&S, mem, size+1, HAS_NULL_TERM, &array, NO_OUT_LIMIT);
|
|
|
|
if (result == LexResult_Finished){
|
|
// Clear out current project
|
|
if (current_project.close_all_code_when_this_project_closes){
|
|
exec_command(app, close_all_code);
|
|
}
|
|
current_project = null_project;
|
|
|
|
// Set new project directory
|
|
{
|
|
current_project.dir = current_project.dir_space;
|
|
String str = make_fixed_width_string(current_project.dir_space);
|
|
copy(&str, project_name);
|
|
terminate_with_null(&str);
|
|
current_project.dir_len = str.size;
|
|
}
|
|
|
|
// Read the settings from project.4coder
|
|
for (int32_t i = 0; i < array.count; ++i){
|
|
Config_Line config_line = read_config_line(array, &i);
|
|
if (config_line.read_success){
|
|
Config_Item item = get_config_item(config_line, mem, array);
|
|
|
|
{
|
|
String str = {0};
|
|
if (config_string_var(item, "extensions", 0, &str)){
|
|
if (str.size < sizeof(current_project.extension_space)){
|
|
set_project_extensions(¤t_project, str);
|
|
print_message(app, str.str, str.size);
|
|
print_message(app, "\n", 1);
|
|
}
|
|
else{
|
|
print_message(app, literal("STRING TOO LONG!\n"));
|
|
}
|
|
}
|
|
}
|
|
|
|
{
|
|
#if defined(_WIN32)
|
|
#define FKEY_COMMAND "fkey_command_win"
|
|
#elif defined(__linux__)
|
|
#define FKEY_COMMAND "fkey_command_linux"
|
|
#else
|
|
#error no project configuration names for this platform
|
|
#endif
|
|
|
|
int32_t index = 0;
|
|
Config_Array_Reader array_reader = {0};
|
|
if (config_array_var(item, FKEY_COMMAND, &index, &array_reader)){
|
|
if (index >= 1 && index <= 16){
|
|
Config_Item array_item = {0};
|
|
int32_t item_index = 0;
|
|
|
|
char space[256];
|
|
String msg = make_fixed_width_string(space);
|
|
append(&msg, FKEY_COMMAND"[");
|
|
append_int_to_str(&msg, index);
|
|
append(&msg, "] = {");
|
|
|
|
for (config_array_next_item(&array_reader, &array_item);
|
|
config_array_good(&array_reader);
|
|
config_array_next_item(&array_reader, &array_item)){
|
|
|
|
if (item_index >= 3){
|
|
break;
|
|
}
|
|
|
|
append(&msg, "[");
|
|
append_int_to_str(&msg, item_index);
|
|
append(&msg, "] = ");
|
|
|
|
bool32 read_string = 0;
|
|
bool32 read_bool = 0;
|
|
|
|
char *dest_str = 0;
|
|
int32_t dest_str_size = 0;
|
|
|
|
bool32 *dest_bool = 0;
|
|
|
|
switch (item_index){
|
|
case 0:
|
|
{
|
|
dest_str = current_project.fkey_commands[index-1].command;
|
|
dest_str_size = sizeof(current_project.fkey_commands[index-1].command);
|
|
read_string = 1;
|
|
}break;
|
|
|
|
case 1:
|
|
{
|
|
dest_str = current_project.fkey_commands[index-1].out;
|
|
dest_str_size = sizeof(current_project.fkey_commands[index-1].out);
|
|
read_string = 1;
|
|
}break;
|
|
|
|
case 2:
|
|
{
|
|
dest_bool = ¤t_project.fkey_commands[index-1].use_build_panel;
|
|
read_bool = 1;
|
|
}break;
|
|
}
|
|
|
|
if (read_string){
|
|
if (config_int_var(array_item, 0, 0, 0)){
|
|
append(&msg, "NULL, ");
|
|
dest_str[0] = 0;
|
|
}
|
|
|
|
String str = {0};
|
|
if (config_string_var(array_item, 0, 0, &str)){
|
|
if (str.size < dest_str_size){
|
|
interpret_escaped_string(dest_str, str);
|
|
append(&msg, dest_str);
|
|
append(&msg, ", ");
|
|
}
|
|
else{
|
|
append(&msg, "STRING TOO LONG!, ");
|
|
}
|
|
}
|
|
}
|
|
|
|
if (read_bool){
|
|
if (config_bool_var(array_item, 0, 0, dest_bool)){
|
|
if (dest_bool){
|
|
append(&msg, "true, ");
|
|
}
|
|
else{
|
|
append(&msg, "false, ");
|
|
}
|
|
}
|
|
}
|
|
|
|
item_index++;
|
|
}
|
|
|
|
append(&msg, "}\n");
|
|
print_message(app, msg.str, msg.size);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (current_project.close_all_files_when_project_opens){
|
|
close_all_files_with_extension(app, &global_part, 0, 0);
|
|
}
|
|
|
|
// Open all project files
|
|
exec_command(app, open_all_code);
|
|
}
|
|
}
|
|
|
|
end_temp_memory(temp);
|
|
}
|
|
else{
|
|
char message_space[512];
|
|
String message = make_fixed_width_string(message_space);
|
|
append_sc(&message, "Did not find project.4coder. ");
|
|
if (current_project.dir != 0){
|
|
append_sc(&message, "Continuing with: ");
|
|
append_sc(&message, current_project.dir);
|
|
}
|
|
else{
|
|
append_sc(&message, "Continuing without a project");
|
|
}
|
|
print_message(app, message.str, message.size);
|
|
}
|
|
}
|
|
else{
|
|
print_message(app, literal("Failed trying to get project file name"));
|
|
}
|
|
}
|
|
|
|
CUSTOM_COMMAND_SIG(project_fkey_command){
|
|
User_Input input = get_command_input(app);
|
|
if (input.type == UserInputKey){
|
|
if (input.key.keycode >= key_f1 && input.key.keycode <= key_f16){
|
|
int32_t ind = (input.key.keycode - key_f1);
|
|
|
|
char *command = current_project.fkey_commands[ind].command;
|
|
char *out = current_project.fkey_commands[ind].out;
|
|
bool32 use_build_panel = current_project.fkey_commands[ind].use_build_panel;
|
|
|
|
if (command[0] != 0){
|
|
int32_t command_len = str_size(command);
|
|
|
|
View_Summary view_ = {0};
|
|
View_Summary *view = 0;
|
|
Buffer_Identifier buffer_id = {0};
|
|
uint32_t flags = 0;
|
|
|
|
bool32 set_fancy_font = 0;
|
|
|
|
if (out[0] != 0){
|
|
int32_t out_len = str_size(out);
|
|
buffer_id = buffer_identifier(out, out_len);
|
|
|
|
view = &view_;
|
|
|
|
if (use_build_panel){
|
|
view_ = get_or_open_build_panel(app);
|
|
if (match(out, "*compilation*")){
|
|
set_fancy_font = 1;
|
|
}
|
|
}
|
|
else{
|
|
view_ = get_active_view(app, AccessAll);
|
|
}
|
|
|
|
prev_location = null_location;
|
|
lock_jump_buffer(out, out_len);
|
|
}
|
|
else{
|
|
// TODO(allen): fix the exec_system_command call so it can take a null buffer_id.
|
|
buffer_id = buffer_identifier(literal("*dump*"));
|
|
}
|
|
|
|
exec_system_command(app, view, buffer_id, current_project.dir, current_project.dir_len, command, command_len, flags);
|
|
if (set_fancy_font){
|
|
set_fancy_compilation_buffer_font(app);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Default Framework
|
|
//
|
|
|
|
void
|
|
init_memory(Application_Links *app){
|
|
int32_t part_size = (32 << 20);
|
|
int32_t general_size = (4 << 20);
|
|
|
|
void *part_mem = memory_allocate(app, part_size);
|
|
global_part = make_part(part_mem, part_size);
|
|
|
|
void *general_mem = memory_allocate(app, general_size);
|
|
general_memory_open(&global_general, general_mem, general_size);
|
|
}
|
|
|
|
static void
|
|
default_4coder_initialize(Application_Links *app){
|
|
init_memory(app);
|
|
process_config_file(app);
|
|
change_theme(app, default_theme_name.str, default_theme_name.size);
|
|
change_font(app, default_font_name.str, default_font_name.size, 1);
|
|
}
|
|
|
|
static void
|
|
default_4coder_side_by_side_panels(Application_Links *app){
|
|
exec_command(app, open_panel_vsplit);
|
|
exec_command(app, hide_scrollbar);
|
|
exec_command(app, change_active_panel);
|
|
exec_command(app, hide_scrollbar);
|
|
}
|
|
|
|
#endif
|
|
|