556 lines
15 KiB
C
556 lines
15 KiB
C
#ifndef _BIM_CORE_H
|
|
#define _BIM_CORE_H
|
|
|
|
#define _XOPEN_SOURCE 700
|
|
#define _DARWIN_C_SOURCE
|
|
#define _DEFAULT_SOURCE
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdint.h>
|
|
#include <stdarg.h>
|
|
#include <unistd.h>
|
|
#include <termios.h>
|
|
#include <signal.h>
|
|
#include <libgen.h>
|
|
#include <locale.h>
|
|
#include <wchar.h>
|
|
#include <ctype.h>
|
|
#include <dirent.h>
|
|
#include <poll.h>
|
|
#include <limits.h>
|
|
#include <errno.h>
|
|
#include <sys/types.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/wait.h>
|
|
#include <kuroko/vm.h>
|
|
|
|
#ifdef __DATE__
|
|
# define BIM_BUILD_DATE " built " __DATE__ " at " __TIME__
|
|
#else
|
|
# define BIM_BUILD_DATE DATE ""
|
|
#endif
|
|
|
|
#ifdef GIT_TAG
|
|
# define TAG "-" GIT_TAG
|
|
#else
|
|
# define TAG "-alpha"
|
|
#endif
|
|
|
|
#define BLOCK_SIZE 4096
|
|
#define ENTER_KEY '\r'
|
|
#define LINE_FEED '\n'
|
|
#define BACKSPACE_KEY 0x08
|
|
#define DELETE_KEY 0x7F
|
|
#define DEFAULT_KEY_WAIT (global_config.background_task ? 0 : -1)
|
|
|
|
enum Key {
|
|
KEY_TIMEOUT = -1,
|
|
KEY_CTRL_AT = 0, /* Base */
|
|
KEY_CTRL_A, KEY_CTRL_B, KEY_CTRL_C, KEY_CTRL_D, KEY_CTRL_E, KEY_CTRL_F, KEY_CTRL_G, KEY_CTRL_H,
|
|
KEY_CTRL_I, KEY_CTRL_J, KEY_CTRL_K, KEY_CTRL_L, KEY_CTRL_M, KEY_CTRL_N, KEY_CTRL_O, KEY_CTRL_P,
|
|
KEY_CTRL_Q, KEY_CTRL_R, KEY_CTRL_S, KEY_CTRL_T, KEY_CTRL_U, KEY_CTRL_V, KEY_CTRL_W, KEY_CTRL_X,
|
|
KEY_CTRL_Y, KEY_CTRL_Z, /* Note we keep ctrl-z mapped in termios as suspend */
|
|
KEY_CTRL_OPEN, KEY_CTRL_BACKSLASH, KEY_CTRL_CLOSE, KEY_CTRL_CARAT, KEY_CTRL_UNDERSCORE,
|
|
/* Space... */
|
|
/* Some of these are equivalent to things above */
|
|
KEY_BACKSPACE = 0x08,
|
|
KEY_LINEFEED = '\n',
|
|
KEY_ENTER = '\r',
|
|
KEY_TAB = '\t',
|
|
/* Basic printable characters go here. */
|
|
/* Delete is special */
|
|
KEY_DELETE = 0x7F,
|
|
/* Unicode codepoints go here */
|
|
KEY_ESCAPE = 0x400000, /* Escape would normally be 27, but is special because reasons */
|
|
KEY_F1, KEY_F2, KEY_F3, KEY_F4,
|
|
KEY_F5, KEY_F6, KEY_F7, KEY_F8,
|
|
KEY_F9, KEY_F10, KEY_F11, KEY_F12,
|
|
/* TODO ALT, SHIFT, etc., for F keys */
|
|
KEY_MOUSE, /* Must be followed with a 3-byte mouse read */
|
|
KEY_MOUSE_SGR, /* Followed by an SGR-style sequence of mouse data */
|
|
KEY_HOME, KEY_END, KEY_PAGE_UP, KEY_PAGE_DOWN,
|
|
KEY_UP, KEY_DOWN, KEY_RIGHT, KEY_LEFT,
|
|
KEY_SHIFT_UP, KEY_SHIFT_DOWN, KEY_SHIFT_RIGHT, KEY_SHIFT_LEFT,
|
|
KEY_CTRL_UP, KEY_CTRL_DOWN, KEY_CTRL_RIGHT, KEY_CTRL_LEFT,
|
|
KEY_ALT_UP, KEY_ALT_DOWN, KEY_ALT_RIGHT, KEY_ALT_LEFT,
|
|
KEY_ALT_SHIFT_UP, KEY_ALT_SHIFT_DOWN, KEY_ALT_SHIFT_RIGHT, KEY_ALT_SHIFT_LEFT,
|
|
KEY_SHIFT_TAB,
|
|
/* Special signals for paste start, paste end */
|
|
KEY_PASTE_BEGIN, KEY_PASTE_END,
|
|
};
|
|
|
|
struct key_name_map {
|
|
enum Key keycode;
|
|
char * name;
|
|
};
|
|
|
|
extern struct key_name_map KeyNames[];
|
|
|
|
/**
|
|
* Syntax highlighting flags.
|
|
*/
|
|
#define FLAG_NONE 0
|
|
#define FLAG_KEYWORD 1
|
|
#define FLAG_STRING 2
|
|
#define FLAG_COMMENT 3
|
|
#define FLAG_TYPE 4
|
|
#define FLAG_PRAGMA 5
|
|
#define FLAG_NUMERAL 6
|
|
#define FLAG_ERROR 7
|
|
#define FLAG_DIFFPLUS 8
|
|
#define FLAG_DIFFMINUS 9
|
|
#define FLAG_NOTICE 10
|
|
#define FLAG_BOLD 11
|
|
#define FLAG_LINK (12 + (1<<4))
|
|
#define FLAG_ESCAPE 13
|
|
#define FLAG_EXTRA 14
|
|
#define FLAG_SPECIAL 15
|
|
|
|
#define FLAG_LINK_COLOR 12
|
|
|
|
#define FLAG_UNDERLINE (1 << 4)
|
|
#define FLAG_SELECT (1 << 5)
|
|
#define FLAG_SEARCH (1 << 6)
|
|
|
|
#define FLAG_MASK_COLORS 0x0F
|
|
#define FLAG_MASK_ATTRIB 0x70
|
|
|
|
/**
|
|
* Line buffer definitions
|
|
*
|
|
* Lines are essentially resizable vectors of char_t structs,
|
|
* which represent single codepoints in the file.
|
|
*/
|
|
typedef struct {
|
|
uint32_t display_width:4;
|
|
uint32_t flags:7;
|
|
uint32_t codepoint:21;
|
|
} __attribute__((packed)) char_t;
|
|
|
|
/**
|
|
* Lines have available and actual lengths, describing
|
|
* how much space was allocated vs. how much is being
|
|
* used at the moment.
|
|
*/
|
|
typedef struct {
|
|
int available;
|
|
int actual;
|
|
int istate;
|
|
int is_current;
|
|
int rev_status;
|
|
char_t text[];
|
|
} line_t;
|
|
|
|
typedef struct background_task {
|
|
struct _env * env;
|
|
void (*func)(struct background_task*);
|
|
struct background_task * next;
|
|
int _private_i;
|
|
void * _private_p;
|
|
} background_task_t;
|
|
|
|
/**
|
|
* Global configuration state
|
|
*
|
|
* At the moment, this is all in a global, but in the future
|
|
* this should be passed around to various functions.
|
|
*/
|
|
typedef struct {
|
|
/* Terminal size */
|
|
int term_width, term_height;
|
|
int bottom_size;
|
|
|
|
line_t ** yanks;
|
|
size_t yank_count;
|
|
int yank_is_full_lines;
|
|
|
|
int tty_in;
|
|
|
|
const char * bimrc_path;
|
|
const char * syntax_fallback;
|
|
uint32_t * search;
|
|
|
|
int overlay_mode;
|
|
line_t * command_buffer;
|
|
|
|
int command_offset, command_col_no;
|
|
struct syntax_definition * command_syn, * command_syn_back;
|
|
int history_point;
|
|
int search_point;
|
|
int search_direction;
|
|
int prev_line, prev_col, prev_coffset, prev_offset;
|
|
|
|
unsigned int highlight_on_open:1;
|
|
unsigned int initial_file_is_read_only:1;
|
|
unsigned int go_to_line:1;
|
|
unsigned int break_from_selection:1;
|
|
unsigned int can_scroll:1;
|
|
unsigned int can_hideshow:1;
|
|
unsigned int can_altscreen:1;
|
|
unsigned int can_mouse:1;
|
|
unsigned int can_unicode:1;
|
|
unsigned int can_bright:1;
|
|
unsigned int can_title:1;
|
|
unsigned int can_bce:1;
|
|
unsigned int can_24bit:1;
|
|
unsigned int can_256color:1;
|
|
unsigned int can_italic:1;
|
|
unsigned int can_insert:1;
|
|
unsigned int can_bracketedpaste:1;
|
|
unsigned int can_sgrmouse:1;
|
|
|
|
unsigned int history_enabled:1;
|
|
unsigned int highlight_parens:1;
|
|
unsigned int smart_case:1;
|
|
unsigned int highlight_current_line:1;
|
|
unsigned int shift_scrolling:1;
|
|
unsigned int check_git:1;
|
|
unsigned int color_gutter:1;
|
|
unsigned int relative_lines:1;
|
|
unsigned int numbers:1;
|
|
unsigned int horizontal_shift_scrolling:1;
|
|
unsigned int hide_statusbar:1;
|
|
unsigned int tabs_visible:1;
|
|
unsigned int autohide_tabs:1;
|
|
unsigned int smart_complete:1;
|
|
unsigned int has_terminal:1;
|
|
unsigned int search_wraps:1;
|
|
unsigned int had_error:1;
|
|
unsigned int use_biminfo:1;
|
|
|
|
int cursor_padding;
|
|
int split_percent;
|
|
int scroll_amount;
|
|
int tab_offset;
|
|
|
|
char * tab_indicator;
|
|
char * space_indicator;
|
|
|
|
background_task_t * background_task;
|
|
background_task_t * tail_task;
|
|
|
|
} global_config_t;
|
|
|
|
#define OVERLAY_MODE_NONE 0
|
|
#define OVERLAY_MODE_READ_ONE 1
|
|
#define OVERLAY_MODE_COMMAND 2
|
|
#define OVERLAY_MODE_SEARCH 3
|
|
#define OVERLAY_MODE_COMPLETE 4
|
|
#define OVERLAY_MODE_FILESEARCH 5
|
|
|
|
#define HISTORY_SENTINEL 0
|
|
#define HISTORY_INSERT 1
|
|
#define HISTORY_DELETE 2
|
|
#define HISTORY_REPLACE 3
|
|
#define HISTORY_REMOVE_LINE 4
|
|
#define HISTORY_ADD_LINE 5
|
|
#define HISTORY_REPLACE_LINE 6
|
|
#define HISTORY_MERGE_LINES 7
|
|
#define HISTORY_SPLIT_LINE 8
|
|
|
|
#define HISTORY_BREAK 10
|
|
|
|
typedef struct history {
|
|
struct history * previous;
|
|
struct history * next;
|
|
int type;
|
|
int line;
|
|
int col;
|
|
union {
|
|
struct {
|
|
int lineno;
|
|
int offset;
|
|
int codepoint;
|
|
int old_codepoint;
|
|
} insert_delete_replace;
|
|
|
|
struct {
|
|
int lineno;
|
|
line_t * contents;
|
|
line_t * old_contents;
|
|
} remove_replace_line;
|
|
|
|
struct {
|
|
int lineno;
|
|
int split;
|
|
} add_merge_split_lines;
|
|
} contents;
|
|
} history_t;
|
|
|
|
/**
|
|
* Buffer data
|
|
*
|
|
* A buffer describes a file, and stores
|
|
* its name as well as the editor state
|
|
* (cursor offsets, etc.) and the actual
|
|
* line buffers.
|
|
*/
|
|
typedef struct _env {
|
|
unsigned int loading:1;
|
|
unsigned int tabs:1;
|
|
unsigned int modified:1;
|
|
unsigned int readonly:1;
|
|
unsigned int indent:1;
|
|
unsigned int checkgitstatusonwrite:1;
|
|
unsigned int crnl:1;
|
|
unsigned int numbers:1;
|
|
unsigned int gutter:1;
|
|
unsigned int slowop:1;
|
|
|
|
int highlighting_paren;
|
|
int maxcolumn;
|
|
|
|
short mode;
|
|
short tabstop;
|
|
|
|
char * file_name;
|
|
int offset;
|
|
int coffset;
|
|
int line_no;
|
|
int line_count;
|
|
int line_avail;
|
|
int col_no;
|
|
int preferred_column;
|
|
struct syntax_definition * syntax;
|
|
line_t ** lines;
|
|
|
|
history_t * history;
|
|
history_t * last_save_history;
|
|
|
|
int width;
|
|
int left;
|
|
|
|
int start_line;
|
|
int sel_col;
|
|
int start_col;
|
|
int prev_line;
|
|
} buffer_t;
|
|
|
|
struct theme_def {
|
|
const char * name;
|
|
void * callable;
|
|
};
|
|
|
|
extern struct theme_def * themes;
|
|
|
|
extern void add_colorscheme(struct theme_def theme);
|
|
|
|
struct syntax_state {
|
|
buffer_t * env;
|
|
line_t * line;
|
|
int line_no;
|
|
int state;
|
|
int i;
|
|
};
|
|
|
|
struct completion_match {
|
|
char * string;
|
|
char * file;
|
|
char * search;
|
|
};
|
|
|
|
struct syntax_definition {
|
|
char * name;
|
|
char ** ext;
|
|
int (*calculate)(struct syntax_state *);
|
|
int prefers_spaces;
|
|
int (*completion_qualifier)(int c);
|
|
int (*completion_matcher)(uint32_t * comp, struct completion_match ** matches, int * matches_count, int complete_match, int * matches_len, buffer_t * env);
|
|
void * krkFunc;
|
|
void * krkClass;
|
|
};
|
|
|
|
extern struct syntax_definition * syntaxes;
|
|
|
|
/**
|
|
* Editor mode states
|
|
*/
|
|
#define MODE_NORMAL 0
|
|
#define MODE_INSERT 1
|
|
#define MODE_LINE_SELECTION 2
|
|
#define MODE_REPLACE 3
|
|
#define MODE_CHAR_SELECTION 4
|
|
#define MODE_COL_SELECTION 5
|
|
#define MODE_COL_INSERT 6
|
|
#define MODE_DIRECTORY_BROWSE 7
|
|
|
|
struct action_def {
|
|
char * name;
|
|
uintptr_t action;
|
|
int options;
|
|
const char * description;
|
|
};
|
|
|
|
extern struct action_def * mappable_actions;
|
|
|
|
#define ARG_IS_INPUT 0x01 /* Takes the key that triggered it as the first argument */
|
|
#define ARG_IS_CUSTOM 0x02 /* Takes a custom argument which is specific to the method */
|
|
#define ARG_IS_PROMPT 0x04 /* Prompts for an argument. */
|
|
#define ACTION_IS_RW 0x08 /* Needs to be able to write. */
|
|
|
|
#define BIM_ACTION(name, options, description, ...) \
|
|
extern void name(__VA_ARGS__); /* Define the action with unknown arguments */ \
|
|
void __attribute__((constructor)) _install_ ## name (void) { \
|
|
add_action((struct action_def){#name, (uintptr_t)name, options, description}); \
|
|
} \
|
|
void name(__VA_ARGS__)
|
|
|
|
struct command_def {
|
|
char * name;
|
|
int (*command)(char *, int, char * arg[]);
|
|
const char * description;
|
|
};
|
|
|
|
#define BIM_COMMAND(cmd_name, cmd_str, description) \
|
|
int bim_command_ ## cmd_name (char * cmd, int argc, char * argv[]); \
|
|
void __attribute__((constructor)) _install_cmd_ ## cmd_name (void) { \
|
|
add_command((struct command_def){cmd_str, bim_command_ ## cmd_name, description}); \
|
|
} \
|
|
int bim_command_ ## cmd_name (char * cmd __attribute__((unused)), int argc __attribute__((unused)), char * argv[] __attribute__((unused)))
|
|
|
|
#define BIM_ALIAS(alias, alias_name, cmd_name) \
|
|
void __attribute__((constructor)) _install_alias_ ## alias_name (void) { \
|
|
add_command((struct command_def){alias, bim_command_ ## cmd_name, "Alias for " #cmd_name}); \
|
|
}
|
|
|
|
#define BIM_PREFIX_COMMAND(cmd_name, cmd_prefix, description) \
|
|
int bim_command_ ## cmd_name (char * cmd, int argc, char * argv[]); \
|
|
void __attribute__((constructor)) _install_cmd_ ## cmd_name (void) { \
|
|
add_prefix_command((struct command_def){cmd_prefix, bim_command_ ## cmd_name, description}); \
|
|
} \
|
|
int bim_command_ ## cmd_name (char * cmd __attribute__((unused)), int argc __attribute__((unused)), char * argv[] __attribute__((unused)))
|
|
|
|
extern const char * flag_to_color(int _flag);
|
|
extern void redraw_line(int x);
|
|
extern int git_examine(char * filename);
|
|
extern void search_next(void);
|
|
extern void set_preferred_column(void);
|
|
extern void quit(const char * message);
|
|
extern void close_buffer(void);
|
|
extern void set_syntax_by_name(const char * name);
|
|
extern void rehighlight_search(line_t * line);
|
|
extern void try_to_center(void);
|
|
extern int read_one_character(char * message);
|
|
extern void bim_unget(int c);
|
|
#define bim_getch() bim_getch_timeout(200)
|
|
extern int bim_getch_timeout(int timeout);
|
|
extern buffer_t * buffer_new(void);
|
|
extern FILE * open_biminfo(void);
|
|
extern int fetch_from_biminfo(buffer_t * buf);
|
|
extern int update_biminfo(buffer_t * buf, int is_open);
|
|
extern buffer_t * buffer_close(buffer_t * buf);
|
|
extern int to_eight(uint32_t codepoint, char * out);
|
|
extern char * name_from_key(enum Key keycode);
|
|
extern void add_action(struct action_def action);
|
|
extern void open_file(char * file);
|
|
extern void recalculate_selected_lines(void);
|
|
extern void add_command(struct command_def command);
|
|
extern void add_prefix_command(struct command_def command);
|
|
extern void render_command_input_buffer(void);
|
|
extern void unhighlight_matching_paren(void);
|
|
|
|
extern void add_syntax(struct syntax_definition def);
|
|
|
|
struct ColorName {
|
|
const char * name;
|
|
const char ** value;
|
|
};
|
|
|
|
extern struct ColorName color_names[];
|
|
|
|
struct bim_function {
|
|
char * command;
|
|
struct bim_function * next;
|
|
};
|
|
|
|
extern struct bim_function ** user_functions;
|
|
extern int run_function(char * name);
|
|
extern int has_function(char * name);
|
|
extern void find_matching_paren(int * out_line, int * out_col, int in_col);
|
|
extern void render_error(char * message, ...);
|
|
extern void render_commandline_message(char * message, ...);
|
|
extern void pause_for_key(void);
|
|
|
|
#define add_match(match_string, match_file, match_search) do { \
|
|
if (*matches_count == *matches_len) { \
|
|
(*matches_len) *= 2; \
|
|
*matches = realloc(*matches, sizeof(struct completion_match) * (*matches_len)); \
|
|
} \
|
|
(*matches)[*matches_count].string = strdup(match_string); \
|
|
(*matches)[*matches_count].file = strdup(match_file); \
|
|
(*matches)[*matches_count].search = strdup(match_search); \
|
|
(*matches_count)++; \
|
|
} while (0)
|
|
|
|
#define add_if_match(name,desc) do { \
|
|
int i = 0; \
|
|
while (comp[i] && comp[i] == (unsigned char)name[i]) i++; \
|
|
if (comp[i] == '\0') { \
|
|
add_match(name,desc,""); \
|
|
} \
|
|
} while (0)
|
|
|
|
struct action_map {
|
|
int key;
|
|
int options;
|
|
union {
|
|
struct {
|
|
uintptr_t method;
|
|
int arg;
|
|
};
|
|
KrkValue callable;
|
|
};
|
|
};
|
|
|
|
#define opt_rep 0x1 /* This action will be repeated */
|
|
#define opt_arg 0x2 /* This action will take a specified argument */
|
|
#define opt_char 0x4 /* This action will read a character to pass as an argument */
|
|
#define opt_nav 0x8 /* This action will consume the nav buffer as its argument */
|
|
#define opt_rw 0x10 /* Must not be read-only */
|
|
#define opt_norm 0x20 /* Returns to normal mode */
|
|
#define opt_byte 0x40 /* Same as opt_char but forces a byte */
|
|
#define opt_krk 0x80
|
|
|
|
struct mode_names {
|
|
const char * description;
|
|
const char * name;
|
|
struct action_map ** mode;
|
|
};
|
|
|
|
extern struct mode_names mode_names[];
|
|
|
|
#define paint(length, flag) do { for (int i = 0; i < (length) && state->i < state->line->actual; i++, state->i++) { \
|
|
state->line->text[state->i].flags = (state->line->text[state->i].flags & (3 << 5)) | (flag); \
|
|
} } while (0)
|
|
#define charat() (state->i < state->line->actual ? state->line->text[(state->i)].codepoint : -1)
|
|
#define nextchar() (state->i + 1 < state->line->actual ? state->line->text[(state->i+1)].codepoint : -1)
|
|
#define lastchar() (state->i - 1 >= 0 ? state->line->text[(state->i-1)].codepoint : -1)
|
|
#define skip() (state->i++)
|
|
#define charrel(x) ((state->i + (x) >= 0 && state->i + (x) < state->line->actual) ? state->line->text[(state->i+(x))].codepoint : -1)
|
|
|
|
static int match_and_paint(struct syntax_state * state, const char * keyword, int flag, int (*keyword_qualifier)(int c));
|
|
static int common_comment_buzzwords(struct syntax_state * state);
|
|
static int paint_comment(struct syntax_state * state);
|
|
static struct syntax_definition * find_syntax_calculator(const char * name);
|
|
|
|
#define nest(lang, low) \
|
|
do { \
|
|
state->state = (state->state < 1 ? 0 : state->state - low); \
|
|
do { state->state = lang(state); } while (state->state == 0); \
|
|
if (state->state == -1) return low; \
|
|
return state->state + low; \
|
|
} while (0)
|
|
|
|
/* Hacky workaround for isdigit not really accepting Unicode stuff */
|
|
static __attribute__((used)) int _isdigit(int c) { if (c > 128) return 0; return isdigit(c); }
|
|
static __attribute__((used)) int _isxdigit(int c) { if (c > 128) return 0; return isxdigit(c); }
|
|
|
|
#undef isdigit
|
|
#undef isxdigit
|
|
#define isdigit(c) _isdigit(c)
|
|
#define isxdigit(c) _isxdigit(c)
|
|
|
|
#endif /* _BIM_CORE_H */
|