* rename and pass flags to byte quoting functions: `byte_quote` -> `strquote_byte` * add `eb_print_style` to output data to a buffer in a given style * add `eb_print_field` to output a field name and value using styles * use styles in describe commands * improve byte stats and page layout output in `describe-buffer` * add `wall-chart` command (sorted list of bindings) * add `describe-help` command
723 lines
25 KiB
C
723 lines
25 KiB
C
/*
|
|
* Module for handling variables in QEmacs
|
|
*
|
|
* Copyright (c) 2000-2026 Charlie Gordon.
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
* THE SOFTWARE.
|
|
*/
|
|
|
|
#include "qe.h"
|
|
#include "variables.h"
|
|
|
|
static QVarType qe_variable_set_value_offset(EditState *s, VarDef *vp, void *ptr,
|
|
const char *value, int num);
|
|
static QVarType qe_variable_set_value_generic(EditState *s, VarDef *vp, void *ptr,
|
|
const char *value, int num);
|
|
static QVarType qe_variable_set_value_style(EditState *s, VarDef *vp, void *ptr,
|
|
const char *value, int num);
|
|
|
|
const char * const var_domain[] = {
|
|
"global", /* VAR_GLOBAL */
|
|
"state", /* VAR_STATE */
|
|
"buffer", /* VAR_BUFFER */
|
|
"window", /* VAR_WINDOW */
|
|
"mode", /* VAR_MODE */
|
|
"self", /* VAR_SELF */
|
|
};
|
|
|
|
static const char * const var_domain_desc[] = {
|
|
"Global", /* VAR_GLOBAL */
|
|
"State", /* VAR_STATE */
|
|
"Buffer", /* VAR_BUFFER */
|
|
"Window", /* VAR_WINDOW */
|
|
"Mode", /* VAR_MODE */
|
|
"Other", /* VAR_SELF */
|
|
};
|
|
|
|
static int use_full_version = 1;
|
|
|
|
static VarDef var_table[] = {
|
|
|
|
S_VAR( "screen-width", width, VAR_NUMBER, VAR_RO,
|
|
"Number of columns available for display on screen." )
|
|
S_VAR( "screen-height", height, VAR_NUMBER, VAR_RO,
|
|
"Number of lines available for display on screen." )
|
|
S_VAR( "is-full-screen", is_full_screen, VAR_NUMBER, VAR_RO,
|
|
"Set if this window is displayed in full screen (without borders)." )
|
|
S_VAR( "flag-split-window-change-focus", flag_split_window_change_focus, VAR_NUMBER, VAR_RW_SAVE,
|
|
"Set if `split-window` should set focus to the new window." )
|
|
S_VAR( "shell-buffer-read-only", shell_buffer_read_only, VAR_NUMBER, VAR_RW_SAVE,
|
|
"Set if shell buffers should become read-only." )
|
|
S_VAR( "shell-mode-auto-interactive", shell_mode_auto_interactive, VAR_NUMBER, VAR_RW_SAVE,
|
|
"Set if moving to end of buffer switches to interactive mode." )
|
|
S_VAR( "shell-command-other-window", shell_command_other_window, VAR_NUMBER, VAR_RW_SAVE,
|
|
"Set if shell command should use another window." )
|
|
|
|
// XXX: need set_value function to perform side effect
|
|
S_VAR( "backspace-is-control-h", backspace_is_control_h, VAR_NUMBER, VAR_RW_SAVE,
|
|
"Set if the Delete key sends a control-H." )
|
|
S_VAR( "ungot-key", ungot_key, VAR_NUMBER, VAR_RW, NULL ) // XXX: need set_value function
|
|
S_VAR( "QEPATH", res_path, VAR_CHARS, VAR_RO,
|
|
"List of directories to search for standard files to load." )
|
|
//S_VAR( "it", it, VAR_NUMBER, VAR_RW, NULL )
|
|
S_VAR( "ignore-spaces", ignore_spaces, VAR_NUMBER, VAR_RW_SAVE,
|
|
"Set to ignore spaces in compare-windows." )
|
|
S_VAR( "ignore-comments", ignore_comments, VAR_NUMBER, VAR_RW_SAVE,
|
|
"Set to ignore comments in compare-windows." )
|
|
S_VAR( "ignore-case", ignore_case, VAR_NUMBER, VAR_RW_SAVE,
|
|
"Set to ignore case in compare-windows." )
|
|
S_VAR( "ignore-preproc", ignore_preproc, VAR_NUMBER, VAR_RW_SAVE,
|
|
"Set to ignore preprocessing directives in compare-windows." )
|
|
S_VAR( "ignore-equivalent", ignore_equivalent, VAR_NUMBER, VAR_RW_SAVE,
|
|
"Set to ignore equivalent strings defined by define-equivalent." )
|
|
S_VAR( "hilite-region", hilite_region, VAR_NUMBER, VAR_RW_SAVE,
|
|
"Set to highlight the region after setting the mark." )
|
|
S_VAR( "mmap-threshold", mmap_threshold, VAR_NUMBER, VAR_RW_SAVE, // XXX: need set_value function
|
|
"Size from which files are mmapped instead of loaded in memory." )
|
|
S_VAR( "max-load-size", max_load_size, VAR_NUMBER, VAR_RW_SAVE, // XXX: need set_value function
|
|
"Maximum size for files to be loaded or mmapped into a buffer." )
|
|
S_VAR( "show-unicode", show_unicode, VAR_NUMBER, VAR_RW_SAVE, // XXX: need set_value function
|
|
"Set to show non-ASCII characters as unicode escape sequences." )
|
|
S_VAR( "default-tab-width", default_tab_width, VAR_NUMBER, VAR_RW_SAVE, // XXX: need set_value function
|
|
"Default value of `tab-width` for buffers that do not override it." )
|
|
S_VAR( "default-fill-column", default_fill_column, VAR_NUMBER, VAR_RW_SAVE, // XXX: need set_value function
|
|
"Default value of `fill-column` for buffers that do not override it" )
|
|
S_VAR( "backup-inhibited", backup_inhibited, VAR_NUMBER, VAR_RW_SAVE,
|
|
"Set to prevent automatic backups of modified files" )
|
|
S_VAR( "c-label-indent", c_label_indent, VAR_NUMBER, VAR_RW_SAVE,
|
|
"Number of columns to adjust indentation of C labels." )
|
|
S_VAR( "macro-counter", macro_counter, VAR_NUMBER, VAR_RW_SAVE,
|
|
"Macro counter: insert with C-x C-k TAB, set with C-x C-k C-c." )
|
|
|
|
//B_VAR( "screen-charset", charset, VAR_NUMBER, VAR_RW, NULL )
|
|
|
|
B_VAR_F( "mark", mark, VAR_NUMBER, VAR_RW, qe_variable_set_value_offset,
|
|
"The position of the beginning of the current region." )
|
|
B_VAR( "bufsize", total_size, VAR_NUMBER, VAR_RO,
|
|
"The number of bytes in the current buffer." )
|
|
B_VAR( "bufname", name, VAR_CHARS, VAR_RO,
|
|
"The name of the current buffer." )
|
|
B_VAR( "filename", filename, VAR_CHARS, VAR_RO,
|
|
"The name of the file associated with the current buffer." )
|
|
B_VAR( "tab-width", tab_width, VAR_NUMBER, VAR_RW, // XXX: need set_value function
|
|
"Distance between tab stops (for display of tab characters), in columns." )
|
|
B_VAR( "fill-column", fill_column, VAR_NUMBER, VAR_RW, // XXX: need set_value function
|
|
"Column beyond which automatic line-wrapping should happen." )
|
|
|
|
W_VAR_F( "point", offset, VAR_NUMBER, VAR_RW, qe_variable_set_value_offset, /* should be window-point */
|
|
"Current value of point in this window." )
|
|
W_VAR( "indent-width", indent_width, VAR_NUMBER, VAR_RW, // XXX: need set_value function
|
|
"Number of columns to indent by for a syntactic level." )
|
|
W_VAR( "indent-tabs-mode", indent_tabs_mode, VAR_NUMBER, VAR_RW,
|
|
"Set if indentation can insert tabs." )
|
|
W_VAR_F( "default-style", default_style, VAR_STYLE, VAR_RW, qe_variable_set_value_style,
|
|
"Default text style for this window." )
|
|
W_VAR_F( "region-style", region_style, VAR_STYLE, VAR_RW, qe_variable_set_value_style,
|
|
"Text style for the current region in this window." )
|
|
W_VAR_F( "curline-style", curline_style, VAR_STYLE, VAR_RW, qe_variable_set_value_style,
|
|
"Text style for the current line in this window." )
|
|
W_VAR( "window-width", width, VAR_NUMBER, VAR_RW, // XXX: need set_value function
|
|
"Number of display columns in this window." )
|
|
W_VAR( "window-height", height, VAR_NUMBER, VAR_RW, // XXX: need set_value function
|
|
"Number of display lines in this window." )
|
|
W_VAR( "window-left", xleft, VAR_NUMBER, VAR_RW, // XXX: need set_value function
|
|
"Display column of the left edge of this window." )
|
|
W_VAR( "window-top", ytop, VAR_NUMBER, VAR_RW, // XXX: need set_value function
|
|
"Display line of the top edge of this window." )
|
|
W_VAR( "window-prompt", prompt, VAR_STRING, VAR_RW,
|
|
"Prompt string to show for this window." )
|
|
W_VAR( "dump-width", dump_width, VAR_NUMBER, VAR_RW, NULL ) // XXX: need set_value function
|
|
|
|
M_VAR( "mode-name", name, VAR_STRING, VAR_RO,
|
|
"Name of the current major mode." )
|
|
M_VAR( "auto-indent", auto_indent, VAR_NUMBER, VAR_RW,
|
|
"Set for automatic indentation on new lines." )
|
|
#ifdef CONFIG_SESSION
|
|
G_VAR( "use-session-file", use_session_file, VAR_NUMBER, VAR_RW, NULL )
|
|
#endif
|
|
G_VAR( "force-tty", force_tty, VAR_NUMBER, VAR_RW,
|
|
"Set to prevent graphics display." )
|
|
G_VAR( "disable-crc", disable_crc, VAR_NUMBER, VAR_RW_SAVE,
|
|
"Set to prevent CRC based display cache." )
|
|
G_VAR( "use-html", use_html, VAR_NUMBER, VAR_RW, NULL )
|
|
G_VAR( "is-player", is_player, VAR_NUMBER, VAR_RW, NULL )
|
|
G_VAR( "full-version", use_full_version, VAR_NUMBER, VAR_RW, NULL )
|
|
|
|
/* more buffer fields: modified, readonly, binary, charset */
|
|
|
|
/* more window fields: mode_line, color, input_method...
|
|
*/
|
|
|
|
//G_VAR( "text-mode-line", text_mode.mode_line, VAR_STRING, VAR_RW, NULL )
|
|
//G_VAR( "binary-mode-line", binary_mode.mode_line, VAR_STRING, VAR_RW, NULL )
|
|
//G_VAR( "hex-mode-line", hex_mode.mode_line, VAR_STRING, VAR_RW, NULL )
|
|
//G_VAR( "unicode-mode-line", unihex_mode.mode_line, VAR_STRING, VAR_RW, NULL )
|
|
|
|
//Dispatch these to the appropriate modules
|
|
//G_VAR( "c-mode-extensions", c_mode.extensions, VAR_STRING, VAR_RW, NULL )
|
|
//G_VAR( "c-mode-keywords", c_mode.keywords, VAR_STRING, VAR_RW, NULL )
|
|
//G_VAR( "c-mode-types", c_mode.types, VAR_STRING, VAR_RW, NULL )
|
|
//G_VAR( "html-src-mode-extensions", htmlsrc_mode.extensions, VAR_STRING, VAR_RW, NULL )
|
|
//G_VAR( "html-mode-extensions", html_mode.extensions, VAR_STRING, VAR_RW, NULL )
|
|
//G_VAR( "perl-mode-extensions", perl_mode.extensions, VAR_STRING, VAR_RW, NULL )
|
|
};
|
|
|
|
static VarDef *qe_find_variable(QEmacsState *qs, const char *name)
|
|
{
|
|
VarDef *vp;
|
|
|
|
for (vp = qs->first_variable; vp; vp = vp->next) {
|
|
if (strequal(vp->name, name))
|
|
return vp;
|
|
}
|
|
/* Should have a list of local variables for buffer/window/mode
|
|
* instances
|
|
*/
|
|
return NULL;
|
|
}
|
|
|
|
void variable_complete(CompleteState *cp, CompleteFunc enumerate) {
|
|
QEmacsState *qs = cp->s->qs;
|
|
const VarDef *vp;
|
|
|
|
for (vp = qs->first_variable; vp; vp = vp->next) {
|
|
(*enumerate)(cp, vp->name, CT_STRX);
|
|
}
|
|
}
|
|
|
|
QVarType qe_get_variable(EditState *s, const char *name,
|
|
char *buf, int size, int *pnum, int as_source)
|
|
{
|
|
const VarDef *vp;
|
|
int num = 0;
|
|
QETermStyle style = 0;
|
|
const char *str = NULL;
|
|
const void *ptr;
|
|
const char *endp;
|
|
|
|
/* find standard variable and user variables */
|
|
vp = qe_find_variable(s->qs, name);
|
|
if (!vp) {
|
|
/* Try environment */
|
|
str = getenv(name);
|
|
if (!str) {
|
|
if (size > 0)
|
|
*buf = '\0';
|
|
return VAR_UNKNOWN;
|
|
}
|
|
num = strtol_c(str, &endp, 0);
|
|
if (pnum && endp != str && *endp == '\0') {
|
|
*pnum = num;
|
|
return VAR_NUMBER;
|
|
}
|
|
if (as_source)
|
|
strquote(buf, size, str, -1, SQB_USE_EXTENDED);
|
|
else
|
|
pstrcpy(buf, size, str);
|
|
return VAR_STRING;
|
|
}
|
|
switch (vp->domain) {
|
|
case VAR_SELF:
|
|
ptr = &vp->value;
|
|
break;
|
|
case VAR_GLOBAL:
|
|
ptr = vp->value.ptr;
|
|
break;
|
|
case VAR_STATE:
|
|
ptr = (const u8*)s->qs + vp->value.offset;
|
|
break;
|
|
case VAR_BUFFER:
|
|
ptr = (const u8*)s->b + vp->value.offset;
|
|
break;
|
|
case VAR_WINDOW:
|
|
ptr = (const u8*)s + vp->value.offset;
|
|
break;
|
|
case VAR_MODE:
|
|
ptr = (const u8*)s->mode + vp->value.offset;
|
|
break;
|
|
default:
|
|
if (size > 0)
|
|
*buf = '\0';
|
|
return VAR_UNKNOWN;
|
|
}
|
|
|
|
switch (vp->type) {
|
|
case VAR_STRING:
|
|
memcpy(&str, ptr, sizeof(str));
|
|
if (as_source)
|
|
strquote(buf, size, str, -1, SQB_USE_EXTENDED);
|
|
else
|
|
pstrcpy(buf, size, str ? str : "");
|
|
break;
|
|
case VAR_CHARS:
|
|
str = (const char*)ptr;
|
|
if (as_source)
|
|
strquote(buf, size, str, -1, SQB_USE_EXTENDED);
|
|
else
|
|
pstrcpy(buf, size, str);
|
|
break;
|
|
case VAR_NUMBER:
|
|
memcpy(&num, ptr, sizeof(num));
|
|
if (pnum) {
|
|
*pnum = num;
|
|
} else
|
|
if (num < 10) {
|
|
snprintf(buf, size, "%d", num);
|
|
} else
|
|
if (num % (1 << 20) == 0) {
|
|
snprintf(buf, size, "%d (0x%x, %d MB)",
|
|
num, (unsigned)num, num >> 20);
|
|
} else
|
|
if (num % (1 << 10) == 0) {
|
|
snprintf(buf, size, "%d (0x%x, %d KB)",
|
|
num, (unsigned)num, num >> 10);
|
|
} else {
|
|
snprintf(buf, size, "%d (0x%x)", num, (unsigned)num);
|
|
}
|
|
break;
|
|
case VAR_STYLE:
|
|
memcpy(&style, ptr, sizeof(style));
|
|
if (pnum) {
|
|
*pnum = (int)style;
|
|
} else {
|
|
int len = snprintf(buf, size, "0x%x (", style);
|
|
len += qe_term_style_string(buf + len, size - len, num);
|
|
snprintf(buf + len, size - len, ")");
|
|
}
|
|
break;
|
|
default:
|
|
if (size > 0)
|
|
*buf = '\0';
|
|
return VAR_UNKNOWN;
|
|
}
|
|
return vp->type;
|
|
}
|
|
|
|
static QVarType qe_variable_set_value_offset(EditState *s, VarDef *vp, void *ptr,
|
|
const char *value, int num)
|
|
{
|
|
if (value) {
|
|
/* XXX: should have default, min and max values */
|
|
return VAR_INVALID;
|
|
} else {
|
|
int *pnum = (int *)ptr;
|
|
*pnum = clamp_offset(num, 0, s->b->total_size);
|
|
return VAR_NUMBER;
|
|
}
|
|
}
|
|
|
|
static QVarType qe_variable_set_value_style(EditState *s, VarDef *vp, void *ptr,
|
|
const char *value, int num)
|
|
{
|
|
QETermStyle style = num;
|
|
if (value && qe_term_get_style(&style, value)) {
|
|
const char *p;
|
|
style = strtol_c(value, &p, 0);
|
|
if (*p)
|
|
return VAR_INVALID;
|
|
}
|
|
memcpy(ptr, &style, sizeof(style));
|
|
return VAR_STYLE;
|
|
}
|
|
|
|
static QVarType qe_variable_set_value_generic(EditState *s, VarDef *vp, void *ptr,
|
|
const char *value, int num)
|
|
{
|
|
char buf[32];
|
|
char **pstr;
|
|
int *pnum;
|
|
|
|
switch (vp->type) {
|
|
case VAR_STRING:
|
|
if (!value) {
|
|
snprintf(buf, sizeof(buf), "%d", num);
|
|
value = buf;
|
|
}
|
|
pstr = (char **)ptr;
|
|
if (!strequal(*pstr, value)) {
|
|
if (vp->str_alloc)
|
|
qe_free(pstr);
|
|
*pstr = qe_strdup(value);
|
|
vp->str_alloc = 1;
|
|
vp->modified = 1;
|
|
}
|
|
break;
|
|
case VAR_CHARS:
|
|
if (!value) {
|
|
snprintf(buf, sizeof(buf), "%d", num);
|
|
value = buf;
|
|
}
|
|
if (!strequal(ptr, value)) {
|
|
pstrcpy(ptr, vp->size, value);
|
|
vp->modified = 1;
|
|
}
|
|
break;
|
|
case VAR_NUMBER:
|
|
case VAR_STYLE:
|
|
/* XXX: should have default, min and max values? */
|
|
if (value) {
|
|
const char *p;
|
|
num = strtol_c(value, &p, 0);
|
|
if (*p) {
|
|
return VAR_INVALID;
|
|
}
|
|
}
|
|
pnum = (int *)ptr;
|
|
if (*pnum != num) {
|
|
*pnum = num;
|
|
vp->modified = 1;
|
|
}
|
|
break;
|
|
default:
|
|
return VAR_UNKNOWN;
|
|
}
|
|
return vp->type;
|
|
}
|
|
|
|
QVarType qe_set_variable(EditState *s, const char *name,
|
|
const char *value, int num)
|
|
{
|
|
QEmacsState *qs = s->qs;
|
|
void *ptr;
|
|
VarDef *vp;
|
|
|
|
vp = qe_find_variable(qs, name);
|
|
if (!vp) {
|
|
/* Create user variable (global/buffer/window/mode?) */
|
|
vp = qe_mallocz(VarDef);
|
|
vp->name = qe_strdup(name);
|
|
vp->var_alloc = 1;
|
|
vp->modified = 1;
|
|
vp->domain = VAR_SELF;
|
|
vp->rw = VAR_RW_SAVE;
|
|
if (value) {
|
|
vp->str_alloc = 1;
|
|
vp->value.str = qe_strdup(value);
|
|
vp->type = VAR_STRING;
|
|
} else {
|
|
vp->value.num = num;
|
|
vp->type = VAR_NUMBER;
|
|
}
|
|
qe_register_variables(qs, vp, 1);
|
|
return vp->type;
|
|
} else
|
|
if (vp->rw == VAR_RO) {
|
|
return VAR_READONLY;
|
|
} else {
|
|
switch (vp->domain) {
|
|
case VAR_SELF:
|
|
ptr = &vp->value;
|
|
break;
|
|
case VAR_GLOBAL:
|
|
ptr = vp->value.ptr;
|
|
break;
|
|
case VAR_STATE:
|
|
ptr = (u8*)qs + vp->value.offset;
|
|
break;
|
|
case VAR_BUFFER:
|
|
ptr = (u8*)s->b + vp->value.offset;
|
|
break;
|
|
case VAR_WINDOW:
|
|
ptr = (u8*)s + vp->value.offset;
|
|
break;
|
|
case VAR_MODE:
|
|
ptr = (u8*)s->mode + vp->value.offset;
|
|
break;
|
|
default:
|
|
return VAR_UNKNOWN;
|
|
}
|
|
return vp->set_value(s, vp, ptr, value, num);
|
|
}
|
|
}
|
|
|
|
void do_show_variable(EditState *s, const char *name)
|
|
{
|
|
char buf[MAX_FILENAME_SIZE];
|
|
|
|
switch (qe_get_variable(s, name, buf, sizeof(buf), NULL, 1)) {
|
|
case VAR_UNKNOWN:
|
|
put_error(s, "No variable %s", name);
|
|
break;
|
|
default:
|
|
put_status(s, "%s -> %s", name, buf);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void do_set_variable(EditState *s, const char *name, const char *value)
|
|
{
|
|
switch (qe_set_variable(s, name, value, 0)) {
|
|
case VAR_UNKNOWN:
|
|
put_error(s, "Variable %s is invalid", name);
|
|
break;
|
|
case VAR_READONLY:
|
|
put_error(s, "Variable %s is read-only", name);
|
|
break;
|
|
case VAR_INVALID:
|
|
put_error(s, "Invalid value for variable %s: %s", name, value);
|
|
break;
|
|
default:
|
|
do_show_variable(s, name);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void do_describe_variable(EditState *s, const char *name) {
|
|
EditBuffer *b;
|
|
VarDef *vp;
|
|
|
|
if ((vp = qe_find_variable(s->qs, name)) == NULL) {
|
|
put_error(s, "No variable %s", name);
|
|
return;
|
|
}
|
|
b = new_help_buffer(s);
|
|
if (!b)
|
|
return;
|
|
|
|
eb_putc(b, '\n');
|
|
/* print name, class, current value and short description */
|
|
eb_variable_print_entry(b, vp, s, 1);
|
|
// XXX: should look up markdown documentation
|
|
show_popup(s, b, "Help");
|
|
}
|
|
|
|
static void do_show_all_variables(EditState *s) {
|
|
EditBuffer *b;
|
|
VarDef *vp;
|
|
int i;
|
|
|
|
b = new_help_buffer(s);
|
|
if (!b)
|
|
return;
|
|
|
|
// XXX: should create links to markdown documentation
|
|
for (i = 0; i < countof(var_domain); i++) {
|
|
eb_print_style(b, DESCRIBE_STYLE_HEAD,
|
|
"%s variables:\n", var_domain_desc[i]);
|
|
for (vp = s->qs->first_variable; vp; vp = vp->next) {
|
|
if (vp->domain == i) {
|
|
/* print name, class, current value and short description */
|
|
eb_variable_print_entry(b, vp, s, 1);
|
|
}
|
|
}
|
|
eb_putc(b, '\n');
|
|
}
|
|
|
|
show_popup(s, b, "Variables");
|
|
}
|
|
|
|
void qe_register_variables(QEmacsState *qs, VarDef *vars, int count)
|
|
{
|
|
VarDef *vp;
|
|
|
|
for (vp = vars; vp < vars + count; vp++) {
|
|
if (!vp->set_value)
|
|
vp->set_value = qe_variable_set_value_generic;
|
|
vp->next = vp + 1;
|
|
}
|
|
vp[-1].next = qs->first_variable;
|
|
qs->first_variable = vars;
|
|
}
|
|
|
|
/* should register this as help function */
|
|
void qe_list_variables(EditState *s, EditBuffer *b)
|
|
{
|
|
char buf[MAX_FILENAME_SIZE];
|
|
char typebuf[32];
|
|
const char *type;
|
|
const VarDef *vp;
|
|
|
|
eb_puts(b, "\n variables:\n\n");
|
|
for (vp = s->qs->first_variable; vp; vp = vp->next) {
|
|
/* XXX: merge with variable_print_entry() */
|
|
switch (vp->type) {
|
|
case VAR_NUMBER:
|
|
type = "int";
|
|
break;
|
|
case VAR_STYLE:
|
|
type = "style";
|
|
break;
|
|
case VAR_STRING:
|
|
type = "string";
|
|
break;
|
|
case VAR_CHARS:
|
|
type = typebuf;
|
|
snprintf(typebuf, sizeof(typebuf), "char[%d]", vp->size);
|
|
break;
|
|
default:
|
|
type = "var";
|
|
break;
|
|
}
|
|
qe_get_variable(s, vp->name, buf, sizeof(buf), NULL, 1);
|
|
eb_printf(b, " %s %s %s%s -> %s\n",
|
|
var_domain[vp->domain], type,
|
|
vp->rw ? "" : "read-only ",
|
|
vp->name, buf);
|
|
}
|
|
}
|
|
|
|
void qe_save_variables(EditState *s, EditBuffer *b)
|
|
{
|
|
char buf[MAX_FILENAME_SIZE];
|
|
char varname[32], *p;
|
|
const VarDef *vp;
|
|
|
|
eb_puts(b, "// variables:\n");
|
|
/* Only save customized variables */
|
|
for (vp = s->qs->first_variable; vp; vp = vp->next) {
|
|
if (vp->rw != VAR_RW_SAVE || !vp->modified)
|
|
continue;
|
|
pstrcpy(varname, countof(varname), vp->name);
|
|
for (p = varname; *p; p++) {
|
|
if (*p == '-')
|
|
*p = '_';
|
|
}
|
|
qe_get_variable(s, vp->name, buf, sizeof(buf), NULL, 1);
|
|
eb_printf(b, "%s = %s;\n", varname, buf);
|
|
}
|
|
eb_putc(b, '\n');
|
|
}
|
|
|
|
int eb_variable_print_entry(EditBuffer *b, VarDef *vp, EditState *s, int long_format) {
|
|
char buf[256];
|
|
char typebuf[32];
|
|
const char *type = typebuf;
|
|
int len = 0;
|
|
|
|
if (!vp)
|
|
return 0;
|
|
|
|
*typebuf = '\0';
|
|
switch (vp->type) {
|
|
case VAR_NUMBER:
|
|
type = "int";
|
|
break;
|
|
case VAR_STYLE:
|
|
type = "style";
|
|
break;
|
|
case VAR_STRING:
|
|
type = "string";
|
|
break;
|
|
case VAR_CHARS:
|
|
type = "char";
|
|
snprintf(typebuf, sizeof(typebuf), "[%d]", vp->size);
|
|
break;
|
|
default:
|
|
type = "var";
|
|
break;
|
|
}
|
|
|
|
if (long_format) {
|
|
len += eb_puts(b, " ");
|
|
if (!vp->rw) {
|
|
len += eb_print_style(b, QE_STYLE_KEYWORD, "const ");
|
|
}
|
|
len += eb_print_style(b, QE_STYLE_TYPE, "%s ", type);
|
|
}
|
|
len += eb_print_style(b, QE_STYLE_VARIABLE, "%s", vp->name);
|
|
if (long_format)
|
|
len += eb_puts(b, typebuf);
|
|
len += eb_puts(b, " = ");
|
|
qe_get_variable(s, vp->name, buf, sizeof(buf), NULL, 1);
|
|
len += eb_print_style(b, *buf == '\"' ? QE_STYLE_STRING : QE_STYLE_NUMBER, "%s", buf);
|
|
if (long_format) {
|
|
eb_putc(b, '\n');
|
|
if (vp->desc && *vp->desc) {
|
|
/* print short description */
|
|
eb_print_style(b, QE_STYLE_COMMENT, " %s\n", vp->desc);
|
|
}
|
|
} else {
|
|
if (len + 2 <= 40) {
|
|
b->tab_width = max_int(len + 2, b->tab_width);
|
|
len += eb_putc(b, '\t');
|
|
} else {
|
|
b->tab_width = 40;
|
|
}
|
|
len += eb_print_style(b, QE_STYLE_COMMENT, " %s%s",
|
|
vp->rw ? "" : "read-only ", var_domain[vp->domain]);
|
|
len += eb_print_style(b, QE_STYLE_TYPE, " %s%s", type, typebuf);
|
|
}
|
|
return len;
|
|
}
|
|
|
|
int variable_print_entry(CompleteState *cp, EditState *s, const char *name) {
|
|
VarDef *vp = qe_find_variable(s->qs, name);
|
|
if (vp) {
|
|
// XXX: should pass the target window
|
|
return eb_variable_print_entry(s->b, vp, s, 0);
|
|
} else {
|
|
return eb_puts(s->b, name);
|
|
}
|
|
}
|
|
|
|
static CompletionDef variable_completion = {
|
|
.name = "variable",
|
|
.enumerate = variable_complete,
|
|
.print_entry = variable_print_entry,
|
|
.get_entry = command_get_entry,
|
|
};
|
|
|
|
static void value_complete(CompleteState *cp, CompleteFunc enumerate) {
|
|
style_complete(cp, enumerate);
|
|
color_complete(cp, enumerate);
|
|
}
|
|
|
|
static int value_print_entry(CompleteState *cp, EditState *s, const char *name) {
|
|
if (find_style(name))
|
|
return style_print_entry(cp, s, name);
|
|
else
|
|
return color_print_entry(cp, s, name);
|
|
}
|
|
|
|
static CompletionDef value_completion = {
|
|
.name = "value",
|
|
.enumerate = value_complete,
|
|
.print_entry = value_print_entry,
|
|
.flags = CF_SPACE_OK,
|
|
};
|
|
|
|
/*---------------- commands ----------------*/
|
|
|
|
static const CmdDef var_commands[] = {
|
|
CMD2( "show-variable", "",
|
|
"Show the value of a given variable",
|
|
do_show_variable, ESs,
|
|
"s{Show variable: }[variable]|variable|")
|
|
CMD2( "set-variable", "f8",
|
|
"Set the value of a variable",
|
|
do_set_variable, ESss,
|
|
"s{Set variable: }[variable]|variable|s{to value: }[value]|value|")
|
|
CMD2( "describe-variable", "C-h v",
|
|
"Show information for a variable",
|
|
do_describe_variable, ESs,
|
|
"s{Describe variable: }[variable]|variable|")
|
|
CMD0( "show-all-variables", "C-h C-v",
|
|
"Show value and information for all variables",
|
|
do_show_all_variables)
|
|
};
|
|
|
|
static int variables_init(QEmacsState *qs) {
|
|
qe_register_variables(qs, var_table, countof(var_table));
|
|
qe_register_commands(qs, NULL, var_commands, countof(var_commands));
|
|
qe_register_completion(qs, &variable_completion);
|
|
qe_register_completion(qs, &value_completion);
|
|
return 0;
|
|
}
|
|
|
|
qe_module_init(variables_init);
|