Skip to content

Commit a520025

Browse files
committed
First version of matchers for completion
A matcher is simply a function that matches needle to a completion item and retruns where it matches and a score for the item. Currently, filter_fuzzy is just an alias for filter_contains.
1 parent 34c9207 commit a520025

14 files changed

Lines changed: 306 additions & 134 deletions

File tree

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ SOURCES = src/dged/binding.c src/dged/buffer.c src/dged/command.c src/dged/displ
4545

4646
MAIN_SOURCES = src/main/main.c src/main/cmds.c src/main/bindings.c src/main/search-replace.c src/main/completion.c \
4747
src/main/frame-hooks.c src/main/completion/buffer.c src/main/completion/command.c \
48-
src/main/completion/path.c src/main/dired.c
48+
src/main/completion/path.c src/main/dired.c src/main/completion/matchers.c
4949

5050
# HACK: added to MAIN_SOURCES to not be picked up in tests
5151
# since they have their own implementation

src/dged/display.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -209,8 +209,10 @@ bool display_initialize(struct display *display) {
209209

210210
display->term = term;
211211

212-
use_alternate_buffer(display);
213-
flush_outbuf(display);
212+
if (getenv("DGED_FORCE_NORMAL_BUFFER") == NULL) {
213+
use_alternate_buffer(display);
214+
flush_outbuf(display);
215+
}
214216

215217
return true;
216218
}

src/dged/s8.c

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,54 @@ char *s8tocstr(struct s8 s) {
167167

168168
const char *s8ascstr(struct s8 s) { return (const char *)s.s; }
169169

170+
ssize_t s8findstr(struct s8 s, struct s8 find) {
171+
if (s8empty(s) || s8empty(find)) {
172+
return -1;
173+
}
174+
175+
if (s.l < find.l) {
176+
return -1;
177+
}
178+
179+
if (s.l == find.l) {
180+
return s8eq(s, find) ? 0 : -1;
181+
}
182+
183+
/* at this point, s is longer than find */
184+
for (size_t i = 0; i < s.l; ++i) {
185+
uint8_t *start = &s.s[i];
186+
if (s8startswith((struct s8){.s = start, .l = s.l - i}, find)) {
187+
return i;
188+
}
189+
}
190+
191+
return -1;
192+
}
193+
194+
ssize_t s8ifindstr(struct s8 s, struct s8 find) {
195+
if (s8empty(s) || s8empty(find)) {
196+
return -1;
197+
}
198+
199+
if (s.l < find.l) {
200+
return -1;
201+
}
202+
203+
if (s.l == find.l) {
204+
return s8icmp(s, find) == 0 ? 0 : -1;
205+
}
206+
207+
/* at this point, s is longer than find */
208+
for (size_t i = 0; i < s.l; ++i) {
209+
uint8_t *start = &s.s[i];
210+
if (s8istartswith((struct s8){.s = start, .l = s.l - i}, find)) {
211+
return i;
212+
}
213+
}
214+
215+
return -1;
216+
}
217+
170218
bool s8startswith(struct s8 s, struct s8 prefix) {
171219
if (prefix.l == 0 || prefix.l > s.l) {
172220
return false;
@@ -175,6 +223,14 @@ bool s8startswith(struct s8 s, struct s8 prefix) {
175223
return memcmp(s.s, prefix.s, prefix.l) == 0;
176224
}
177225

226+
bool s8istartswith(struct s8 s, struct s8 prefix) {
227+
if (prefix.l == 0 || prefix.l > s.l) {
228+
return false;
229+
}
230+
231+
return memicmp(s.s, prefix.s, prefix.l) == 0;
232+
}
233+
178234
bool s8endswith(struct s8 s, struct s8 suffix) {
179235
if (suffix.l > s.l) {
180236
return false;
@@ -184,6 +240,19 @@ bool s8endswith(struct s8 s, struct s8 suffix) {
184240
return memcmp(s.s + ldiff, suffix.s, suffix.l) == 0;
185241
}
186242

243+
bool s8iendswith(struct s8 s, struct s8 suffix) {
244+
if (suffix.l > s.l) {
245+
return false;
246+
}
247+
248+
size_t ldiff = s.l - suffix.l;
249+
struct s8 tail = {
250+
.s = s.s + ldiff,
251+
.l = s.l - ldiff,
252+
};
253+
return s8icmp(tail, suffix) == 0;
254+
}
255+
187256
struct s8 s8dup(struct s8 s) {
188257
struct s8 new = {0};
189258

src/dged/s8.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,15 @@ const char *s8ascstr(struct s8 s);
2424

2525
uint8_t s8at(struct s8 s, size_t index);
2626
ssize_t s8find(struct s8 s, uint8_t c);
27+
ssize_t s8findstr(struct s8 s, struct s8 find);
28+
ssize_t s8ifindstr(struct s8 s, struct s8 find);
2729
bool s8eq(struct s8 s1, struct s8 s2);
2830
int s8cmp(struct s8 s1, struct s8 s2);
2931
int s8icmp(struct s8 s1, struct s8 s2);
3032
bool s8startswith(struct s8 s, struct s8 prefix);
33+
bool s8istartswith(struct s8 s, struct s8 prefix);
3134
bool s8endswith(struct s8 s, struct s8 suffix);
35+
bool s8iendswith(struct s8 s, struct s8 prefix);
3236
struct s8 s8dup(struct s8 s);
3337
bool s8empty(struct s8 s);
3438
bool s8onlyws(struct s8 s);

src/dged/window.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,9 @@
22
#define _WINDOW_H
33

44
#include <stdbool.h>
5+
#include <stddef.h>
56
#include <stdint.h>
67

7-
#include "btree.h"
8-
98
struct command_list;
109
struct display;
1110
struct keymap;

src/main/cmds.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ int32_t unimplemented_command(struct command_ctx ctx, int argc,
5656
const char *argv[]) {
5757
(void)argc;
5858
(void)argv;
59+
5960
minibuffer_echo("TODO: %s is not implemented", (const char *)ctx.userdata);
6061
return 0;
6162
}

src/main/completion.c

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -200,11 +200,6 @@ static void clear_completions(struct completion_state *state) {
200200
}
201201

202202
VEC_CLEAR(&state->completions);
203-
204-
if (completion_active()) {
205-
buffer_view_goto(window_buffer_view(popup_window()),
206-
(struct location){0, 0});
207-
}
208203
}
209204

210205
static void update_window_position(struct completion_state *state) {
@@ -244,6 +239,12 @@ static void update_window_position(struct completion_state *state) {
244239
size_t available = window_width(root_wind) - xpos - 5;
245240
max_width = max_width >= available ? available : max_width;
246241

242+
// if we are opening anew, let's start on the first item
243+
if (!popup_window_visible()) {
244+
buffer_view_goto(window_buffer_view(popup_window()),
245+
(struct location){0, 0});
246+
}
247+
247248
windows_show_popup(ypos, xpos, max_width, height);
248249
}
249250

src/main/completion/buffer.c

Lines changed: 29 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,13 @@
22

33
#include <string.h>
44

5+
#include "completion/matchers.h"
56
#include "dged/buffer.h"
67
#include "dged/buffer_view.h"
78
#include "dged/buffers.h"
89
#include "dged/minibuffer.h"
910

11+
#include "dged/vec.h"
1012
#include "main/completion.h"
1113

1214
static bool is_space(const struct codepoint *c) {
@@ -19,6 +21,9 @@ typedef void (*on_buffer_selected_cb)(struct buffer *);
1921
struct buffer_completion {
2022
struct buffer *buffer;
2123
on_buffer_selected_cb on_buffer_selected;
24+
size_t match_begin;
25+
size_t match_end;
26+
size_t score;
2227
};
2328

2429
struct buffer_provider_data {
@@ -52,31 +57,35 @@ static void buffer_comp_cleanup(void *data) {
5257
free(bc);
5358
}
5459

55-
struct needle_match_ctx {
56-
const char *needle;
57-
struct completion *completions;
58-
uint32_t max_ncompletions;
59-
uint32_t ncompletions;
60+
typedef VEC(struct completion) completion_vec;
61+
62+
struct buffer_completions {
63+
completion_vec *completions;
6064
on_buffer_selected_cb on_buffer_selected;
65+
struct s8 needle;
6166
};
6267

63-
static void buffer_matches(struct buffer *buffer, void *userdata) {
64-
struct needle_match_ctx *ctx = (struct needle_match_ctx *)userdata;
65-
66-
if (strncmp(ctx->needle, buffer->name, strlen(ctx->needle)) == 0 &&
67-
ctx->ncompletions < ctx->max_ncompletions) {
68+
static void fill_buffer_completions(struct buffer *buffer, void *userdata) {
69+
struct buffer_completions *ctx = (struct buffer_completions *)userdata;
6870

71+
size_t match_begin, match_end;
72+
uint32_t score;
73+
if (filter_contains(ctx->needle, s8(buffer->name), &match_begin, &match_end,
74+
&score)) {
6975
struct buffer_completion *comp_data =
7076
calloc(1, sizeof(struct buffer_completion));
7177
comp_data->buffer = buffer;
7278
comp_data->on_buffer_selected = ctx->on_buffer_selected;
73-
ctx->completions[ctx->ncompletions] = (struct completion){
79+
comp_data->match_begin = match_begin;
80+
comp_data->match_end = match_end;
81+
82+
struct completion comp = (struct completion){
7483
.render = buffer_comp_render,
7584
.selected = buffer_comp_selected,
7685
.cleanup = buffer_comp_cleanup,
7786
.data = comp_data,
7887
};
79-
++ctx->ncompletions;
88+
VEC_PUSH(ctx->completions, comp);
8089
}
8190
}
8291

@@ -110,20 +119,18 @@ static void buffer_complete(struct completion_context ctx, bool deletion,
110119
free(txt.text);
111120
}
112121

113-
struct completion *completions = calloc(50, sizeof(struct completion));
114-
115-
struct needle_match_ctx match_ctx = (struct needle_match_ctx){
116-
.needle = needle,
117-
.max_ncompletions = 50,
118-
.completions = completions,
119-
.ncompletions = 0,
122+
completion_vec completions;
123+
VEC_INIT(&completions, 32);
124+
struct buffer_completions match_ctx = (struct buffer_completions){
125+
.completions = &completions,
120126
.on_buffer_selected = pd->on_buffer_selected,
127+
.needle = s8(needle),
121128
};
122129

123-
buffers_for_each(buffers, buffer_matches, &match_ctx);
124-
ctx.add_completions(match_ctx.completions, match_ctx.ncompletions);
125-
free(completions);
130+
buffers_for_each(buffers, fill_buffer_completions, &match_ctx);
126131
free(needle);
132+
ctx.add_completions(VEC_ENTRIES(&completions), VEC_SIZE(&completions));
133+
VEC_DESTROY(&completions);
127134
}
128135

129136
static void cleanup_provider(void *data) {

src/main/completion/command.c

Lines changed: 28 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,14 @@
33
#include <stdbool.h>
44
#include <string.h>
55

6+
#include "completion/matchers.h"
67
#include "dged/buffer.h"
78
#include "dged/buffer_view.h"
89
#include "dged/command.h"
910
#include "dged/minibuffer.h"
1011
#include "dged/utf8.h"
1112

13+
#include "dged/vec.h"
1214
#include "main/completion.h"
1315

1416
static bool is_space(const struct codepoint *c) {
@@ -21,6 +23,8 @@ typedef void (*on_command_selected_cb)(struct command *);
2123
struct command_completion {
2224
struct command *command;
2325
on_command_selected_cb on_command_selected;
26+
size_t match_begin;
27+
size_t match_end;
2428
};
2529

2630
struct command_provider_data {
@@ -55,31 +59,35 @@ static void command_comp_cleanup(void *data) {
5559
free(cc);
5660
}
5761

58-
struct needle_match_ctx {
59-
const char *needle;
60-
struct completion *completions;
61-
uint32_t max_ncompletions;
62-
uint32_t ncompletions;
62+
typedef VEC(struct completion) completion_vec;
63+
64+
struct buffer_completions {
65+
completion_vec *completions;
6366
on_command_selected_cb on_command_selected;
67+
struct s8 needle;
6468
};
6569

66-
static void command_matches(struct command *command, void *userdata) {
67-
struct needle_match_ctx *ctx = (struct needle_match_ctx *)userdata;
68-
69-
if (strncmp(ctx->needle, command->name, strlen(ctx->needle)) == 0 &&
70-
ctx->ncompletions < ctx->max_ncompletions) {
70+
static void fill_commands(struct command *command, void *userdata) {
71+
struct buffer_completions *ctx = (struct buffer_completions *)userdata;
7172

73+
size_t match_begin, match_end;
74+
uint32_t score;
75+
if (filter_contains(ctx->needle, s8(command->name), &match_begin, &match_end,
76+
&score)) {
7277
struct command_completion *comp_data =
7378
calloc(1, sizeof(struct command_completion));
7479
comp_data->command = command;
7580
comp_data->on_command_selected = ctx->on_command_selected;
76-
ctx->completions[ctx->ncompletions] = (struct completion){
81+
comp_data->match_begin = match_begin;
82+
comp_data->match_end = match_end;
83+
84+
struct completion comp = (struct completion){
7785
.render = command_comp_render,
7886
.selected = command_comp_selected,
7987
.cleanup = command_comp_cleanup,
8088
.data = comp_data,
8189
};
82-
++ctx->ncompletions;
90+
VEC_PUSH(ctx->completions, comp);
8391
}
8492
}
8593

@@ -113,19 +121,18 @@ static void command_complete(struct completion_context ctx, bool deletion,
113121
free(txt.text);
114122
}
115123

116-
struct completion *completions = calloc(50, sizeof(struct completion));
124+
completion_vec completions;
125+
VEC_INIT(&completions, 32);
117126

118-
struct needle_match_ctx match_ctx = (struct needle_match_ctx){
119-
.needle = needle,
120-
.max_ncompletions = 50,
121-
.completions = completions,
122-
.ncompletions = 0,
127+
struct buffer_completions match_ctx = (struct buffer_completions){
128+
.completions = &completions,
123129
.on_command_selected = pd->on_command_selected,
130+
.needle = s8(needle),
124131
};
125132

126-
commands_for_each(commands, command_matches, &match_ctx);
127-
ctx.add_completions(match_ctx.completions, match_ctx.ncompletions);
128-
free(completions);
133+
commands_for_each(commands, fill_commands, &match_ctx);
134+
ctx.add_completions(VEC_ENTRIES(&completions), VEC_SIZE(&completions));
135+
VEC_DESTROY(&completions);
129136
free(needle);
130137
}
131138

0 commit comments

Comments
 (0)