summaryrefslogtreecommitdiff
path: root/ted.h
blob: 4ab1feaf767e801db361307b7717a066472a8d86 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
#define TED_PATH_MAX 256
#define TED_UNTITLED "Untitled" // what to call untitled buffers

#define TEXT_SIZE_MIN 6
#define TEXT_SIZE_MAX 70

enum {
	SYNTAX_STATE_SINGLE_LINE_COMMENT_SHIFT,
	SYNTAX_STATE_MULTI_LINE_COMMENT_SHIFT,
	SYNTAX_STATE_PREPROCESSOR_SHIFT,
	SYNTAX_STATE_STRING_SHIFT,
};

enum {
	SYNTAX_STATE_MULTI_LINE_COMMENT = 1<<SYNTAX_STATE_MULTI_LINE_COMMENT_SHIFT, // are we in a multi-line comment? (delineated by /* */)
	SYNTAX_STATE_SINGLE_LINE_COMMENT = 1<<SYNTAX_STATE_SINGLE_LINE_COMMENT_SHIFT, // if you add a \ to the end of a single-line comment, it is continued to the next line.
	SYNTAX_STATE_PREPROCESSOR = 1<<SYNTAX_STATE_PREPROCESSOR_SHIFT, // similar to above
	SYNTAX_STATE_STRING = 1<<SYNTAX_STATE_STRING_SHIFT,
};

typedef u8 SyntaxState;

ENUM_U16 {
	LANG_NONE,
	LANG_C,
	LANG_COUNT
} ENUM_U16_END(Language);

typedef struct {
	Language lang;
	char const *name;
} LanguageName;

static LanguageName const language_names[] = {
	{LANG_NONE, "None"},
	{LANG_C, "C"},
};

ENUM_U8 {
	SYNTAX_NORMAL,
	SYNTAX_KEYWORD,
	SYNTAX_COMMENT,
	SYNTAX_PREPROCESSOR,
	SYNTAX_STRING,
	SYNTAX_CHARACTER,
	SYNTAX_CONSTANT
} ENUM_U8_END(SyntaxCharType);

typedef struct {
	float cursor_blink_time_on, cursor_blink_time_off;
	u32 colors[COLOR_COUNT];
	u16 text_size;
	u16 max_menu_width;
	u16 error_display_time;
	bool auto_indent;
	bool auto_add_newline;
	bool syntax_highlighting;
	u8 tab_width;
	u8 cursor_width;
	u8 undo_save_time;
	u8 border_thickness;
	u8 padding;
	u8 scrolloff;
	// [i] = comma-separated string of file extensions for language i, or NULL for none
	char *language_extensions[LANG_COUNT];
} Settings;

#define SCANCODE_COUNT 0x120 // SDL scancodes should be less than this value.
// a "key combo" is some subset of {control, shift, alt} + some key.
#define KEY_COMBO_COUNT (SCANCODE_COUNT << 3)
#define KEY_MODIFIER_CTRL_BIT 0
#define KEY_MODIFIER_SHIFT_BIT 1
#define KEY_MODIFIER_ALT_BIT 2
#define KEY_MODIFIER_CTRL ((u32)1<<KEY_MODIFIER_CTRL_BIT)
#define KEY_MODIFIER_SHIFT ((u32)1<<KEY_MODIFIER_SHIFT_BIT)
#define KEY_MODIFIER_ALT ((u32)1<<KEY_MODIFIER_ALT_BIT)
// ctrl+alt+c is encoded as SDL_SCANCODE_C << 3 | KEY_MODIFIER_CTRL | KEY_MODIFIER_ALT

typedef struct KeyAction {
	u32 line_number; // config line number where this was set
	Command command; // this will be 0 (COMMAND_UNKNOWN) if there's no action for the key
	i64 argument;
} KeyAction;

// a position in the buffer
typedef struct {
	u32 line;
	u32 index; // index of character in line (not the same as column, since a tab is settings->tab_width columns)
} BufferPos;

typedef struct {
	SyntaxState syntax;
	u32 len;
	u32 capacity;
	char32_t *str;
} Line;

// this refers to replacing prev_len characters (found in prev_text) at pos with new_len characters
typedef struct BufferEdit {
	BufferPos pos;
	u32 new_len;
	u32 prev_len;
	char32_t *prev_text;
	double time; // time at start of edit (i.e. the time just before the edit), in seconds since epoch
} BufferEdit;

typedef struct {
	char *filename; // NULL if this buffer doesn't correspond to a file (e.g. line buffers)
	struct Ted *ted; // we keep a back-pointer to the ted instance so we don't have to pass it in to every buffer function
	double scroll_x, scroll_y; // number of characters scrolled in the x/y direction
	BufferPos cursor_pos;
	BufferPos selection_pos; // if selection is true, the text between selection_pos and cursor_pos is selected.
	bool is_line_buffer; // "line buffers" are buffers which can only have one line of text (used for inputs)
	bool selection;
	bool store_undo_events; // set to false to disable undo events
	bool modified; // has the buffer been modified since it was loaded/saved?
	float x1, y1, x2, y2;
	u32 nlines;
	u32 lines_capacity;

	u32 longest_line_on_screen; // length of the longest line on screen. used to determine how far right we can scroll.

	// to cache syntax highlighting properly, it is important to keep track of the
	// first and last line modified since last frame.
	u32 frame_earliest_line_modified;
	u32 frame_latest_line_modified;

	Line *lines;
	char error[256];
	BufferEdit *undo_history; // dynamic array of undo history
	BufferEdit *redo_history; // dynamic array of redo history
} TextBuffer;

ENUM_U16 {
	MENU_NONE,
	MENU_OPEN,
	MENU_SAVE_AS,
	MENU_WARN_UNSAVED // warn about unsaved changes
} ENUM_U16_END(Menu);

// file entries for file selectors
typedef struct {
	char *name; // just the file name
	char *path; // full path
	FsType type;
} FileEntry;

typedef struct {
	Rect bounds;
	u32 n_entries;
	u32 selected;
	float scroll;
	FileEntry *entries;
	char cwd[TED_PATH_MAX];
	bool open; // is the file selector on screen?
	bool submitted; // set to true if the line buffer was just submitted this frame.
	bool create_menu; // this is for creating files, not opening files
} FileSelector;

// a node is a collection of tabs OR a split of two nodes
typedef struct Node {
	u16 *tabs; // dynamic array of indices into ted->buffers, or NULL if this is a split
	float split_pos; // number from 0 to 1 indicating where the split is.
	u16 active_tab;
	bool vertical_split; // is the split vertical? if false, this split looks like a|b
	u16 split_a; // split left/upper half; index into ted->nodes
	u16 split_b; // split right/lower half
} Node;

#define TED_MAX_BUFFERS 256
#define TED_MAX_NODES 256
// max tabs per node
#define TED_MAX_TABS 100
typedef struct Ted {
	Font *font_bold;
	Font *font;
	TextBuffer *active_buffer;
	// buffer we are currently drag-to-selecting in, if any
	TextBuffer *drag_buffer;
	// while a menu or something is open, there is no active buffer. when the menu is closed,
	// the old active buffer needs to be restored. that's what this stores.
	TextBuffer *prev_active_buffer; 
	Node *root;
	Node *active_node;
	Settings settings;
	float window_width, window_height;
	v2 mouse_pos;
	u8 nmouse_clicks[4]; // nmouse_clicks[i] = length of mouse_clicks[i]
	v2 mouse_clicks[4][32]; // mouse_clicks[SDL_BUTTON_RIGHT], for example, is all the right mouse-clicks that have happened this frame
	int scroll_total_x, scroll_total_y; // total amount scrolled in the x and y direction this frame
	Menu menu;
	FileSelector file_selector;
	TextBuffer line_buffer; // general-purpose line buffer for inputs -- used for menus
	double error_time; // time error box was opened (in seconds -- see time_get_seconds)
	KeyAction key_actions[KEY_COMBO_COUNT];
	bool search_cwd; // should the working directory be searched for files? set to true if the executable isn't "installed"
	bool quit; // if set to true, the window will close next frame. NOTE: this doesn't check for unsaved changes!!
	Command warn_unsaved; // if non-zero, the user is trying to execute this command, but there are unsaved changes
	char warn_unsaved_names[TED_PATH_MAX]; // comma-separated list of files with unsaved changes (only applicable if warn_unsaved != 0)
	char warn_overwrite[TED_PATH_MAX]; // file name user is trying to overwrite
	char local_data_dir[TED_PATH_MAX];
	char global_data_dir[TED_PATH_MAX];
	char home[TED_PATH_MAX];
	char cwd[TED_PATH_MAX]; // current working directory
	bool nodes_used[TED_MAX_NODES];
	Node nodes[TED_MAX_NODES];
	bool buffers_used[TED_MAX_BUFFERS];
	TextBuffer buffers[TED_MAX_BUFFERS];
	char error[512];
	char error_shown[512]; // error display in box on screen
} Ted;

void command_execute(Ted *ted, Command c, i64 argument);