git.y1.nz

lwl

Less with links.
download: http://git.y1.nz/archives/lwl.tar.gz
README | Files | Log | Refs

state.c

      1 #include <string.h> /* strstr */
      2 #include <ctype.h> /* isprint */
      3 #include <stdlib.h> /* malloc */
      4 
      5 #include "config.h"
      6 #include "state.h"
      7 
      8 /* for printing dynamic messages into */
      9 char message_buffer[MAX_SCREEN_WIDTH]; 
     10 
     11 State *
     12 init_state()
     13 {
     14 	State *ret = malloc(sizeof(State));
     15 	ret->markup_count = 0;
     16 	ret->markup_index = 0;
     17 	ret->results = malloc(MAX_MARKUPS * sizeof(Markup *));
     18 	ret->result_count = 0;
     19 	ret->input = malloc(MAX_INPUT_LENGTH * sizeof(char));
     20 	ret->input[0] = '\0';
     21 	ret->input_length = 0;
     22 	ret->mode = MODE_GROUND;
     23 	ret->line_offset = 0;
     24 	return ret;
     25 }
     26 
     27 /* functions for managing text input */
     28 void
     29 set_mode(State *state, int mode)
     30 {
     31 	state->mode = mode;
     32 	state->input_length = 0;
     33 	state->input[0] = '\0';
     34 }
     35 void
     36 type_input_char(State *state, Wrap *wrap, char ch)
     37 {
     38 	if (
     39 		state->input_length+1 >= wrap->width ||
     40 		!isprint(ch)
     41 	)
     42 		return;
     43 
     44 	state->input[state->input_length++] = ch;
     45 	state->input[state->input_length] = '\0';
     46 }
     47 void
     48 type_input_backspace(State *state)
     49 {
     50 	if (state->input_length == 0) {
     51 		state->mode = MODE_GROUND;
     52 		return;
     53 	}
     54 	state->input_length--;
     55 	state->input[state->input_length] = '\0';
     56 }
     57 
     58 /* then, some wrap-related utilities */
     59 // TODO extract wrap module from here (state.c) and text.c?
     60 int
     61 get_screen_start(Wrap *wrap, int line_offset)
     62 {
     63 	return wrap->lines[line_offset];
     64 }
     65 int
     66 get_screen_end(Wrap *wrap, int line_offset)
     67 {
     68 	int last_line = line_offset + wrap->height;
     69 	if (last_line > wrap->line_count) last_line = wrap->line_count;
     70 	return wrap->lines[last_line];
     71 }
     72 int
     73 is_onscreen(Wrap *wrap, int line_offset, int index)
     74 {
     75 	int first = get_screen_start(wrap, line_offset);
     76 	int last = get_screen_end(wrap, line_offset);
     77 	return index >= first && index <= last;
     78 }
     79 int
     80 get_line_of(Wrap *wrap, int index)
     81 {
     82 	// TODO use binary instead of linear search
     83 	int line = 0;
     84 	for (;;) {
     85 		if (
     86 			line+1 >= wrap->line_count ||
     87 			wrap->lines[line+1] > index
     88 		) break;
     89 		line++;
     90 	}
     91 	return line;
     92 }
     93 
     94 void
     95 scroll_to(State *state, Wrap *wrap, int offset)
     96 {
     97 	/* clamp the offset */
     98 	if (offset + wrap->height - 1 > wrap->line_count)
     99 		offset = wrap->line_count + 1 - wrap->height;
    100 	if (offset < 0) offset = 0;
    101 
    102 	state->line_offset = offset;
    103 
    104 	/* update message, but don't overwrite old ones */
    105 	if (state->message == NULL) {
    106 		if (state->line_offset >= wrap->line_count - wrap->height)
    107 			state->message = "(END)";
    108 
    109 		if (state->line_offset == 0)
    110 			state->message = state->filename;
    111 	}
    112 }
    113 
    114 void
    115 unset_active(State *state)
    116 {
    117 	if (
    118 		state->markup_index >= state->markup_count ||
    119 		state->markups == NULL
    120 	) return;
    121 	state->markups[state->markup_index]->active = 0;
    122 }
    123 
    124 void
    125 set_markup_index(State *state, int next)
    126 {
    127 	if (state->markup_count == 0) return;
    128 
    129 	// TODO do we actually still need to clamp this?
    130 	/* clamp the index */
    131 	if (next < 0) next = 0;
    132 	if (next > state->markup_count - 1)
    133 		next = state->markup_count - 1;
    134 
    135 	/* update colors */
    136 	unset_active(state);
    137 	state->markups[next]->active = 1;
    138 
    139 	state->markup_index = next;
    140 }
    141 
    142 void
    143 set_markups(State *state, Markup **new_markups, int new_count)
    144 {
    145 	unset_active(state);
    146 	state->markups = new_markups;
    147 	state->markup_count = new_count;
    148 	set_markup_index(state, 0);
    149 }
    150 
    151 int
    152 get_first_markup(State *state, Wrap *wrap)
    153 {
    154 	int i = state->markup_count - 1;
    155 	int limit = get_screen_start(wrap, state->line_offset);
    156 	for (;;) {
    157 		if (i-1 < 0 ||  state->markups[i-1]->start < limit)
    158 			return i;
    159 		i--;
    160 	}
    161 }
    162 
    163 int
    164 get_last_markup(State *state, Wrap *wrap)
    165 {
    166 	int i = 0;
    167 	int limit = get_screen_end(wrap, state->line_offset);
    168 	for (;;) {
    169 		if (
    170 			i+1 >= state->markup_count ||
    171 			state->markups[i+1]->start >= limit
    172 		) return i;
    173 		i++;
    174 	}
    175 }
    176 
    177 void
    178 do_search(FileContents *contents, State *state, Wrap *wrap)
    179 {
    180 	char *start = contents->buffer;
    181 	char *match;
    182 	Markup *markup;
    183 
    184 	/* reset search state, free the old results */
    185 	state->mode = MODE_GROUND;
    186 	set_markups(state, contents->links, contents->link_count);
    187 	for (int i = 0; i < state->result_count; i++)
    188 		free_markup(state->results[i]);
    189 	state->result_count = 0;
    190 
    191 	/* return if the query is empty */
    192 	if (state->input_length == 0)
    193 		return;
    194 
    195 	for (;;) {
    196 		match = strstr(start, state->input);
    197 		if (match == NULL) break;
    198 		
    199 		markup = create_markup(
    200 			match - contents->buffer,
    201 			state->input_length,
    202 			SEARCH_COLOR
    203 		);
    204 		state->results[state->result_count++] = markup;
    205 		start = match + state->input_length;
    206 	}
    207 
    208 	if (state->result_count == 0) {
    209 		state->message = "No matches found";
    210 		return;
    211 	}
    212 
    213 	set_markups(state, state->results, state->result_count);
    214 
    215 	state->message = message_buffer;
    216 	snprintf(message_buffer, wrap->width, "%d matches", state->result_count);
    217 }
    218 
    219 /* update scroll to ensure that a specific index is onscreen. */
    220 void
    221 scroll_to_show(State *state, Wrap *wrap, int index)
    222 {
    223 	int new_line_offset, line_to_show = get_line_of(wrap, index);
    224 	int scrolling_up;
    225 
    226 	/* if the line is already onscreen, do nothing */
    227 	if (
    228 		line_to_show >= state->line_offset &&
    229 		line_to_show < state->line_offset + wrap->height
    230 	) return;
    231 
    232 	/* calculate the line offset, minimizing distance moved */
    233 	scrolling_up = line_to_show < state->line_offset;
    234 	new_line_offset = scrolling_up ?
    235 		line_to_show:
    236 		line_to_show - wrap->height + 1;
    237 	scroll_to(state, wrap, new_line_offset);
    238 }
    239 
    240 void
    241 show_markup_data(State *state, Wrap *wrap)
    242 {
    243 	const char *data;
    244 	int width;
    245 
    246 	data =  state->markups[state->markup_index]->data;
    247 	if (data == NULL) return;
    248 
    249 	/* -3 for "...", +1 for "\0" */
    250 	width = wrap->width - 3 + 1;
    251 
    252 	state->message = message_buffer;
    253 	int length = snprintf(message_buffer, width, "Data: %s", data);
    254 	if (length >= width)
    255 		snprintf(
    256 			message_buffer + (wrap->width-3),
    257 			3 + 1, "..."
    258 		);
    259 }
    260 
    261 void
    262 next_markup(State *state, Wrap *wrap, int step)
    263 {
    264 	int start;
    265 	int next = state->markup_index + step;
    266 
    267 	if (state->markup_count == 0) {
    268 		state->message = "None";
    269 		return;
    270 	}
    271 
    272 	start = state->markups[state->markup_index]->start;
    273 	if (!is_onscreen(wrap, state->line_offset, start)) {
    274 		next = step > 0 ?
    275 			get_first_markup(state, wrap) :
    276 			get_last_markup(state, wrap);
    277 	} else {
    278 		if (next < 0) next = 0;
    279 		if (next > state->markup_count-1) next = state->markup_count-1;
    280 		if (next == state->markup_index && state->markup_count != 1) {
    281 			state->message = "No more";
    282 			return;
    283 		}
    284 	}
    285 
    286 	set_markup_index(state, next);
    287 
    288 	/* scroll to the start of the markup */
    289 	start = state->markups[state->markup_index]->start;
    290 	scroll_to_show(state, wrap, start);
    291 
    292 	show_markup_data(state, wrap);
    293 }

This webpage is intended to be an accessible preview of this repository. To get a fuller picture, clone it and use the git CLI.