Remove help in favor of wisdom

This commit is contained in:
markmental 2026-09-05 20:28:20 -04:00
commit 64bf88dadd
12 changed files with 131 additions and 94 deletions

View file

@ -20,7 +20,8 @@ The implementation is messy, full of hacky C pointer arithmetic, and probably of
* **Substitute:** `:%s/old/new/[g]` and `:s/old/new/`, including patterns and replacements that contain spaces.
* **Indenting:** `>>` and `<<` with `:set sw=N`.
* **Window chords:** `C-w w`/`C-w C-w` cycle focus, `C-w h/j/k/l` jump directionally — vim-style, with `C-w ESC` cancelling the chord. They work from text buffers *and* from the Dired pane (which isn't vi-modal; the binding table covers it), so `C-x C-d` followed by `C-w w` ping-pongs focus between directory and buffer.
* **Ex command line:** `:w [file]`, `:q`, `:q!`, `:wq`, `:x`, `:<N>` (goto line), `:set sw=N`, `:%s/.../.../[g]`, `:quotes [N]` (random coder/philosophy quote popup, `N` is 0-based index, `q` to close — including from normal mode if focus moved elsewhere).
* **Ex command line:** `:w [file]`, `:q`, `:q!`, `:wq`, `:x`, `:<N>` (goto line), `:set sw=N`, `:%s/.../.../[g]`, `:wisdom [N]` (`:quotes` alias; random coder/philosophy quote popup, `N` is 0-based index, `q` to close — including from normal mode if focus moved elsewhere).
* **Wisdom over help:** `F1` or `M-x wisdom` shows a random quote. The old QEmacs `C-h` help tree is gone (it never fired on standard terminals anyway — `^H` arrives as `KEY_DEL`; all `describe-*`/manual/faq commands remain on `M-x` with no key bindings); `help-for-help` was deleted.
* **Terminal theme auto-detect:** queries your terminal's foreground/background via OSC 10/11 at startup and adopts the palette, falling back to peach-on-black if the terminal doesn't answer.
### Not Yet (the roadmap)

View file

@ -3758,7 +3758,10 @@ static const CmdDef extra_commands[] = {
"Swap words before and after point",
do_transpose, ESi, "*" "v", CMD_TRANSPOSE_WORDS)
CMD2( "show-bindings", "C-h B, f5",
/* NOTE: the C-h help key tree was removed in favor of the `wisdom`
* command (F1, M-x wisdom). The commands below remain
* available via M-x with no default key bindings (except f5). */
CMD2( "show-bindings", "f5",
"Show current bindings for a given command",
do_show_bindings, ESs,
"s{Show bindings of command: }[command]|command|")
@ -3774,55 +3777,55 @@ static const CmdDef extra_commands[] = {
"v", 1)
// XXX: the commands below should use do_load_file_from_path()
CMDx( "qemacs-hello", "C-h h",
CMDx( "qemacs-hello", "",
"Create a test buffer with various charsets",
do_qemacs_hello)
CMDx( "qemacs-manual", "C-h m",
CMDx( "qemacs-manual", "",
"Show the Quick Emacs manual",
do_qemacs_manual)
CMDx( "qemacs-faq", "C-h C-f",
CMDx( "qemacs-faq", "",
"Show the Quick Emacs FAQ",
do_qemacs_faq)
CMD0( "about-qemacs", "C-h q",
CMD0( "about-qemacs", "",
"Display information about Quick Emacs",
do_about_qemacs)
CMD2( "apropos", "C-h a, C-h C-a",
CMD2( "apropos", "",
"List commands and variables matching a topic",
do_apropos, ESs,
"s{Apropos: }[symbol]|apropos|")
CMD2( "wall-chart", "C-h w",
CMD2( "wall-chart", "",
"List local and global key bindings as a wall chart",
do_wall_chart, ESi, "p")
CMD2( "describe-bindings", "C-h b",
CMD2( "describe-bindings", "",
"List local and global key bindings",
do_describe_bindings, ESi, "p")
CMD2( "describe-buffer", "C-h C-b",
CMD2( "describe-buffer", "",
"Show information about the current buffer",
do_describe_buffer, ESi, "p")
CMD2( "describe-function", "C-h f",
CMD2( "describe-function", "",
"Show information and bindings for a command",
do_describe_function, ESs,
"s{Describe function: }[command]|command|")
CMD2( "describe-screen", "C-h s, C-h C-s",
CMD2( "describe-screen", "",
"Show information about the current screen",
do_describe_screen, ESi, "p")
CMD2( "describe-window", "C-h C-w",
CMD2( "describe-window", "",
"Show information about the current window",
do_describe_window, ESi, "p")
CMD2( "describe-prefix-bindings", "C-h p",
CMD2( "describe-prefix-bindings", "",
"Display a list of commands with a common binding prefix",
do_describe_prefix_bindings, ESs, "s{Binding prefix: }")
CMD2( "describe-C-h", "C-h ?",
CMD2( "describe-C-h", "",
"Display a summary of C-h commands",
do_describe_prefix_bindings, ESs, "@{C-h}")
CMD2( "describe-C-c", "C-c ?, C-c C-h",
CMD2( "describe-C-c", "",
"Display a summary of C-c commands",
do_describe_prefix_bindings, ESs, "@{C-c}")
CMD2( "describe-C-x", "C-x ?, C-x C-h",
CMD2( "describe-C-x", "",
"Display a summary of C-x commands",
do_describe_prefix_bindings, ESs, "@{C-x}")
CMD2( "describe-C-x-RET", "C-x RET ?, C-x RET C-h",
CMD2( "describe-C-x-RET", "",
"Display a summary of C-x RET commands",
do_describe_prefix_bindings, ESs, "@{C-x RET}")

View file

@ -1246,7 +1246,7 @@ static void do_mandelbrot_test(EditState *s, int argval) {
}
static const CmdDef fractal_global_commands[] = {
CMD2( "mandelbrot-test", "C-h m",
CMD2( "mandelbrot-test", "",
"Explore the Mandelbrot set in fractal-mode",
do_mandelbrot_test, ESi, "#" "p")
};

View file

@ -351,15 +351,15 @@ static int vi_parse_substitute(EditState *s, const char *cmd)
return 0;
}
/* Quotes popup: static table of open-source / philosophy quotes.
/* Wisdom popup: static table of open-source / philosophy quotes.
* Display format is always "\"text\" - author" (double quotes outside,
* single quotes for any quotation inside the text). Index N is 0-based. */
typedef struct {
const char *text;
const char *author;
} ViQuote;
} ViWisdom;
static const ViQuote vi_quotes[] = {
static const ViWisdom vi_wisdom[] = {
{ "Talk is cheap. Show me the code.", "Linus Torvalds" },
{ "Every program attempts to expand until it can read mail. Those that can't do this, are replaced by ones that can.", "Jamie Zawinski" },
{ "Programs must be written for people to read, and only incidentally for machines to execute.", "Harold Abelson" },
@ -367,6 +367,7 @@ static const ViQuote vi_quotes[] = {
{ "The best way to predict the future is to invent it.", "Alan Kay" },
{ "Free software is a matter of liberty, not price; think of 'free speech', not 'free beer'.", "Richard M. Stallman" },
{ "Given enough eyeballs, all bugs are shallow.", "Eric S. Raymond" },
{ "Abstraction is for complexity; silicon is for speed.", "MARKMENTAL" },
{ "Stay hungry, stay foolish.", "Stewart Brand" },
{ "The question of whether a computer can think is no more interesting than the question of whether a submarine can swim.", "Edsger W. Dijkstra" },
{ "First, solve the problem. Then, write the code.", "John Johnson" },
@ -375,22 +376,22 @@ static const ViQuote vi_quotes[] = {
{ "A primary cause of complexity is that software vendors uncritically adopt almost any feature that users want.", "Niklaus Wirth" },
};
static int vi_quotes_seeded = 0;
static int vi_wisdom_seeded = 0;
/* Show one quote in a help-style popup (q to close, buffer navigation).
* arg: "" for random, otherwise a 0-based index into vi_quotes. */
static void do_vi_quotes(EditState *s, const char *arg)
* arg: "" for random, otherwise a 0-based index into vi_wisdom. */
static void do_vi_wisdom(EditState *s, const char *arg)
{
int n = countof(vi_quotes);
int n = countof(vi_wisdom);
int idx;
EditBuffer *b;
while (*arg == ' ' || *arg == '\t')
arg++;
if (*arg == '\0') {
if (!vi_quotes_seeded) {
if (!vi_wisdom_seeded) {
srand((unsigned)time(NULL));
vi_quotes_seeded = 1;
vi_wisdom_seeded = 1;
}
idx = rand() % n;
} else {
@ -399,40 +400,49 @@ static void do_vi_quotes(EditState *s, const char *arg)
while (*end == ' ' || *end == '\t')
end++;
if (end == arg || *end != '\0' || val < 0 || val >= n) {
put_status(s, "Invalid quotes index '%s' (0..%d)", arg, n - 1);
put_status(s, "Invalid wisdom index '%s' (0..%d)", arg, n - 1);
return;
}
idx = (int)val;
}
b = qe_new_buffer(s->qs, "*Quotes*", BF_SYSTEM | BF_UTF8);
b = qe_new_buffer(s->qs, "*Wisdom*", BF_SYSTEM | BF_UTF8);
if (!b)
return;
eb_printf(b, "\"%s\" - %s\n\n-- press q to close --\n",
vi_quotes[idx].text, vi_quotes[idx].author);
vi_wisdom[idx].text, vi_wisdom[idx].author);
{
EditState *e = show_popup(s, b, "Quotes");
EditState *e = show_popup(s, b, "Wisdom");
if (e)
e->wrap = WRAP_WORD; /* long quotes word-wrap instead of truncating */
}
}
/* Find the topmost Quotes popup, if any. Used so bare `q` in vi normal
/* Find the topmost Wisdom popup, if any. Used so bare `q` in vi normal
* mode can dismiss it even after focus moved back to a text window
* (e.g. via C-w w / C-x o), where the popup's own `q` binding would
* otherwise never fire because dispatch is active-window-only. */
static EditState *vi_find_quotes_popup(QEmacsState *qs)
static EditState *vi_find_wisdom_popup(QEmacsState *qs)
{
EditState *e, *found = NULL;
for (e = qs->first_window; e; e = e->next_window) {
if ((e->flags & WF_POPUP) && e->b &&
!strcmp(e->b->name, "*Quotes*")) {
!strcmp(e->b->name, "*Wisdom*")) {
found = e;
}
}
return found;
}
/* Global `wisdom` command body: show a random quote (F1, M-x wisdom).
* NOTE: no C-h binding on purpose: on standard terminals the tty layer
* translates ^H to KEY_DEL (KBS_CONTROL_H), so a "C-h ..." binding would
* never fire -- the same reason the old C-h help tree felt dead. */
static void do_wisdom(EditState *s)
{
do_vi_wisdom(s, "");
}
/* Ex command prompt callback */
static void vi_ex_callback(void *opaque, char *buf, CompletionDef *completion)
{
@ -504,8 +514,8 @@ static void vi_ex_callback(void *opaque, char *buf, CompletionDef *completion)
(strstart(cmd, "s", &arg) && *arg == '/')) {
if (vi_parse_substitute(s, cmd) < 0)
put_status(s, "Invalid substitute command");
} else if (strcmp(cmd, "quotes") == 0) {
do_vi_quotes(s, arg);
} else if (strcmp(cmd, "wisdom") == 0 || strcmp(cmd, "quotes") == 0) {
do_vi_wisdom(s, arg);
} else if (cmd[0] >= '0' && cmd[0] <= '9') {
/* :<number> goes to that line */
do_goto_line(s, strtol(cmd, NULL, 10), 0);
@ -808,12 +818,12 @@ static int vi_normal_key(EditState *s, int key)
minibuffer_edit(s, "", ":", NULL, NULL, vi_ex_callback, s);
return 1;
case 'q': {
/* Dismiss a stranded Quotes popup when focus has moved back to
/* Dismiss a stranded Wisdom popup when focus has moved back to
* a text window. Guard on no pending / no visual so `dq` and
* visual flows keep their existing behavior. */
EditState *qp;
if (!s->vi_visual_active && !s->vi_pending) {
qp = vi_find_quotes_popup(s->qs);
qp = vi_find_wisdom_popup(s->qs);
if (qp) {
do_popup_exit(qp);
return 1;
@ -886,6 +896,7 @@ static const CmdDef vi_commands[] = {
CMD0("vi-mode", "", "Toggle Vi/Evil modal editing", do_vi_mode)
CMD0("vi-normal-mode", "", "Enter Vi normal mode", do_vi_normal_mode)
CMD0("vi-insert-mode", "", "Enter Vi insert mode", do_vi_insert_mode)
CMD0("wisdom", "f1", "Show a random wisdom quote", do_wisdom)
};
static int vi_init(QEmacsState *qs)

View file

@ -272,15 +272,15 @@ is simply repeated <code>N</code> times.
<td valign="middle" align="left">[Index]</td>
<td valign="middle" align="left">[<a href="#SEC_About" title="About (help)"> ? </a>]</td>
</tr></table>
<h2 class="section">3.2 Help</h2>
<h2 class="section">3.2 Wisdom</h2>
<p>You can press <kbd>C-h b</kbd> to have the list of all the currently active
bindings, including the ones of the current editing mode.
<p>Press <kbd>F1</kbd> for a random wisdom quote,
or <kbd>M-x wisdom</kbd>. In vi normal mode, <code>:wisdom [N]</code> shows
quote number <code>N</code> (0-based), <code>:quotes</code> is kept as an alias.
Press <kbd>q</kbd> to close the popup.
</p>
<div class="example">
<pre class="example">C-h C-h, F1 : help-for-help
C-h b : describe-bindings
C-h c : describe-key-briefly
<pre class="example">F1 : wisdom
</pre></div>
<hr>
@ -524,12 +524,10 @@ A-= : compare-windows
<td valign="middle" align="left">[Index]</td>
<td valign="middle" align="left">[<a href="#SEC_About" title="About (help)"> ? </a>]</td>
</tr></table>
<h2 class="section">3.10 Help functions</h2>
<h2 class="section">3.10 Wisdom</h2>
<div class="example">
<pre class="example">C-h C-h, f1 : help-for-help
C-h b : describe-bindings
C-h c, C-h k : describe-key-briefly
<pre class="example">F1 : wisdom
</pre></div>
<hr>

View file

@ -132,15 +132,15 @@ optional number is used to provide a numeric argument to the commands which
can handle them. If the command cannot handle a numerical argument, it
is simply repeated @code{N} times.
@section Help
@section Wisdom
You can press @kbd{C-h b} to have the list of all the currently active
bindings, including the ones of the current editing mode.
Press @kbd{F1} for a random wisdom quote,
or @kbd{M-x wisdom}. In vi normal mode, @code{:wisdom [N]} shows
quote number @code{N} (0-based), @code{:quotes} is kept as an alias.
Press @kbd{q} to close the popup.
@example
C-h C-h, F1 : help-for-help
C-h b : describe-bindings
C-h c : describe-key-briefly
F1 : wisdom
@end example
@section Simple commands
@ -248,12 +248,10 @@ C-x f : toggle-full-screen
A-= : compare-windows
@end example
@section Help functions
@section Wisdom
@example
C-h C-h, f1 : help-for-help
C-h b : describe-bindings
C-h c, C-h k : describe-key-briefly
F1 : wisdom
@end example
@section International

33
qe.c
View file

@ -10728,23 +10728,6 @@ EditBuffer *new_help_buffer(EditState *s)
return qe_new_buffer(s->qs, "*Help*", flags);
}
void do_help_for_help(EditState *s)
{
EditBuffer *b;
b = new_help_buffer(s);
if (!b)
return;
eb_style_puts(b, DESCRIBE_STYLE_HEAD, "QEmacs help for help - Press q to quit:\n\n");
eb_puts(b,
"C-h C-h Show this help\n"
"C-h b Display table of all key bindings\n"
"C-h c Describe key briefly\n"
);
show_popup(s, b, "QEmacs help for help");
}
#ifdef CONFIG_WIN32
void qe_event_init(QEmacsState *qs)
@ -12166,22 +12149,22 @@ static const CmdDef basic_commands[] = {
#endif
/*---------------- Help ----------------*/
/* NOTE: the C-h help tree was removed in favor of the `wisdom`
* command (F1, M-x wisdom). The describe/trace commands
* below remain available via M-x with no default key bindings. */
CMD2( "toggle-trace-mode", "C-h d",
CMD2( "toggle-trace-mode", "",
"Enable or disable trace mode: show the *Trace* buffer with debugging info",
do_toggle_trace_mode, ESi, "P")
CMD2( "set-trace-options", "C-h t",
CMD2( "set-trace-options", "",
"Select the trace options: all, none, command, debug, emulate, shell, tty, pty",
do_set_trace_options, ESs,
"s{Trace options: }[trace]|trace|")
CMD2( "describe-key-briefly", "C-h c, C-h k, f6",
CMD2( "describe-key-briefly", "f6",
"Describe a key binding",
do_describe_key_briefly, ESsi,
"s{Describe key: }|keys|"
"P")
CMD0( "help-for-help", "C-h C-h, f1",
"Show the qemacs help window",
do_help_for_help)
/*---------------- International ----------------*/
@ -12655,9 +12638,9 @@ static int qe_init(QEmacsState *qs, int argc, char **argv)
}
#endif
#ifdef CONFIG_TINY
put_status(s, "Tiny Evil QEmacs %s - Press F1 for help", QE_VERSION);
put_status(s, "Tiny Evil QEmacs %s", QE_VERSION);
#else
put_status(s, "Evil QEmacs %s - Press F1 for help", QE_VERSION);
put_status(s, "Evil QEmacs %s", QE_VERSION);
b = qe_find_buffer_name(qs, "*errors*");
if (b != NULL) {
show_popup(s, b, "Errors");

1
qe.h
View file

@ -1793,7 +1793,6 @@ void do_hide_window(EditState *s, int set);
void do_delete_hidden_windows(EditState *s);
void do_describe_key_briefly(EditState *s, const char *keystr, int argval);
EditBuffer *new_help_buffer(EditState *s);
void do_help_for_help(EditState *s);
void qe_event_init(QEmacsState *qs);
void window_get_min_size(EditState *s, int *w_ptr, int *h_ptr);
int window_resize(EditState *s, int target_w, int target_h);

View file

@ -1545,7 +1545,7 @@ static const CmdDef isearch_commands[] = {
CMD2( "isearch-exit", "RET, QUIT",
"Exit isearch, leave point at location found",
isearch_exit, ESi, "k")
CMDx( "isearch-mode-help", "f1, C-h",
CMDx( "isearch-mode-help", "",
"show the help page for isearch",
isearch_mode_help, ES, "")
CMD2( "isearch-printing-char", "TAB, C-j",

View file

@ -287,9 +287,14 @@ def run_tqe(binary, keys, content, timeout, keep_dir=None, want_path=None):
# in one chunk while it is busy drawing, and the terminal input layer
# then composes ESC + following byte into a META key, which changes
# what the vi layer sees.
# A test may pass a list of byte strings instead: each element is
# written atomically, which is how real terminals deliver multi-byte
# sequences such as function keys (ESC O P) or META chords (ESC x).
segs = keys if isinstance(keys, list) else [keys[i:i + 1]
for i in range(len(keys))]
idle = 0.0
for i in range(len(keys)):
os.write(fd, keys[i:i + 1])
for seg in segs:
os.write(fd, seg)
idle = 0.0
while idle < 0.08:
data = read_avail(0.02)
@ -351,15 +356,22 @@ class T:
def __init__(self, name, keys, init, want,
cat='general', status=None, timeout=5.0,
want_path=None):
want_path=None, want_out=None):
self.name = name
self.keys = keys
self.keys = keys # bytes (sent byte-at-a-time) or list of
# bytes (each element written atomically,
# for F-keys / META chords)
self.init = init
self.want = want
self.cat = cat
self.status = status # str or list of str, or None
self.timeout = timeout
self.want_path = want_path # may contain {DIR}
self.want_out = want_out # bytes or list of bytes that must
# appear in the ANSI-stripped
# terminal output (for popups,
# whose body never hits the file
# or the status line)
# -------------------------------------------------------------------------
@ -529,11 +541,36 @@ TESTS = [
cat='status', status='-- NORMAL --'),
T('status_insert', b'i\x1b:wq\r', 'x\n', b'x\n',
cat='status', status=['-- INSERT --', '-- NORMAL --']),
# ---- wisdom quotes ----
T('wisdom_ex', b':wisdom 0\rq:wq\r', 'hello\n', b'hello\n',
cat='wisdom',
want_out=[b'Wisdom', b'Talk is cheap', b'press q to close']),
T('wisdom_alias', b':quotes 0\rq:wq\r', 'hello\n', b'hello\n',
cat='wisdom',
want_out=[b'Wisdom', b'Talk is cheap']),
T('wisdom_bad_index', b':wisdom 99\r:wq\r', 'hello\n', b'hello\n',
cat='wisdom', status='Invalid wisdom index'),
# F1 arrives as one SS3 sequence on real terminals; send atomically
# (byte-at-a-time would let vi eat the leading ESC).
T('wisdom_f1', [b'\x1bOP', b'q:wq\r'], 'hello\n', b'hello\n',
cat='wisdom',
want_out=[b'Wisdom', b'press q to close']),
# M-x itself is unreachable as a META chord in evil mode (the vi
# layer consumes META-x as `x`), but F2 is bound to execute-command,
# so drive M-x wisdom through F2 (vt100 SS3 Q, sent atomically).
T('wisdom_mx', [b'\x1bOQ', b'wisdom\r', b'q:wq\r'],
'hello\n', b'hello\n',
cat='wisdom',
want_out=[b'Wisdom', b'press q to close']),
]
def run_one(t, binary, workdir, default_timeout):
keys = t.keys.replace(b'{DIR}', workdir.encode())
if isinstance(t.keys, list):
keys = [k.replace(b'{DIR}', workdir.encode()) for k in t.keys]
else:
keys = t.keys.replace(b'{DIR}', workdir.encode())
want_path = t.want_path.replace('{DIR}', workdir) if t.want_path else None
file_bytes, term_out, frames, status = run_tqe(
binary, keys, t.init, t.timeout or default_timeout,
@ -553,6 +590,13 @@ def run_one(t, binary, workdir, default_timeout):
if not any(w in line for line in frames):
errors.append('status %r not seen on the status line'
% (w,))
if t.want_out:
clean = strip_ansi(term_out)
wanted = t.want_out if isinstance(t.want_out, list) else [t.want_out]
for w in wanted:
if w not in clean:
errors.append('output %r not seen on the terminal'
% (w,))
return errors

View file

@ -703,11 +703,11 @@ static const CmdDef var_commands[] = {
"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",
CMD2( "describe-variable", "",
"Show information for a variable",
do_describe_variable, ESs,
"s{Describe variable: }[variable]|variable|")
CMD0( "show-all-variables", "C-h C-v",
CMD0( "show-all-variables", "",
"Show value and information for all variables",
do_show_all_variables)
};

2
x11.c
View file

@ -2036,7 +2036,7 @@ static void x11_list_fonts(EditState *s, int argval)
}
static const CmdDef x11_commands[] = {
CMD2( "x11-list-fonts", "C-h C-f",
CMD2( "x11-list-fonts", "",
"List the X11 fonts in a popup window",
x11_list_fonts, ESi, "P")
};