xaizek / vifm (License: GPLv2+) (since 2018-12-07)
Vifm is a file manager with curses interface, which provides Vi[m]-like environment for managing objects within file systems, extended with some useful ideas from mutt.
Commit 3bfdf64752c09b63156bfe396b44640d9397c13b

Add global variables (with "g:" prefix)
This is to avoid using environment variables in their place as a
workaround.
Author: xaizek
Author date (UTC): 2024-11-02 16:37
Committer name: xaizek
Committer date (UTC): 2024-11-02 19:20
Parent(s): be605162f53763e67aaca6e54d447c068cd65c3c
Signing key: 99DC5E4DB05F6BE2
Tree: 72e9e2f61a31dfd6b2faaf8315ceaa413cc3bc1e
File Lines added Lines deleted
ChangeLog 3 0
data/man/vifm.1 13 3
data/vim/doc/app/vifm-app.txt 13 4
src/cmd_completion.c 2 1
src/engine/parsing.c 12 9
src/engine/variables.c 197 44
tests/parsing/suite.c 7 0
tests/parsing/variables.c 16 0
tests/test-data/syntax-highlight/syntax.vifm 1 0
tests/variables/completion.c 21 0
tests/variables/globals.c 95 0
tests/variables/unlet.c 7 0
File ChangeLog changed (mode: 100644) (index 3a15c9ec6..95145e8e9)
79 79 Added handling of Enter/Escape keys to prompts and error dialogs. Thanks Added handling of Enter/Escape keys to prompts and error dialogs. Thanks
80 80 to laur89, Alexandre Viau and multiple other users who asked about this. to laur89, Alexandre Viau and multiple other users who asked about this.
81 81
82 Added global variables (with "g:" prefix) to avoid using environment
83 variables in their place as a workaround.
84
82 85 Don't draw right padding on a truncated rightmost column of a transposed Don't draw right padding on a truncated rightmost column of a transposed
83 86 ls-like view. ls-like view.
84 87
File data/man/vifm.1 changed (mode: 100644) (index fc229e457..a29581689)
... ... dialogs" section for controls.
3206 3206 .TP .TP
3207 3207 .BI " :unlet" .BI " :unlet"
3208 3208 .TP .TP
3209 .BI ":unl[et][!] $ENV_VAR1 $ENV_VAR2 ..."
3210 remove environment variables. Add ! to omit displaying of warnings about
3211 nonexistent variables.
3209 .BI ":unl[et][!] {var}..."
3210 remove one or more environment (`$VAR`) or global (`g:global`) variables.
3211 Use "!" to omit displaying warnings about nonexistent variables.
3212 3212 .TP .TP
3213 3213 .BI " :unselect" .BI " :unselect"
3214 3214 .TP .TP
 
... ... expr7 number number constant
5832 5832 'string' string constant, ' is doubled 'string' string constant, ' is doubled
5833 5833 &option option value &option option value
5834 5834 $VAR environment variable $VAR environment variable
5835 g:var global variable
5835 5836 v:var builtin variable (read-only) v:var builtin variable (read-only)
5836 5837 function(expr1, ...) function call function(expr1, ...) function call
5837 5838 (expr1) nested expression (expr1) nested expression
 
... ... Examples:
6063 6064 'vifmrc at ' . $MYVIFMRC . ' is used.' 'vifmrc at ' . $MYVIFMRC . ' is used.'
6064 6065 .EE .EE
6065 6066
6067 global variable
6068 .br
6069 ---------------
6070 .br
6071 g:var global variable
6072
6073 A typed storage of data for use in scripting. Can be created/removed
6074 dynamically (via :let and :unlet) and used in expressions.
6075
6066 6076 builtin variable builtin variable
6067 6077 .br .br
6068 6078 ---------------- ----------------
File data/vim/doc/app/vifm-app.txt changed (mode: 100644) (index 843b3f0c8..9fd8e0fad)
... ... yet supported) and might be changed in future releases, or get an alias.
2695 2695 display list of latest changes. Use "!" to see actual commands. display list of latest changes. Use "!" to see actual commands.
2696 2696 See |vifm-menus-and-dialogs| for controls. See |vifm-menus-and-dialogs| for controls.
2697 2697
2698 :unl[et][!] $ENV_VAR1 $ENV_VAR2 ... *vifm-:unlet* *vifm-:unl*
2699 remove environment variables. Use "!" to omit displaying of warnings
2700 about nonexistent variables.
2698 :unl[et][!] {var}... *vifm-:unlet* *vifm-:unl*
2699 remove one or more environment (`$VAR`) or global (`g:global`) variables.
2700 Use "!" to omit displaying warnings about nonexistent variables.
2701 2701
2702 2702 *vifm-:unselect* *vifm-:unselect*
2703 2703 :[range]unselect :[range]unselect
 
... ... Expression syntax summary, from least to most significant:
4907 4907 'string' string constant, ' is doubled 'string' string constant, ' is doubled
4908 4908 &option option value &option option value
4909 4909 $VAR environment variable $VAR environment variable
4910 g:var global variable
4910 4911 v:var builtin variable (read-only) v:var builtin variable (read-only)
4911 4912 function(expr1, ...) function call function(expr1, ...) function call
4912 4913 (expr1) nested expression (expr1) nested expression
 
... ... Examples: >
5079 5080 'vifmrc at ' . $MYVIFMRC . ' is used.' 'vifmrc at ' . $MYVIFMRC . ' is used.'
5080 5081 < <
5081 5082
5082 builtin variable *vifm-expr-variable*
5083 *vifm-expr-variable*
5084 global variable
5085 ---------------
5086 g:var global variable
5087
5088 A typed storage of data for use in scripting. Can be created/removed
5089 dynamically (via |vifm-:let| and |vifm-:unlet|) and used in expressions.
5090
5091 builtin variable
5083 5092 ---------------- ----------------
5084 5093 v:var builtin variable (read-only) v:var builtin variable (read-only)
5085 5094
File src/cmd_completion.c changed (mode: 100644) (index 68911f9e3..f34f26c98)
... ... complete_expr(const char str[], const char **start)
515 515
516 516 vle_opts_complete_real(*start, scope); vle_opts_complete_real(*start, scope);
517 517 } }
518 else if(dollar == NULL && !starts_with_lit(str, "v:"))
518 else if(dollar == NULL && !starts_with_lit(str, "v:") &&
519 !starts_with_lit(str, "g:"))
519 520 { {
520 521 function_complete_name(str, start); function_complete_name(str, start);
521 522 } }
File src/engine/parsing.c changed (mode: 100644) (index 9c8183155..7351e3d94)
... ... static var_t parse_doubly_quoted_string(parse_context_t *ctx, const char **in);
178 178 static int parse_doubly_quoted_char(parse_context_t *ctx, const char **in, static int parse_doubly_quoted_char(parse_context_t *ctx, const char **in,
179 179 sbuffer *sbuf); sbuffer *sbuf);
180 180 static var_t eval_envvar(parse_context_t *ctx, const char **in); static var_t eval_envvar(parse_context_t *ctx, const char **in);
181 static var_t eval_builtinvar(parse_context_t *ctx, const char **in);
181 static var_t eval_var(parse_context_t *ctx, const char **in);
182 182 static var_t eval_opt(parse_context_t *ctx, const char **in); static var_t eval_opt(parse_context_t *ctx, const char **in);
183 183 static expr_t parse_logical_not(parse_context_t *ctx, const char **in); static expr_t parse_logical_not(parse_context_t *ctx, const char **in);
184 184 static int parse_sequence(parse_context_t *ctx, const char **in, static int parse_sequence(parse_context_t *ctx, const char **in,
 
... ... parse_concat_expr(parse_context_t *ctx, const char **in)
849 849 return result; return result;
850 850 } }
851 851
852 /* term ::= signed_number | number | sqstr | dqstr | envvar | builtinvar |
853 * funccall | opt | logical_not | '(' or_expr ')' */
852 /* term ::= signed_number | number | sqstr | dqstr | envvar | var | funccall |
853 * opt | logical_not | '(' or_expr ')' */
854 854 static expr_t static expr_t
855 855 parse_term(parse_context_t *ctx, const char **in) parse_term(parse_context_t *ctx, const char **in)
856 856 { {
 
... ... parse_term(parse_context_t *ctx, const char **in)
909 909 { {
910 910 if(**in == ':') if(**in == ':')
911 911 { {
912 result.value = eval_builtinvar(ctx, in);
912 result.value = eval_var(ctx, in);
913 913 } }
914 914 else else
915 915 { {
 
... ... eval_envvar(parse_context_t *ctx, const char **in)
1132 1132 return var_from_str(getenv_fu(name)); return var_from_str(getenv_fu(name));
1133 1133 } }
1134 1134
1135 /* builtinvar ::= 'v:' varname */
1135 /* var ::= 'g:' varname | 'v:' varname */
1136 1136 static var_t static var_t
1137 eval_builtinvar(parse_context_t *ctx, const char **in)
1137 eval_var(parse_context_t *ctx, const char **in)
1138 1138 { {
1139 1139 var_t var_value; var_t var_value;
1140 char name[VAR_NAME_LENGTH_MAX + 1];
1141 strcpy(name, "v:");
1142 1140
1143 if(ctx->last_token.c != 'v' || **in != ':')
1141 if(!ONE_OF(ctx->last_token.c, 'v', 'g') || **in != ':')
1144 1142 { {
1145 1143 ctx->last_error = PE_INVALID_EXPRESSION; ctx->last_error = PE_INVALID_EXPRESSION;
1146 1144 return var_false(); return var_false();
1147 1145 } }
1148 1146
1147 char name[VAR_NAME_LENGTH_MAX + 1];
1148 name[0] = ctx->last_token.c;
1149 name[1] = ':';
1150 name[2] = '\0';
1151
1149 1152 get_next(ctx, in); get_next(ctx, in);
1150 1153 get_next(ctx, in); get_next(ctx, in);
1151 1154
File src/engine/variables.c changed (mode: 100644) (index 4d0da68bc..de24b9384)
43 43 typedef enum typedef enum
44 44 { {
45 45 VT_ENVVAR, /* Environment variable. */ VT_ENVVAR, /* Environment variable. */
46 VT_GVAR, /* Global variable. */
46 47 VT_ANY_OPTION, /* Global and local options (if local exists). */ VT_ANY_OPTION, /* Global and local options (if local exists). */
47 48 VT_GLOBAL_OPTION, /* Global option. */ VT_GLOBAL_OPTION, /* Global option. */
48 49 VT_LOCAL_OPTION, /* Local option. */ VT_LOCAL_OPTION, /* Local option. */
 
... ... VariableOperation;
62 63 /* Description of a variable. */ /* Description of a variable. */
63 64 typedef struct typedef struct
64 65 { {
65 char *name; /* Name of the variable (including "v:" prefix). */
66 char *name; /* Name of the variable (including "[gv]:" prefix). */
66 67 var_t val; /* Current value of the variable. */ var_t val; /* Current value of the variable. */
67 68 } }
68 69 var_info_t; var_info_t;
 
... ... static int parse_name(const char **in, const char first[], const char other[],
91 92 size_t buf_len, char buf[]); size_t buf_len, char buf[]);
92 93 static int is_valid_op(const char name[], VariableType vt, static int is_valid_op(const char name[], VariableType vt,
93 94 VariableOperation vo); VariableOperation vo);
94 static int perform_op(const char name[], VariableType vt,
95 VariableOperation vo, const char value[]);
95 static int perform_op(const char name[], VariableType vt, VariableOperation vo,
96 var_t val, const char value[]);
96 97 static void append_envvar(const char *name, const char *val); static void append_envvar(const char *name, const char *val);
97 98 static void set_envvar(const char *name, const char *val); static void set_envvar(const char *name, const char *val);
99 static int append_internal_var(var_info_t *var, var_t val);
98 100 static int perform_opt_op(const char name[], VariableType vt, static int perform_opt_op(const char name[], VariableType vt,
99 101 VariableOperation vo, const char value[]); VariableOperation vo, const char value[]);
100 102 static envvar_t * get_record(const char *name); static envvar_t * get_record(const char *name);
 
... ... static void free_record(envvar_t *record);
104 106 static void clear_record(envvar_t *record); static void clear_record(envvar_t *record);
105 107 static void complete_envvars(const char var[], const char **start); static void complete_envvars(const char var[], const char **start);
106 108 static void complete_internal_vars(const char var[], const char **start); static void complete_internal_vars(const char var[], const char **start);
109 static var_info_t * find_internal_var(const char varname[], int create);
107 110
108 111 static int initialized; static int initialized;
109 112 static envvar_t *env_vars; static envvar_t *env_vars;
 
... ... let_variables(const char cmd[])
280 283
281 284 str_val = var_to_str(result.value); str_val = var_to_str(result.value);
282 285
283 error = perform_op(name, type, op, str_val);
284
286 error = perform_op(name, type, op, result.value, str_val);
285 287 free(str_val); free(str_val);
288
289 if(error)
290 {
291 vle_tb_append_line(vle_err, "Failed to perform an operation");
292 goto fail;
293 }
294
286 295 var_free(result.value); var_free(result.value);
287 296 return error; return error;
288 297
 
... ... extract_name(const char **in, VariableType *type, size_t buf_len, char buf[])
306 315 buf_len, buf) != 0); buf_len, buf) != 0);
307 316 *type = VT_ENVVAR; *type = VT_ENVVAR;
308 317 } }
318 else if((*in)[0] == 'g' && (*in)[1] == ':')
319 {
320 *in += 2;
321 copy_str(buf, buf_len, "g:");
322 error = (parse_name(in, ENV_VAR_NAME_FIRST_CHAR, ENV_VAR_NAME_CHARS,
323 buf_len - 2, buf + 2) != 0);
324 *type = VT_GVAR;
325 }
309 326 else if(**in == '&') else if(**in == '&')
310 327 { {
311 328 ++*in; ++*in;
 
... ... is_valid_op(const char name[], VariableType vt, VariableOperation vo)
416 433 return (vo == VO_ASSIGN || vo == VO_APPEND); return (vo == VO_ASSIGN || vo == VO_APPEND);
417 434 } }
418 435
436 if(vt == VT_GVAR)
437 {
438 var_info_t *var = find_internal_var(name, /*create=*/0);
439 if(var == NULL)
440 {
441 /* Let this error be handled somewhere else. */
442 return (vo == VO_ASSIGN);
443 }
444
445 switch(var->val.type)
446 {
447 case VTYPE_ERROR:
448 /* Unreachable. */
449 return 0;
450 case VTYPE_STRING:
451 return ONE_OF(vo, VO_ASSIGN, VO_APPEND);
452 case VTYPE_INT:
453 return ONE_OF(vo, VO_ASSIGN, VO_ADD, VO_SUB);
454 }
455 /* Unreachable. */
456 return 0;
457 }
458
419 459 opt = vle_opts_find(name, OPT_GLOBAL); opt = vle_opts_find(name, OPT_GLOBAL);
420 460 if(opt == NULL) if(opt == NULL)
421 461 { {
 
... ... is_valid_op(const char name[], VariableType vt, VariableOperation vo)
439 479 /* Performs operation on a value. Returns zero on success, otherwise non-zero /* Performs operation on a value. Returns zero on success, otherwise non-zero
440 480 * is returned. */ * is returned. */
441 481 static int static int
442 perform_op(const char name[], VariableType vt, VariableOperation vo,
482 perform_op(const char name[], VariableType vt, VariableOperation vo, var_t val,
443 483 const char value[]) const char value[])
444 484 { {
445 485 if(vt == VT_ENVVAR) if(vt == VT_ENVVAR)
 
... ... perform_op(const char name[], VariableType vt, VariableOperation vo,
456 496 return 0; return 0;
457 497 } }
458 498
499 if(vt == VT_GVAR)
500 {
501 var_info_t *var = find_internal_var(name, vo == VO_ASSIGN);
502 switch(vo)
503 {
504 int a, b;
505
506 case VO_ASSIGN:
507 var_free(var->val);
508 var->val = var_clone(val);
509 return 0;
510 case VO_ADD:
511 case VO_SUB:
512 a = var_to_int(var->val);
513 b = var_to_int(val);
514 var_free(var->val);
515 var->val = var_from_int(vo == VO_ADD ? a + b : a - b);
516 return 0;
517 case VO_APPEND:
518 return append_internal_var(var, val);
519 }
520 /* Unreachable. */
521 return 1;
522 }
523
459 524 /* Update an option. */ /* Update an option. */
460 525
461 526 if(vt == VT_ANY_OPTION || vt == VT_LOCAL_OPTION) if(vt == VT_ANY_OPTION || vt == VT_LOCAL_OPTION)
 
... ... set_envvar(const char *name, const char *val)
526 591 env_set(name, val); env_set(name, val);
527 592 } }
528 593
594 /* Appends two variables as strings, updating LHS in place. Returns zero on
595 * success. */
596 static int
597 append_internal_var(var_info_t *var, var_t val)
598 {
599 char *current = var_to_str(var->val);
600 char *suffix = var_to_str(val);
601
602 if(current == NULL || suffix == NULL)
603 {
604 goto oom;
605 }
606
607 char *final = format_str("%s%s", current, suffix);
608 if(final == NULL)
609 {
610 goto oom;
611 }
612
613 var_free(var->val);
614 var->val = var_out_of_str(final);
615
616 free(current);
617 free(suffix);
618 return 0;
619
620 oom:
621 vle_tb_append_line(vle_err, "Not enough memory");
622 free(current);
623 free(suffix);
624 return 1;
625 }
626
529 627 /* Performs operation on an option. Returns zero on success, otherwise non-zero /* Performs operation on an option. Returns zero on success, otherwise non-zero
530 628 * is returned. */ * is returned. */
531 629 static int static int
 
... ... unlet_variables(const char cmd[])
624 722
625 723 while(*cmd != '\0') while(*cmd != '\0')
626 724 { {
627 envvar_t *record;
725 /* Options can't be removed, use this to mean "no value". */
726 VariableType type = VT_ANY_OPTION;
628 727
629 728 char name[VAR_NAME_MAX + 1]; char name[VAR_NAME_MAX + 1];
630 char *p;
631 int envvar = 1;
729 char *p = name;
632 730
633 731 /* Check if it's environment variable. */ /* Check if it's environment variable. */
634 if(*cmd != '$')
635 envvar = 0;
636 else
732 if(*cmd == '$')
733 {
734 type = VT_ENVVAR;
637 735 cmd++; cmd++;
736 }
737 else if(starts_with_lit(cmd, "g:"))
738 {
739 type = VT_GVAR;
740 *p++ = *cmd++;
741 *p++ = *cmd++;
742 }
638 743
639 744 /* Copy variable name. */ /* Copy variable name. */
640 p = name;
641 745 while(*cmd != '\0' && char_is_one_of(ENV_VAR_NAME_CHARS, *cmd) && while(*cmd != '\0' && char_is_one_of(ENV_VAR_NAME_CHARS, *cmd) &&
642 746 (size_t)(p - name) < sizeof(name) - 1) (size_t)(p - name) < sizeof(name) - 1)
643 747 { {
 
... ... unlet_variables(const char cmd[])
654 758
655 759 cmd = skip_whitespace(cmd); cmd = skip_whitespace(cmd);
656 760
657 /* Currently we support only environment variables. */
658 if(!envvar)
761 if(type == VT_ANY_OPTION)
659 762 { {
660 763 vle_tb_append_linef(vle_err, "%s: %s", "Unsupported variable type", name); vle_tb_append_linef(vle_err, "%s: %s", "Unsupported variable type", name);
661 764
 
... ... unlet_variables(const char cmd[])
673 776 continue; continue;
674 777 } }
675 778
676 record = find_record(name);
677 if(record == NULL || record->removed)
779 if(type == VT_ENVVAR)
678 780 { {
679 vle_tb_append_linef(vle_err, "%s: %s", "No such variable", name);
680 error++;
681 continue;
781 envvar_t *record = find_record(name);
782 if(record == NULL || record->removed)
783 {
784 vle_tb_append_linef(vle_err, "%s: %s", "No such variable", name);
785 error++;
786 continue;
787 }
788
789 if(record->from_parent)
790 record->removed = 1;
791 else
792 free_record(record);
793 env_remove(name);
682 794 } }
795 else if(type == VT_GVAR)
796 {
797 var_info_t *var = find_internal_var(name, /*create=*/0);
798 if(var == NULL)
799 {
800 vle_tb_append_linef(vle_err, "%s: %s", "No such variable", name);
801 error++;
802 continue;
803 }
683 804
684 if(record->from_parent)
685 record->removed = 1;
686 else
687 free_record(record);
688 env_remove(name);
805 free(var->name);
806 var_free(var->val);
807 *var = internal_vars[internal_var_count - 1];
808 --internal_var_count;
809 }
689 810 } }
690 811
691 812 return error; return error;
 
... ... complete_variables(const char var[], const char **start)
743 864 { {
744 865 complete_envvars(var, start); complete_envvars(var, start);
745 866 } }
746 else if(var[0] == 'v' && var[1] == ':')
867 else if(ONE_OF(var[0], 'v', 'g') && var[1] == ':')
747 868 { {
748 869 complete_internal_vars(var, start); complete_internal_vars(var, start);
749 870 } }
 
... ... complete_envvars(const char var[], const char **start)
780 901 vle_compl_add_last_match(var); vle_compl_add_last_match(var);
781 902 } }
782 903
783 /* Completes a variable name. var should point to "v:...". *start is set to
904 /* Completes a variable name. var should point to "[gv]:...". *start is set to
784 905 * completion insertion position in var. */ * completion insertion position in var. */
785 906 static void static void
786 907 complete_internal_vars(const char var[], const char **start) complete_internal_vars(const char var[], const char **start)
 
... ... complete_internal_vars(const char var[], const char **start)
808 929 var_t var_t
809 930 getvar(const char varname[]) getvar(const char varname[])
810 931 { {
811 size_t i;
812 for(i = 0U; i < internal_var_count; ++i)
932 var_info_t *var = find_internal_var(varname, /*create=*/0);
933 if(var == NULL)
813 934 { {
814 if(strcmp(internal_vars[i].name, varname) == 0)
815 {
816 return internal_vars[i].val;
817 }
935 return var_error();
818 936 } }
819 937
820 return var_error();
938 return var->val;
821 939 } }
822 940
823 941 int int
824 942 setvar(const char varname[], var_t val) setvar(const char varname[], var_t val)
825 943 { {
826 944 var_info_t new_var; var_info_t new_var;
827 size_t i;
828 945 void *p; void *p;
829 946
830 947 if(!starts_with_lit(varname, "v:")) if(!starts_with_lit(varname, "v:"))
 
... ... setvar(const char varname[], var_t val)
842 959 return 1; return 1;
843 960 } }
844 961
845 /* Search for existing variable. */
846 for(i = 0U; i < internal_var_count; ++i)
962 /* Try updating an existing variable. */
963 var_info_t *var = find_internal_var(varname, /*create=*/0);
964 if(var != NULL)
847 965 { {
848 if(strcmp(internal_vars[i].name, varname) == 0)
849 {
850 free(internal_vars[i].name);
851 var_free(internal_vars[i].val);
852 internal_vars[i] = new_var;
853 return 0;
854 }
966 free(var->name);
967 var_free(var->val);
968 *var = new_var;
969 return 0;
855 970 } }
856 971
857 972 /* Try to reallocate list of variables. */ /* Try to reallocate list of variables. */
 
... ... setvar(const char varname[], var_t val)
869 984 return 0; return 0;
870 985 } }
871 986
987 /* Search for an existing variable by its name and optionally create it if
988 * missing. Returns NULL on unsuccessful search. */
989 static var_info_t *
990 find_internal_var(const char varname[], int create)
991 {
992 size_t i;
993 for(i = 0U; i < internal_var_count; ++i)
994 {
995 if(strcmp(internal_vars[i].name, varname) == 0)
996 {
997 return &internal_vars[i];
998 }
999 }
1000
1001 if(!create)
1002 {
1003 return NULL;
1004 }
1005
1006 void *p =
1007 realloc(internal_vars, sizeof(*internal_vars)*(internal_var_count + 1U));
1008 if(p == NULL)
1009 {
1010 return NULL;
1011 }
1012
1013 internal_vars = p;
1014
1015 internal_vars[internal_var_count].name = strdup(varname);
1016 if(internal_vars[internal_var_count].name == NULL)
1017 {
1018 return NULL;
1019 }
1020
1021 internal_vars[internal_var_count].val = var_error();
1022 return &internal_vars[internal_var_count++];
1023 }
1024
872 1025 /* vim: set tabstop=2 softtabstop=2 shiftwidth=2 noexpandtab cinoptions-=(0 : */ /* vim: set tabstop=2 softtabstop=2 shiftwidth=2 noexpandtab cinoptions-=(0 : */
873 1026 /* vim: set cinoptions+=t0 filetype=c : */ /* vim: set cinoptions+=t0 filetype=c : */
File tests/parsing/suite.c changed (mode: 100644) (index 4f76ed792..806dd65be)
4 4
5 5 #include "../../src/engine/parsing.h" #include "../../src/engine/parsing.h"
6 6
7 #include <test-utils.h>
8
7 9 DEFINE_SUITE(); DEFINE_SUITE();
8 10
11 SETUP_ONCE()
12 {
13 fix_environ();
14 }
15
9 16 SETUP() SETUP()
10 17 { {
11 18 vle_parser_init(NULL); vle_parser_init(NULL);
File tests/parsing/variables.c changed (mode: 100644) (index 3c7a57ae9..e29c15cdc)
6 6
7 7 #include "asserts.h" #include "asserts.h"
8 8
9 SETUP()
10 {
11 init_variables();
12 }
13
14 TEARDOWN()
15 {
16 clear_variables();
17 }
18
9 19 TEST(nonexistent_variable_errors) TEST(nonexistent_variable_errors)
10 20 { {
21 ASSERT_FAIL("g:test", PE_INVALID_EXPRESSION);
11 22 ASSERT_FAIL("v:test", PE_INVALID_EXPRESSION); ASSERT_FAIL("v:test", PE_INVALID_EXPRESSION);
12 23 } }
13 24
14 25 TEST(existent_variable_is_expanded) TEST(existent_variable_is_expanded)
15 26 { {
27 assert_success(let_variables("g:test = 1"));
28 ASSERT_OK("g:test", "1");
29 assert_success(let_variables("g:test = 'abc'"));
30 ASSERT_OK("g:test", "abc");
31
16 32 assert_success(setvar("v:test", var_from_bool(1))); assert_success(setvar("v:test", var_from_bool(1)));
17 33 ASSERT_OK("v:test", "1"); ASSERT_OK("v:test", "1");
18 34 } }
File tests/test-data/syntax-highlight/syntax.vifm changed (mode: 100644) (index 7ee1b3137..56e6764e3)
... ... autocmd DirEnter ~/repos/vifm/**/* setl previewprg='git log --color -- %c 2>&1'
689 689 33delete 33delete
690 690
691 691 " variables " variables
692 echo g:somevar
692 693 echo v:servername echo v:servername
693 694
694 695 " ":echo" should be highlighted as :commands " ":echo" should be highlighted as :commands
File tests/variables/completion.c changed (mode: 100644) (index a6dee5d7e..16e9987a8)
... ... TEST(do_not_complete_removed_variables)
81 81 free(completed); free(completed);
82 82 } }
83 83
84 TEST(globals_completion)
85 {
86 assert_success(let_variables("g:test1 = 1"));
87 assert_success(let_variables("g:test2 = 1"));
88
89 const char *start;
90 char buf[] = "g:";
91 complete_variables(buf, &start);
92 assert_string_equal(&buf[0], start);
93
94 char *completed;
95
96 completed = vle_compl_next();
97 assert_string_equal("g:test1", completed);
98 free(completed);
99
100 completed = vle_compl_next();
101 assert_string_equal("g:test2", completed);
102 free(completed);
103 }
104
84 105 TEST(builtinvars_completion) TEST(builtinvars_completion)
85 106 { {
86 107 char buf[] = "v:"; char buf[] = "v:";
File tests/variables/globals.c added (mode: 100644) (index 000000000..57a2fca63)
1 #include <stic.h>
2
3 #include "../../src/engine/variables.h"
4
5 TEST(get_var_fails_for_unknown_global)
6 {
7 assert_true(getvar("g:test").type == VTYPE_ERROR);
8 }
9
10 TEST(get_var_works_for_known_global)
11 {
12 assert_success(let_variables("g:test = 1"));
13 assert_true(getvar("g:test").type == VTYPE_INT);
14 }
15
16 TEST(globals_can_be_removed)
17 {
18 assert_success(let_variables("g:test1 = 'a'"));
19 assert_success(let_variables("g:test2 = 0"));
20 assert_success(unlet_variables("g:test1 g:test2"));
21 assert_true(getvar("g:test1").type == VTYPE_ERROR);
22 assert_true(getvar("g:test2").type == VTYPE_ERROR);
23 }
24
25 TEST(globals_can_be_strings)
26 {
27 assert_success(let_variables("g:test = 'abc'"));
28 assert_true(getvar("g:test").type == VTYPE_STRING);
29 }
30
31 TEST(globals_can_change_value)
32 {
33 assert_success(let_variables("g:test = 0"));
34 assert_int_equal(0, var_to_int(getvar("g:test")));
35
36 assert_success(let_variables("g:test = 2"));
37 assert_int_equal(2, var_to_int(getvar("g:test")));
38 }
39
40 TEST(globals_can_change_type)
41 {
42 assert_success(let_variables("g:test = 0"));
43 assert_true(getvar("g:test").type == VTYPE_INT);
44 assert_int_equal(0, var_to_int(getvar("g:test")));
45
46 assert_success(let_variables("g:test = 'abc'"));
47 assert_true(getvar("g:test").type == VTYPE_STRING);
48 assert_string_equal("abc", getvar("g:test").value.string);
49 }
50
51 TEST(int_globals_can_be_incremented_or_decremented)
52 {
53 assert_success(let_variables("g:test = 7"));
54 assert_int_equal(7, var_to_int(getvar("g:test")));
55
56 assert_success(let_variables("g:test += 2"));
57 assert_int_equal(9, var_to_int(getvar("g:test")));
58
59 assert_success(let_variables("g:test -= 10"));
60 assert_int_equal(-1, var_to_int(getvar("g:test")));
61 }
62
63 TEST(string_globals_cannot_be_incremented_or_decremented)
64 {
65 assert_success(let_variables("g:test = 'a'"));
66
67 assert_failure(let_variables("g:test += 'b'"));
68 assert_string_equal("a", getvar("g:test").value.string);
69
70 assert_failure(let_variables("g:test -= 'a'"));
71 assert_string_equal("a", getvar("g:test").value.string);
72 }
73
74 TEST(int_globals_cannot_be_appended)
75 {
76 assert_success(let_variables("g:test = 0"));
77
78 assert_failure(let_variables("g:test .= 1"));
79 assert_int_equal(0, var_to_int(getvar("g:test")));
80 assert_true(getvar("g:test").type == VTYPE_INT);
81
82 assert_failure(let_variables("g:test .= '1'"));
83 assert_int_equal(0, var_to_int(getvar("g:test")));
84 assert_true(getvar("g:test").type == VTYPE_INT);
85 }
86
87 TEST(string_globals_can_be_appended)
88 {
89 assert_success(let_variables("g:test = 'a'"));
90 assert_success(let_variables("g:test .= 'b'"));
91 assert_string_equal("ab", getvar("g:test").value.string);
92 }
93
94 /* vim: set tabstop=2 softtabstop=2 shiftwidth=2 noexpandtab cinoptions-=(0 : */
95 /* vim: set cinoptions+=t0 : */
File tests/variables/unlet.c changed (mode: 100644) (index 40abeacf3..11f662629)
... ... TEST(envvar_table_updates_do_not_crash)
20 20 assert_int_equal(0, let_variables("$" VAR_NAME_BASE "3='VAL'")); assert_int_equal(0, let_variables("$" VAR_NAME_BASE "3='VAL'"));
21 21 } }
22 22
23 TEST(cannot_unlet_builtin_vars)
24 {
25 assert_success(setvar("v:test", var_from_bool(1)));
26 assert_failure(unlet_variables("v:test"));
27 assert_true(getvar("v:test").type == VTYPE_INT);
28 }
29
23 30 /* vim: set tabstop=2 softtabstop=2 shiftwidth=2 noexpandtab cinoptions-=(0 : */ /* vim: set tabstop=2 softtabstop=2 shiftwidth=2 noexpandtab cinoptions-=(0 : */
24 31 /* vim: set cinoptions+=t0 filetype=c : */ /* vim: set cinoptions+=t0 filetype=c : */
Hints

Before first commit, do not forget to setup your git environment:
git config --global user.name "your_name_here"
git config --global user.email "your@email_here"

Clone this repository using HTTP(S):
git clone https://code.reversed.top/user/xaizek/vifm

Clone this repository using ssh (do not forget to upload a key first):
git clone ssh://rocketgit@code.reversed.top/user/xaizek/vifm

You are allowed to anonymously push to this repository.
This means that your pushed commits will automatically be transformed into a pull request:
... clone the repository ...
... make some changes and some commits ...
git push origin master