4coder » Forums » Useful custom functions
Lucas89
19 posts
#7379 Useful custom functions
2 years, 10 months ago Edited by Lucas89 on June 22, 2016, 2:42 p.m.

ratchetfreak
instead of moving the whole range up, why not move the line above the range down below the range (and vice versa)


I'm glad I decided to post these ideas on the forums.
I've been using C for just over a week now, so sometimes I focus too much on the language and can get stuck looking for a specific solution to a problem.

My solution works, but I like yours better so I'll get onto that after I've implemented my other required functionality :)
Mr4thDimention
Allen Webster
469 posts / 2 projects

Heyo

#7380 Useful custom functions
2 years, 10 months ago

I know the docs are abysmal so here's a tip, the "refresh" calls are only necessary after you call an exec_command that changes the state of the view. So for instance lucas_delete_line could be written as:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
View_Summary view = app->get_active_view(app);

app->view_set_mark(app, &view, seek_line_char(view.cursor.line, 0));
app->view_set_cursor(app, &view, seek_line_char(view.cursor.line, 65536), 1);
//app->refresh_view(app, &view);
exec_command(app, delete_range);
exec_command(app, delete_char);
app->refresh_view(app, &view);
app->view_set_cursor(app, &view, seek_line_char(view.cursor.line, 65536), 1);
//app->refresh_view(app, &view);


Also, in the next version, as apart of my work to cut down on the API cruft, the refresh is actually gone, and replaced with this helper:

1
2
3
4
void
refresh_view(Application_Links *app, View_Summary *view){
    *view = app->get_view(app, view->view_id, AccessAll);
}


So, as you can see, it literally just does another get so that your local copy is up to date.
Lucas89
19 posts
#7381 Useful custom functions
2 years, 10 months ago Edited by Lucas89 on June 23, 2016, 12:01 a.m.

Thanks for the tip :)
I've edited my previous posts to remove useless calls to `refresh_view`.

I'm writing these things as I need them while I do my work, so I'm mostly just guessing how to use the entire API.
It's starting to make more sense to me now, but it's useful to know these things so I can tidy up my code a bit.

I'm starting to run out of things I want to add now, but here is the latest.
When I type (, { or [ I like it to automatically insert the closing ), } or ].
In addition when the cursor is on a ), } or ], and I type the same character I like it to just skip the character, so this is what I came up with:

(This uses the `seek_char` function from this post)

 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
void
write_companion_character(Application_Links *app, char companion)
{
    View_Summary view = app->get_active_view(app);

    exec_command(app, write_character);
    app->refresh_view(app, &view);

    Buffer_Summary buffer = app->get_buffer(app, view.buffer_id);
    int pos = view.cursor.pos;

    app->buffer_replace_range(app, &buffer, pos, pos, &companion, 1);
    exec_command(app, move_left);
}

void
write_or_skip_character(Application_Links *app, char character)
{
    View_Summary view = app->get_active_view(app);

    if (view.cursor.pos == seek_char(app, view.cursor.pos, character, 1))
    {
        exec_command(app, move_right);
    }
    else
    {
        exec_command(app, write_character);
    }
}

CUSTOM_COMMAND_SIG(lucas_write_bracket_pair)
{
    write_companion_character(app, ')');
}

CUSTOM_COMMAND_SIG(lucas_write_brace_pair)
{
    write_companion_character(app, '}');
}

CUSTOM_COMMAND_SIG(lucas_write_square_pair)
{
    write_companion_character(app, ']');
}

CUSTOM_COMMAND_SIG(lucas_write_close_bracket)
{
    write_or_skip_character(app, ')');
}

CUSTOM_COMMAND_SIG(lucas_write_close_brace)
{
    write_or_skip_character(app, '}');
}

CUSTOM_COMMAND_SIG(lucas_write_close_square)
{
    write_or_skip_character(app, ']');
}

bind(context, '(', MDFR_NONE, lucas_write_bracket_pair);
bind(context, '{', MDFR_NONE, lucas_write_brace_pair);
bind(context, '[', MDFR_NONE, lucas_write_square_pair);

bind(context, ')', MDFR_NONE, lucas_write_close_bracket);
bind(context, '}', MDFR_NONE, lucas_write_close_brace);
bind(context, ']', MDFR_NONE, lucas_write_close_square);


Is there a built in function to get the character at the cursor, or next to the cursor etc?

As you can see, right now I use my own `seek_char` function a lot to compare positions, but being able to do something like `char_at_cursor(view.cursor)` or `char_after_cursor(view.cursor)` would be really useful.
Mr4thDimention
Allen Webster
469 posts / 2 projects

Heyo

#7385 Useful custom functions
2 years, 10 months ago

Are you familiar with the function "buffer_read_range"? It lets you read out text from an arbitrary range within the buffer. So to get the character at the cursor you could do:

1
2
3
4
5
6
static char
get_char(Application_Links *app, Buffer_Summary *buffer, int pos){
    char result = 0;
    app->buffer_read_range(app, buffer, pos, pos+1, &result);
    return(result);
}
Lucas89
19 posts
#7387 Useful custom functions
2 years, 10 months ago

Mr4thDimention
Are you familiar with the function "buffer_read_range"? It lets you read out text from an arbitrary range within the buffer.


I didn't see that one. When I had a look through the header files I think I was focusing on words like 'seek' and 'view' so even if I did see it I must have just skipped over it without thinking.

Thanks for letting me know about it!
I can go back and replace some of my own functions with a call to `buffer_read_range` now :)
Yam
James Fulop
21 posts / 1 project

None

#7402 Toggle between a period and arrow
2 years, 9 months ago Edited by James Fulop on June 26, 2016, 8:11 a.m.

Here's one I use all the time, just switches a period to an arrow or vice versa.

 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
CUSTOM_COMMAND_SIG(james_toggle_period_arrow)
{
    String cursor_character;//character under the cursor
    Buffer_Summary buffer;
    View_Summary view;

    view = app->get_active_view(app, AccessOpen);
    buffer = app->get_buffer(app, view.buffer_id, AccessAll);

    cursor_character.str = (char*)app->memory;
    cursor_character.size = 1;

    app->buffer_read_range(app, &buffer, view.cursor.pos, view.cursor.pos + 1, cursor_character.str);

    if (cursor_character.str[0] == '.')
    {
        app->buffer_replace_range(app, &buffer, view.cursor.pos, view.cursor.pos + 1, "->", 2);
    }
    else if (cursor_character.str[0] == '-')
    {
        app->buffer_replace_range(app, &buffer, view.cursor.pos, view.cursor.pos + 2, ".", 1);
    }
    else if (cursor_character.str[0] == '>')
    {
        app->buffer_replace_range(app, &buffer, view.cursor.pos - 1, view.cursor.pos + 1, ".", 1);
    }
}



None
ratchetfreak
441 posts
#7404 Useful custom functions
2 years, 9 months ago

In name of robustness I'd have read a char on either side of the cursor and made sure there was an arrow before replacing.

If buffer read range doesn't crash on out of bounds read it's pretty simple. otherwise you'd need to adjust the call based on whether the cursor is at the start or end of the buffer.
Mr4thDimention
Allen Webster
469 posts / 2 projects

Heyo

#7412 Useful custom functions
2 years, 9 months ago

If you do an out of range read, the read will fail. The fail is indicated as a zero return value from buffer_read_range.
MandleBro
Jack Mott
112 posts / 1 project

Web Developer by day, game hobbyist by night.

#7432 Useful custom functions
2 years, 9 months ago

Would it be possible to make a custom function to pop to the matching header file when in a C/cpp file, and vice versa?
The idea would be a recursive search through the folder and any subfolders looking for a .h/.cpp with the same name. This is really handy in visual studio.

If the necessary things are exposed to make that happen I'll have a go at it.
Mr4thDimention
Allen Webster
469 posts / 2 projects

Heyo

#7438 Useful custom functions
2 years, 9 months ago

Yes this is definitely possible.

You can get the buffer's file name. You can then use the string library to make a copy with the changed extension, or you can do that by hand obviously. Then you can use the app->directory_cd function or the app->get_file_list/app->free_file_list functions to explore the file system. Finally you can use app->create_buffer or the helper view_open_file to open the file.
Marchin
6 posts
#17039 Useful custom functions
3 months, 3 weeks ago

Hi, i'm new to 4coder and i wanted to create the "newline_above/below" functions like Lucas has. I understand the API has changed since then. I read the documentation to try updating it but i failed. Here is what i currently have

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
CUSTOM_COMMAND_SIG(insert_newline_above)
{
    View_Summary view = app->get_active_view_(app, AccessAll);
    exec_command(app, move_up);
    refresh_view(app, &view);
    exec_command(app, seek_end_of_line);
    refresh_view(app, &view);
    exec_command(app, write_and_auto_tab);
    refresh_view(app, &view);
}

CUSTOM_COMMAND_SIG(insert_newline_below)
{
    View_Summary view = app->get_active_view_(app, AccessAll);
    exec_command(app, seek_end_of_line);
    refresh_view(app, &view);
    exec_command(app, write_and_auto_tab);
    refresh_view(app, &view);
}
mrmixer
Simon Anciaux
580 posts
#17042 Useful custom functions
3 months, 3 weeks ago

write_and_auto_tab insert the character used to call the command as text. Unless you bind the command to '\n' it's not going to create a new line. You can use write_string to write a new line character instead. And since you don't use the View_Summary struct in the function there is no need to create and refresh it.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
CUSTOM_COMMAND_SIG(insert_newline_above)
CUSTOM_DOC( "Insert a new line above the current line." )
{
    exec_command(app, move_up);
    exec_command(app, seek_end_of_line);
    write_string(app, make_lit_string( "\n" ));
}

CUSTOM_COMMAND_SIG(insert_newline_below)
CUSTOM_DOC( "Insert a new line below the current line." )
{
    exec_command(app, seek_end_of_line);
    write_string(app, make_lit_string( "\n" ));
}
Marchin
6 posts
#17043 Useful custom functions
3 months, 3 weeks ago

Ohh i see, that works perfectly, thank you very much :).
a65596
Jackson
6 posts
#20605 Useful custom functions
1 month, 3 weeks ago Edited by Jackson on Feb. 26, 2019, 1:35 a.m.

Recently purchased 4Coder, and was a little overwhelmed due to lack of official getting started documentation and almost put it away, but this forum really helped me, I found my feet a bit, so I'll try and give back, if just a little.

My "work" productivity has gone to zero, because I've just spent so much time customising so many things in the tiny little ways I like. I've really enjoyed it and want to make this my long term editor.

Anyway, the couple of functions that might be of interest to others (disclaimer - probably buggy, made in version 4.0.30):

Automatically add include guards based on the filename, if a header file is created:

 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
OPEN_FILE_HOOK_SIG(custom_new_file_hook)
{
    Buffer_Summary buffer = get_buffer(app, buffer_id, AccessOpen);
    
    char filename[256];
    for (int i = 0; i < buffer.buffer_name_len; i++)
    {
        char c = buffer.buffer_name[i];
        if (c == '.')
            c = '_';
        c = char_to_upper(c);   
        filename[i] = c;
    }
    filename[buffer.buffer_name_len] = '\0';
    
    int filename_length = str_size(filename);
    
    if (filename[filename_length - 2] == '_' && filename[filename_length - 1] == 'H')
    {
        char out[512];
        strcpy(out, "#ifndef ");
        strcat(out, filename);
        strcat(out, "\n#define ");
        strcat(out, filename);
        strcat(out, "\n\n#endif\n");
        
        buffer_replace_range(app, &buffer, 0, 0, out, str_size(out));
    }
    
    
    return 0;
}


Display a lister of the last few items on the clipboard:

 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
void paste_clipboard_index(Application_Links* app, int clip_index)
{
    View_Summary view = get_active_view(app, AccessOpen);
    
    int32_t len = clipboard_index(app, 0, clip_index, 0, 0);
    char *str = 0;
    
    if (len <= app->memory_size)
        str = (char*)app->memory;
    
    if (str != 0)
    {
        clipboard_index(app, 0, clip_index, str, len);
        
        Buffer_Summary buffer = get_buffer(app, view.buffer_id, AccessOpen);
		int32_t pos = view.cursor.pos;
        
        buffer_replace_range(app, &buffer, pos, pos, str, len);
        view_set_cursor(app, &view, seek_pos(pos + len), true);
        
        Theme_Color paste = {};
        paste.tag = Stag_Paste;
        get_theme_colors(app, &paste, 1);
        view_post_fade(app, &view, 0.667f, pos, pos + len, paste.color);
        
        auto_tab_range(app);
    }
}

static void
activate_clipboard_lister(Application_Links *app, Partition *scratch, Heap *heap,
                          View_Summary *view, Lister_State *state,
                          String text_field, void *user_data, bool32 activated_by_mouse)
{
    lister_default(app, scratch, heap, view, state, ListerActivation_Finished);
	int clip_index = (int) PtrAsInt(user_data);
    paste_clipboard_index(app, clip_index);
}

CUSTOM_COMMAND_SIG(clipmate_lister)
{
    Partition* arena = &global_part;
    Temp_Memory temp = begin_temp_memory(arena);
    
    View_Summary view = get_active_view(app, AccessOpen);
    view_end_ui_mode(app, &view);
    
    int32_t option_count = clipboard_count(app, 0);
    if (option_count > 10)
        option_count = 10;
    
    Lister_Option* options = push_array(arena, Lister_Option, option_count);
    for (int32_t i = 0; i < option_count; i++)
    {
        int32_t contents_length = clipboard_index(app, 0, i, 0, 0);
        
		char* str_index = push_array(arena, char, 5);
		itoa(i, str_index, 10);
        
        char* clipboard_contents = push_array(arena, char, contents_length);
        clipboard_index(app, 0, i, clipboard_contents, contents_length);
        
        options[i].string = make_string(str_index, (int) strlen(str_index));
        options[i].status = make_string(clipboard_contents, contents_length);
        options[i].user_data = IntAsPtr(i);
    }
    
    begin_integrated_lister__basic_list(app, "Clipboard:", activate_clipboard_lister, 0, 0, options, option_count, 0, &view);
    
    end_temp_memory(temp);
}


Seek function/struct definition up (look for a '{' in the first column of a line):

 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
CUSTOM_COMMAND_SIG(seek_first_column_brace_up)
{
    View_Summary view = get_active_view(app, AccessOpen);
    Buffer_Summary buffer = get_buffer(app, view.buffer_id, AccessOpen);
    
    int current_line = view.cursor.line - 1;
    
    while (current_line > 1)
    {
        char read_result[1];
        
        Full_Cursor cursor = {};
        view_compute_cursor(app, &view, seek_line_char(current_line, 1), &cursor);
        buffer_read_range(app, &buffer, cursor.pos, cursor.pos + 1, read_result);
        
        if (read_result[0] == '{')
        {
            current_line--;
            break;
        }
        
        current_line--;
    }
    
    view_set_cursor(app, &view, seek_line_char(current_line, 1), 1);
}