ascii-chat 0.11.33
Video chat in your terminal
Loading...
Searching...
No Matches
zsh.c File Reference

Zsh shell completion script generator with category grouping. More...

Go to the source code of this file.

Functions

 __attribute__ ((unused))
 
asciichat_error_t completions_generate_zsh (FILE *output)
 Generate zsh completion script.
 

Detailed Description

Zsh shell completion script generator with category grouping.

Definition in file zsh.c.

Function Documentation

◆ __attribute__()

__attribute__ ( (unused)  )

Write options grouped by category using _describe for proper group headers

Definition at line 172 of file zsh.c.

173 {
174 if (!opts || count == 0)
175 return;
176
177 size_t group_count = 0;
178 const char **groups = zsh_collect_groups(opts, count, &group_count);
179
180 // First pass: declare arrays for each group
181 for (size_t g = 0; g < group_count; g++) {
182 const char *group = groups[g];
183 fprintf(output, " local -a %s_%s_opts=(\n", func_prefix, group);
184
185 // Write all options in this group (long options only to avoid "corrections" duplicates)
186 for (size_t i = 0; i < count; i++) {
187 if (!opts[i].group || strcmp(opts[i].group, group) != 0)
188 continue;
189
190 // Long option only (short options are less discoverable via TAB)
191 fprintf(output, " '--%s:", opts[i].long_name);
192 zsh_escape_desc(output, opts[i].help_text);
193 fprintf(output, "'\n");
194 }
195
196 fprintf(output, " )\n");
197 }
198
199 // Second pass: call _describe for each group (with lowercase display)
200 for (size_t g = 0; g < group_count; g++) {
201 const char *group = groups[g];
202 fprintf(output, " _describe -t %s '", group);
203 utf8_write_lowercase(output, group);
204 fprintf(output, " options' %s_%s_opts\n", func_prefix, group);
205 }
206
207 SAFE_FREE(groups);
208}
#define SAFE_FREE(ptr)
Definition common.h:376
void utf8_write_lowercase(FILE *output, const char *text)
Write UTF-8 string to file with ASCII lowercase conversion.
Definition utf8.c:360

References SAFE_FREE, and utf8_write_lowercase().

◆ completions_generate_zsh()

asciichat_error_t completions_generate_zsh ( FILE *  output)

Generate zsh completion script.

Definition at line 210 of file zsh.c.

210 {
211 if (!output) {
212 return SET_ERRNO(ERROR_INVALID_PARAM, "Output stream cannot be NULL");
213 }
214
215 /* Load binary-level and discovery options for binary-level completion.
216 * Binary-level completion (options before mode name like --color-mode) uses
217 * _arguments with -n -S flags to establish proper zsh completion context.
218 * Without _arguments, completion functions fail with "can only be called from
219 * completion function" error, causing zsh to display "corrections (errors: N)"
220 * messages. The -n flag disables defaults and -S disables short option
221 * processing, allowing safe context establishment. */
222 size_t binary_count = 0;
223 const option_descriptor_t *binary_opts = options_registry_get_for_display(MODE_DISCOVERY, true, &binary_count);
224
225 size_t discovery_count = 0;
226 const option_descriptor_t *discovery_opts = options_registry_get_for_display(MODE_DISCOVERY, false, &discovery_count);
227
228 fprintf(output, "# Zsh completion script for ascii-chat\n"
229 "# Generated from options registry - DO NOT EDIT MANUALLY\n"
230 "\n"
231 "# Configure completion for ascii-chat\n"
232 "# Use only _complete (no corrections or approximate matching)\n"
233 "zstyle ':completion:*:ascii-chat:*' completer _complete\n"
234 "\n"
235 "# Device index completion helpers\n"
236 "_ascii_chat_webcam_indices() {\n"
237 " local -a indices\n"
238 " local output\n"
239 " output=$(\"${words[1]}\" --list-webcams 2>/dev/null) || return\n"
240 " while IFS= read -r line; do\n"
241 " [[ \"$line\" =~ ^[[:space:]]*([0-9]+)[[:space:]]+(.+)$ ]] && \\\n"
242 " indices+=(\"${match[1]}:${match[2]}\")\n"
243 " done <<< \"$output\"\n"
244 " _describe 'webcam' indices\n"
245 "}\n"
246 "\n"
247 "_ascii_chat_microphone_indices() {\n"
248 " local -a indices\n"
249 " local output\n"
250 " output=$(\"${words[1]}\" --list-microphones 2>/dev/null) || return\n"
251 " while IFS= read -r line; do\n"
252 " [[ \"$line\" =~ ^[[:space:]]*([0-9-]+)[[:space:]]+(.+)$ ]] && \\\n"
253 " indices+=(\"${match[1]}:${match[2]}\")\n"
254 " done <<< \"$output\"\n"
255 " _describe 'microphone' indices\n"
256 "}\n"
257 "\n"
258 "_ascii_chat_speakers_indices() {\n"
259 " local -a indices\n"
260 " local output\n"
261 " output=$(\"${words[1]}\" --list-speakers 2>/dev/null) || return\n"
262 " while IFS= read -r line; do\n"
263 " [[ \"$line\" =~ ^[[:space:]]*([0-9-]+)[[:space:]]+(.+)$ ]] && \\\n"
264 " indices+=(\"${match[1]}:${match[2]}\")\n"
265 " done <<< \"$output\"\n"
266 " _describe 'speakers' indices\n"
267 "}\n"
268 "\n"
269 "_ascii_chat_binary() {\n"
270 " local -a binary_opts=(\n");
271
272 /* Add discovery options to binary-level completion */
273 if (discovery_opts) {
274 for (size_t i = 0; i < discovery_count; i++) {
275 fprintf(output, " '--%s:", discovery_opts[i].long_name);
276 zsh_escape_desc(output, discovery_opts[i].help_text);
277 fprintf(output, "'\n");
278 }
279 }
280
281 fprintf(output, " )\n"
282 " _values 'option' \"${binary_opts[@]}\"\n"
283 "}\n"
284 "\n"
285 "_ascii_chat_binary_grouped() {\n"
286 " local curcontext=$curcontext\n"
287 " local -a context line state state_descr args\n"
288 " local -A opt_args\n\n");
289
290 /* Write binary-level options in grouped format */
291 if (binary_opts) {
292 zsh_write_combined_args(output, binary_opts, binary_count);
293 }
294
295 fprintf(output, "}\n"
296 "\n"
297 "_ascii_chat() {\n"
298 " if [[ ${words[2]} == -* ]]; then\n"
299 " # Binary-level options: show both binary and discovery mode options\n"
300 " # (since discovery is the default mode that runs at binary level)\n"
301 " _ascii_chat_binary_grouped\n"
302 " _ascii_chat_discovery\n"
303 " return\n"
304 " fi\n"
305 "\n"
306 " # Mode routing: check words[2] to determine which mode function to call\n"
307 " case $words[2] in\n"
308 " # Server-like modes\n"
309 " server)\n"
310 " _ascii_chat_server\n"
311 " return\n"
312 " ;;\n"
313 " discovery-service)\n"
314 " _ascii_chat_discovery_service\n"
315 " return\n"
316 " ;;\n"
317 "\n"
318 " # Client-like modes\n"
319 " client)\n"
320 " _ascii_chat_client\n"
321 " return\n"
322 " ;;\n"
323 " mirror)\n"
324 " _ascii_chat_mirror\n"
325 " return\n"
326 " ;;\n"
327 " # Default/unknown input - show mode selection only if truly empty, otherwise discovery\n"
328 " *)\n"
329 " # If words[2] is empty AND we're at position 2, show mode selection\n"
330 " # Otherwise (even for partial input like 'disc'), use discovery mode (the default)\n"
331 " if [[ -z \"${words[2]}\" && $CURRENT -eq 2 ]]; then\n"
332 " # Completing the mode name itself - show available modes\n"
333 " local -a server_modes client_modes\n"
334 " server_modes=(\n");
335
336 /* Generate server-like modes from registry */
337 size_t mode_server_count = 0;
338 const mode_descriptor_t *mode_server_descs = get_modes_by_group("server-like", &mode_server_count);
339 if (mode_server_descs) {
340 for (size_t i = 0; i < mode_server_count; i++) {
341 fprintf(output, " '%s:%s'\n", mode_server_descs[i].name, mode_server_descs[i].description);
342 }
343 SAFE_FREE(mode_server_descs);
344 }
345
346 fprintf(output, " )\n"
347 " client_modes=(\n");
348
349 /* Generate client-like modes from registry (exclude discovery since it's the default mode) */
350 size_t mode_client_count = 0;
351 const mode_descriptor_t *mode_client_descs = get_modes_by_group("client-like", &mode_client_count);
352 if (mode_client_descs) {
353 for (size_t i = 0; i < mode_client_count; i++) {
354 /* Skip discovery mode - it's the default when no mode is specified */
355 if (strcmp(mode_client_descs[i].name, "discovery") == 0) {
356 continue;
357 }
358 fprintf(output, " '%s:%s'\n", mode_client_descs[i].name, mode_client_descs[i].description);
359 }
360 SAFE_FREE(mode_client_descs);
361 }
362
363 fprintf(output, " )\n"
364 " _describe -t server-modes 'server-like modes' server_modes\n"
365 " _describe -t client-modes 'client-like modes' client_modes\n"
366 " else\n"
367 " # Default mode (discovery) - complete its options\n"
368 " _ascii_chat_discovery\n"
369 " fi\n"
370 " ;;\n"
371 " esac\n"
372 "}\n"
373 "\n"
374 "# Server-like modes: handle incoming connections and stream management\n"
375 "_ascii_chat_server() {\n"
376 " local curcontext=$curcontext\n"
377 " local -a context line state state_descr args\n"
378 " local -A opt_args\n\n");
379
380 /* Server options - grouped by category */
381 size_t server_count = 0;
382 const option_descriptor_t *server_opts = options_registry_get_for_display(MODE_SERVER, false, &server_count);
383
384 if (server_opts) {
385 zsh_write_combined_args(output, server_opts, server_count);
386 SAFE_FREE(server_opts);
387 }
388
389 fprintf(output, "}\n"
390 "\n"
391 "_ascii_chat_discovery_service() {\n"
392 " local curcontext=$curcontext\n"
393 " local -a context line state state_descr args\n"
394 " local -A opt_args\n\n");
395
396 /* Discovery-service options - grouped by category */
397 size_t discovery_svc_count = 0;
398 const option_descriptor_t *discovery_svc_opts =
399 options_registry_get_for_display(MODE_DISCOVERY_SERVICE, false, &discovery_svc_count);
400
401 if (discovery_svc_opts) {
402 zsh_write_combined_args(output, discovery_svc_opts, discovery_svc_count);
403 SAFE_FREE(discovery_svc_opts);
404 }
405
406 fprintf(output, "}\n"
407 "\n"
408 "# Client-like modes: connect to servers or render local media\n"
409 "_ascii_chat_client() {\n"
410 " local curcontext=$curcontext\n"
411 " local -a context line state state_descr args\n"
412 " local -A opt_args\n\n");
413
414 /* Client options - grouped by category */
415 size_t client_count = 0;
416 const option_descriptor_t *client_opts = options_registry_get_for_display(MODE_CLIENT, false, &client_count);
417
418 if (client_opts) {
419 zsh_write_combined_args(output, client_opts, client_count);
420 SAFE_FREE(client_opts);
421 }
422
423 fprintf(output, "}\n"
424 "\n"
425 "_ascii_chat_mirror() {\n"
426 " local curcontext=$curcontext\n"
427 " local -a context line state state_descr args\n"
428 " local -A opt_args\n\n");
429
430 /* Mirror options - grouped by category */
431 size_t mirror_count = 0;
432 const option_descriptor_t *mirror_opts = options_registry_get_for_display(MODE_MIRROR, false, &mirror_count);
433
434 if (mirror_opts) {
435 zsh_write_combined_args(output, mirror_opts, mirror_count);
436 SAFE_FREE(mirror_opts);
437 }
438
439 fprintf(output, "}\n"
440 "\n"
441 "# Discovery mode: find sessions via discovery service (default mode when no mode specified)\n"
442 "_ascii_chat_discovery() {\n"
443 " local curcontext=$curcontext\n"
444 " local -a context line state state_descr args\n"
445 " local -A opt_args\n\n");
446
447 /* Discovery options - grouped by category (reuse already-loaded options) */
448 if (discovery_opts) {
449 zsh_write_combined_args(output, discovery_opts, discovery_count);
450 }
451
452 fprintf(output, "}\n"
453 "\n"
454 "# Register completion functions (must come after definitions)\n"
455 "compdef _ascii_chat ascii-chat\n"
456 "compdef _ascii_chat 'build/bin/ascii-chat'\n");
457
458 SAFE_FREE(binary_opts);
459 SAFE_FREE(discovery_opts);
460
461 return ASCIICHAT_OK;
462}
#define SET_ERRNO(code, context_msg,...)
Set error code with custom context message and log it, returning the error code.
@ ASCIICHAT_OK
Definition error_codes.h:51
@ ERROR_INVALID_PARAM
@ MODE_CLIENT
Client mode - network client options.
@ MODE_DISCOVERY_SERVICE
Discovery server mode - session management and WebRTC signaling.
@ MODE_SERVER
Server mode - network server options.
@ MODE_MIRROR
Mirror mode - local webcam viewing (no network)
@ MODE_DISCOVERY
Discovery mode - participant that can dynamically become host.
const mode_descriptor_t * get_modes_by_group(const char *group, size_t *out_count)
Get all mode descriptors grouped by category.
const option_descriptor_t * options_registry_get_for_display(asciichat_mode_t mode, bool for_binary_help, size_t *num_options)
Get options for help/completions display with unified filtering.
Definition public_api.c:324
Metadata for a mode.
Definition main.c:366
Option descriptor.
Definition builder.h:241

References ASCIICHAT_OK, ERROR_INVALID_PARAM, get_modes_by_group(), MODE_CLIENT, MODE_DISCOVERY, MODE_DISCOVERY_SERVICE, MODE_MIRROR, MODE_SERVER, options_registry_get_for_display(), SAFE_FREE, and SET_ERRNO.

Referenced by completions_generate_for_shell().