Skip to content

Instantly share code, notes, and snippets.

@luis-reyes-a
Last active July 10, 2021 09:05
Show Gist options
  • Save luis-reyes-a/f25517a5e6a32b8b13d3434cc24998e1 to your computer and use it in GitHub Desktop.
Save luis-reyes-a/f25517a5e6a32b8b13d3434cc24998e1 to your computer and use it in GitHub Desktop.
/*
NOTE this doesn't really check if a specifier is entirely valid in order to highlight
for example if you have %lls we highlight it
we simply check each subspecifier in order to see if it seems valid and move on and don't have logic to see if
the subspecifiers are comptaible or not
we don't highlight if any of the subspecifier checks fail so if you do %.qd we don't highlight it
Also I commented out some checks that I wasn't sure if they were invalid in c++ but valid in C or ones that I just never use
Instructions
In 4coder_draw.cpp there a function draw_cpp_token_colors() that decides what color to draw tokens and draws them.
In the function there should be this:
ARGB_Color argb = fcolor_resolve(color);
paint_text_color(app, text_layout_id, Ii64_size(token->pos, token->size), argb);
You want to #if 0 the top of that and add an #else #endif right below and stick the following code in between the #else and #endif
so you can toggle this on and off in case you discover some bugs. And you might! I haven't had any problems with it though
Now when you compile you should get an error like "buffer_id" unrecognized identifier.
We have to pass the buffer_id of the buffer whose tokens we are rendering into draw_cpp_token_colors()
so we can parse the contents of the string
4coder_default_hooks.cpp has a default_render_buffer() which seems to be the only place that that function gets called and the
only place you need to pass in the buffer_id. Anyways cheers and let me know if you find bugs!
*/
if(token->kind == TokenBaseKind_LiteralString && token->sub_kind != TokenCppKind_LiteralCharacter)
{
Scratch_Block scratch(app);
String_Const_u8 string = push_token_lexeme(app, scratch, buffer_id, token);
ARGB_Color alt_argb = fcolor_resolve(fcolor_id(defcolor_comment));
ARGB_Color str_argb = fcolor_resolve(fcolor_id(defcolor_str_constant));
u64 normal_at = 0; //represents the substring before the specifier that we color normally
u64 normal_length = 0; //we iterate until we reach a specifier, color normal substring, specifier, and continue
for(u64 i = 0; i < string.size; /*we increment i ourselves*/ )
{
if(string.str[i] == '\\' || string.str[i] == '%')
{
if(normal_length) //draw normal substring
paint_text_color(app, text_layout_id, Ii64_size(token->pos + normal_at, normal_length), str_argb);
//decides whether to actually color in the case where user hasn't finished typing the specifier
b32 actually_found_specifier = false;
i32 specifier_length = 0;
if(string.str[i] == '\\')
{
//NOTE 4coder seems to not lex a token as a string token if nothing comes after the first '\'
//meaning it seems safe to just assume the specifier is complete and of length 2
//NOTE I don't actually check if the escape character is valid because I don't care :P
specifier_length = 2;
actually_found_specifier = true;
goto JUST_DRAW_THE_FREAKING_STRING; //we use this extensively below to 'break' out if we pass the end of str
}
//else{} we have a printf specifier like so %[flags][width][.precision][length]specifier
//increments length but if we reach the ending " we just 'break' out and draw
#define INCREMENT_SPECIFIER_LENGTH do {specifier_length += 1; \
if((i + specifier_length) >= (string.size - 1)) goto JUST_DRAW_THE_FREAKING_STRING; } while(0)
//next character to see if it's part of the specifier
#define NEXT string.str[i + specifier_length]
INCREMENT_SPECIFIER_LENGTH; //for first %
//[flags]
if(NEXT == '+' || NEXT == '-' || NEXT == '#' || NEXT == '0' /*|| NEXT == ' '*/)
INCREMENT_SPECIFIER_LENGTH;
//[width] is either '*' or a sequence of numbers denoting the width
if(NEXT == '*') INCREMENT_SPECIFIER_LENGTH;
else if(character_is_base10(NEXT))
{
while(character_is_base10(NEXT)) INCREMENT_SPECIFIER_LENGTH;
}
//[precision] begins with '.' and followed by either '*' or a sequence of numbers
if(NEXT == '.')
{
INCREMENT_SPECIFIER_LENGTH;
if(NEXT == '*') INCREMENT_SPECIFIER_LENGTH;
else if(character_is_base10(NEXT))
{
while(character_is_base10(NEXT)) INCREMENT_SPECIFIER_LENGTH;
}
}
//[length] length can either be h, l, j, z, t, hh, ll
if(NEXT == 'j' || NEXT == 'z' || NEXT == 't') INCREMENT_SPECIFIER_LENGTH;
else if(NEXT == 'h') //hh
{
INCREMENT_SPECIFIER_LENGTH;
if(NEXT == 'h') INCREMENT_SPECIFIER_LENGTH; //second h
}
else if(NEXT == 'l') //ll
{
INCREMENT_SPECIFIER_LENGTH;
if(NEXT == 'l') INCREMENT_SPECIFIER_LENGTH; //second l
}
//the actual specifier where we decide if to actually highlight
if(NEXT == 'd' || NEXT == 'i' || NEXT == 'u' || NEXT == 'o' || NEXT == 'x' || NEXT == 'X' ||
NEXT == 'f' || NEXT == 'e' || NEXT == 'g' || NEXT == '%' || NEXT == 'c' || NEXT == 's' ||
NEXT == 'p' || NEXT == 'n')
/*NEXT == 'F' || NEXT == 'E' || NEXT == 'G' || NEXT == 'a' || NEXT == 'A' ||*/
{
actually_found_specifier = true;
INCREMENT_SPECIFIER_LENGTH;
}
JUST_DRAW_THE_FREAKING_STRING:
paint_text_color(app, text_layout_id, Ii64_size(token->pos + i, specifier_length),
actually_found_specifier ? alt_argb : str_argb);
i += specifier_length;
normal_at = i;
normal_length = 0; //to begin search for a new normal substring
#undef NEXT
#undef INCREMENT_SPECIFIER_LENGTH
}
else
{
normal_length += 1;
i += 1;
}
}
//we do this one more time bc we only draw the normnal substring *before* specifiers
//so the normal substring after the last specifier won't come up in the loop
if(normal_length) paint_text_color(app, text_layout_id, Ii64_size(token->pos + normal_at, normal_length), str_argb);
}
else //normal token coloring
{
ARGB_Color argb = fcolor_resolve(color);
paint_text_color(app, text_layout_id, Ii64_size(token->pos, token->size), argb);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment