add c and jai
This commit is contained in:
235
c/config/config.c
Normal file
235
c/config/config.c
Normal file
@@ -0,0 +1,235 @@
|
||||
// config.c — Global program configuration (config.ini next to binary)
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <unistd.h>
|
||||
#include <libgen.h>
|
||||
#include <mach-o/dyld.h>
|
||||
#else
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
Config g_config = {0};
|
||||
|
||||
////////////////////////////////
|
||||
// Path resolution
|
||||
|
||||
static void config_get_path(char *out, S32 out_size) {
|
||||
char exe_path[CONFIG_PATH_MAX];
|
||||
#ifdef __APPLE__
|
||||
U32 sz = sizeof(exe_path);
|
||||
_NSGetExecutablePath(exe_path, &sz);
|
||||
#else
|
||||
GetModuleFileNameA(NULL, exe_path, CONFIG_PATH_MAX);
|
||||
#endif
|
||||
// Strip binary name to get directory
|
||||
char *sep = strrchr(exe_path, '/');
|
||||
#ifndef __APPLE__
|
||||
char *bsep = strrchr(exe_path, '\\');
|
||||
if (bsep && (!sep || bsep > sep)) sep = bsep;
|
||||
#endif
|
||||
if (sep) sep[1] = '\0';
|
||||
snprintf(out, out_size, "%sconfig.ini", exe_path);
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
// Helpers
|
||||
|
||||
static void config_trim(char *s) {
|
||||
char *start = s;
|
||||
while (*start == ' ' || *start == '\t') start++;
|
||||
if (start != s) memmove(s, start, strlen(start) + 1);
|
||||
S32 len = (S32)strlen(s);
|
||||
while (len > 0 && (s[len - 1] == ' ' || s[len - 1] == '\t' ||
|
||||
s[len - 1] == '\r' || s[len - 1] == '\n'))
|
||||
s[--len] = '\0';
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
// Defaults
|
||||
|
||||
static void config_set_defaults(void) {
|
||||
MemoryZeroStruct(&g_config);
|
||||
snprintf(g_config.theme, sizeof(g_config.theme), "%s", "Gruvbox Dark");
|
||||
g_config.show_line_numbers = 1;
|
||||
g_config.syntax_enabled = 1;
|
||||
g_config.block_cursor = 1;
|
||||
g_config.cursor_smear = 1;
|
||||
g_config.smooth_scroll = 1;
|
||||
g_config.ui_scale = 1.0f;
|
||||
g_config.editor_font_size = 15.0f;
|
||||
snprintf(g_config.editor_font, sizeof(g_config.editor_font), "%s", "FiraCode");
|
||||
snprintf(g_config.ui_font, sizeof(g_config.ui_font), "%s", "Inter");
|
||||
g_config.window_width = 1280;
|
||||
g_config.window_height = 800;
|
||||
g_config.active_project[0] = '\0';
|
||||
g_config.recent_dir_count = 0;
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
// Load
|
||||
|
||||
static void config_load(void) {
|
||||
config_set_defaults();
|
||||
|
||||
char path[CONFIG_PATH_MAX];
|
||||
config_get_path(path, sizeof(path));
|
||||
|
||||
FILE *f = fopen(path, "rb");
|
||||
if (!f) {
|
||||
// No config file — write defaults
|
||||
config_save();
|
||||
return;
|
||||
}
|
||||
|
||||
char line_buf[CONFIG_PATH_MAX + 64];
|
||||
char current_section[64] = {0};
|
||||
|
||||
while (fgets(line_buf, sizeof(line_buf), f)) {
|
||||
config_trim(line_buf);
|
||||
if (line_buf[0] == '\0' || line_buf[0] == '#' || line_buf[0] == ';')
|
||||
continue;
|
||||
|
||||
// Section header
|
||||
if (line_buf[0] == '[') {
|
||||
char *close = strchr(line_buf, ']');
|
||||
if (close) {
|
||||
*close = '\0';
|
||||
snprintf(current_section, sizeof(current_section), "%s", line_buf + 1);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Key = value
|
||||
char *eq = strchr(line_buf, '=');
|
||||
if (!eq) continue;
|
||||
*eq = '\0';
|
||||
char *key = line_buf;
|
||||
char *val = eq + 1;
|
||||
config_trim(key);
|
||||
config_trim(val);
|
||||
|
||||
if (strcmp(current_section, "general") == 0) {
|
||||
if (strcmp(key, "theme") == 0)
|
||||
snprintf(g_config.theme, sizeof(g_config.theme), "%s", val);
|
||||
else if (strcmp(key, "show_line_numbers") == 0)
|
||||
g_config.show_line_numbers = (strcmp(val, "true") == 0);
|
||||
else if (strcmp(key, "syntax_enabled") == 0)
|
||||
g_config.syntax_enabled = (strcmp(val, "true") == 0);
|
||||
else if (strcmp(key, "block_cursor") == 0)
|
||||
g_config.block_cursor = (strcmp(val, "true") == 0);
|
||||
else if (strcmp(key, "cursor_smear") == 0)
|
||||
g_config.cursor_smear = (strcmp(val, "true") == 0);
|
||||
else if (strcmp(key, "smooth_scroll") == 0)
|
||||
g_config.smooth_scroll = (strcmp(val, "true") == 0);
|
||||
else if (strcmp(key, "ui_scale") == 0)
|
||||
g_config.ui_scale = (F32)atof(val);
|
||||
else if (strcmp(key, "editor_font_size") == 0)
|
||||
g_config.editor_font_size = (F32)atof(val);
|
||||
else if (strcmp(key, "editor_font") == 0)
|
||||
snprintf(g_config.editor_font, sizeof(g_config.editor_font), "%s", val);
|
||||
else if (strcmp(key, "ui_font") == 0)
|
||||
snprintf(g_config.ui_font, sizeof(g_config.ui_font), "%s", val);
|
||||
else if (strcmp(key, "active_project") == 0)
|
||||
snprintf(g_config.active_project, sizeof(g_config.active_project), "%s", val);
|
||||
else if (strcmp(key, "window_width") == 0)
|
||||
g_config.window_width = atoi(val);
|
||||
else if (strcmp(key, "window_height") == 0)
|
||||
g_config.window_height = atoi(val);
|
||||
} else if (strcmp(current_section, "recent_dirs") == 0) {
|
||||
// Keys are "0", "1", "2", etc.
|
||||
S32 idx = atoi(key);
|
||||
if (idx >= 0 && idx < CONFIG_MAX_RECENT_DIRS && val[0] != '\0') {
|
||||
snprintf(g_config.recent_dirs[idx], CONFIG_PATH_MAX, "%s", val);
|
||||
if (idx + 1 > g_config.recent_dir_count)
|
||||
g_config.recent_dir_count = idx + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
// Save
|
||||
|
||||
static void config_save(void) {
|
||||
char path[CONFIG_PATH_MAX];
|
||||
config_get_path(path, sizeof(path));
|
||||
|
||||
FILE *f = fopen(path, "wb");
|
||||
if (!f) return;
|
||||
|
||||
fprintf(f, "# codeMAX configuration — managed by the program\r\n\r\n");
|
||||
|
||||
fprintf(f, "[general]\r\n");
|
||||
fprintf(f, "theme = %s\r\n", g_config.theme);
|
||||
fprintf(f, "show_line_numbers = %s\r\n", g_config.show_line_numbers ? "true" : "false");
|
||||
fprintf(f, "syntax_enabled = %s\r\n", g_config.syntax_enabled ? "true" : "false");
|
||||
fprintf(f, "block_cursor = %s\r\n", g_config.block_cursor ? "true" : "false");
|
||||
fprintf(f, "cursor_smear = %s\r\n", g_config.cursor_smear ? "true" : "false");
|
||||
fprintf(f, "smooth_scroll = %s\r\n", g_config.smooth_scroll ? "true" : "false");
|
||||
fprintf(f, "ui_scale = %.2f\r\n", g_config.ui_scale);
|
||||
fprintf(f, "editor_font_size = %.1f\r\n", g_config.editor_font_size);
|
||||
fprintf(f, "editor_font = %s\r\n", g_config.editor_font);
|
||||
fprintf(f, "ui_font = %s\r\n", g_config.ui_font);
|
||||
fprintf(f, "window_width = %d\r\n", g_config.window_width);
|
||||
fprintf(f, "window_height = %d\r\n", g_config.window_height);
|
||||
if (g_config.active_project[0] != '\0')
|
||||
fprintf(f, "active_project = %s\r\n", g_config.active_project);
|
||||
|
||||
fprintf(f, "\r\n[recent_dirs]\r\n");
|
||||
for (S32 i = 0; i < g_config.recent_dir_count; i++) {
|
||||
if (g_config.recent_dirs[i][0] != '\0')
|
||||
fprintf(f, "%d = %s\r\n", i, g_config.recent_dirs[i]);
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
// Recent directories
|
||||
|
||||
static void config_push_recent_dir(const char *dir) {
|
||||
if (!dir || dir[0] == '\0') return;
|
||||
|
||||
// Normalize path for comparison
|
||||
char norm[CONFIG_PATH_MAX];
|
||||
snprintf(norm, sizeof(norm), "%s", dir);
|
||||
for (char *p = norm; *p; p++) {
|
||||
if (*p == '/') *p = '\\';
|
||||
}
|
||||
|
||||
// Check if already in list — if so, move to front
|
||||
S32 existing = -1;
|
||||
for (S32 i = 0; i < g_config.recent_dir_count; i++) {
|
||||
#ifdef __APPLE__
|
||||
if (strcmp(g_config.recent_dirs[i], norm) == 0) { existing = i; break; }
|
||||
#else
|
||||
if (_stricmp(g_config.recent_dirs[i], norm) == 0) { existing = i; break; }
|
||||
#endif
|
||||
}
|
||||
|
||||
if (existing == 0) return; // already at front
|
||||
|
||||
// Save the entry if it exists, or use the new dir
|
||||
char entry[CONFIG_PATH_MAX];
|
||||
snprintf(entry, sizeof(entry), "%s", norm);
|
||||
|
||||
// Shift entries down
|
||||
S32 start = (existing >= 0) ? existing : g_config.recent_dir_count;
|
||||
if (start >= CONFIG_MAX_RECENT_DIRS) start = CONFIG_MAX_RECENT_DIRS - 1;
|
||||
for (S32 i = start; i > 0; i--)
|
||||
memcpy(g_config.recent_dirs[i], g_config.recent_dirs[i - 1], CONFIG_PATH_MAX);
|
||||
|
||||
// Insert at front
|
||||
snprintf(g_config.recent_dirs[0], CONFIG_PATH_MAX, "%s", entry);
|
||||
|
||||
if (existing < 0 && g_config.recent_dir_count < CONFIG_MAX_RECENT_DIRS)
|
||||
g_config.recent_dir_count++;
|
||||
|
||||
config_save();
|
||||
}
|
||||
51
c/config/config.h
Normal file
51
c/config/config.h
Normal file
@@ -0,0 +1,51 @@
|
||||
#pragma once
|
||||
// config.h — Global program configuration (config.ini next to binary)
|
||||
//
|
||||
// Stores internal program state: active theme, recent project directories,
|
||||
// and other preferences. This file is managed by the program, not the user.
|
||||
// Per-project settings belong in .editorconfig.
|
||||
|
||||
#include "base/base_inc.h"
|
||||
|
||||
////////////////////////////////
|
||||
// Constants
|
||||
|
||||
#define CONFIG_MAX_RECENT_DIRS 10
|
||||
#define CONFIG_PATH_MAX 1024
|
||||
|
||||
////////////////////////////////
|
||||
// Config state
|
||||
|
||||
typedef struct Config {
|
||||
char theme[64]; // active theme name
|
||||
char recent_dirs[CONFIG_MAX_RECENT_DIRS][CONFIG_PATH_MAX]; // most recent first
|
||||
S32 recent_dir_count;
|
||||
B32 show_line_numbers;
|
||||
B32 syntax_enabled;
|
||||
B32 block_cursor;
|
||||
B32 cursor_smear;
|
||||
B32 smooth_scroll;
|
||||
F32 ui_scale;
|
||||
F32 editor_font_size;
|
||||
char editor_font[64]; // "FiraCode", "JetBrains Mono", "Cascadia Mono"
|
||||
char ui_font[64]; // "Inter", "FiraCode", etc.
|
||||
S32 window_width;
|
||||
S32 window_height;
|
||||
char active_project[CONFIG_PATH_MAX];
|
||||
} Config;
|
||||
|
||||
// Global config instance
|
||||
extern Config g_config;
|
||||
|
||||
// Resolve the path to config.ini (next to the running binary).
|
||||
static void config_get_path(char *out, S32 out_size);
|
||||
|
||||
// Load config from disk. Populates g_config. If the file doesn't exist,
|
||||
// writes a default one and returns defaults.
|
||||
static void config_load(void);
|
||||
|
||||
// Save current g_config state to disk.
|
||||
static void config_save(void);
|
||||
|
||||
// Push a directory to the front of the recent list (deduplicates).
|
||||
static void config_push_recent_dir(const char *dir);
|
||||
Reference in New Issue
Block a user