#include "editor.h" #include #include #include #include static char statusmsg[256]; static void load_desc(Editor *e) { e->desc[0] = '\0'; if (!e->buf.filename) return; char tmp[512]; strncpy(tmp, e->buf.filename, sizeof(tmp) - 1); tmp[sizeof(tmp) - 1] = '\0'; char *dir = dirname(tmp); char path[512]; snprintf(path, sizeof(path), "%s/desc.txt", dir); FILE *f = fopen(path, "r"); if (!f) return; int pos = 0; int c; while ((c = fgetc(f)) != EOF && pos < (int)sizeof(e->desc) - 1) e->desc[pos++] = (char)c; e->desc[pos] = '\0'; fclose(f); } void editor_init(Editor *e) { buf_init(&e->buf); e->cx = e->cy = 0; e->rowoff = e->coloff = 0; e->mode = MODE_NORMAL; e->ft = FT_NONE; e->ghost[0] = '\0'; e->idle_count = 0; e->nano_mode = 0; e->cutbuf = (Line){NULL, 0, 0}; e->desc[0] = '\0'; e->autocomplete_row = -1; e->autocomplete_col = 0; getmaxyx(stdscr, e->screenrows, e->screencols); e->screenrows -= 2; /* reserve status + command line */ statusmsg[0] = '\0'; } void editor_open(Editor *e, const char *filename) { buf_load(&e->buf, filename); e->ft = syntax_detect(filename); load_desc(e); } void editor_scroll(Editor *e) { if (e->cy < e->rowoff) e->rowoff = e->cy; if (e->cy >= e->rowoff + e->screenrows) e->rowoff = e->cy - e->screenrows + 1; if (e->cx < e->coloff) e->coloff = e->cx; if (e->cx >= e->coloff + e->screencols) e->coloff = e->cx - e->screencols + 1; } void editor_set_status(Editor *e, const char *fmt, ...) { (void)e; va_list ap; va_start(ap, fmt); vsnprintf(statusmsg, sizeof(statusmsg), fmt, ap); va_end(ap); } void editor_draw(Editor *e) { editor_scroll(e); erase(); if (e->nano_mode) { /* nano layout: title bar row 0, content rows 1..screenrows, status at screenrows+1, shortcut bars at screenrows+2..+3 */ int content_start = 1; for (int y = 0; y < e->screenrows; y++) { int filerow = y + e->rowoff; int sy = y + content_start; if (filerow < e->buf.numlines) { Line *l = &e->buf.lines[filerow]; int len = l->len - e->coloff; if (len < 0) len = 0; if (len > e->screencols) len = e->screencols; if (len > 0 && l->chars) { if (e->ft != FT_NONE) { int bc = syntax_line_in_block_comment( &e->buf, filerow, e->ft); syntax_draw_line(l->chars + e->coloff, len, sy, 0, e->screencols, e->ft, bc); } else { mvaddnstr(sy, 0, l->chars + e->coloff, len); } } } } /* ghost text */ if (e->ghost[0] != '\0') { int gy = e->cy - e->rowoff + content_start; int gx = e->cx - e->coloff; if (gy > 0 && gy <= e->screenrows && gx >= 0) { attron(A_DIM); int glen = (int)strlen(e->ghost); if (gx + glen > e->screencols) glen = e->screencols - gx; if (glen > 0) mvaddnstr(gy, gx, e->ghost, glen); attroff(A_DIM); } } /* status message line */ if (statusmsg[0]) mvaddnstr(e->screenrows + 1, 0, statusmsg, e->screencols); /* nano title bar + shortcut bars */ nano_draw_bars(e); /* cursor */ move(e->cy - e->rowoff + content_start, e->cx - e->coloff); } else { /* vi layout */ for (int y = 0; y < e->screenrows; y++) { int filerow = y + e->rowoff; if (filerow < e->buf.numlines) { Line *l = &e->buf.lines[filerow]; int len = l->len - e->coloff; if (len < 0) len = 0; if (len > e->screencols) len = e->screencols; if (len > 0 && l->chars) { if (e->ft != FT_NONE) { int bc = syntax_line_in_block_comment( &e->buf, filerow, e->ft); syntax_draw_line(l->chars + e->coloff, len, y, 0, e->screencols, e->ft, bc); } else { mvaddnstr(y, 0, l->chars + e->coloff, len); } } } else { mvaddch(y, 0, '~'); } } /* ghost text */ if (e->mode == MODE_INSERT && e->ghost[0] != '\0') { int gy = e->cy - e->rowoff; int gx = e->cx - e->coloff; if (gy >= 0 && gy < e->screenrows && gx >= 0) { attron(A_DIM); int glen = (int)strlen(e->ghost); if (gx + glen > e->screencols) glen = e->screencols - gx; if (glen > 0) mvaddnstr(gy, gx, e->ghost, glen); attroff(A_DIM); } } /* status bar */ attron(A_REVERSE); char status[256]; const char *modestr = e->mode == MODE_INSERT ? "-- INSERT --" : e->mode == MODE_COMMAND ? ":" : ""; const char *fname = e->buf.filename ? e->buf.filename : "[No Name]"; int slen = snprintf(status, sizeof(status), " %s %s%s", modestr, fname, e->buf.dirty ? " [+]" : ""); char rinfo[64]; int rlen = snprintf(rinfo, sizeof(rinfo), "%d/%d ", e->cy + 1, e->buf.numlines); for (int i = slen; i < e->screencols - rlen; i++) status[i] = ' '; memcpy(status + e->screencols - rlen, rinfo, rlen); status[e->screencols] = '\0'; mvaddnstr(e->screenrows, 0, status, e->screencols); attroff(A_REVERSE); /* message/command line */ mvaddnstr(e->screenrows + 1, 0, statusmsg, e->screencols); /* cursor */ move(e->cy - e->rowoff, e->cx - e->coloff); } refresh(); } void editor_prompt_desc(Editor *e) { int row = e->nano_mode ? e->screenrows + 1 : e->screenrows + 1; char buf[sizeof(e->desc)]; int len = 0; /* pre-fill with current desc */ if (e->desc[0]) { len = (int)strlen(e->desc); memcpy(buf, e->desc, len); } buf[len] = '\0'; editor_set_status(e, "Desc: %s", buf); editor_draw(e); move(row, 6 + len); refresh(); for (;;) { int c = getch(); if (c == '\r' || c == '\n' || c == KEY_ENTER) break; if (c == 27) { return; } /* ESC cancels */ if ((c == KEY_BACKSPACE || c == 127) && len > 0) { buf[--len] = '\0'; } else if (c >= 32 && c < 127 && len < (int)sizeof(buf) - 1) { buf[len++] = (char)c; buf[len] = '\0'; } editor_set_status(e, "Desc: %s", buf); editor_draw(e); move(row, 6 + len); refresh(); } memcpy(e->desc, buf, len); e->desc[len] = '\0'; editor_set_status(e, len > 0 ? "[Description set]" : "[Description cleared]"); }