4coder/4ed_api_implementation.cpp

1618 lines
52 KiB
C++

/*
The implementation for the custom API
*/
// TOP
inline b32
access_test(u32 lock_flags, u32 access_flags){
b32 result = 0;
if ((lock_flags & ~access_flags) == 0){
result = 1;
}
return(result);
}
internal void
fill_buffer_summary(Buffer_Summary *buffer, Editing_File *file, Working_Set *working_set){
*buffer = buffer_summary_zero();
if (!file->is_dummy){
buffer->exists = 1;
buffer->ready = file_is_ready(file);
buffer->is_lexed = file->settings.tokens_exist;
buffer->buffer_id = file->id.id;
buffer->size = file->state.buffer.size;
buffer->buffer_cursor_pos = file->state.cursor_pos;
buffer->file_name_len = file->name.source_path.size;
buffer->buffer_name_len = file->name.live_name.size;
buffer->file_name = file->name.source_path.str;
buffer->buffer_name = file->name.live_name.str;
buffer->map_id = file->settings.base_map_id;
buffer->lock_flags = 0;
if (file->settings.read_only){
buffer->lock_flags |= AccessProtected;
}
}
}
internal void
fill_buffer_summary(Buffer_Summary *buffer, Editing_File *file, Command_Data *cmd){
Working_Set *working_set = &cmd->models->working_set;
fill_buffer_summary(buffer, file, working_set);
}
internal void
fill_view_summary(View_Summary *view, View *vptr, Live_Views *live_set, Working_Set *working_set){
int buffer_id;
File_Viewing_Data *data = &vptr->file_data;
*view = view_summary_zero();
if (vptr->in_use){
view->exists = 1;
view->view_id = (int)(vptr - live_set->views) + 1;
view->line_height = (float)(vptr->line_height);
view->unwrapped_lines = vptr->file_data.unwrapped_lines;
view->show_whitespace = vptr->file_data.show_whitespace;
view->lock_flags = view_lock_flags(vptr);
if (data->file){
buffer_id = vptr->file_data.file->id.id;
view->buffer_id = buffer_id;
view->mark = view_compute_cursor_from_pos(vptr, vptr->recent->mark);
view->cursor = vptr->recent->cursor;
view->preferred_x = vptr->recent->preferred_x;
view->file_region = vptr->file_region;
view->scroll_vars = *vptr->current_scroll;
}
}
}
inline void
fill_view_summary(View_Summary *view, View *vptr, Command_Data *cmd){
fill_view_summary(view, vptr, &cmd->vars->live_set, &cmd->models->working_set);
}
internal Editing_File*
get_file_from_identifier(System_Functions *system, Working_Set *working_set, Buffer_Identifier buffer){
i32 buffer_id = buffer.id;
i32 buffer_name_len = buffer.name_len;
char *buffer_name = buffer.name;
Editing_File *file = 0;
if (buffer_id){
file = working_set_get_active_file(working_set, buffer_id);
}
else if (buffer_name){
file = working_set_contains(system, working_set, make_string(buffer_name, buffer_name_len));
}
return(file);
}
internal Editing_File*
imp_get_file(Command_Data *cmd, Buffer_Summary *buffer){
Editing_File *file = 0;
Working_Set *working_set = &cmd->models->working_set;;
if (buffer->exists){
file = working_set_get_active_file(working_set, buffer->buffer_id);
if (file != 0 && !file_is_ready(file)){
file = 0;
}
}
return(file);
}
internal View*
imp_get_view(Command_Data *cmd, int view_id){
Live_Views *live_set = cmd->live_set;
View *vptr = 0;
view_id = view_id - 1;
if (view_id >= 0 && view_id < live_set->max){
vptr = live_set->views + view_id;
}
return(vptr);
}
internal View*
imp_get_view(Command_Data *cmd, View_Summary *view){
View *vptr = 0;
if (view->exists){
vptr = imp_get_view(cmd, view->view_id);
}
return(vptr);
}
#define API_EXPORT
API_EXPORT void
Exec_Command(Application_Links *app, int command_id)/*
DOC_PARAM(command_id, an integer id enumerated in 4coder_custom.h starting with cmdid)
DOC(Executes the command associated with the command_id passed in)
*/{
Command_Data *cmd = (Command_Data*)app->cmd_context;
Command_Function function = command_table[command_id];
Command_Binding binding = {};
binding.function = function;
if (function) function(cmd->system, cmd, binding);
update_command_data(cmd->vars, cmd);
}
// TODO(allen): This is a bit of a mess and needs to be fixed soon
API_EXPORT int
Exec_System_Command(Application_Links *app, View_Summary *view, Buffer_Identifier buffer, char *path, int path_len, char *command, int command_len, unsigned int flags)/*
DOC_PARAM(view, the target view that will display the output buffer, may be NULL, see description for details)
DOC_PARAM(buffer, a buffer identifier for the buffer that will be filled with the output from the command)
DOC_PARAM(path, the path from which the command is executed)
DOC_PARAM(path_len, the length of the path string)
DOC_PARAM(command, the command to be executed)
DOC_PARAM(command_len, the length of the command string)
DOC_PARAM(flags, may be zero or one or more CLI flags ORed together)
DOC_RETURN(returns non-zero if the command is successfully started, returns zero otherwise)
DOC
(
Executes a system command as if called from the command line, and sends the output to a buffer. The buffer
identifier can either name a new buffer that does not exist, name a buffer that does exist, or provide the
id of a buffer that does exist. If the buffer already exists the command will fail, unless
CLI_OverlapWithConflict is set in the flags.
If the buffer is not already in an open view, and the view parameter is no NULL, then the provided view
will display the output buffer. If the view parameter is NULL, no view will display the output.
If CLI_OverlapWithConflict is set in the flags, the command will always be executed even if another command
was outputting to the same buffer still.
If CLI_AlwaysBindToView is set and the view parameter is not NULL, then the specified view will always
begin displaying the output buffer, even if another open view already displays that buffer.
If CLI_CursorAtEnd is set the cursor in the output buffer will be placed at the end of the buffer instead
of at the beginning.
)
*/{
Command_Data *cmd = (Command_Data*)app->cmd_context;
System_Functions *system = cmd->system;
App_Vars *vars = cmd->vars;
Models *models = cmd->models;
char feedback_space[256];
String feedback_str = make_fixed_width_string(feedback_space);
Working_Set *working_set = &models->working_set;
CLI_Process *procs = vars->cli_processes.procs, *proc = 0;
Editing_File *file = 0;
b32 bind_to_new_view = true;
General_Memory *general = &models->mem.general;
Partition *part = &models->mem.part;
Temp_Memory temp = begin_temp_memory(part);
int result = true;
View *vptr = imp_get_view(cmd, view);
if (vars->cli_processes.count < vars->cli_processes.max){
file = get_file_from_identifier(system, working_set, buffer);
if (file){
if (file->settings.read_only == 0){
append(&feedback_str, "ERROR: ");
append(&feedback_str, file->name.live_name);
append(&feedback_str, " is not a read-only buffer\n");
do_feedback_message(system, models, feedback_str);
result = false;
goto done;
}
if (file->settings.never_kill){
append(&feedback_str, "The buffer ");
append(&feedback_str, file->name.live_name);
append(&feedback_str, " is not killable");
do_feedback_message(system, models, feedback_str);
result = false;
goto done;
}
}
else if (buffer.name){
file = working_set_alloc_always(working_set, general);
if (file == 0){
append(&feedback_str, "ERROR: unable to allocate a new buffer\n");
do_feedback_message(system, models, feedback_str);
result = false;
goto done;
}
file_create_read_only(system, models, file, buffer.name);
working_set_add(system, working_set, file, general);
}
if (file){
i32 proc_count = vars->cli_processes.count;
for (i32 i = 0; i < proc_count; ++i){
if (procs[i].out_file == file){
if (flags & CLI_OverlapWithConflict){
procs[i].out_file = 0;
}
else{
file = 0;
}
break;
}
}
if (file){
file_clear(system, models, file, 1);
file->settings.unimportant = 1;
if (!(flags & CLI_AlwaysBindToView)){
View_Iter iter = file_view_iter_init(&models->layout, file, 0);
if (file_view_iter_good(iter)){
bind_to_new_view = false;
}
}
}
else{
append(&feedback_str, "did not begin command-line command because the target buffer is already in use\n");
do_feedback_message(system, models, feedback_str);
result = false;
goto done;
}
}
String path_string = {0};
if (!path){
terminate_with_null(&models->hot_directory.string);
path_string = models->hot_directory.string;
}
else{
path_string = make_string_terminated(part, path, path_len);
}
{
String command_string = {0};
if (!command){
#define NO_SCRIPT " echo no script specified"
command_string.str = NO_SCRIPT;
command_string.size = sizeof(NO_SCRIPT)-1;
#undef NO_SCRIPT
}
else{
command_string = make_string_terminated(part, command, command_len);
}
if (vptr && bind_to_new_view){
view_set_file(vptr, file, models);
view_show_file(vptr);
}
proc = procs + vars->cli_processes.count++;
proc->out_file = file;
if (flags & CLI_CursorAtEnd){
proc->cursor_at_end = 1;
}
else{
proc->cursor_at_end = 0;
}
if (!system->cli_call(path_string.str, command_string.str, &proc->cli)){
--vars->cli_processes.count;
}
}
}
else{
append(&feedback_str, "ERROR: no available process slot\n");
do_feedback_message(system, models, feedback_str);
result = false;
goto done;
}
done:
end_temp_memory(temp);
return(result);
}
API_EXPORT void
Clipboard_Post(Application_Links *app, char *str, int len)/*
DOC_PARAM(str, the string to post to the clipboard)
DOC_PARAM(len, the length of the string str)
DOC
(
Stores the string str in the clipboard initially with index 0.
Also reports the copy to the operating system, so that it may
be pasted into other applications.
)
*/{
Command_Data *cmd = (Command_Data*)app->cmd_context;
System_Functions *system = cmd->system;
Models *models = cmd->models;
General_Memory *general = &models->mem.general;
Working_Set *working = &models->working_set;
String *dest = working_set_next_clipboard_string(general, working, len);
copy(dest, make_string(str, len));
system->post_clipboard(*dest);
}
API_EXPORT int
Clipboard_Count(Application_Links *app)/*
DOC(returns the number of items in the clipboard)
*/{
Command_Data *cmd = (Command_Data*)app->cmd_context;
Working_Set *working = &cmd->models->working_set;
int count = working->clipboard_size;
return(count);
}
API_EXPORT int
Clipboard_Index(Application_Links *app, int index, char *out, int len)/*
DOC_PARAM(index, the index of the item to be read)
DOC_PARAM(out, a buffer where the clipboard contents are written or NULL)
DOC_PARAM(len, the length of the out buffer)
DOC_RETURN(returns the size of the item on the clipboard associated with the given index)
DOC
(
There are multiple items on the 4coder clipboard. The most recent copy is always at
index 0. The second most recent is at index 1, and so on for all the stored clipboard items.
This function reads one of the clipboard items and stores it into the out buffer, if the out
buffer is not NULL. This function always returns the size of the clipboard item specified
even if the output buffer is NULL. If the output buffer is too small to contain the whole
string, it is filled with the first len character of the clipboard contents. The output
string is not null terminated.
)
*/{
Command_Data *cmd = (Command_Data*)app->cmd_context;
Working_Set *working = &cmd->models->working_set;
int size = 0;
String *str = working_set_clipboard_index(working, index);
if (str){
size = str->size;
if (out){
String out_str = make_string(out, 0, len);
copy(&out_str, *str);
}
}
return(size);
}
internal void
internal_get_buffer_first(Working_Set *working_set, Buffer_Summary *buffer){
if (working_set->file_count > 0){
fill_buffer_summary(buffer, (Editing_File*)working_set->used_sentinel.next, working_set);
}
}
internal void
internal_get_buffer_next(Working_Set *working_set, Buffer_Summary *buffer){
Editing_File *file;
file = working_set_get_active_file(working_set, buffer->buffer_id);
if (file){
file = (Editing_File*)file->node.next;
fill_buffer_summary(buffer, file, working_set);
}
else{
*buffer = buffer_summary_zero();
}
}
API_EXPORT Buffer_Summary
Get_Buffer_First(Application_Links *app, unsigned int access)/*
DOC_PARAM(access, the access flags for the access)
DOC_RETURN(returns the summary of the first buffer in a buffer loop)
DOC
(
Begins a loop across all the buffers.
If the buffer returned does not exist, the loop is finished. Buffers
should not be killed durring a buffer loop.
)
DOC_SEE(Access_Flag)
DOC_SEE(get_buffer_next)
*/{
Command_Data *cmd = (Command_Data*)app->cmd_context;
Working_Set *working_set = &cmd->models->working_set;
Buffer_Summary result = {};
internal_get_buffer_first(working_set, &result);
while (result.exists && !access_test(result.lock_flags, access)){
internal_get_buffer_next(working_set, &result);
}
return(result);
}
API_EXPORT void
Get_Buffer_Next(Application_Links *app, Buffer_Summary *buffer, unsigned int access)/*
DOC_PARAM(buffer, pointer to the loop buffer originally returned by get_buffer_first)
DOC_PARAM(access, the access flags for the access)
DOC
(
Writes the next buffer into the buffer struct. To get predictable results every
call to get_buffer_first and get_buffer_next in the loop should have the same
access flags.
If the buffer returned does not exist, the loop is finished. Buffers
should not be killed durring a buffer loop.
)
DOC_SEE(Access_Flag)
DOC_SEE(get_buffer_first)
*/{
Command_Data *cmd = (Command_Data*)app->cmd_context;
Working_Set *working_set = &cmd->models->working_set;
internal_get_buffer_next(working_set, buffer);
while (buffer->exists && !access_test(buffer->lock_flags, access)){
internal_get_buffer_next(working_set, buffer);
}
}
API_EXPORT Buffer_Summary
Get_Buffer(Application_Links *app, int buffer_id, unsigned int access)/*
DOC_PARAM(buffer_id, the id of the buffer to get)
DOC_PARAM(access, the access flags for the access)
DOC_RETURN(returns a summary that describes the indicated buffer if it exists and is accessible)
*/{
Command_Data *cmd = (Command_Data*)app->cmd_context;
Working_Set *working_set = &cmd->models->working_set;
Buffer_Summary buffer = {};
Editing_File *file;
file = working_set_get_active_file(working_set, buffer_id);
if (file){
fill_buffer_summary(&buffer, file, working_set);
if (!access_test(buffer.lock_flags, access)){
buffer = buffer_summary_zero();
}
}
return(buffer);
}
API_EXPORT Buffer_Summary
Get_Buffer_By_Name(Application_Links *app, char *name, int len, unsigned int access)/*
DOC_PARAM(name, the name of the buffer)
DOC_PARAM(len, the length of the name string)
DOC_PARAM(access, the access flags for the access)
DOC_RETURN(returns a summary that describes the indicated buffer if it exists and is accessible)
*/{
Command_Data *cmd = (Command_Data*)app->cmd_context;
Buffer_Summary buffer = {};
Editing_File *file;
Working_Set *working_set = &cmd->models->working_set;
file = working_set_contains(cmd->system, working_set, make_string(name, len));
if (file && !file->is_dummy){
fill_buffer_summary(&buffer, file, working_set);
if (!access_test(buffer.lock_flags, access)){
buffer = buffer_summary_zero();
}
}
return(buffer);
}
API_EXPORT int
Buffer_Boundary_Seek(Application_Links *app, Buffer_Summary *buffer, int start_pos, int seek_forward, unsigned int flags)/*
DOC_PARAM(buffer, the buffer to seek through)
DOC_PARAM(start_pos, the absolute position in the buffer to begin the seek)
DOC_PARAM(seek_forward, non-zero indicates to seek forward otherwise the seek goes backward)
DOC_PARAM(flags, one or more types of boundaries to use for stopping the seek)
DOC_RETURN(returns the position where the seek stops)
DOC_SEE(Seek_Boundary_Flag)
*/{
Command_Data *cmd = (Command_Data*)app->cmd_context;
Editing_File *file;
int result = false;
file = imp_get_file(cmd, buffer);
if (file){
// TODO(allen): reduce duplication?
i32 size = buffer_size(&file->state.buffer);
i32 pos[4] = {0};
i32 new_pos = 0;
if (start_pos < 0){
start_pos = 0;
}
else if (start_pos > size){
start_pos = size;
}
if (seek_forward){
for (i32 i = 0; i < ArrayCount(pos); ++i) pos[i] = size;
if (flags & (1)){
pos[0] = buffer_seek_whitespace_right(&file->state.buffer, start_pos);
}
if (flags & (1 << 1)){
if (file->state.tokens_complete){
pos[1] = seek_token_right(&file->state.token_stack, start_pos);
}
else{
pos[1] = buffer_seek_whitespace_right(&file->state.buffer, start_pos);
}
}
if (flags & (1 << 2)){
pos[2] = buffer_seek_alphanumeric_right(&file->state.buffer, start_pos);
if (flags & (1 << 3)){
pos[3] = buffer_seek_range_camel_right(&file->state.buffer, start_pos, pos[2]);
}
}
else{
if (flags & (1 << 3)){
pos[3] = buffer_seek_alphanumeric_or_camel_right(&file->state.buffer, start_pos);
}
}
new_pos = size;
for (i32 i = 0; i < ArrayCount(pos); ++i){
if (pos[i] < new_pos) new_pos = pos[i];
}
}
else{
if (flags & (1)){
pos[0] = buffer_seek_whitespace_left(&file->state.buffer, start_pos);
}
if (flags & (1 << 1)){
if (file->state.tokens_complete){
pos[1] = seek_token_left(&file->state.token_stack, start_pos);
}
else{
pos[1] = buffer_seek_whitespace_left(&file->state.buffer, start_pos);
}
}
if (flags & (1 << 2)){
pos[2] = buffer_seek_alphanumeric_left(&file->state.buffer, start_pos);
if (flags & (1 << 3)){
pos[3] = buffer_seek_range_camel_left(&file->state.buffer, start_pos, pos[2]);
}
}
else{
if (flags & (1 << 3)){
pos[3] = buffer_seek_alphanumeric_or_camel_left(&file->state.buffer, start_pos);
}
}
new_pos = 0;
for (i32 i = 0; i < ArrayCount(pos); ++i){
if (pos[i] > new_pos) new_pos = pos[i];
}
}
result = new_pos;
fill_buffer_summary(buffer, file, cmd);
}
return(result);
}
API_EXPORT int
Buffer_Read_Range(Application_Links *app, Buffer_Summary *buffer, int start, int end, char *out)/*
DOC_PARAM(buffer, the buffer to read out of)
DOC_PARAM(start, the beginning of the read range)
DOC_PARAM(end, one past the end of the read range)
DOC_PARAM(out, the output buffer to fill with the result of the read)
DOC_RETURN(returns non-zero on success)
DOC
(
The output buffer must have a capacity of at least (end - start)
The output is not null terminated.
This call fails if the buffer does not exist, or if the read range
is not within the bounds of the buffer.
)
*/{
Command_Data *cmd = (Command_Data*)app->cmd_context;
Editing_File *file = imp_get_file(cmd, buffer);
int result = false;
int size;
if (file){
size = buffer_size(&file->state.buffer);
if (0 <= start && start <= end && end <= size){
result = true;
buffer_stringify(&file->state.buffer, start, end, out);
}
fill_buffer_summary(buffer, file, cmd);
}
return(result);
}
API_EXPORT int
Buffer_Replace_Range(Application_Links *app, Buffer_Summary *buffer, int start, int end, char *str, int len)/*
DOC_PARAM(buffer, the buffer to edit)
DOC_PARAM(start, the start of the range to edit)
DOC_PARAM(end, the end of the range to edit)
DOC_PARAM(str, the string to write into the range)
DOC_PARAM(len, the length of the str string)
DOC_RETURN(returns non-zero if the replacement succeeds)
DOC
(
If this call succeeds it deletes the range from start to end
and writes str in the same position. If end == start then
this call is equivalent to inserting the string at start.
If len == 0 this call is equivalent to deleteing the range
from start to end.
This call fails if the buffer does not exist, or if the replace
range is not within the bounds of the buffer.
)
*/{
Command_Data *cmd = (Command_Data*)app->cmd_context;
Editing_File *file = imp_get_file(cmd, buffer);
int result = false;
int size = 0;
int next_cursor = 0, pos = 0;
if (file){
size = buffer_size(&file->state.buffer);
if (0 <= start && start <= end && end <= size){
result = true;
pos = file->state.cursor_pos;
if (pos < start) next_cursor = pos;
else if (pos < end) next_cursor = start;
else next_cursor = pos + end - start - len;
file_replace_range(cmd->system, cmd->models,
file, start, end, str, len, next_cursor);
}
fill_buffer_summary(buffer, file, cmd);
}
return(result);
}
API_EXPORT int
Buffer_Set_Setting(Application_Links *app, Buffer_Summary *buffer, int setting, int value)/*
DOC_PARAM(buffer, the buffer to set a setting on)
DOC_PARAM(setting, one of the Buffer_Setting_ID enum values that identifies the setting to set)
DOC_PARAM(value, the value to set the specified setting to)
DOC_SEE(Buffer_Setting_ID)
*/{
Command_Data *cmd = (Command_Data*)app->cmd_context;
System_Functions *system = cmd->system;
Models *models = cmd->models;
Editing_File *file = imp_get_file(cmd, buffer);
int result = false;
i32 new_mapid = 0;
if (file){
result = true;
switch (setting){
case BufferSetting_Lex:
{
#if BUFFER_EXPERIMENT_SCALPEL <= 0
if (file->settings.tokens_exist){
if (!value){
file_kill_tokens(system, &models->mem.general, file);
}
}
else{
if (value){
file_first_lex_parallel(system, &models->mem.general, file);
}
}
#endif
}break;
case BufferSetting_WrapLine:
{
file->settings.unwrapped_lines = !value;
}break;
case BufferSetting_MapID:
{
if (value == mapid_global){
file->settings.base_map_id = mapid_global;
}
else if (value == mapid_file){
file->settings.base_map_id = mapid_file;
}
else if (value < mapid_global){
new_mapid = get_map_index(models, value);
if (new_mapid < models->user_map_count){
file->settings.base_map_id = value;
}
else{
file->settings.base_map_id = mapid_file;
}
}
for (View_Iter iter = file_view_iter_init(&models->layout, file, 0);
file_view_iter_good(iter);
iter = file_view_iter_next(iter)){
iter.view->map = get_map(models, file->settings.base_map_id);
}
}break;
}
fill_buffer_summary(buffer, file, cmd);
}
return(result);
}
API_EXPORT int
Buffer_Auto_Indent(Application_Links *app, Buffer_Summary *buffer, int start, int end, int tab_width, unsigned int flags)/*
DOC_PARAM(buffer, the buffer in which to apply the auto indenting)
DOC_PARAM(start, the position to start the auto indenting)
DOC_PARAM(end, the position to end the auto indenting)
DOC_PARAM(tab_width, the number of spaces to place as a tab)
DOC_PARAM(flags, the auto tab behavior flags)
DOC_RETURN(returns non-zero when the call succeeds)
DOC
(
Applies the built in auto-indentation rule to the code in the range from
start to end by inserting spaces or tabs at the beginning of the lines.
If the buffer does not have lexing enabled or the lexing job has not
completed this function will fail.
)
DOC_SEE(Auto_Indent_Flag)
*/{
Command_Data *cmd = (Command_Data*)app->cmd_context;
System_Functions *system = cmd->system;
Models *models = cmd->models;
Indent_Options opts = {0};
int result = false;
Editing_File *file = imp_get_file(cmd, buffer);
if (file && file->state.token_stack.tokens &&
file->state.tokens_complete && !file->state.still_lexing){
result = true;
opts.empty_blank_lines = (flags & AutoIndent_ClearLine);
opts.use_tabs = (flags & AutoIndent_UseTab);
opts.tab_width = tab_width;
file_auto_tab_tokens(system, models, file, start, start, end, opts);
fill_buffer_summary(buffer, file, cmd);
}
return(result);
}
API_EXPORT Buffer_Summary
Create_Buffer(Application_Links *app, char *filename, int filename_len, unsigned int flags)/*
DOC_PARAM(filename, the name of the file to be opened or created)
DOC_PARAM(filename_len, the length of the filename string)
DOC_PARAM(flags, flags for buffer creation behavior)
DOC_RETURN(returns the summary of the created buffer on success or a NULL buffer otherwise)
DOC
(
Tries to create a new buffer and associate it to the given filename. If such a buffer already
exists the existing buffer is returned in the buffer summary and no new buffer is created.
If the buffer does not exist a new buffer is created and named after the given filename. If
the filename corresponds to a file on the disk that file is loaded and put into buffer, if
the filename does not correspond to a file on disk the buffer is created empty.
)
DOC_SEE(Buffer_Create_Flag)
*/{
Command_Data *cmd = (Command_Data*)app->cmd_context;
System_Functions *system = cmd->system;
Models *models = cmd->models;
Working_Set *working_set = &models->working_set;
General_Memory *general = &models->mem.general;
Partition *part = &models->mem.part;
Buffer_Summary result = {0};
Temp_Memory temp = begin_temp_memory(part);
if (filename != 0){
String filename_string = make_string_terminated(part, filename, filename_len);
Editing_File *file = working_set_contains(system, working_set, filename_string);
if (file == 0){
File_Loading loading = {0};
b32 do_new_file = false;
if (flags & BufferCreate_AlwaysNew){
do_new_file = true;
}
else{
loading = system->file_load_begin(filename_string.str);
if (loading.exists){
do_new_file = true;
}
}
if (do_new_file){
b32 in_general_mem = false;
char *buffer = push_array(part, char, loading.size);
if (buffer == 0){
buffer = (char*)general_memory_allocate(general, loading.size);
if (buffer != 0){
in_general_mem = true;
}
}
if (system->file_load_end(loading, buffer)){
file = working_set_alloc_always(working_set, general);
if (file){
file_init_strings(file);
file_set_name(working_set, file, filename_string);
working_set_add(system, working_set, file, general);
init_normal_file(system, models, file,
buffer, loading.size);
fill_buffer_summary(&result, file, cmd);
}
}
if (in_general_mem){
general_memory_free(general, buffer);
}
}
else{
file = working_set_alloc_always(working_set, general);
if (file){
file_init_strings(file);
file_set_name(working_set, file, filename_string);
working_set_add(system, working_set, file, general);
init_normal_file(system, models, file, 0, 0);
fill_buffer_summary(&result, file, cmd);
}
}
}
else{
fill_buffer_summary(&result, file, cmd);
}
}
end_temp_memory(temp);
return(result);
}
API_EXPORT int
Save_Buffer(Application_Links *app, Buffer_Summary *buffer, char *filename, int filename_len, unsigned int flags)/*
DOC_PARAM(buffer, the buffer to save to a file)
DOC_PARAM(filename, the name of the file to save the buffer into)
DOC_PARAM(filename_len, length of the filename string)
DOC_PARAM(flags, not currently used)
DOC_RETURN(returns non-zero if the save succeeds)
*/{
Command_Data *cmd = (Command_Data*)app->cmd_context;
System_Functions *system = cmd->system;
Models *models = cmd->models;
int result = false;
Editing_File *file = imp_get_file(cmd, buffer);
if (file){
result = true;
String name = make_string(filename, filename_len);
view_save_file(system, models, file, 0, name, false);
}
return(result);
}
API_EXPORT int
Kill_Buffer(Application_Links *app, Buffer_Identifier buffer, int view_id, unsigned int flags)/*
DOC_PARAM(buffer, a buffer identifier specifying the buffer to try to kill)
DOC_PARAM(view_id, the id of view that will contain the "are you sure" dialogue)
DOC_PARAM(flags, flags for buffer kill behavior)
DOC_RETURN(returns non-zero if the kill succeeds)
DOC
(
Tries to kill the idenfied buffer. If the buffer is dirty and the "are you sure"
dialogue needs to be displayed the provided view is used to show the dialogue.
If the view is not open the kill fails.
)
DOC_SEE(Buffer_Kill_Flags)
*/{
Command_Data *cmd = (Command_Data*)app->cmd_context;
System_Functions *system = cmd->system;
Models *models = cmd->models;
Working_Set *working_set = &models->working_set;
View *vptr = imp_get_view(cmd, view_id);
Editing_File *file = get_file_from_identifier(system, working_set, buffer);
int result = false;
if (file){
if (flags & BufferKill_AlwaysKill){
result = true;
kill_file(system, models, file, string_zero());
}
else{
if (vptr == 0){
result = true;
try_kill_file(system, models, file, vptr, string_zero());
}
else{
// TODO(allen): message
}
}
}
return(result);
}
internal void
internal_get_view_first(Command_Data *cmd, View_Summary *view){
Editing_Layout *layout = &cmd->models->layout;
Panel *panel = layout->used_sentinel.next;
Assert(panel != &layout->used_sentinel);
fill_view_summary(view, panel->view, cmd);
}
internal void
internal_get_view_next(Command_Data *cmd, View_Summary *view){
Editing_Layout *layout = &cmd->models->layout;
Live_Views *live_set = &cmd->vars->live_set;
int index = view->view_id - 1;
View *vptr = 0;
Panel *panel = 0;
if (index >= 0 && index < live_set->max){
vptr = live_set->views + index;
panel = vptr->panel;
if (panel){
panel = panel->next;
}
if (panel && panel != &layout->used_sentinel){
fill_view_summary(view, panel->view, &cmd->vars->live_set, &cmd->models->working_set);
}
else{
*view = view_summary_zero();
}
}
else{
*view = view_summary_zero();
}
}
API_EXPORT View_Summary
Get_View_First(Application_Links *app, unsigned int access)/*
DOC_PARAM(access, the access flags for the access)
DOC_RETURN(returns the summary of the first view in a view loop)
DOC
(
Begins a loop across all the open views.
If the view summary returned is NULL, the loop is finished.
Views should not be closed or opened durring a view loop.
)
DOC_SEE(Access_Flag)
DOC_SEE(get_view_next)
*/{
Command_Data *cmd = (Command_Data*)app->cmd_context;
View_Summary view = {};
internal_get_view_first(cmd, &view);
while (view.exists && !access_test(view.lock_flags, access)){
internal_get_view_next(cmd, &view);
}
return(view);
}
API_EXPORT void
Get_View_Next(Application_Links *app, View_Summary *view, unsigned int access)/*
DOC_PARAM(view, pointer to the loop view originally returned by get_view_first)
DOC_PARAM(access, the access flags for the access)
DOC
(
Writes the next view into the view struct. To get predictable results every
call to get_view_first and get_view_next in the loop should have the same
access flags.
If the view summary returned is NULL, the loop is finished.
Views should not be closed or opened durring a view loop.
)
DOC_SEE(Access_Flag)
DOC_SEE(get_view_first)
*/{
Command_Data *cmd = (Command_Data*)app->cmd_context;
internal_get_view_next(cmd, view);
while (view->exists && !access_test(view->lock_flags, access)){
internal_get_view_next(cmd, view);
}
}
API_EXPORT View_Summary
Get_View(Application_Links *app, int view_id, unsigned int access)/*
DOC_PARAM(view_id, the id of the view to get)
DOC_PARAM(access, the access flags for the access)
DOC_RETURN(returns a summary that describes the indicated view if it is open and is accessible)
*/{
Command_Data *cmd = (Command_Data*)app->cmd_context;
View_Summary view = {0};
Live_Views *live_set = cmd->live_set;
int max = live_set->max;
View *vptr = 0;
view_id -= 1;
if (view_id >= 0 && view_id < max){
vptr = live_set->views + view_id;
fill_view_summary(&view, vptr, live_set, &cmd->models->working_set);
if (!access_test(view.lock_flags, access)){
view = view_summary_zero();
}
}
return(view);
}
API_EXPORT View_Summary
Get_Active_View(Application_Links *app, unsigned int access)/*
DOC_PARAM(access, the access flags for the access)
DOC_RETURN(returns a summary that describes the active view)
DOC_SEE(set_active_view)
*/{
Command_Data *cmd = (Command_Data*)app->cmd_context;
View_Summary view = {0};
fill_view_summary(&view, cmd->view, &cmd->vars->live_set, &cmd->models->working_set);
if (!access_test(view.lock_flags, access)){
view = view_summary_zero();
}
return(view);
}
API_EXPORT int
Set_Active_View(Application_Links *app, View_Summary *view)/*
DOC_PARAM(view, the view to set as active)
DOC_RETURN(returns non-zero on success)
DOC
(
If the given view is a currently open view, it is set as the
active view, and takes subsequent commands and is returned
from get_active_view.
)
DOC_SEE(get_active_view)
*/{
Command_Data *cmd = (Command_Data*)app->cmd_context;
Models *models = cmd->models;
View *vptr = imp_get_view(cmd, view);
int result = false;
if (vptr){
result = true;
Panel *panel = vptr->panel;
models->layout.active_panel = (i32)(panel - models->layout.panels);
update_command_data(cmd->vars, cmd);
}
return(result);
}
/*
API_EXPORT int
View_Set_Split_Proportion_(Application_Links *app, View_Summary *view, float t){
Command_Data *cmd = (Command_Data*)app->cmd_context;
Models *models = cmd->models;
Editing_Layout *layout = &models->layout;
View *vptr = imp_get_view(cmd, view);
int result = false;
if (vptr){
result = true;
Panel *panel = vptr->panel;
Panel_Divider *div = layout->dividers + panel->parent;
}
return(result);
}
*/
API_EXPORT int
View_Compute_Cursor(Application_Links *app, View_Summary *view, Buffer_Seek seek, Full_Cursor *cursor_out)/*
DOC_PARAM(view, the view on which to run the cursor computation)
DOC_PARAM(seek, the seek position)
DOC_PARAM(cursor_out, on success this is filled with result of the seek)
DOC_RETURN(returns non-zero on success)
DOC
(
Computes a full cursor for the given seek position.
)
DOC_SEE(Buffer_Seek)
*/{
Command_Data *cmd = (Command_Data*)app->cmd_context;
View *vptr = imp_get_view(cmd, view);
Editing_File *file = 0;
int result = false;
if (vptr){
file = vptr->file_data.file;
if (file && !file->is_loading){
if (seek.type == buffer_seek_line_char && seek.character <= 0){
seek.character = 1;
}
result = true;
*cursor_out = view_compute_cursor(vptr, seek);
fill_view_summary(view, vptr, cmd);
}
}
return(result);
}
API_EXPORT int
View_Set_Cursor(Application_Links *app, View_Summary *view, Buffer_Seek seek, int set_preferred_x)/*
DOC_PARAM(view, the view in which to set the cursor)
DOC_PARAM(seek, the seek position)
DOC_PARAM(set_preferred_x, if true the preferred x is updated to match the new cursor position)
DOC_RETURN(returns non-zero on success)
DOC
(
Sets the the view's cursor position. set_preferred_x should usually be true unless the change in
cursor position is is a vertical motion that tries to keep the cursor in the same column or x position.
)
DOC_SEE(Buffer_Seek)
*/{
Command_Data *cmd = (Command_Data*)app->cmd_context;
View *vptr = imp_get_view(cmd, view);
Editing_File *file = 0;
int result = false;
if (vptr){
file = vptr->file_data.file;
if (file && !file->is_loading){
result = true;
if (seek.type == buffer_seek_line_char && seek.character <= 0){
seek.character = 1;
}
vptr->recent->cursor = view_compute_cursor(vptr, seek);
if (set_preferred_x){
vptr->recent->preferred_x = view_get_cursor_x(vptr);
}
fill_view_summary(view, vptr, cmd);
file->state.cursor_pos = vptr->recent->cursor.pos;
}
}
return(result);
}
API_EXPORT int
View_Set_Mark(Application_Links *app, View_Summary *view, Buffer_Seek seek)/*
DOC_PARAM(view, the view in which to set the mark)
DOC_PARAM(seek, the seek position)
DOC_RETURN(returns non-zero on success)
DOC
(
Sets the the view's mark position.
)
DOC_SEE(Buffer_Seek)
*/{
Command_Data *cmd = (Command_Data*)app->cmd_context;
View *vptr = imp_get_view(cmd, view);
Full_Cursor cursor = {0};
int result = false;
if (vptr){
result = true;
if (seek.type != buffer_seek_pos){
cursor = view_compute_cursor(vptr, seek);
vptr->recent->mark = cursor.pos;
}
else{
vptr->recent->mark = seek.pos;
}
fill_view_summary(view, vptr, cmd);
}
return(result);
}
API_EXPORT int
View_Set_Highlight(Application_Links *app, View_Summary *view, int start, int end, int turn_on)/*
DOC_PARAM(view, the view to set the highlight in)
DOC_PARAM(start, the start of the highlight range)
DOC_PARAM(end, the end of the highlight range)
DOC_PARAM(turn_on, indicates whether the highlight is being turned on or off)
DOC_RETURN(returns non-zero on success)
DOC
(
The highlight is mutually exclusive to the cursor. When the turn_on parameter
is set to true the highlight will be shown and the cursor will be hidden. After
that either setting the with view_set_cursor or calling view_set_highlight and
the turn_on set to false, will switch back to showing the cursor.
)
*/{
Command_Data *cmd = (Command_Data*)app->cmd_context;
View *vptr = imp_get_view(cmd, view);
int result = false;
if (vptr){
result = true;
if (turn_on){
view_set_temp_highlight(vptr, start, end);
}
else{
vptr->file_data.show_temp_highlight = 0;
}
fill_view_summary(view, vptr, cmd);
}
return(result);
}
API_EXPORT int
View_Set_Buffer(Application_Links *app, View_Summary *view, int buffer_id, unsigned int flags)/*
DOC_PARAM(view, the view to display the buffer in)
DOC_PARAM(buffer_id, the buffer to show in the view)
DOC_PARAM(flags, set buffer behavior flags)
DOC_RETURN(returns non-zero on success)
DOC
(
On success view_set_buffer sets the specified view's current buffer and
cancels and dialogue shown in the view and displays the file.
)
DOC_SEE(Set_Buffer_Flag)
*/{
Command_Data *cmd = (Command_Data*)app->cmd_context;
View *vptr = imp_get_view(cmd, view);
Models *models = cmd->models;
Editing_File *file = 0;
int result = false;
if (vptr){
file = working_set_get_active_file(&models->working_set, buffer_id);
if (file){
result = true;
if (file != vptr->file_data.file){
view_set_file(vptr, file, models);
if (!(flags & SetBuffer_KeepOriginalGUI)){
view_show_file(vptr);
}
}
}
fill_view_summary(view, vptr, cmd);
}
return(result);
}
API_EXPORT int
View_Post_Fade(Application_Links *app, View_Summary *view, float seconds, int start, int end, unsigned int color)/*
DOC_PARAM(view, the veiw to post a fade effect to)
DOC_PARAM(seconds, the number of seconds the fade effect should last)
DOC_PARAM(start, the first character in the fade range)
DOC_PARAM(end, one after the last character in the fade range)
DOC_PARAM(color, the color to fade from)
*/{
Command_Data *cmd = (Command_Data*)app->cmd_context;
View *vptr = imp_get_view(cmd, view);
int result = false;
int size = end - start;
if (vptr){
if (size > 0){
result = true;
view_post_paste_effect(vptr, seconds, start, size, color);
}
}
return(result);
}
API_EXPORT void
View_Set_Paste_Rewrite_(Application_Links *app, View_Summary *view){
Command_Data *cmd = (Command_Data*)app->cmd_context;
View *vptr = imp_get_view(cmd, view);
if (vptr){
vptr->next_mode.rewrite = true;
}
}
API_EXPORT int
View_Get_Paste_Rewrite_(Application_Links *app, View_Summary *view){
Command_Data *cmd = (Command_Data*)app->cmd_context;
View *vptr = imp_get_view(cmd, view);
int result = false;
if (vptr){
result = vptr->mode.rewrite;
}
return(result);
}
API_EXPORT User_Input
Get_User_Input(Application_Links *app, unsigned int get_type, unsigned int abort_type)/*
DOC_PARAM(get_type, input type flag that specifies the types of inputs that should be returned)
DOC_PARAM(abort_type, input type flag that specifies the types of inputs that should cause an abort signal)
DOC_RETURN(returns a User_Input that describes an event passed to the command)
DOC
(
This call preempts the command. The command is resumed if either a get or abort condition
is met, or if another command is executed. If either the abort condition is met or another
command is executed an abort signal is returned. If an abort signal is ever returned the
command should finish execution without any more calls that preempt the command.
If a get condition is met the user input is returned
)
DOC_SEE(Input_Type_Flag)
DOC_SEE(User_Input)
*/{
Command_Data *cmd = (Command_Data*)app->cmd_context;
System_Functions *system = cmd->system;
Coroutine *coroutine = (Coroutine*)app->current_coroutine;
User_Input result = {0};
if (app->type_coroutine == Co_Command){
Assert(coroutine);
*((u32*)coroutine->out+0) = get_type;
*((u32*)coroutine->out+1) = abort_type;
system->yield_coroutine(coroutine);
result = *(User_Input*)coroutine->in;
}
return(result);
}
API_EXPORT User_Input
Get_Command_Input (Application_Links *app)/*
DOC_RETURN(returns the input that triggered the command in execution.)
DOC_SEE(User_Input)
*/{
Command_Data *cmd = (Command_Data*)app->cmd_context;
User_Input result;
result.type = UserInputKey;
result.abort = 0;
result.key = cmd->key;
result.command = 0;
return(result);
}
API_EXPORT Mouse_State
Get_Mouse_State(Application_Links *app)/*
DOC_RETURN(returns the current mouse state)
DOC_SEE(Mouse_State)
*/{
Command_Data *cmd = (Command_Data*)app->cmd_context;
App_Vars *vars = cmd->vars;
Mouse_State mouse = direct_get_mouse_state(&vars->available_input);
return(mouse);
}
#if 0
//API_EXPORT
Event_Message
Get_Event_Message (Application_Links *app){
Event_Message message = {0};
System_Functions *system = (System_Functions*)app->system_links;
Coroutine *coroutine = (Coroutine*)app->current_coroutine;
if (app->type_coroutine == Co_View){
Assert(coroutine);
system->yield_coroutine(coroutine);
message = *(Event_Message*)coroutine->in;
}
return(message);
}
#endif
API_EXPORT int
Start_Query_Bar(Application_Links *app, Query_Bar *bar, unsigned int flags)/*
DOC_PARAM(bar, a pointer to the Query_Bar struct that defines the bar's contents)
DOC_PARAM(flags, not currently used)
DOC_RETURN(returns non-zero on success)
DOC
(
The memory pointed to by bar must remain valid until a call to end_query_bar or
until the command returns.
)
*/{
Command_Data *cmd = (Command_Data*)app->cmd_context;
Query_Slot *slot = 0;
View *vptr;
vptr = cmd->view;
slot = alloc_query_slot(&vptr->query_set);
slot->query_bar = bar;
return(slot != 0);
}
API_EXPORT void
End_Query_Bar(Application_Links *app, Query_Bar *bar, unsigned int flags)/*
DOC_PARAM(bar, a pointer to the Query_Bar struct to end)
DOC_PARAM(flags, not currently used)
DOC
(
bar must be a pointer previously passed to start_query_bar previously in the same command.
)
*/{
Command_Data *cmd = (Command_Data*)app->cmd_context;
View *vptr;
vptr = cmd->view;
free_query_slot(&vptr->query_set, bar);
}
API_EXPORT void
Print_Message(Application_Links *app, char *str, int len)/*
DOC_PARAM(str, the string to post to *messages*)
DOC_PARAM(len, the length of str string)
*/{
Command_Data *cmd = (Command_Data*)app->cmd_context;
Models *models = cmd->models;
do_feedback_message(cmd->system, models, make_string(str, len));
}
API_EXPORT void
Change_Theme(Application_Links *app, char *name, int len)/*
DOC_PARAM(name, the name of the built in theme to change to)
DOC_PARAM(len, the length of the name string)
*/{
Command_Data *cmd = (Command_Data*)app->cmd_context;
Style_Library *styles = &cmd->models->styles;
String theme_name = make_string(name, len);
i32 i = 0;
i32 count = styles->count;
Style *s = styles->styles;
for (i = 0; i < count; ++i, ++s){
if (match(s->name, theme_name)){
style_copy(main_style(cmd->models), s);
break;
}
}
}
API_EXPORT void
Change_Font(Application_Links *app, char *name, int len)/*
DOC_PARAM(name, the name of the built in font to change to)
DOC_PARAM(len, the length of the name string)
*/{
Command_Data *cmd = (Command_Data*)app->cmd_context;
Font_Set *set = cmd->models->font_set;
Style_Font *global_font = &cmd->models->global_font;
String font_name = make_string(name, len);
i16 font_id = 0;
if (font_set_extract(set, font_name, &font_id)){
global_font->font_id = font_id;
global_font->font_changed = 1;
}
}
API_EXPORT void
Set_Theme_Colors(Application_Links *app, Theme_Color *colors, int count)/*
DOC_PARAM(colors, an array of color structs pairing differet style tags to color codes)
DOC_PARAM(count, the number of color structs in the colors array)
DOC
(
For each color struct in the array, the color in the style pallet is set to the color
code paired with the tag.
)
*/{
Command_Data *cmd = (Command_Data*)app->cmd_context;
Style *style = main_style(cmd->models);
u32 *color = 0;
i32 i = 0;
Theme_Color *theme_color = colors;
for (i = 0; i < count; ++i, ++theme_color){
color = style_index_by_tag(&style->main, theme_color->tag);
if (color) *color = theme_color->color | 0xFF000000;
}
}
API_EXPORT void
Get_Theme_Colors(Application_Links *app, Theme_Color *colors, int count)/*
DOC_PARAM(colors, an array of color structs listing style tags to get color values for)
DOC_PARAM(count, the number of color structs in the colors array)
DOC
(
For each color struct in the array, the color field of the struct is filled with the
color from the specified color in the pallet.
)
*/{
Command_Data *cmd = (Command_Data*)app->cmd_context;
Style *style = main_style(cmd->models);
u32 *color = 0;
i32 i = 0;
Theme_Color *theme_color = colors;
for (i = 0; i < count; ++i, ++theme_color){
color = style_index_by_tag(&style->main, theme_color->tag);
if (color){
theme_color->color = *color | 0xFF000000;
}
else{
theme_color->color = 0xFF000000;
}
}
}
API_EXPORT int
Directory_Get_Hot(Application_Links *app, char *out, int capacity)/*
DOC_PARAM(out, a buffer that receives the 4coder 'hot directory')
DOC_PARAM(capacity, the maximum size to be output to the output buffer)
DOC_RETURN(returns the size of the string written into the buffer)
DOC
(
4coder has a concept of a 'hot directory' which is the directory most recently
accessed in the GUI. Whenever the GUI is opened it shows the hot directory.
In the future this will be deprecated and eliminated in favor of more flexible
directories controlled by the custom side.
)
*/{
Command_Data *cmd = (Command_Data*)app->cmd_context;
Hot_Directory *hot = &cmd->models->hot_directory;
i32 copy_max = capacity - 1;
hot_directory_clean_end(hot);
if (copy_max > hot->string.size)
copy_max = hot->string.size;
memcpy(out, hot->string.str, copy_max);
out[copy_max] = 0;
return(hot->string.size);
}
#define Get_4ed_Path system->get_4ed_path
#define File_Exists system->file_exists
#define Directory_CD system->directory_cd
API_EXPORT File_List
Get_File_List(Application_Links *app, char *dir, int len)/*
DOC_PARAM(dir, the directory whose files will be enumerated in the returned list)
DOC_PARAM(len, the length of the dir string)
DOC_RETURN
(
returns a File_List struct containing pointers to the names of the files in
the specified directory. The File_List returned should be passed to free_file_list
when it is no longer in use.
)
*/{
Command_Data *cmd = (Command_Data*)app->cmd_context;
System_Functions *system = cmd->system;
File_List result = {};
system->set_file_list(&result, make_string(dir, len));
return(result);
}
API_EXPORT void
Free_File_List(Application_Links *app, File_List list)/*
DOC_PARAM(list, the file list to be freed)
DOC(after this call the file list passed in should not be read or written to)
*/{
Command_Data *cmd = (Command_Data*)app->cmd_context;
System_Functions *system = cmd->system;
system->set_file_list(&list, make_string(0, 0));
}
// BOTTOM