Skip to content

Instantly share code, notes, and snippets.

@teryror
Created January 17, 2018 07:56
Show Gist options
  • Save teryror/f07fb553a0a935e6c5a9cbd1a0add54c to your computer and use it in GitHub Desktop.
Save teryror/f07fb553a0a935e6c5a9cbd1a0add54c to your computer and use it in GitHub Desktop.
4coder Customization: Support for Golang
// ...
static int32_t*
get_indentation_marks(Application_Links *app, Partition *part, Buffer_Summary *buffer, Cpp_Token_Array tokens, int32_t line_start, int32_t line_end, bool32 exact_align, int32_t tab_width){
int32_t indent_mark_count = line_end - line_start;
int32_t *indent_marks = push_array(part, int32_t, indent_mark_count);
// Shift the array so line_index works correctly.
indent_marks -= line_start;
// NOTE: TLD modifications begin here
bool tld_golang_indentation = false;
Parse_Context_ID parse_context_id = 0;
buffer_get_setting(app, buffer, BufferSetting_ParserContext, (int32_t *) &parse_context_id);
if (parse_context_id == tld_parse_context_language_golang) {
tld_golang_indentation = true;
}
// NOTE: TLD modifications end here
// Decide where to start indentation parsing.
Indent_Parse_State indent = {0};
Cpp_Token *token_ptr = find_anchor_token(app, buffer, tokens, line_start, tab_width, &indent.current_indent);
if (token_ptr == 0){
for (int32_t line_index = line_start; line_index < line_end; ++line_index){
indent_marks[line_index] = 0;
}
}
else{
int32_t line_number = buffer_get_line_number(app, buffer, token_ptr->start);
if (line_number > line_start){
line_number = line_start;
}
if (token_ptr == tokens.tokens){
indent.current_indent = 0;
}
int32_t next_line_start_pos = buffer_get_line_start(app, buffer, line_number);
indent.previous_line_indent = indent.current_indent;
Cpp_Token prev_token = {0};
Cpp_Token token = {0};
if (token_ptr < tokens.tokens + tokens.count){
token = *token_ptr;
}
// Back up and consume this token too IF it is a scope opener.
if (token.type == CPP_TOKEN_BRACE_OPEN || token.type == CPP_TOKEN_BRACKET_OPEN){
--token_ptr;
}
// LOOP OVER TOKENS
for (;;){
if (line_number >= line_end){
break;
}
prev_token = token;
++token_ptr;
if (token_ptr < tokens.tokens + tokens.count){
token = *token_ptr;
}
else{
token.type = CPP_TOKEN_EOF;
token.start = buffer->size;
token.flags = 0;
}
for (;token.start >= next_line_start_pos && line_number < line_end;){
next_line_start_pos = buffer_get_line_start(app, buffer, line_number + 1);
int32_t this_indent = 0;
int32_t previous_indent = indent.previous_line_indent;
int32_t this_line_start = buffer_get_line_start(app, buffer, line_number);
int32_t next_line_start = next_line_start_pos;
bool32 did_multi_line_behavior = false;
// NOTE(allen): Check for multi-line tokens
if (prev_token.start <= this_line_start && prev_token.start + prev_token.size > this_line_start){
if (prev_token.type == CPP_TOKEN_COMMENT || prev_token.type == CPP_TOKEN_STRING_CONSTANT){
Hard_Start_Result hard_start = buffer_find_hard_start(app, buffer, this_line_start, tab_width);
if (exact_align){
this_indent = indent.previous_comment_indent;
}
else{
if (hard_start.all_whitespace){
this_indent = previous_indent;
}
else{
int32_t line_pos = hard_start.char_pos - this_line_start;
this_indent = line_pos + indent.comment_shift;
if (this_indent < 0){
this_indent = 0;
}
}
}
if (!hard_start.all_whitespace){
if (line_number >= line_start){
indent.previous_comment_indent = this_indent;
}
else{
indent.previous_comment_indent = hard_start.indent_pos;
}
}
did_multi_line_behavior = true;
}
}
if (!did_multi_line_behavior){
this_indent = indent.current_indent;
if (token.start < next_line_start){
if (token.flags & CPP_TFLAG_PP_DIRECTIVE){
this_indent = 0;
}
else{
switch (token.type){
case CPP_TOKEN_BRACKET_CLOSE: this_indent -= tab_width; break;
case CPP_TOKEN_BRACE_CLOSE: this_indent -= tab_width; break;
case CPP_TOKEN_BRACE_OPEN: break;
default:
if (indent.current_indent > 0){
bool32 statement_complete = false;
Cpp_Token *prev_usable_token_ptr = token_ptr - 1;
Cpp_Token prev_usable_token = {0};
if (prev_usable_token_ptr >= tokens.tokens){
prev_usable_token = *prev_usable_token_ptr;
}
// Scan backwards for the previous token that actually tells us about the statement.
bool32 has_prev_usable_token = true;
#define NotUsable(T) \
(((T).flags&CPP_TFLAG_PP_BODY) || ((T).flags&CPP_TFLAG_PP_DIRECTIVE) || ((T).type == CPP_TOKEN_COMMENT))
if (NotUsable(prev_usable_token)){
has_prev_usable_token = false;
for (--prev_usable_token_ptr;
prev_usable_token_ptr >= tokens.tokens;
--prev_usable_token_ptr){
prev_usable_token = *prev_usable_token_ptr;
if (!NotUsable(prev_usable_token)){
has_prev_usable_token = true;
break;
}
}
}
#undef NotUsable
if (!has_prev_usable_token){
statement_complete = true;
}
else{
if (prev_usable_token.type == CPP_TOKEN_BRACKET_OPEN ||
prev_usable_token.type == CPP_TOKEN_BRACE_OPEN ||
prev_usable_token.type == CPP_TOKEN_BRACE_CLOSE ||
prev_usable_token.type == CPP_TOKEN_SEMICOLON ||
prev_usable_token.type == CPP_TOKEN_COLON ||
prev_usable_token.type == CPP_TOKEN_COMMA ||
(tld_golang_indentation && ( // NOTE: TLD modifications begin here
prev_usable_token.type == CPP_TOKEN_KEY_CONTROL_FLOW ||
prev_usable_token.type == CPP_TOKEN_BOOLEAN_CONSTANT ||
prev_usable_token.type == CPP_TOKEN_STRING_CONSTANT ||
prev_usable_token.type == CPP_TOKEN_IDENTIFIER ||
prev_usable_token.type == CPP_TOKEN_INTEGER_CONSTANT ||
prev_usable_token.type == CPP_TOKEN_FLOATING_CONSTANT ||
prev_usable_token.type == CPP_TOKEN_CHARACTER_CONSTANT ||
prev_usable_token.type == CPP_TOKEN_PARENTHESE_CLOSE ||
prev_usable_token.type == CPP_TOKEN_BRACKET_CLOSE))){
// NOTE: TLD modification ends here
statement_complete = true;
}
}
if (!statement_complete){
this_indent += tab_width;
}
}
}
}
}
if (this_indent < 0) this_indent = 0;
}
if (indent.paren_nesting > 0){
if (prev_token.type != CPP_TOKEN_PARENTHESE_OPEN){
int32_t level = indent.paren_nesting-1;
if (level >= ArrayCount(indent.paren_anchor_indent)){
level = ArrayCount(indent.paren_anchor_indent)-1;
}
this_indent = indent.paren_anchor_indent[level];
}
}
// Rebase the paren anchor if the first token
// after the open paren is on the next line.
if (indent.paren_nesting > 0){
if (prev_token.type == CPP_TOKEN_PARENTHESE_OPEN){
int32_t level = indent.paren_nesting-1;
if (level >= ArrayCount(indent.paren_anchor_indent)){
level = ArrayCount(indent.paren_anchor_indent)-1;
}
indent.paren_anchor_indent[level] = this_indent;
}
}
if (line_number >= line_start){
indent_marks[line_number] = this_indent;
}
++line_number;
indent.previous_line_indent = this_indent;
}
// Update indent state.
switch (token.type){
case CPP_TOKEN_BRACKET_OPEN: indent.current_indent += tab_width; break;
case CPP_TOKEN_BRACKET_CLOSE: indent.current_indent -= tab_width; break;
case CPP_TOKEN_BRACE_OPEN: indent.current_indent += tab_width; break;
case CPP_TOKEN_BRACE_CLOSE: indent.current_indent -= tab_width; break;
case CPP_TOKEN_COMMENT:
{
int32_t line = buffer_get_line_number(app, buffer, token.start);
int32_t start = buffer_get_line_start(app, buffer, line);
indent.comment_shift = (indent.current_indent - (token.start - start));
indent.previous_comment_indent = (token.start - start);
}break;
case CPP_TOKEN_PARENTHESE_OPEN:
{
if (!(token.flags & CPP_TFLAG_PP_BODY)){
if (indent.paren_nesting < ArrayCount(indent.paren_anchor_indent)){
int32_t line = buffer_get_line_number(app, buffer, token.start);
int32_t start = buffer_get_line_start(app, buffer, line);
int32_t char_pos = token.start - start;
Hard_Start_Result hard_start = buffer_find_hard_start(app, buffer, start, tab_width);
int32_t line_pos = hard_start.char_pos - start;
indent.paren_anchor_indent[indent.paren_nesting] = char_pos - line_pos + indent.previous_line_indent + 1;
}
++indent.paren_nesting;
}
}break;
case CPP_TOKEN_PARENTHESE_CLOSE:
{
if (!(token.flags & CPP_TFLAG_PP_BODY)){
if (indent.paren_nesting > 0){
--indent.paren_nesting;
}
}
}break;
}
}
}
// Unshift the indent_marks array.
indent_marks += line_start;
return(indent_marks);
}
// ...
/******************************************************************************
Author: Tristan Dannenberg
Notice: No warranty is offered or implied; use this code at your own risk.
*******************************************************************************
LICENSE
This software is dual-licensed to the public domain and under the following
license: you are granted a perpetual, irrevocable license to copy, modify,
publish, and distribute this file as you see fit.
*******************************************************************************
This file provides a parse context for golang syntax highlighting.
******************************************************************************/
#if !defined(FTLD_LANGUAGE_GO_HPP)
#define FTLD_LANGUAGE_GO_HPP
static Parse_Context_ID tld_parse_context_language_golang;
#define PSAT(s, t) {s, sizeof(s)-1, t}
static void
tld_init_language_golang(Application_Links *app){
if (tld_parse_context_language_golang != 0) return;
Parser_String_And_Type kw[] = {
PSAT("true", CPP_TOKEN_BOOLEAN_CONSTANT),
PSAT("false", CPP_TOKEN_BOOLEAN_CONSTANT),
// NOTE: Not a _boolean_ constant, obviously, but a keyword literal that
// should be highlighted and influence indentationo as such.
PSAT("nil", CPP_TOKEN_BOOLEAN_CONSTANT),
// NOTE: Some keywords can be statements in their own right. We'll designate
// those as CPP_TOKEN_KEY_CONTROL_FLOW, and all others as CPP_TOKEN_KEY_OTHER.
//
// This may not necessarily reflect their use in the language, but the indenter
// needs to be able to tell the two apart.
PSAT("continue", CPP_TOKEN_KEY_CONTROL_FLOW),
PSAT("break", CPP_TOKEN_KEY_CONTROL_FLOW),
PSAT("fallthrough", CPP_TOKEN_KEY_CONTROL_FLOW),
PSAT("return", CPP_TOKEN_KEY_CONTROL_FLOW),
PSAT("case", CPP_TOKEN_KEY_OTHER),
PSAT("chan", CPP_TOKEN_KEY_OTHER),
PSAT("const", CPP_TOKEN_KEY_OTHER),
PSAT("default", CPP_TOKEN_KEY_OTHER),
PSAT("defer", CPP_TOKEN_KEY_OTHER),
PSAT("else", CPP_TOKEN_KEY_OTHER),
PSAT("for", CPP_TOKEN_KEY_OTHER),
PSAT("func", CPP_TOKEN_KEY_OTHER),
PSAT("go", CPP_TOKEN_KEY_OTHER),
PSAT("goto", CPP_TOKEN_KEY_OTHER),
PSAT("if", CPP_TOKEN_KEY_OTHER),
PSAT("import", CPP_TOKEN_KEY_OTHER),
PSAT("interface", CPP_TOKEN_KEY_OTHER),
PSAT("map", CPP_TOKEN_KEY_OTHER),
PSAT("package", CPP_TOKEN_KEY_OTHER),
PSAT("range", CPP_TOKEN_KEY_OTHER),
PSAT("select", CPP_TOKEN_KEY_OTHER),
PSAT("struct", CPP_TOKEN_KEY_OTHER),
PSAT("switch", CPP_TOKEN_KEY_OTHER),
PSAT("type", CPP_TOKEN_KEY_OTHER),
PSAT("var", CPP_TOKEN_KEY_OTHER),
};
tld_parse_context_language_golang = create_parse_context(app, kw, ArrayCount(kw), 0, 0);
}
#undef PSAT
#endif
@ClaudioBarros
Copy link

I'm having trouble implementing this, and I was hoping you could provide me with some guidance.
Does this work with 4coder's latest version?
Do I have to replace the 4coder_auto_indent.cpp file with the one provided here, or do I simply add the given code to the end of the original file?
I guess the 4ltd_language_go.h should just be copied over to the 4coder 'languages' directory, right?
And then I would just run the buildsuper script and it should now support golang?
Thanks in advance, have a nice day!

@catsocks
Copy link

catsocks commented Sep 3, 2019

@ClaudioBarros I wonder if you have managed to implement it? I have 4coder 4.0.30 and so far I've figured that 4tld_language_go.h indeed belongs in the languages folder, and that it should be included in 4coder_default_hooks.cpp with the other languages' headers, and the function tld_init_language_golang called and parse_context_id set in the default_file_settings hook, like so:

if (match(ext, "go")){
    if (tld_parse_context_language_golang == 0){
        tld_init_language_golang(app);
    }
    parse_context_id = tld_parse_context_language_golang;
}

.go has to be included in treat_as_code in your config.4coder for that to work.

I think you should also copy the "TLD" notes in this gist's 4coder_auto_indent.cpp to the get_indentation_marks function in your own 4coder_auto_indent.cpp but that doesn't work because tld_parse_context_language_golang is not available in that scope. I'm guessing that it has something to do with the fact that this code is 2 years old.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment