mirror of
https://git.savannah.gnu.org/git/emacs.git
synced 2024-11-26 07:33:47 +00:00
Allow minibuffer to stay in its original frame. Tidy up this area.
* doc/emacs/mini.texi (Basic Minibuffer): Add an entry for minibuffer-follows-selected-frame. * doc/lispref/minibuf.texi (Minibuffer Misc): Describe the new parameter to minibufferp, LIVE. * etc/NEWS: Add an entry describing the new minibuffer strategy. * lisp/cus-start.el (minibuffer-prompt-properties--setter): Add an entry for minibuffer-follows-selected-frame. * lisp/minibuffer.el (minibuffer-message): Check for the current buffer being an _active_ minibuffer rather than merely a minibuffer. * src/frame.c (do_switch_frame): Call move_minibuffer_onto_frame. * src/lisp.h (Top level): Add prototypes for move_minibuffer_onto_frame and is_minibuffer. * src/minibuf.c (minibuf_follows_frame): New function which ignores local and let-bound values of minibuffer-follows-selected-frame. (choose_minibuf_frame): Reformulate this function to reuse a minibuffer window where possible, and to ensure no other frame has its minibuffer current, but only when `minibuffer-follows-selected-frame'. (move_minibuffer_onto_frame): New function. (live_minibuffer_p): New function. (Fminibufferp): Add a new &optional parameter LIVE. Reformulate, possibly calling live_minibuffer_p. (read_minibuf): move the incrementation of minibuf_level to before the call of choose_minibuf_frame. Empty the miniwindows of frames without an active minibuffer, rather than of all but the current frame. (is_minibuffer): New function. (read_minibuf_unwind): Note the miniwindow being restored and resize all other miniwindows to zero size. (minibuffer-follows-selected-frame): New configuration variable. * src/window.c (candidate_window_p): In some scenarios, check the miniwindow holds an active minibuffer. * src/xdisp.c (get_window_cursor_type): Suppress the cursor for non-active miniwindows, regardless of minibuf_level.
This commit is contained in:
parent
bfd3124202
commit
2ecbf4cfae
@ -69,6 +69,17 @@ the minibuffer for a few seconds, or until you type something; then
|
||||
the minibuffer comes back. While the minibuffer is in use, Emacs does
|
||||
not echo keystrokes.
|
||||
|
||||
@vindex minibuffer-follows-selected-frame
|
||||
While using the minibuffer, you can switch to a different frame,
|
||||
perhaps to note text you need to enter (@pxref{Frame Commands}). By
|
||||
default, the active minibuffer moves to this new frame. If you set
|
||||
the user option @code{minibuffer-follows-selected-frame} to
|
||||
@code{nil}, then the minibuffer stays in the frame where you opened
|
||||
it, and you must switch back to that frame in order to complete (or
|
||||
abort) the current command. Note that the effect of the command, when
|
||||
you finally finish using the minibuffer, always takes place in the
|
||||
frame where you first opened it.
|
||||
|
||||
@node Minibuffer File
|
||||
@section Minibuffers for File Names
|
||||
|
||||
|
@ -2586,10 +2586,12 @@ The minibuffer command @code{next-matching-history-element} (normally
|
||||
@node Minibuffer Misc
|
||||
@section Minibuffer Miscellany
|
||||
|
||||
@defun minibufferp &optional buffer-or-name
|
||||
@defun minibufferp &optional buffer-or-name live
|
||||
This function returns non-@code{nil} if @var{buffer-or-name} is a
|
||||
minibuffer. If @var{buffer-or-name} is omitted, it tests the current
|
||||
buffer.
|
||||
minibuffer. If @var{buffer-or-name} is omitted or @code{nil}, it
|
||||
tests the current buffer. When @var{live} is non-@code{nil}, the
|
||||
function returns non-@code{nil} only when @var{buffer-or-name} is an
|
||||
active minibuffer.
|
||||
@end defun
|
||||
|
||||
@defvar minibuffer-setup-hook
|
||||
|
11
etc/NEWS
11
etc/NEWS
@ -88,6 +88,17 @@ useful on systems such as FreeBSD which ships only with "etc/termcap".
|
||||
** Minibuffer scrolling is now conservative by default.
|
||||
This is controlled by the new variable 'scroll-minibuffer-conservatively'.
|
||||
|
||||
+++
|
||||
** Improved handling of minibuffers on switching frames.
|
||||
By default, an active minibuffer now moves to a newly selected frame.
|
||||
When continuing the current command (by completing the minibuffer
|
||||
action), the effect happens in the frame where the minibuffer was
|
||||
first opened. An alternative behavior is available by customizing
|
||||
'minibuffer-follows-selected-frame' to nil. Here, the minibuffer
|
||||
stays in the frame where you first opened it, and you must switch back
|
||||
to this frame to continue or abort its command. The old, somewhat
|
||||
unsystematic behavior is no longer available.
|
||||
|
||||
+++
|
||||
** New system for displaying documentation for groups of functions.
|
||||
This can either be used by saying 'M-x shortdoc-display-group' and
|
||||
|
@ -394,6 +394,7 @@ Leaving \"Default\" unchecked is equivalent with specifying a default of
|
||||
;; (directory :format "%v"))))
|
||||
(load-prefer-newer lisp boolean "24.4")
|
||||
;; minibuf.c
|
||||
(minibuffer-follows-selected-frame minibuffer boolean "28.1")
|
||||
(enable-recursive-minibuffers minibuffer boolean)
|
||||
(history-length minibuffer
|
||||
(choice (const :tag "Infinite" t) integer)
|
||||
|
@ -701,7 +701,7 @@ The text is displayed for `minibuffer-message-timeout' seconds,
|
||||
or until the next input event arrives, whichever comes first.
|
||||
Enclose MESSAGE in [...] if this is not yet the case.
|
||||
If ARGS are provided, then pass MESSAGE through `format-message'."
|
||||
(if (not (minibufferp (current-buffer)))
|
||||
(if (not (minibufferp (current-buffer) t))
|
||||
(progn
|
||||
(if args
|
||||
(apply #'message message args)
|
||||
|
@ -1482,6 +1482,7 @@ do_switch_frame (Lisp_Object frame, int track, int for_deletion, Lisp_Object nor
|
||||
#endif
|
||||
internal_last_event_frame = Qnil;
|
||||
|
||||
move_minibuffer_onto_frame ();
|
||||
return frame;
|
||||
}
|
||||
|
||||
|
@ -4336,6 +4336,8 @@ extern void clear_regexp_cache (void);
|
||||
|
||||
extern Lisp_Object Vminibuffer_list;
|
||||
extern Lisp_Object last_minibuf_string;
|
||||
extern void move_minibuffer_onto_frame (void);
|
||||
extern bool is_minibuffer (EMACS_INT, Lisp_Object);
|
||||
extern Lisp_Object get_minibuffer (EMACS_INT);
|
||||
extern void init_minibuf_once (void);
|
||||
extern void syms_of_minibuf (void);
|
||||
|
166
src/minibuf.c
166
src/minibuf.c
@ -64,6 +64,12 @@ static Lisp_Object minibuf_prompt;
|
||||
static ptrdiff_t minibuf_prompt_width;
|
||||
|
||||
|
||||
static bool
|
||||
minibuf_follows_frame (void)
|
||||
{
|
||||
return !NILP (Fdefault_toplevel_value (Qminibuffer_follows_selected_frame));
|
||||
}
|
||||
|
||||
/* Put minibuf on currently selected frame's minibuffer.
|
||||
We do this whenever the user starts a new minibuffer
|
||||
or when a minibuffer exits. */
|
||||
@ -76,37 +82,72 @@ choose_minibuf_frame (void)
|
||||
&& !EQ (minibuf_window, XFRAME (selected_frame)->minibuffer_window))
|
||||
{
|
||||
struct frame *sf = XFRAME (selected_frame);
|
||||
Lisp_Object buffer;
|
||||
|
||||
/* I don't think that any frames may validly have a null minibuffer
|
||||
window anymore. */
|
||||
if (NILP (sf->minibuffer_window))
|
||||
emacs_abort ();
|
||||
|
||||
/* Under X, we come here with minibuf_window being the
|
||||
minibuffer window of the unused termcap window created in
|
||||
init_window_once. That window doesn't have a buffer. */
|
||||
buffer = XWINDOW (minibuf_window)->contents;
|
||||
if (BUFFERP (buffer))
|
||||
/* Use set_window_buffer instead of Fset_window_buffer (see
|
||||
discussion of bug#11984, bug#12025, bug#12026). */
|
||||
set_window_buffer (sf->minibuffer_window, buffer, 0, 0);
|
||||
minibuf_window = sf->minibuffer_window;
|
||||
/* If we've still got another minibuffer open, use its mini-window
|
||||
instead. */
|
||||
if (minibuf_level && !minibuf_follows_frame ())
|
||||
{
|
||||
Lisp_Object buffer = get_minibuffer (minibuf_level);
|
||||
Lisp_Object tail, frame;
|
||||
|
||||
FOR_EACH_FRAME (tail, frame)
|
||||
if (EQ (XWINDOW (XFRAME (frame)->minibuffer_window)->contents,
|
||||
buffer))
|
||||
{
|
||||
minibuf_window = XFRAME (frame)->minibuffer_window;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Make sure no other frame has a minibuffer as its selected window,
|
||||
because the text would not be displayed in it, and that would be
|
||||
confusing. Only allow the selected frame to do this,
|
||||
and that only if the minibuffer is active. */
|
||||
{
|
||||
Lisp_Object tail, frame;
|
||||
if (minibuf_follows_frame ())
|
||||
/* Make sure no other frame has a minibuffer as its selected window,
|
||||
because the text would not be displayed in it, and that would be
|
||||
confusing. Only allow the selected frame to do this,
|
||||
and that only if the minibuffer is active. */
|
||||
{
|
||||
Lisp_Object tail, frame;
|
||||
|
||||
FOR_EACH_FRAME (tail, frame)
|
||||
if (MINI_WINDOW_P (XWINDOW (FRAME_SELECTED_WINDOW (XFRAME (frame))))
|
||||
&& !(EQ (frame, selected_frame)
|
||||
&& minibuf_level > 0))
|
||||
Fset_frame_selected_window (frame, Fframe_first_window (frame), Qnil);
|
||||
}
|
||||
FOR_EACH_FRAME (tail, frame)
|
||||
if (MINI_WINDOW_P (XWINDOW (FRAME_SELECTED_WINDOW (XFRAME (frame))))
|
||||
&& !(EQ (frame, selected_frame)
|
||||
&& minibuf_level > 0))
|
||||
Fset_frame_selected_window (frame, Fframe_first_window (frame),
|
||||
Qnil);
|
||||
}
|
||||
}
|
||||
|
||||
/* If `minibuffer_follows_selected_frame' and we have a minibuffer, move it
|
||||
from its current frame to the selected frame. This function is
|
||||
intended to be called from `do_switch_frame' in frame.c. */
|
||||
void move_minibuffer_onto_frame (void)
|
||||
{
|
||||
if (!minibuf_level)
|
||||
return;
|
||||
if (!minibuf_follows_frame ())
|
||||
return;
|
||||
if (FRAMEP (selected_frame)
|
||||
&& FRAME_LIVE_P (XFRAME (selected_frame))
|
||||
&& !EQ (minibuf_window, XFRAME (selected_frame)->minibuffer_window))
|
||||
{
|
||||
struct frame *sf = XFRAME (selected_frame);
|
||||
Lisp_Object old_frame = XWINDOW (minibuf_window)->frame;
|
||||
struct frame *of = XFRAME (old_frame);
|
||||
Lisp_Object buffer = XWINDOW (minibuf_window)->contents;
|
||||
|
||||
set_window_buffer (sf->minibuffer_window, buffer, 0, 0);
|
||||
minibuf_window = sf->minibuffer_window;
|
||||
if (XWINDOW (minibuf_window)->frame == selected_frame)
|
||||
/* The minibuffer might be on another frame. */
|
||||
Fset_frame_selected_window (selected_frame, sf->minibuffer_window,
|
||||
Qnil);
|
||||
set_window_buffer (of->minibuffer_window, get_minibuffer (0), 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
DEFUN ("active-minibuffer-window", Factive_minibuffer_window,
|
||||
@ -261,15 +302,31 @@ read_minibuf_noninteractive (Lisp_Object prompt, bool expflag,
|
||||
return val;
|
||||
}
|
||||
|
||||
DEFUN ("minibufferp", Fminibufferp,
|
||||
Sminibufferp, 0, 1, 0,
|
||||
doc: /* Return t if BUFFER is a minibuffer.
|
||||
No argument or nil as argument means use current buffer as BUFFER.
|
||||
BUFFER can be a buffer or a buffer name. */)
|
||||
(Lisp_Object buffer)
|
||||
/* Return true when BUFFER is an active minibuffer. */
|
||||
static bool
|
||||
live_minibuffer_p (Lisp_Object buffer)
|
||||
{
|
||||
Lisp_Object tem;
|
||||
EMACS_INT i;
|
||||
|
||||
if (EQ (buffer, Fcar (Vminibuffer_list)))
|
||||
/* *Minibuf-0* is never active. */
|
||||
return false;
|
||||
tem = Fcdr (Vminibuffer_list);
|
||||
for (i = 1; i <= minibuf_level; i++, tem = Fcdr (tem))
|
||||
if (EQ (Fcar (tem), buffer))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
DEFUN ("minibufferp", Fminibufferp,
|
||||
Sminibufferp, 0, 2, 0,
|
||||
doc: /* Return t if BUFFER is a minibuffer.
|
||||
No argument or nil as argument means use current buffer as BUFFER.
|
||||
BUFFER can be a buffer or a buffer name. If LIVE is non-nil, then
|
||||
return t only if BUFFER is an active minibuffer. */)
|
||||
(Lisp_Object buffer, Lisp_Object live)
|
||||
{
|
||||
if (NILP (buffer))
|
||||
buffer = Fcurrent_buffer ();
|
||||
else if (STRINGP (buffer))
|
||||
@ -277,8 +334,10 @@ BUFFER can be a buffer or a buffer name. */)
|
||||
else
|
||||
CHECK_BUFFER (buffer);
|
||||
|
||||
tem = Fmemq (buffer, Vminibuffer_list);
|
||||
return ! NILP (tem) ? Qt : Qnil;
|
||||
return (NILP (live)
|
||||
? !NILP (Fmemq (buffer, Vminibuffer_list))
|
||||
: live_minibuffer_p (buffer))
|
||||
? Qt : Qnil;
|
||||
}
|
||||
|
||||
DEFUN ("minibuffer-prompt-end", Fminibuffer_prompt_end,
|
||||
@ -433,6 +492,8 @@ read_minibuf (Lisp_Object map, Lisp_Object initial, Lisp_Object prompt,
|
||||
return unbind_to (count, val);
|
||||
}
|
||||
|
||||
minibuf_level++; /* Before calling choose_minibuf_frame. */
|
||||
|
||||
/* Choose the minibuffer window and frame, and take action on them. */
|
||||
|
||||
/* Prepare for restoring the current buffer since choose_minibuf_frame
|
||||
@ -484,7 +545,6 @@ read_minibuf (Lisp_Object map, Lisp_Object initial, Lisp_Object prompt,
|
||||
= Fcons (Fthis_command_keys_vector (), minibuf_save_list);
|
||||
|
||||
record_unwind_protect_void (read_minibuf_unwind);
|
||||
minibuf_level++;
|
||||
/* We are exiting the minibuffer one way or the other, so run the hook.
|
||||
It should be run before unwinding the minibuf settings. Do it
|
||||
separately from read_minibuf_unwind because we need to make sure that
|
||||
@ -566,8 +626,8 @@ read_minibuf (Lisp_Object map, Lisp_Object initial, Lisp_Object prompt,
|
||||
if (minibuf_level == 1 || !EQ (minibuf_window, selected_window))
|
||||
minibuf_selected_window = selected_window;
|
||||
|
||||
/* Empty out the minibuffers of all frames other than the one
|
||||
where we are going to display one now.
|
||||
/* Empty out the minibuffers of all frames, except those frames
|
||||
where there is an active minibuffer.
|
||||
Set them to point to ` *Minibuf-0*', which is always empty. */
|
||||
empty_minibuf = get_minibuffer (0);
|
||||
|
||||
@ -575,12 +635,17 @@ read_minibuf (Lisp_Object map, Lisp_Object initial, Lisp_Object prompt,
|
||||
{
|
||||
Lisp_Object root_window = Fframe_root_window (frame);
|
||||
Lisp_Object mini_window = XWINDOW (root_window)->next;
|
||||
Lisp_Object buffer;
|
||||
|
||||
if (! NILP (mini_window) && ! EQ (mini_window, minibuf_window)
|
||||
&& !NILP (Fwindow_minibuffer_p (mini_window)))
|
||||
/* Use set_window_buffer instead of Fset_window_buffer (see
|
||||
discussion of bug#11984, bug#12025, bug#12026). */
|
||||
set_window_buffer (mini_window, empty_minibuf, 0, 0);
|
||||
if (!NILP (mini_window) && !EQ (mini_window, minibuf_window)
|
||||
&& !NILP (Fwindow_minibuffer_p (mini_window)))
|
||||
{
|
||||
buffer = XWINDOW (mini_window)->contents;
|
||||
if (!live_minibuffer_p (buffer))
|
||||
/* Use set_window_buffer instead of Fset_window_buffer (see
|
||||
discussion of bug#11984, bug#12025, bug#12026). */
|
||||
set_window_buffer (mini_window, empty_minibuf, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Display this minibuffer in the proper window. */
|
||||
@ -714,6 +779,16 @@ read_minibuf (Lisp_Object map, Lisp_Object initial, Lisp_Object prompt,
|
||||
return val;
|
||||
}
|
||||
|
||||
/* Return true if BUF is a particular existing minibuffer. */
|
||||
bool
|
||||
is_minibuffer (EMACS_INT depth, Lisp_Object buf)
|
||||
{
|
||||
Lisp_Object tail = Fnthcdr (make_fixnum (depth), Vminibuffer_list);
|
||||
return
|
||||
!NILP (tail)
|
||||
&& EQ (Fcar (tail), buf);
|
||||
}
|
||||
|
||||
/* Return a buffer to be used as the minibuffer at depth `depth'.
|
||||
depth = 0 is the lowest allowed argument, and that is the value
|
||||
used for nonrecursive minibuffer invocations. */
|
||||
@ -775,6 +850,7 @@ read_minibuf_unwind (void)
|
||||
{
|
||||
Lisp_Object old_deactivate_mark;
|
||||
Lisp_Object window;
|
||||
Lisp_Object future_mini_window;
|
||||
|
||||
/* If this was a recursive minibuffer,
|
||||
tie the minibuffer window back to the outer level minibuffer buffer. */
|
||||
@ -809,6 +885,7 @@ read_minibuf_unwind (void)
|
||||
if (FRAME_LIVE_P (XFRAME (WINDOW_FRAME (XWINDOW (temp)))))
|
||||
minibuf_window = temp;
|
||||
#endif
|
||||
future_mini_window = Fcar (minibuf_save_list);
|
||||
minibuf_save_list = Fcdr (minibuf_save_list);
|
||||
|
||||
/* Erase the minibuffer we were using at this level. */
|
||||
@ -825,7 +902,8 @@ read_minibuf_unwind (void)
|
||||
|
||||
/* When we get to the outmost level, make sure we resize the
|
||||
mini-window back to its normal size. */
|
||||
if (minibuf_level == 0)
|
||||
if (minibuf_level == 0
|
||||
|| !EQ (selected_frame, WINDOW_FRAME (XWINDOW (future_mini_window))))
|
||||
resize_mini_window (XWINDOW (window), 0);
|
||||
|
||||
/* Deal with frames that should be removed when exiting the
|
||||
@ -1911,6 +1989,8 @@ syms_of_minibuf (void)
|
||||
staticpro (&minibuf_prompt);
|
||||
staticpro (&minibuf_save_list);
|
||||
|
||||
DEFSYM (Qminibuffer_follows_selected_frame,
|
||||
"minibuffer-follows-selected-frame");
|
||||
DEFSYM (Qcompletion_ignore_case, "completion-ignore-case");
|
||||
DEFSYM (Qminibuffer_default, "minibuffer-default");
|
||||
Fset (Qminibuffer_default, Qnil);
|
||||
@ -1954,6 +2034,14 @@ For example, `eval-expression' uses this. */);
|
||||
The function is called with the arguments passed to `read-buffer'. */);
|
||||
Vread_buffer_function = Qnil;
|
||||
|
||||
DEFVAR_BOOL ("minibuffer-follows-selected-frame", minibuffer_follows_selected_frame,
|
||||
doc: /* Non-nil means the active minibuffer always displays on the selected frame.
|
||||
Nil means that a minibuffer will appear only in the frame which created it.
|
||||
|
||||
Any buffer local or dynamic binding of this variable is ignored. Only the
|
||||
default top level value is used. */);
|
||||
minibuffer_follows_selected_frame = 1;
|
||||
|
||||
DEFVAR_BOOL ("read-buffer-completion-ignore-case",
|
||||
read_buffer_completion_ignore_case,
|
||||
doc: /* Non-nil means completion ignores case when reading a buffer name. */);
|
||||
|
@ -2643,8 +2643,10 @@ candidate_window_p (Lisp_Object window, Lisp_Object owindow,
|
||||
/* To qualify as candidate, it's not sufficient for WINDOW's frame
|
||||
to just share the minibuffer window - it must be active as well
|
||||
(see Bug#24500). */
|
||||
candidate_p = (EQ (XWINDOW (all_frames)->frame, w->frame)
|
||||
|| EQ (XWINDOW (all_frames)->frame, FRAME_FOCUS_FRAME (f)));
|
||||
candidate_p = ((EQ (XWINDOW (all_frames)->frame, w->frame)
|
||||
|| (EQ (f->minibuffer_window, all_frames)
|
||||
&& EQ (XWINDOW (all_frames)->frame, FRAME_FOCUS_FRAME (f))))
|
||||
&& !is_minibuffer (0, XWINDOW (all_frames)->contents));
|
||||
else if (FRAMEP (all_frames))
|
||||
candidate_p = EQ (all_frames, w->frame);
|
||||
|
||||
|
@ -31224,7 +31224,9 @@ get_window_cursor_type (struct window *w, struct glyph *glyph, int *width,
|
||||
{
|
||||
*active_cursor = false;
|
||||
|
||||
if (MINI_WINDOW_P (w) && minibuf_level == 0)
|
||||
if (MINI_WINDOW_P (w) &&
|
||||
(minibuf_level == 0
|
||||
|| is_minibuffer (0, w->contents)))
|
||||
return NO_CURSOR;
|
||||
|
||||
non_selected = true;
|
||||
|
Loading…
Reference in New Issue
Block a user