How to reload buffer in custom layer?

I'm currently trying to work-in clang-format into my custom layer. Currently, I'm able to call out and use clang format to format the current buffer and save. However, the file does not reload automatically and so instead I do not see the changes reflected immediately in the view. I have to manually kill and reload the buffer to see the changes. I'm trying to correct this in code but don't know how. Here is the code I'm currently trying to get working:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
CUSTOM_COMMAND_SIG(jason_save)
{
    clean_all_lines(app);
    jason_clang_format(app);
    save(app);

    View_Summary view = get_active_view(app, AccessProtected);
    Buffer_Summary buffer = get_buffer(app, view.buffer_id, AccessProtected);
    Buffer_Reopen_Flag flag{0};
    Buffer_Reopen_Result result = reopen_buffer(app, &buffer, flag);
    Assert(result);
};


So 'reopen_buffer' is not returning a valid result. I've also tried with things like 'refresh_buffer' and 'refresh_view' to no avail. How would I implement things to get my buffer to refresh instantly after clang-format has run?

Edited by Jason on Reason: Initial post
You seem to be trying to save the buffer before you are seeing the changes inside 4Coder/the-buffer?
Doesn't that overwrite whatever changes clang-format did outside of 4Coder?

Have you tried just calling 'reopen(app);' and not 'save(app);' since you're trying to get the current version of the file from disk.

I'm assuming you are working with the active view and buffer, so the call will pickup the correct view and buffer.
I'm also assuming the 'jason_clang_format(app);' function opens the file and re-formats it and saves it to disk?

1
2
3
4
5
6
CUSTOM_COMMAND_SIG(jason_save)
{
    clean_all_lines(app);
    jason_clang_format(app);
    reopen(app);
}

This is what my 'jason_clang_format' custom command is doing at the moment:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
CUSTOM_COMMAND_SIG(jason_clang_format)
{
    View_Summary view = get_active_view(app, AccessProtected);
    Buffer_Summary buffer = get_buffer(app, view.buffer_id, AccessProtected);

    char cmdStr[100] = {};
    char* cmdPart1 = "clang-format -i ";
    char* cmdPart2 = buffer.buffer_name;
    strcat_s(cmdStr, cmdPart1);
    strcat_s(cmdStr, cmdPart2);

    Buffer_Identifier buffer_id{};
    Assert(current_project.loaded);
    exec_system_command(app, &view, buffer_id, current_project.dir.str, 
    current_project.dir.size, cmdStr, sizeof(cmdStr), 1);
    save_buffer(app, &buffer, buffer.file_name, buffer.file_name_len, 0);
};


So I am trying to save the buffer. I rearranged the 'jason_save' command to this:

1
2
3
4
5
6
CUSTOM_COMMAND_SIG(jason_save)
{
    clean_all_lines(app);
    jason_clang_format(app);
    reopen(app);
};


but the file is still just coming up with the ! meaning I have to manually close and reopen the file buffer to see the changes.
Okay, since I haven't used 'exec_system_command(...)' before but this makes me think clang-format isn't done running when that call returns... so you end up doing the 'reopen(app)' before the formatting is done so it either fails to open the file or changes are made after you "reopen" the file.

Also if I'm not wrong that 'save_buffer(...)' call is redundant or potentially destructive since it would save the buffer from 4Coder memory (while it was hopefully changed on disk by the clang-format call).

I'm not sure this is just what I would test for.
You got me thinking when you mentioned how I might not be giving clang-format enough time to format before reloading the buffer, so I tried a hacky fix and it worked:

 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
void jason_clang_format(struct Application_Links* app, View_Summary view)
{
    Buffer_Summary buf = get_buffer(app, view.buffer_id, AccessProtected);
    char cmdStr[100] = {};
    char* cmdPart1 = "C:/LLVM/bin/clang-format -i ";
    char* cmdPart2 = buf.buffer_name;
    strcat_s(cmdStr, cmdPart1);
    strcat_s(cmdStr, cmdPart2);

    Buffer_Identifier buffer_id{};
    Assert(current_project.loaded);
    exec_system_command(app, &view, buffer_id, current_project.dir.str, current_project.dir.size, cmdStr, sizeof(cmdStr), 1);
};

CUSTOM_COMMAND_SIG(jason_save)
{
    clean_all_lines(app);
    save(app);

    View_Summary view = get_active_view(app, AccessAll);
    jason_clang_format(app, view);

    for (int i = 0; i < 1000000; i++)
    {
        printf("hehllooo");
    };

    reopen(app);
};


So giving enough time for clang-format to format the document before reopening seems to do the trick. However, I'm wondering if there's a way to automatically detect when a buffer has been modified outside of 4coder so I can just reopen once that happens? With this current setup I'm still seeing the exclamation mark in 4coder's grey, gui buffer bar at the top.
Yeah I actually wrote this up myself and added a really hacky hack to wait.

(This is just the wait part)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
// TODO(Jim0_o 2019-02-18): Need a wait here for file changes to be finished by clang-format.
{
   Buffer_Summary *Buffer = &buffer; 
   s32 BackupOutCounter = 0; 
   // NOTE(Jim0_o 2019-02-18): Waiting for 4Coder to mark buffer dirty..  100x20 == 2 second max sleep.
   while(BackupOutCounter < 20){ 
      _sleep(100);
      BackupOutCounter++; // LOG_DATA("BOC[%i]", BackupOutCounter); 
      Buffer_Summary FreshBuffer = get_buffer(app, view.buffer_id, AccessOpen);
      Buffer = &FreshBuffer;
      if(Buffer->dirty){ break; }
   } 
   // HACK(Jim0_o 2019-02-18): Trying to workaround the still dirty thing.. (Hack inside a hack... such hackey.)
   _sleep(100);
   reopen_buffer(app, Buffer, AccessOpen);
} 


I just loop for up to 2 seconds waiting (This hack does freezes 4Coder tho.) for the buffer to be be marked "dirty" (that means 4Coder has seen the file be changed outside of 4Coder). Tho I found that the file/buffer sometimes gets made dirty again after the reload... so I'm now thinking the changes are STILL being made when the buffer gets reloaded.

But yeah I asked Allen on the discord:
"Allen4th: @Jim0_o no there is no built in support for notifications from new child process output or completion."

So I guess the next step could be to use the libraries and calling the functions and updating the buffers manually.

I had been putting off adding clang-tools support into my customization, so manually updating a buffer with ctrl+shift+'O' is good enough for me for now.
I've implemented your code and noticed that the buffer->dirty flag never seems to be set within the while loop, at least on my machine, not matter how long you wait. So even when waiting for 10 seconds the 'break' statement within the 'if(buffer->dirty)' is never hit. However, the flag seems to be set if the command is called again and you check the buffer before anything within that command runs again. I guess this means 4coder doesn't update the dirty flag until after you leave the scope of your command? If this is the case then it seems that ya, you have to run clang-format as a separate command and then save afterward (If you want 4coder to remove the exclamation the top indicating a modified buffer).
Hmm you might be right

I assumed 4Coder was doing the filesystem/buffer updates on a different thread, otherwise the updating of the Buffer_Summary and checking of the dirty flag would both be getting the same values/data for both the Buffer_Summary and the "dirty" value.

So yeah that might have been just a complicated way of waiting 2 seconds.
There isn't going to be any way of writing code like:

1. Save file
2. Kick off clang-format
3. After clang-format finishes:
4. Reload

At least... not in the way you're thinking about it. What you could do is simulate this behavior by:

1. " same as above "
2. " same as above "
3. Insert a node into a linked list that stores this Buffer_ID

... elsewhere ...

On a regular interval check all buffers in the linked list to see if they now have a dirty flag, if they do, reload them and remove them from the list. To achieve this "regular interval" effect you could attach the activiting to an event like key_animate. Or do what someone else did and create a special thread that regularly simulates a key press by sending a message to the 4coder window, and then tying the "check the buffers in the list" command to the key that gets a simulated press. For instance you could allocate away your F12 key for this purpose.

Hope that helps.


Probably not the very next build, but soon I will be upgrading the entire event handling language and infrastructure of the 4coder custom layer, and then you will be able to do this more the way you are thinking about.
Oh, cool. My customization doesn't have/use a "state"/update-loop-point yet so I will probably just wait.
Awesome, thanks for the suggestion Allen!