4coder»Forums
Jens
51 posts / 1 project
Linux Command Line Colors
Edited by Jens on
I've recently started working in a linux environment and noticed the build scripts I'm using are producing colored output, something that I don't think is supported in 4coder at the moment.

If you're not familiar with the feature it's explained here

Does something like this exist already in 4coder?
Simon Anciaux
1341 posts
Linux Command Line Colors
This isn't part of 4coder, but it might be possible to implement in the custom layer.
Jens
51 posts / 1 project
Linux Command Line Colors
I'd love to give it a try but I've just started playing with adding a custom layer today. From what I've learnt so far, I should put a hook for the HookID_RenderCaller and check if it's say *compliation* buffer or similar, then do take it from there.

Does that sound like the right approach?
Simon Anciaux
1341 posts
Linux Command Line Colors
Yes. But it would probably be easier to modify or replace the default_render_buffer function so you don't rewrite everything. Something like:
1
2
3
4
5
6
7
8
9
void default_render_caller ( ... ) {
    ...
    if ( buffer_is_compilation ) {
        do_custom_code( );
    } else {
        default_render_buffer( ... );
    }
    ...
}


What I'm not sure of is if you'll have the color escape code in the buffer.
Jens
51 posts / 1 project
Linux Command Line Colors
I got the coloring working, but as you mentioned the color escape codes are being rendered.

I'm not sure how to overwrite that behavior. I see there is Layout_Item_Flag, would it be possible to add an Invisible flag and somehow overwrite the rendering or do I have to write my own version of draw_text_layout?
Simon Anciaux
1341 posts
Linux Command Line Colors
draw_text_layout is in the core of 4coder so you can't rewrite it. You can rewrite layout rules (4coder_layout_rule.cpp, the default is layout_unwrapped, to change it use the HookID_Layout hook) but I don't think that it allows you to do what you want (I never used that so I don't actually know, it might be worth looking into that first).

You might want to parse the buffer and when you read a color escaped sequence add scope managed variables with the position and the color, remove the text (you might need to have use another buffer for that as *compilation* is read only I think) and use the attached information when rendering to add color to the text.

Scope managed variables (scope attachments) are variables attached to a combination of "scopes". For example buffer_get_managed_scope will return a scope that has the life time of the buffer. If the buffer is closed, the variable attached to it will be freed. You can combined several scopes. For example the error highlight feature combine the *compilation* buffer scope and the target source file for an error to store error positions used for highlighting the line in red. If you close either file, the variable are freed. In you case you only need to use the *compilation* buffer scope (I think). In pseudo code:

 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
 
/* At global scope */
CUSTOM_ID( attachment, name_for_the_array );

/* If a function */
/* Create / write */
Managed_Scope buffer_scope = buffer_get_managed_scope( app, buffer );

Managed_Object handle_to_array = alloc_managed_memory_in_scope( buffer_scope, item_size, item_count );
managed_object_store_data( app, handle_to_array, first_index, count, memory_ptr );

/* We want to give a name to the array, so we create a managed object attached to a name, and store the array handle in it. */
Managed_Object* name_for_the_array_ptr = scope_attachment( app, buffer_scope, name_for_the_array, Managed_Object );
    
if ( name_for_the_array_ptr != 0 ) {
    *name_for_the_array_ptr  = handle_to_array;
}

/* Read */
Managed_Scope buffer_scope = buffer_get_managed_scope( app, buffer );
Managed_Object* name_for_the_array_ptr = scope_attachment( app, buffer_scope, name_for_the_array, Managed_Object );
Managed_Object handle_to_array = 0;
    
if ( name_for_the_array_ptr ) {
    handle_to_array = *name_for_the_array_ptr;
}
    
if ( handle_to_array ) {
    managed_object_load_data( app, handle_to_array, first_index, count, memory_ptr );
    ...
}


Here is an article about managed scope, but it was written while the feature was being developed and the actual api is different.
New Features P1: Memory Management Overview. Search for managed in 4coder's documentation.

If the buffer can be modified by the user after you parsed it, for position you can use the marker api. Marker will be moved by 4coder when a buffer is edited (for example if you mark a line, than add new line above, the marker will stay on the correct line). But there isn't much documentation on that and some function are deprecated in the doc, with no information on how to do otherwise (those functions are still used in 4coder code, but they might be removed in the future).

Here is an example:

  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
/* Declare the name of the attachment */
CUSTOM_ID( attachment, ghost_text_marker );

/*
- Create an array of 2 markers on the buffer managed memory;
- store data in it;
- create or get a scope attachment (scope variable) using a name (ghost_text_marker);
- put the handle to the array in the scope attachment
*/
function void add_ghost_text_marker( Application_Links *app, Buffer_ID buffer, i64 position, i64 size ) {
    
    /* The boolean (lean_right) seems to indicate the behavior of the maker when text is inserted at the marker position.*/
    Marker markers[ 2 ] = { position, true, position + size, false };
    
    Managed_Scope buffer_scope = buffer_get_managed_scope( app, buffer );
    
    Managed_Object marker_handle = alloc_buffer_markers_on_buffer( app, buffer, 2, 0 );
    managed_object_store_data( app, marker_handle, 0, 2, markers );
    
    Assert( managed_object_get_item_size( app, marker_handle ) == sizeof( Marker ) );
    Assert( managed_object_get_item_count( app, marker_handle ) == 2 );
    Assert( managed_object_get_type( app, marker_handle ) == ManagedObjectType_Markers );
    
    Managed_Object *marker_handle_ptr = scope_attachment( app, buffer_scope, ghost_text_marker, Managed_Object );
    
    if ( marker_handle_ptr != 0 ) {
        *marker_handle_ptr = marker_handle;
    }
}

CUSTOM_COMMAND_SIG( clean_ghost_text_marker )
CUSTOM_DOC( "Remove ghost text marker from the current buffer." ) {
    
    Marker markers[ 2 ] = { 0 };
    
    View_ID view = get_active_view( app, 0 );
    Buffer_ID buffer = view_get_buffer( app, view, 0 );
    
    Managed_Scope buffer_scope = buffer_get_managed_scope( app, buffer );
    Managed_Object *marker_handle_ptr = scope_attachment( app, buffer_scope, ghost_text_marker, Managed_Object );
    
    if ( marker_handle_ptr ) {
        
        Managed_Object marker_handle = *marker_handle_ptr;
        
        if ( marker_handle ) {
            managed_object_free( app, marker_handle );
            ( *marker_handle_ptr ) = 0;
        }
    }
}

CUSTOM_COMMAND_SIG( clean_ghost_text_marker_all_buffer )
CUSTOM_DOC( "Remove ghost text marker from all buffers." ) {
    
    Buffer_ID buffer = get_buffer_next( app, 0, Access_Always );
    
    while ( buffer ) {
        
        Marker markers[ 2 ] = { 0 };
        
        Managed_Scope buffer_scope = buffer_get_managed_scope( app, buffer );
        Managed_Object *marker_handle_ptr = scope_attachment( app, buffer_scope, ghost_text_marker, Managed_Object );
        
        if ( marker_handle_ptr ) {
            
            Managed_Object marker_handle = *marker_handle_ptr;
            
            if ( marker_handle ) {
                managed_object_free( app, marker_handle );
                ( *marker_handle_ptr ) = 0;
            }
        }
        
        buffer = get_buffer_next( app, buffer, Access_Always );
    }
}

function void draw_ghost_text( Application_Links* app, Buffer_ID buffer, View_ID view, Text_Layout_ID text_layout_id ) {
    
    Marker markers[ 2 ] = { 0 };
    
    Managed_Scope buffer_scope = buffer_get_managed_scope( app, buffer );
    Managed_Object *marker_handle_ptr = scope_attachment( app, buffer_scope, ghost_text_marker, Managed_Object );
    Managed_Object marker_handle = 0;
    
    if ( marker_handle_ptr ) {
        marker_handle = *marker_handle_ptr;
    }
    
    if ( marker_handle ) {
        managed_object_load_data( app, marker_handle, 0, 2, markers );
        Range_i64 range = { markers[ 0 ].pos, markers[ 1 ].pos };
        i64 position = view_get_cursor_pos( app, view );
        
        if ( position >= markers[ 0 ].pos && position < markers[ 1 ].pos ) {
            range.min = position;
        }
        
        paint_text_color( app, text_layout_id, range, finalize_color( defcolor_comment, 0 ) );
    }
}