|
|
@@ -158,72 +158,80 @@ static int syntax_define (int enrange, int derange, char * begin, char * end, ch |
|
|
|
} |
|
|
|
|
|
|
|
/* |
|
|
|
TODO: WE LEFT OF HERE... |
|
|
|
This is more complex, but if you use your eyes to look, your brain to comprehend and your heart to love, I'm sure that you'll understand it. |
|
|
|
*/ |
|
|
|
|
|
|
|
static int syntax_select (char * string, int * length) { |
|
|
|
int offset, subset, select; |
|
|
|
int offset, select; |
|
|
|
|
|
|
|
fatal_failure (syntax_active == FALSE, "syntax_select: Syntax is not active."); |
|
|
|
fatal_failure (syntax_active == FALSE, "syntax_select: Syntax is not active."); // Don't select without rules, abort! |
|
|
|
fatal_failure (string == NULL, "syntax_select: String is null."); |
|
|
|
fatal_failure (length == NULL, "syntax_select: Length is null."); |
|
|
|
|
|
|
|
for (select = offset = 0; select != syntax_count; ++select) { |
|
|
|
if (syntax_enrange [select] == FALSE) { |
|
|
|
if (string_compare_limit (string, syntax_begin [select], string_length (syntax_begin [select])) == TRUE) { |
|
|
|
break; // We need to limit our string comparisson function. |
|
|
|
} else { |
|
|
|
continue; |
|
|
|
} |
|
|
|
} else { |
|
|
|
for (subset = 0; subset != string_length (syntax_begin [select]); ++subset) { |
|
|
|
if (string [offset] == syntax_begin [select] [subset]) { |
|
|
|
goto selected; // We can't use 'break' here, because it will exit only one loop, not both of them. |
|
|
|
} else { |
|
|
|
continue; |
|
|
|
} |
|
|
|
// In this first part of the function, we need to check if our syntax rule has been detected at the string offset we've provided. We're looping defined syntax rules and |
|
|
|
// choosing whether to compare any of the characters, or full string, depending on 'syntax_enrange' value which is essentially boolean, true or false, which I express with |
|
|
|
// 'int' type for "type-safety simplicity". Keep in mind that we're not returning or modifying the string we provided, so it won't be null-terminated, instead I think |
|
|
|
// it's best to modify only variable 'length', hence we check with 'string_compare_limit' function. |
|
|
|
for (select = offset = 0; select != syntax_count; ++select) { // We're looping defined syntax rules: |
|
|
|
if (syntax_enrange [select] == FALSE) { // Choosing the comparisson: |
|
|
|
if (string_compare_limit (string, syntax_begin [select], string_length (syntax_begin [select])) == TRUE) { // Limiting our string comparisson. |
|
|
|
break; // If strings are same, we exit the loop. |
|
|
|
} |
|
|
|
} else { // Else, we compare any character. |
|
|
|
if (character_compare_array (string [offset], syntax_begin [select]) == TRUE) { // With our obviously named function... |
|
|
|
break; // We found it, exit the loop! |
|
|
|
} // If we didn't, just continue. |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
selected: |
|
|
|
} // And now we have our 'select' value. |
|
|
|
|
|
|
|
// If there was no syntax rule detected, we need to return from a function, and increment the offset by setting variable 'length' to 1. If we don't increment it, at the |
|
|
|
// first unrecognized character, our second nested-loop inside function 'preview_c_file' would use uninitialized or zero value, depending on how we structured our code |
|
|
|
// before that. We also return 'syntax_count' as the syntax rule index, which is invalid, and would produce Valgrind warning if we didn't handle it. In my unimportant |
|
|
|
// opinion, this if statement is the ugliest part of the function. |
|
|
|
if (select >= syntax_count) { |
|
|
|
* length = 1; |
|
|
|
|
|
|
|
return (syntax_count); |
|
|
|
} |
|
|
|
|
|
|
|
for (offset = 1; string [offset - 1] != '\0'; ++offset) { |
|
|
|
if (string [offset] == syntax_escape [select]) { |
|
|
|
// In this second part, we have our 'select' value, index of the syntax rule, and we want to know the 'length' value, by trying to match with 'syntax_end' string. We have |
|
|
|
// to again, separate two cases for matching any character or full string, except that we use it to determine its' match-length. Important difference is also that there's |
|
|
|
// special case where we have escape character matching, and where 'syntax_end' string is empty (but not NULL), so in that case we match only one character. We could have |
|
|
|
// nested loop there, and second loop would need goto statement to exit it, so we only use one loop. |
|
|
|
for (offset = 1; string [offset - 1] != '\0'; ++offset) { // Now, offset must be 1, and we loop... |
|
|
|
if (string [offset] == syntax_escape [select]) { // Here's our escape exception. |
|
|
|
++offset; |
|
|
|
continue; |
|
|
|
} |
|
|
|
|
|
|
|
if (syntax_derange [select] == FALSE) { |
|
|
|
if (string_compare_limit (& string [offset], syntax_end [select], string_length (syntax_end [select])) == TRUE) { |
|
|
|
* length = offset + string_length (syntax_end [select]); |
|
|
|
return (select); |
|
|
|
if (syntax_derange [select] == FALSE) { // Choosing what to compare, yet again... |
|
|
|
if (string_compare_limit (& string [offset], syntax_end [select], string_length (syntax_end [select])) == TRUE) { // Again, we're comparing full string. |
|
|
|
* length = offset + string_length (syntax_end [select]); // We found it, yaay! |
|
|
|
break; |
|
|
|
} |
|
|
|
} else { |
|
|
|
subset = 0; |
|
|
|
if (string_compare (syntax_end [select], "") == TRUE) { |
|
|
|
if (string_compare (syntax_end [select], "") == TRUE) { // And here's our empty string exception. |
|
|
|
break; |
|
|
|
} do { |
|
|
|
if (string [offset] == syntax_end [select] [subset]) { |
|
|
|
* length = offset; |
|
|
|
goto finished; |
|
|
|
} |
|
|
|
} while (++subset != string_length (syntax_end [select])); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
finished: |
|
|
|
} |
|
|
|
if (character_compare_array (string [offset], syntax_end [select]) == TRUE) { |
|
|
|
* length = offset; |
|
|
|
break; |
|
|
|
} |
|
|
|
} // These two loops look similar, but no! |
|
|
|
} // And now we have our 'length' value. |
|
|
|
|
|
|
|
return (select); |
|
|
|
return (select); // Lastly, return syntax rule index. |
|
|
|
} |
|
|
|
|
|
|
|
/* |
|
|
|
Imagine my shock, we can now print coloured text, without regular expressions. Nothing much, we can print it without using 'curses_*' functions, but if we want to preview large, |
|
|
|
well more than 24 line of code, we'd want to scroll it or modify it if we're making a text editor, hence, using curses is good. Lets see how our "mini-main" subprogram-like |
|
|
|
function does its' work, and how we use 'syntax_*' functions in them. |
|
|
|
*/ |
|
|
|
|
|
|
|
void preview_c_file (char * text_file, int width, int height, int x, int y) { |
|
|
|
// Before we begin (Ada pun intended, remove this in final version), I won't (re)align 'separators' and 'keywords', because they fuck-up my comments, which I never write |
|
|
|
// in my "official" programs. I write comments only here, to explain stuff in more details. Have fun... Oh, and type of variable 'keywords' an array of string pointers of |
|
|
|
// automatic length, which we get with "sizeof (keywords) / sizeof (keywords [0])" part, for those keywords, it would be 32UL, and we cast it to integer. |
|
|
|
char * separators = ".,:;<=>+*-/%!&~^?|()[]{}'\" \t\r\n"; |
|
|
|
|
|
|
|
char * keywords [] = { |
|
|
@@ -240,7 +248,7 @@ void preview_c_file (char * text_file, int width, int height, int x, int y) { |
|
|
|
(void) width; |
|
|
|
(void) height; |
|
|
|
|
|
|
|
syntax_define (FALSE, FALSE, "/*", "*/", '\0', COLOUR_GREY, EFFECT_BOLD); |
|
|
|
syntax_define (FALSE, FALSE, "/*", "*/", '\0', COLOUR_GREY, EFFECT_BOLD); // Below, we're simply using our 'syntax_define' function. |
|
|
|
syntax_define (FALSE, FALSE, "//", "\n", '\0', COLOUR_GREY, EFFECT_BOLD); |
|
|
|
syntax_define (FALSE, FALSE, "#", "\n", '\\', COLOUR_YELLOW, EFFECT_ITALIC); |
|
|
|
syntax_define (FALSE, FALSE, "'", "'", '\\', COLOUR_PINK, EFFECT_BOLD); |
|
|
@@ -258,27 +266,33 @@ void preview_c_file (char * text_file, int width, int height, int x, int y) { |
|
|
|
syntax_define (TRUE, TRUE, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", separators, '\0', COLOUR_WHITE, EFFECT_BOLD); |
|
|
|
syntax_define (TRUE, TRUE, "_", separators, '\0', COLOUR_WHITE, EFFECT_ITALIC); |
|
|
|
|
|
|
|
text_data = file_record (text_file); |
|
|
|
text_data = file_record (text_file); // And, imagine, importing our file data into a buffer! |
|
|
|
|
|
|
|
reset_x = x; |
|
|
|
reset_y = y; |
|
|
|
|
|
|
|
for (curses_active = 1; curses_active != 0; ) { |
|
|
|
for (curses_active = 1; curses_active != 0; ) { // We enter our main subprogram loop. |
|
|
|
int offset, select, length; |
|
|
|
|
|
|
|
curses_render_background (' ', COLOUR_WHITE, EFFECT_NORMAL); |
|
|
|
curses_render_background (' ', COLOUR_WHITE, EFFECT_NORMAL); // We need to clear the screen buffer before rendering. |
|
|
|
|
|
|
|
x = reset_x; |
|
|
|
y = reset_y; |
|
|
|
select = syntax_count; |
|
|
|
length = 0; |
|
|
|
|
|
|
|
for (offset = 0; offset < string_length (text_data); offset += length) { |
|
|
|
for (offset = 0; offset < string_length (text_data); offset += length) { // And it's time to start rendering our C file. |
|
|
|
int suboffset, colour, effect; |
|
|
|
|
|
|
|
select = syntax_select (& text_data [offset], & length); |
|
|
|
select = syntax_select (& text_data [offset], & length); // Here we're evaluating variables 'select' and 'length'. |
|
|
|
|
|
|
|
if (select >= syntax_count) { |
|
|
|
// We can do the same thing in 2 lines of code, but it's less readable in my opinion, I prefer longer verbose way below... |
|
|
|
// colour = (select >= syntax_count) ? COLOUR_WHITE : syntax_colour [select]; |
|
|
|
// effect = (select >= syntax_count) ? EFFECT_NORMAL : syntax_effect [select]; |
|
|
|
// Or, if you find this more intuitive: |
|
|
|
// colour = (select < syntax_count) ? syntax_colour [select] : COLOUR_WHITE; |
|
|
|
// effect = (select < syntax_count) ? syntax_effect [select] : EFFECT_NORMAL; |
|
|
|
if (select >= syntax_count) { // Here, we're handling error value of 'syntax_select'. |
|
|
|
colour = COLOUR_WHITE; |
|
|
|
effect = EFFECT_NORMAL; |
|
|
|
} else { |
|
|
@@ -286,25 +300,23 @@ void preview_c_file (char * text_file, int width, int height, int x, int y) { |
|
|
|
effect = syntax_effect [select]; |
|
|
|
} |
|
|
|
|
|
|
|
for (suboffset = 0; suboffset < length; ++suboffset) { |
|
|
|
if (text_data [offset + suboffset] == CHARACTER_LINE_FEED) { |
|
|
|
x = reset_x; |
|
|
|
y += 1; |
|
|
|
} else if (text_data [offset + suboffset] == CHARACTER_TAB_HORIZONTAL) { |
|
|
|
x += 8; |
|
|
|
for (suboffset = 0; suboffset < length; ++suboffset) { // Sadly, we need to render them one by one character. |
|
|
|
if (text_data [offset + suboffset] == CHARACTER_LINE_FEED) { // Rendering of blank characters isn't counted, so: |
|
|
|
x = reset_x; // If there's a new line, we need to reset 'x' value. |
|
|
|
y += 1; // And increment 'y' value. |
|
|
|
} else if (text_data [offset + suboffset] == CHARACTER_TAB_HORIZONTAL) { // If there's a tab, we offset 'x' value by normal count. |
|
|
|
x += 8; // Normal indentation is 8-characters wide. |
|
|
|
} else { |
|
|
|
curses_render_character (text_data [offset + suboffset], colour, effect, x, y); |
|
|
|
curses_render_character (text_data [offset + suboffset], colour, effect, x, y); // Finally, we can render it character by character. |
|
|
|
x += 1; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// curses_render_string_limit (& text_data [offset], length, syntax_colour [select], syntax_effect [select], x, y); |
|
|
|
} |
|
|
|
|
|
|
|
curses_synchronize (); |
|
|
|
curses_synchronize (); // Lastly, we synchronize our terminal. |
|
|
|
} |
|
|
|
|
|
|
|
text_data = deallocate (text_data); |
|
|
|
text_data = deallocate (text_data); // And deallocate the memory when we exit the subprogram. |
|
|
|
} |
|
|
|
|
|
|
|
#endif |