Files
beluga/src/buffer.c
T
2026-05-22 13:40:03 +02:00

304 lines
7.6 KiB
C

/**
* @file buffer.c
* @brief Buffer management implementation for multiple open files
*/
#include "../include/buffer.h"
#include "../include/file_io.h"
#include "../include/editor_op.h"
#include "../include/data.h"
#include "include/split_screen.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "include/input.h"
extern struct editorConfig E;
/**
* @brief Finds a buffer by filename
* @param filename The filename to search for
* @return Buffer ID if found, -1 if not found
*/
static int bufferFindByFilename(const char* filename)
{
for (int i = 0; i < E.number_of_buffer; i++)
{
if (E.buffers[i].filename != NULL &&
strcmp(E.buffers[i].filename, filename) == 0)
{
return E.buffers[i].buffer_id;
}
}
return -1;
}
/**
* @brief Finds a buffer by ID
* @param buffer_id The buffer ID to find
* @return Pointer to buffer, NULL if not found
*/
struct buffer_t* bufferFindById(int buffer_id)
{
for (int i = 0; i < E.number_of_buffer; i++)
{
if (E.buffers[i].buffer_id == buffer_id)
{
return &E.buffers[i];
}
}
return NULL;
}
/**
* @brief Creates a new buffer for a file
* @details Allocates buffer slot, loads file content, saves buffer metadata.
* If file is already open, switches to existing buffer instead.
* @param filename Path to the file
* @return Buffer ID on success, -1 on failure
*/
int bufferCreate(const char* filename)
{
// Check if file is already open
const int existing_id = bufferFindByFilename(filename);
if (existing_id != -1)
{
return bufferSwitch(existing_id);
}
// Check if we have space for more buffers
if (E.number_of_buffer >= 64)
{
editorSetStatusMessage("Error: maximum buffers reached (64)");
return -1;
}
// Initialize new buffer
struct buffer_t* new_buf = &E.buffers[E.number_of_buffer];
new_buf->buffer_id = E.number_of_buffer;
new_buf->filename = strdup(filename);
new_buf->type = FILE_BUFF;
new_buf->state = READ_AND_WRITE;
new_buf->x = 0;
new_buf->y = 0;
new_buf->dirty = 0; // New file starts clean
// Load file content using existing editorOpen
editorOpen(new_buf);
E.number_of_buffer++;
editorSetStatusMessage("Opened: %s (buffer %d)", filename, new_buf->buffer_id);
return new_buf->buffer_id;
}
/**
* @brief Switches to a specific buffer by ID
* @details Saves current buffer state, loads target buffer content and state.
* @param buffer_id The buffer ID to switch to
* @return 0 on success, -1 on failure
*/
int bufferSwitch(int buffer_id)
{
struct buffer_t* target = bufferFindById(buffer_id);
if (target == NULL)
{
E.layout.panes[E.layout.active_pane].buffer_id = buffer_id;
editorSetStatusMessage("Error: buffer not found");
return -1;
}
editorSetStatusMessage("Switched to: %s (buffer %d)",
target->filename, buffer_id);
return 0;
}
/**
* @brief Closes a buffer by ID
* @details Frees buffer resources and removes from buffer list.
* Prompts to save unsaved changes. If closing current buffer,
* switches to next available buffer.
* @param buffer_id The buffer ID to close
* @return 0 on success, -1 on failure
*/
int bufferClose(int buffer_id)
{
struct buffer_t* buf = bufferFindById(buffer_id);
if (buf == NULL)
{
editorSetStatusMessage("Error: buffer not found");
return -1;
}
EditorPane* active = splitScreenGetActivePane();
// If this is the current buffer, find next buffer to switch to
if (active->buffer_id == buffer_id)
{
int next_id = -1;
// Try to switch to next buffer
for (int i = 0; i < E.number_of_buffer; i++)
{
if (E.buffers[i].buffer_id != buffer_id)
{
next_id = E.buffers[i].buffer_id;
break;
}
}
if (next_id != -1)
{
bufferSwitch(next_id);
}
else
{
// No other buffers, clear editor
editorCloseFile();
}
}
// Free buffer resources
free(buf->filename);
buf->filename = NULL;
buf->buffer_id = -1;
// Remove from buffer list by shifting
for (int i = 0; i < E.number_of_buffer; i++)
{
if (E.buffers[i].buffer_id == buffer_id)
{
for (int j = i; j < E.number_of_buffer - 1; j++)
{
E.buffers[j] = E.buffers[j + 1];
}
E.number_of_buffer--;
break;
}
}
editorSetStatusMessage("Closed buffer %d", buffer_id);
return 0;
}
/**
* @brief Gets the current active buffer
* @return Pointer to current buffer_t, NULL if none
*/
struct buffer_t* bufferGetCurrent(void)
{
EditorPane* active = splitScreenGetActivePane();
return bufferFindById(active->buffer_id);
}
/**
* @brief Lists all open buffers
* @details Prints buffer information to status message
* @return Number of open buffers
*/
int bufferListAll(void)
{
if (E.number_of_buffer == 0)
{
editorSetStatusMessage("No buffers open");
return 0;
}
char buf[256] = "";
int offset = 0;
EditorPane* active = splitScreenGetActivePane();
for (int i = 0; i < E.number_of_buffer; i++)
{
struct buffer_t* b = &E.buffers[i];
char marker = (b->buffer_id == active->buffer_id) ? '*' : ' ';
offset += snprintf(&buf[offset], sizeof(buf) - offset,
"%c%d:%s ", marker, b->buffer_id,
b->filename ? b->filename : "[No Name]");
}
editorSetStatusMessage("Buffers: %s", buf);
return E.number_of_buffer;
}
/**
* @brief Saves current buffer to disk
* @return 0 on success, -1 on failure
*/
int bufferSave(void)
{
EditorPane* active = splitScreenGetActivePane();
if (active->buffer_id == -1)
{
editorSetStatusMessage("Error: no buffer active");
return -1;
}
editorSave();
return 0;
}
/**
* @brief Saves all buffers to disk
* @details Iterates through all buffers, saves each one
* @return 0 on success, -1 on failure
*/
int bufferSaveAll(void)
{
int saved = 0;
int failed = 0;
for (int i = 0; i < E.number_of_buffer; i++)
{
if (bufferSwitch(E.buffers[i].buffer_id) == 0)
{
if (E.dirty && bufferSave() == 0)
{
saved++;
}
}
else
{
failed++;
}
}
editorSetStatusMessage("Saved %d buffers (%d failed)", saved, failed);
return (failed == 0) ? 0 : -1;
}
/**
* @brief Searches for a string in the document
* @details Prompts user for a search query, then searches forward from current
* cursor position. Updates cursor position to the first match found.
* @note Updates global editor state E (cursor position, row_offset)
* @note Search is case-sensitive and operates on rendered line content
* @note Searches begin from the line after current cursor position
* @see editorPrompt()
* @see editorRowRxToCx()
*/
void bufferFind(struct buffer_t* buf)
{
appDebug("searching\n");
char* query = editorPrompt("Search: %s (ESC to cancel)", "", 0);
EditorPane* active = splitScreenGetActivePane();
if (query == NULL)
return;
int i;
for (i = active->cursor_y + 1; i < buf->numrows; i++)
{
row_t* row = &buf->row[i];
char* match = strstr(row->chars, query);
if (match)
{
active->cursor_y = i;
buf->y = buf->numrows;
break;
}
}
free(query);
}