diff --git a/.gitignore b/.gitignore index 1095f19..066fa12 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ *.o build/ -*.test \ No newline at end of file +*.test +*.dSYM/ +nutshell \ No newline at end of file diff --git a/include/nutshell/theme.h b/include/nutshell/theme.h index 5079c57..644c963 100644 --- a/include/nutshell/theme.h +++ b/include/nutshell/theme.h @@ -13,6 +13,7 @@ typedef struct { // Theme segment structure typedef struct { bool enabled; + char *key; // Add this field to store the segment name from JSON char *format; int command_count; ThemeCommand **commands; // Array of commands for this segment diff --git a/scripts/debug-nutshell.sh b/scripts/debug_nutshell.sh old mode 100644 new mode 100755 similarity index 100% rename from scripts/debug-nutshell.sh rename to scripts/debug_nutshell.sh diff --git a/src/utils/theme.c b/src/utils/theme.c index ad9246f..194fe02 100644 --- a/src/utils/theme.c +++ b/src/utils/theme.c @@ -333,6 +333,10 @@ Theme *load_theme(const char *theme_name) { ThemeSegment *segment = calloc(1, sizeof(ThemeSegment)); if (!segment) continue; + // Store the segment key from JSON directly + segment->key = strdup(key); + THEME_DEBUG("Loading segment with key: %s", segment->key); + json_t *enabled = json_object_get(value, "enabled"); json_t *format = json_object_get(value, "format"); @@ -443,6 +447,7 @@ void free_theme(Theme *theme) { free(theme->prompt_symbol_color); for (int i = 0; i < theme->segment_count; i++) { + free(theme->segments[i]->key); // Free the segment key free(theme->segments[i]->format); // Free all commands in the segment @@ -543,194 +548,97 @@ char *get_theme_prompt(Theme *theme) { left_prompt = str_replace(temp, "{success}", theme->colors->success); free(temp); - // Process all segments in the prompt + // Extract all segment placeholders from the prompt format + THEME_DEBUG("Extracting segments from prompt format"); + + // Process all segments in the theme definition for (int i = 0; i < theme->segment_count; i++) { ThemeSegment *segment = theme->segments[i]; - if (!segment || !segment->enabled) continue; - - THEME_DEBUG("Processing segment %d: format = '%s'", i, segment->format); - - // Extract the segment key from the segment name/key in the JSON file - // instead of trying to extract it from the format string - char *segment_key = NULL; + if (!segment || !segment->enabled || !segment->key) continue; - // In our JSON format, the segment key is the name used in the segments object - // This is stored in the JSON object's key but we don't have direct access here - // So we extract it from the left_prompt->format and look for a matching segment + // Create the placeholder from the segment key that was stored during load + char placeholder[256]; + snprintf(placeholder, sizeof(placeholder), "{%s}", segment->key); + THEME_DEBUG("Checking for placeholder: %s", placeholder); - // First check if this is a well-known segment - if (i == 0 && strstr(segment->format, "{directory}")) { - segment_key = strdup("directory"); - } else if (i == 1 && strstr(segment->format, "git:({branch})")) { - segment_key = strdup("git_info"); // Match the key used in the prompt format - } else { - // For other segments, try to infer from format but skip color codes - const char *colors[] = {"primary", "secondary", "success", "error", "warning", "info", "reset"}; - const char *format_str = segment->format; - const char *key_start = NULL; - const char *key_end = NULL; + // Only process if the placeholder exists in the prompt + if (strstr(left_prompt, placeholder)) { + THEME_DEBUG("Processing segment: %s", segment->key); - // Find the first bracketed token that is not a color code - const char *pos = format_str; - while ((pos = strchr(pos, '{')) != NULL) { - // Extract potential key - const char *end = strchr(pos, '}'); - if (!end) break; - - // Check if it's a color code - bool is_color = false; - // Fix sign comparison using size_t for array index - for (size_t c = 0; c < sizeof(colors)/sizeof(colors[0]); c++) { - size_t len = strlen(colors[c]); - // Fix by casting to size_t for comparison - if ((size_t)(end - pos - 1) == len && strncmp(pos + 1, colors[c], len) == 0) { - is_color = true; - break; - } - } - - if (!is_color) { - // Found a non-color token - key_start = pos; - key_end = end; - break; - } - - pos = end + 1; // Move past this color code - } + // Execute all commands for this segment to get live data + execute_segment_commands(segment); - // Extract the key name - if (key_start && key_end && key_end > key_start) { - int key_len = key_end - key_start - 1; - segment_key = malloc(key_len + 1); - if (segment_key) { - strncpy(segment_key, key_start + 1, key_len); - segment_key[key_len] = '\0'; - } - } + // Create formatted segment with all replaced values + char *formatted_segment = strdup(segment->format); + if (!formatted_segment) continue; - // Fallback - use segment index if we couldn't find a key - if (!segment_key) { - segment_key = malloc(20); - if (segment_key) { - sprintf(segment_key, "segment_%d", i); - } - } - } - - THEME_DEBUG("Using segment key: '%s'", segment_key ? segment_key : "NULL"); - - // Only proceed if we have a segment key - if (segment_key) { - // Check if this segment is referenced in the prompt - char placeholder[256]; - snprintf(placeholder, sizeof(placeholder), "{%s}", segment_key); + // Replace color codes first + char *temp; + temp = formatted_segment; + formatted_segment = str_replace(temp, "{primary}", theme->colors->primary); + free(temp); - THEME_DEBUG("Looking for placeholder '%s' in prompt: '%s'", placeholder, left_prompt); + temp = formatted_segment; + formatted_segment = str_replace(temp, "{secondary}", theme->colors->secondary); + free(temp); - // Continue with existing processing... - if (strstr(left_prompt, placeholder)) { - THEME_DEBUG("Found placeholder %s in prompt", placeholder); - - // Execute all commands for this segment - execute_segment_commands(segment); - - // Create formatted segment with all replaced values - char *formatted_segment = strdup(segment->format); - if (!formatted_segment) { - free(segment_key); - continue; - } - - // Replace color codes - char *temp; - temp = formatted_segment; - formatted_segment = str_replace(temp, "{primary}", theme->colors->primary); - free(temp); - - temp = formatted_segment; - formatted_segment = str_replace(temp, "{secondary}", theme->colors->secondary); - free(temp); + temp = formatted_segment; + formatted_segment = str_replace(temp, "{reset}", theme->colors->reset); + free(temp); + + temp = formatted_segment; + formatted_segment = str_replace(temp, "{info}", theme->colors->info); + free(temp); + + temp = formatted_segment; + formatted_segment = str_replace(temp, "{warning}", theme->colors->warning); + free(temp); + + temp = formatted_segment; + formatted_segment = str_replace(temp, "{error}", theme->colors->error); + free(temp); + + temp = formatted_segment; + formatted_segment = str_replace(temp, "{success}", theme->colors->success); + free(temp); + + // Replace command outputs in the segment format + bool has_output = false; + for (int j = 0; j < segment->command_count; j++) { + ThemeCommand *cmd = segment->commands[j]; + if (!cmd || !cmd->name || !cmd->output) continue; - temp = formatted_segment; - formatted_segment = str_replace(temp, "{reset}", theme->colors->reset); - free(temp); + THEME_DEBUG("Command %s output: %s", cmd->name, cmd->output); - temp = formatted_segment; - formatted_segment = str_replace(temp, "{info}", theme->colors->info); - free(temp); + // Replace {command_name} with its output + char cmd_placeholder[256]; + snprintf(cmd_placeholder, sizeof(cmd_placeholder), "{%s}", cmd->name); temp = formatted_segment; - formatted_segment = str_replace(temp, "{warning}", theme->colors->warning); + formatted_segment = str_replace(temp, cmd_placeholder, cmd->output); free(temp); - temp = formatted_segment; - formatted_segment = str_replace(temp, "{error}", theme->colors->error); - free(temp); + has_output = true; + } + + // Only replace in the prompt if there's actual content + if (has_output) { + THEME_DEBUG("Replacing placeholder %s with: %s", placeholder, formatted_segment); - temp = formatted_segment; - formatted_segment = str_replace(temp, "{success}", theme->colors->success); + temp = left_prompt; + left_prompt = str_replace(temp, placeholder, formatted_segment); free(temp); - - // Replace actual command outputs - bool has_output = false; - THEME_DEBUG("Command count for segment: %d", segment->command_count); - - for (int j = 0; j < segment->command_count; j++) { - ThemeCommand *cmd = segment->commands[j]; - if (!cmd) { - THEME_DEBUG("Command %d is NULL", j); - continue; - } - - THEME_DEBUG("Processing command %d: name='%s', output='%s'", - j, cmd->name ? cmd->name : "NULL", - cmd->output ? cmd->output : "NULL"); - - if (!cmd->name || !cmd->output) continue; - - char cmd_placeholder[256]; - snprintf(cmd_placeholder, sizeof(cmd_placeholder), "{%s}", cmd->name); - - THEME_DEBUG("Replacing '%s' with: '%s' in format: '%s'", - cmd_placeholder, cmd->output, formatted_segment); - - char *temp = formatted_segment; - formatted_segment = str_replace(temp, cmd_placeholder, cmd->output); - free(temp); - has_output = true; - - THEME_DEBUG("After replacement: '%s'", formatted_segment); - } - - // Only replace the segment if at least one command produced output - if (has_output) { - THEME_DEBUG("Replacing placeholder '%s' in prompt with: '%s'", - placeholder, formatted_segment); - - char *temp = left_prompt; - left_prompt = str_replace(temp, placeholder, formatted_segment); - free(temp); - - THEME_DEBUG("Prompt after replacement: '%s'", left_prompt); - } else { - // If no command produced output, remove the segment - THEME_DEBUG("No command output, removing placeholder '%s'", placeholder); - - char *temp = left_prompt; - left_prompt = str_replace(temp, placeholder, ""); - free(temp); - } - - free(formatted_segment); } else { - THEME_DEBUG("Placeholder '%s' NOT found in prompt", placeholder); + // Otherwise remove the placeholder + temp = left_prompt; + left_prompt = str_replace(temp, placeholder, ""); + free(temp); } - free(segment_key); + free(formatted_segment); } } - + // Append the prompt symbol if it's not already in the format if (!strstr(theme->left_prompt->format, "{prompt_symbol}")) { char *prompt_color = NULL; diff --git a/tests/test_theme.c b/tests/test_theme.c index 5bf33eb..3bcab32 100644 --- a/tests/test_theme.c +++ b/tests/test_theme.c @@ -46,8 +46,8 @@ Theme* create_test_theme() { // Directory segment theme->segments[0] = calloc(1, sizeof(ThemeSegment)); theme->segments[0]->enabled = true; + theme->segments[0]->key = strdup("directory"); // Add this line to set the segment key theme->segments[0]->format = strdup("{directory}"); - // New format with multiple commands theme->segments[0]->command_count = 1; theme->segments[0]->commands = calloc(2, sizeof(ThemeCommand*)); // +1 for NULL terminator @@ -61,6 +61,7 @@ Theme* create_test_theme() { theme->segments[1] = calloc(1, sizeof(ThemeSegment)); theme->segments[1]->enabled = true; // Use git_info as the segment key to match prompt format + theme->segments[1]->key = strdup("git_info"); // Set key to match prompt format theme->segments[1]->format = strdup("{secondary}git:({branch}){dirty_flag}{reset}"); theme->segments[1]->command_count = 2; @@ -271,7 +272,8 @@ int test_segment_commands() { printf("DEBUG: Theme segment count: %d\n", theme->segment_count); printf("DEBUG: Left prompt format: '%s'\n", theme->left_prompt->format); for (int i = 0; i < theme->segment_count; i++) { - printf("DEBUG: Segment %d format: '%s'\n", i, theme->segments[i]->format); + printf("DEBUG: Segment %d key: '%s', format: '%s'\n", i, + theme->segments[i]->key, theme->segments[i]->format); } // Test executing commands for a segment diff --git a/themes/default.json b/themes/default.json index c5b377a..8555f67 100644 --- a/themes/default.json +++ b/themes/default.json @@ -12,7 +12,7 @@ }, "prompt": { "left": { - "format": "{primary}{icon} {directory}{reset}", + "format": "{primary}{icon} {directory} {git_branch}{reset}", "icon": "🥜" }, "right": {