From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.io!.POSTED.blaine.gmane.org!not-for-mail From: Arthur Miller Newsgroups: gmane.emacs.devel Subject: Re: Control help- and Info-mode buffers from other buffers Date: Tue, 30 May 2023 15:31:27 +0200 Message-ID: References: <87h6ruf09e.fsf@ledu-giraud.fr> Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" Injection-Info: ciao.gmane.io; posting-host="blaine.gmane.org:116.202.254.214"; logging-data="30423"; mail-complaints-to="usenet@ciao.gmane.io" User-Agent: Gnus/5.13 (Gnus v5.13) Cc: emacs-devel@gnu.org To: Manuel Giraud Original-X-From: emacs-devel-bounces+ged-emacs-devel=m.gmane-mx.org@gnu.org Tue May 30 18:04:37 2023 Return-path: Envelope-to: ged-emacs-devel@m.gmane-mx.org Original-Received: from lists.gnu.org ([209.51.188.17]) by ciao.gmane.io with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1q41ps-0007bJ-4F for ged-emacs-devel@m.gmane-mx.org; Tue, 30 May 2023 18:04:37 +0200 Original-Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1q41oo-0007hK-Ed; Tue, 30 May 2023 12:03:30 -0400 Original-Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1q3zRp-0004W5-IW for emacs-devel@gnu.org; Tue, 30 May 2023 09:31:38 -0400 Original-Received: from mail-vi1eur04olkn2054.outbound.protection.outlook.com ([40.92.75.54] helo=EUR04-VI1-obe.outbound.protection.outlook.com) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1q3zRj-0003V3-W5 for emacs-devel@gnu.org; Tue, 30 May 2023 09:31:37 -0400 ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=n8nvgKoEWuRzpCyOdqi3xAFPLreuWOV1l1F7O/Ws8GqOZarl9rcB7gKLSVxI8V2ysCaINvR9/feRa5OJuQzB7WPcyXPJEwOIPhy34KtnXUKPnshfGbMDwLwfXLNO8zLtM0nLGNCnpSbL6ERJPpeskgzUmiIgP2v7pBsrjqPuwTwk8Vw1RPrE1d/XnxBgMmPfwdw7wGOWYBTP5KdjnrRvBxPBhlQGhA+XnbXPy37aVCfg5n29ey8cNmDJzIuYk28PVppZX/GsM/90UgXUiQFRDvSRk4mrCJ678IdQ8mtMGOHep8spDajuKNV5rnbZ+rH5cNcdLuyubhSr6dmgbxPjmA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=JWIyujEG5H/ht9+RhpxklY8gWDxe2DRS8CGYl/YJdtY=; b=Sh9ReiHvklhkF46LMiziaZrgIz+rIwlXRRdbE+xYudApqn5AQiF+XxklF3M6/cOq3hYKGwxsa+JLeH5QBbyaEr0qdYJXTMmeM1PDZA07Co5sQuWU88PLCWoK0bcBtlooLZjaPSDAw4Z/lAHpakYhDCoS5ao+MrJOxzqriaNvYg5OmWm4z2LKqe2LC2jrGAdCG3N3BBvFAP5E6pPA7JiWgAo3J7HG9zLd+nWNfTbFfw/RzdZecHFZbpkZn9tSf1liK+VZbiTNgM02zlcxSBbX9tbYrBjefyBKFxMt1eBIvAS2nPlgREO+mAzBKa8tdCnn3T0um3tY+HGrfK9Zpoj1rg== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=none; dmarc=none; dkim=none; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=live.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=JWIyujEG5H/ht9+RhpxklY8gWDxe2DRS8CGYl/YJdtY=; b=Qd+Nlu2etJj8ZEAUzIy1HwSabi6txBHQHJuyvBOCjCrAdqZl06Z/001Pn0P3jnivqmzAPmJjPOmeujNb3KsKAzwTCCPehrJ8dpwIe6aMZF6FGBzhJdTTvt9+aQ9kGg4TmIF9/Pk13YrSRVKt5yyAYIqqfciSPzNfANUwdx2zG6Ymwbyi8aPOqiKzVBww8eyQ4wC34jNicKQ35vgiqVpqqWpeTGKU/BAEbwbXJpbHgAKowp2MOVJ9ktqzv7HPmrgvILmq7C1VbGtbieL2HpL0nP9aVrQESx67Lfd3uiaOPr8fqO0RkWaAjJzy6wBtdqEa1Mka1DJo47/oDyJoFFH/CA== Original-Received: from AM9PR09MB4977.eurprd09.prod.outlook.com (2603:10a6:20b:304::20) by AM0PR09MB4354.eurprd09.prod.outlook.com (2603:10a6:20b:148::22) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.6433.23; Tue, 30 May 2023 13:31:29 +0000 Original-Received: from AM9PR09MB4977.eurprd09.prod.outlook.com ([fe80::6983:1def:fb14:21bf]) by AM9PR09MB4977.eurprd09.prod.outlook.com ([fe80::6983:1def:fb14:21bf%6]) with mapi id 15.20.6433.022; Tue, 30 May 2023 13:31:29 +0000 In-Reply-To: <87h6ruf09e.fsf@ledu-giraud.fr> (Manuel Giraud's message of "Tue, 30 May 2023 14:54:05 +0200") X-TMN: [1evwsHsPdQsYVwXOiVGD1KbdCtqL6pnOxPtUckRQxcg=] X-ClientProxiedBy: FR0P281CA0096.DEUP281.PROD.OUTLOOK.COM (2603:10a6:d10:a9::9) To AM9PR09MB4977.eurprd09.prod.outlook.com (2603:10a6:20b:304::20) X-Microsoft-Original-Message-ID: <87r0qy9c9c.fsf@live.com> X-MS-Exchange-MessageSentRepresentingType: 1 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: AM9PR09MB4977:EE_|AM0PR09MB4354:EE_ X-MS-Office365-Filtering-Correlation-Id: 6064cb2a-4e9b-4c79-2b10-08db61122c61 X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: 8REhL9PCa+mwyQ7s8fcEPXq3q+IGgSrWiemaUS7G4ww8mbIxApBa9In3oZsJW3Ln5cBAiNOWGqsIxV0rV4uAG7PBRK4ExkoM2zHzKlkn8piJJ0zmW52ZYnMM6j6vPPvxN30o8KRiu1pHjxQxHWoHvauBi75DwgtRRHYxgkqXP4PzpKOls+x4DU59hh4qwAk2XjBhM+m2jq6rJDkPdI9f6nplTQTUeIrzCvyS0nXs3bReRU+6SBtyHeIVC8tOVsx3EPwTvfLcz5vuMG0L1AmRM9jq09PfjTtKcmdbd0fMzOTR0iXnqpxrJ7Za8HmqRt/PL3THRzkTa19S989g3i3DBs2wMFC0hMDgnbGJLw5ycWG8W/lEe4MUTlpsyyoSwtjtPj2vt5+kFNe9WOW46xMpiepCgyRE1GSnt5jJCMgLjCaG0AehyI5tfRwgi4emtzrcuNJOYKB/vSt7NzNQb+UpQc7RHn6LWtx0x2t5VrPEMDocZDEjC2UzwtySq8VWsFP0s03CHcgFQHWNLYv2WHAwo1jpRUhyXZO0ZE83isiVSUicsefy1ChZZU3L8OkNwR2e2q94e+2jejB+M1Ghc/d68sg/znBGNckMppBhPS4GRdYPZr/it5xazKSFhnLBPUVW X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: =?us-ascii?Q?sx0/C4dq1G+aa33pLGIO8bHccwnqoQibEF7AGFuqftZa6m34Xmb/A9MrVeRS?= =?us-ascii?Q?W59UEhaNAKh8NCgCaFg/p9jlSPlyzGYBkhH6IH70OlPwLwnvTmvftochbJ5N?= =?us-ascii?Q?Hj/gD9dBJmxUd4UdtjmlRnUmEoXtMI980ZY+FQ7gS2YWtdT/Cra+K/6Xw2w2?= =?us-ascii?Q?tsykZhlDFIqGWc3R01D/lYME8J2K64DCCPNC3oUlDhiUHd4dRpNEq8YncHpp?= =?us-ascii?Q?R0ARsoESDPyjHKD/+K+LX8loVn+2cmCgp/UBJwpFmsNNGyXKZk1O5H77dh4F?= =?us-ascii?Q?vSkdHhZkkp6x1LBuGVYVgdCeOaj1ip7zLkvD+u2mFbZ1AMwioL+gjgLjg6M1?= =?us-ascii?Q?W0iEXQK2mYBXzhDIgyi2uosd1Ee0RO37IESNafNpgnFOjNm6ntLsP2Gfsh5j?= =?us-ascii?Q?Rh9fMCGgELws3WomTsiPnpIcOqkb+tVALVixkdPi2TWW2/zpJ2Jqt82tdqTV?= =?us-ascii?Q?AMGMBjfuLxZJ+GkCL6SvMP/WpRX3VB7aqNiq25biBHnl+D7vpLSMoH/XlGoi?= =?us-ascii?Q?/N83SGeezZ+i+FhMmwEz9HKbifpSEDWrmocy80oa891yG7NkPmVmsE0dBMPY?= =?us-ascii?Q?M3rTbeLATqvpo4bRVaR4hPF2r6JUAFk3e/m3DVceOO7aB/G6N+QnpeIcyVfO?= =?us-ascii?Q?IS X-OriginatorOrg: sct-15-20-4755-11-msonline-outlook-64da6.templateTenant X-MS-Exchange-CrossTenant-Network-Message-Id: 6064cb2a-4e9b-4c79-2b10-08db61122c61 X-MS-Exchange-CrossTenant-AuthSource: AM9PR09MB4977.eurprd09.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 30 May 2023 13:31:29.2841 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: 84df9e7f-e9f6-40af-b435-aaaaaaaaaaaa X-MS-Exchange-CrossTenant-RMS-PersistedConsumerOrg: 00000000-0000-0000-0000-000000000000 X-MS-Exchange-Transport-CrossTenantHeadersStamped: AM0PR09MB4354 Received-SPF: pass client-ip=40.92.75.54; envelope-from=arthur.miller@live.com; helo=EUR04-VI1-obe.outbound.protection.outlook.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, FREEMAIL_FROM=0.001, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H2=-0.001, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001, T_SCC_BODY_TEXT_LINE=-0.01 autolearn=ham autolearn_force=no X-Spam_action: no action X-Mailman-Approved-At: Tue, 30 May 2023 12:03:14 -0400 X-BeenThere: emacs-devel@gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: "Emacs development discussions." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: emacs-devel-bounces+ged-emacs-devel=m.gmane-mx.org@gnu.org Original-Sender: emacs-devel-bounces+ged-emacs-devel=m.gmane-mx.org@gnu.org Xref: news.gmane.io gmane.emacs.devel:306417 Archived-At: --=-=-= Content-Type: text/plain Manuel Giraud writes: > Arthur Miller writes: > >> I would like to be able to control Info and Help buffers from other buffers, to >> lessen switch between windows. > > Hi Arthur, > > I like this. I'm often reading some documentation while testing things > into the current buffer so I think I would be a great addition. I'm > testing your patch now (and will report any issue if needed). Hi Manuel; thank you very much for testing it, and for the kind words. I have reworked somewhat the patch, because of some unnecessary warnings. I am attaching the reworked version in this mail too (I have send it in another mail). Sorry for the inconvenience, but if you test, I suggest testing with the later one. It is very much the same, minus some re-furnituring for the code, some local variables renaming and some doc string changes. > As a potential future user, I have some comments/requests: > > - Could it be extend to Man buffers? I think it would even be more > useful than *Help* buffers which are short most of the time (or > maybe it is just me). Probably, I don't use man a lot so I am not sure to be honest. The technique can be extended to "remotely" control any buffer, but it works well only with "unique" buffers (one of a kind), like Help buffer for example. Even with *info* buffers it is a bit less clean since there can be few of those. But technically it is possible (I have been doing this programmatically too). > - Is it possible to have repeat mode for this? So one could do > "C-h M-i SPC SPC SPC DEL" to browse some info pages. I am not a repeat-mode user myself to be honest, so IDK, but I guess there should be no problems. These are just commands that can be called very well via M-x as any other command, so I guess it should work with repeat mode as well? Also I suggest rebinding to M-i instead of C-h M-i for more pleasant experience (or whichever key suits the best :)). I have chosen C-h M-i just to be on the conservative side to spare GNU server maintainers from excessive electrical bills, which may result every time we suggest rebinding a default key on this list. M-i does something in Outline mode by default, likewise, M-h is bound to mark-pragraph by default, so I have opted for C-h M-h. > - Could we have a "umbrella" keybinding that does this for some > common denominator functions the other "documentation" buffer > (be it *info*, *Help* or a man page)? Basicly yes. The idea is: 1. switch to desired buffer 2. do whatever needed in that buffer 3. switch back to original buffer I have been doing this for quite a while, and I have a little mode were I do this programmatically as mentioned above. I have a little library < 100 sloc, where I iterate through help/info mode-maps, check if the command starts with help- or Info- prefix and attch an advice around the command where I do this switching between windows and call original function in-between. If it does not, I create a new symbol with the correct prefix, alias it to the old function, and then advise the new symbol. It worked very well with help-mode, but some commands in Info-mode are not possible to advice that way, about a couple of those. Info-menu was one for example. But in Info-mode there are some nested char-tables in the keymap which turns out to be quite slow to process programmatically. Also, even if it was fast, doing that every time Emacs starts is unnecessary, it is probably better to do it once and for all, so I have implemented it now manually. Similar can be implemented for any command to make it "buffer specific", but as said, works well only in cases where there is just one buffer at a time, so I doubt in generality of this technique. I have actually tried to find some general way to do this programmatically with any buffer, but thus far, I don't see any general way that is both efficient and 100% failsafe. > Best regards, Best regards and thanks for testing! --=-=-= Content-Type: text/x-patch; charset=utf-8 Content-Disposition: attachment; filename=0001-Use-help-and-Info-mode-commands-from-any-buffer.patch Content-Transfer-Encoding: quoted-printable >From 04334fe2a97d8f21f684fa2860f343b4a236a391 Mon Sep 17 00:00:00 2001 From: Arthur Miller Date: Tue, 30 May 2023 14:09:42 +0200 Subject: [PATCH] Use help- and Info-mode commands from any buffer Allow commands that act on help-mode and Info-mode to be called from other buffers than just help and Info buffer. * etc/NEWS: Mention the change. * doc/emacs/help.texi: Update relevant commands mentioned in the text. * lisp/help-mode.el (help-jump): New symbol used for both command and global variable. Command jumps to/from help window; var records 'jumped from'. * lisp/help-mode.el (help-view-source): * lisp/help-mode.el (help-goto-info): * lisp/help-mode.el (help-go-back): * lisp/help-mode.el (help-go-forward): * lisp/help-mode.el (help-goto-next-page): * lisp/help-mode.el (help-goto-previous-page): * lisp/help-mode.el (help-goto-lispref-info): * lisp/help-mode.el (help-customize): Help-mode commands adapted to be called from any buffer. * lisp/help-mode.el (help-quit-window): * lisp/help-mode.el (help-revert-buffer): * lisp/help-mode.el (help-describe-mode): * lisp/help-mode.el (help-beginning-of-buffer): * lisp/help-mode.el (help-end-of-buffer): * lisp/help-mode.el (help-scroll-up-command): * lisp/help-mode.el (help-scroll-down-command): * lisp/help-mode.el (help-forward-button): * lisp/help-mode.el (help-backward-button): * lisp/help-mode.el (help-button-describe): * lisp/help-mode.el (help-push-button): New commands. Do what they wrapped counterparts without 'help-' prefix do, but specifically in help buffer. * lisp/help-mode.el (help-mode-map): * lisp/help-mode.el (help-mode-menu): Update bindings to reflect the new commands for previously generic commands Bind help-jump to 'j' (previously unused). * lisp/help-mode.el (help-mode-prefix-key): New defcustom declaration. Prefix key for help-mode-map. * lisp/info.el (Info-mode-prefix-key): New defcustom declaration. Prefix key for Info-mode-map. * lisp/info.el (Info-menu): * lisp/info.el (Info-next): * lisp/info.el (Info-prev): * lisp/info.el (Info-up): * lisp/info.el (Info-history-back): * lisp/info.el (Info-history-forward): * lisp/info.el (Info-directory): * lisp/info.el (Info-toc): * lisp/info.el (Info-nth-menu-item): * lisp/info.el (Info-top-node): * lisp/info.el (Info-final-node): * lisp/info.el (Info-forward-node): * lisp/info.el (Info-backward-node): * lisp/info.el (Info-next-menu-item): * lisp/info.el (Info-last-menu-item): * lisp/info.el (Info-next-preorder): * lisp/info.el (Info-last-preorder): * lisp/info.el (Info-scroll-up): * lisp/info.el (Info-scroll-down): * lisp/info.el (Info-next-reference): * lisp/info.el (Info-prev-reference): * lisp/info.el (Info-index): * lisp/info.el (Info-index-next): * lisp/info.el (Info-virtual-next): * lisp/info.el (Info-apropos): * lisp/info.el (Info-finder): * lisp/info.el (Info-summary): * lisp/info.el (Info-copy-current-node-name): Info-mode commands adapted to be called from any buffer. * lisp/info.el (Info-describe-mode): * lisp/info.el (Info-quit-window): * lisp/info.el (Info-beginning-of-buffer): * lisp/info.el (Info-end-of-buffer): New commands. Do what they wrapped counterparts without 'Info-' prefix do, but specifically in Info buffer. * lisp/info.el (Info-jump): New symbol used for both command and global variable. Command jumps to/from Info window; var records 'jumped from'. * lisp/info.el (Info-mode-map): * lisp/info.el (Info-mode-menu): Update bindings to reflect the new commands for previously generic commands Bind Info-jump to 'j' (previously unused). --- doc/emacs/help.texi | 12 +- etc/NEWS | 19 + lisp/help-mode.el | 260 ++++++++-- lisp/info.el | 1156 +++++++++++++++++++++++++------------------ 4 files changed, 928 insertions(+), 519 deletions(-) diff --git a/doc/emacs/help.texi b/doc/emacs/help.texi index 945e12a05d2..b50905c3dc4 100644 --- a/doc/emacs/help.texi +++ b/doc/emacs/help.texi @@ -494,9 +494,9 @@ Help Mode @item @key{RET} Follow a cross reference at point (@code{help-follow}). @item @key{TAB} -Move point forward to the next hyperlink (@code{forward-button}). +Move point forward to the next hyperlink (@code{help-forward-button}). @item S-@key{TAB} -Move point back to the previous hyperlink (@code{backward-button}). +Move point back to the previous hyperlink (@code{help-backward-button}). @item mouse-1 @itemx mouse-2 Follow a hyperlink that you click on. @@ -544,12 +544,12 @@ Help Mode (@code{help-go-forward}). =20 @kindex TAB @r{(Help mode)} -@findex forward-button +@findex help-forward-button @kindex S-TAB @r{(Help mode)} -@findex backward-button +@findex help-backward-button To move between hyperlinks in a help buffer, use @key{TAB} -(@code{forward-button}) to move forward to the next hyperlink and -@kbd{S-@key{TAB}} (@code{backward-button}) to move back to the +(@code{help-forward-button}) to move forward to the next hyperlink and +@kbd{S-@key{TAB}} (@code{help-backward-button}) to move back to the previous hyperlink. These commands act cyclically; for instance, typing @key{TAB} at the last hyperlink moves back to the first hyperlink. diff --git a/etc/NEWS b/etc/NEWS index 3c71e52fff4..fe241d604cb 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -30,6 +30,25 @@ applies, and please also update docstrings as needed. =0C * Changes in Emacs 30.1 =20 ++++ +** New user option 'info-mode-prefix-key. +Prefix key for `Info-mode-map, so it can be used from other buffer but +just *info* buffer. + +** Commands acting on Info buffer can now be used from any buffer +Similar to help-mode, keyboard-driven commands acting in Info-mode and +bound in Info-mode-map can now be used from any buffer (acting on Info +buffer). ++++ +** New user option 'help-mode-prefix-key'. +The key is used to assign `help-mode-map' to a prefix, so it can be +used as a prefix-key from other buffers but just *Help* buffer. + +** Commands acting on Help buffer can now be used from any buffer +This means that all commands that normally required a user to switch +to *Help* buffer before the invocation, can now be invoked from any +buffer. With other words, you can now control Help buffer from any +other buffer, which lessens amount of switching between buffers. --- ** New user option 'describe-bindings-outline-rules'. This user option controls outline visibility in the output buffer of diff --git a/lisp/help-mode.el b/lisp/help-mode.el index bf64d032b65..af41686e3fd 100644 --- a/lisp/help-mode.el +++ b/lisp/help-mode.el @@ -47,7 +47,22 @@ help-mode-map "s" #'help-view-source "I" #'help-goto-lispref-info "i" #'help-goto-info - "c" #'help-customize) + "j" #'help-jump + "c" #'help-customize + "g" #'help-revert-buffer + "q" #'help-quit-window + "<" #'help-beginning-of-buffer + ">" #'help-end-of-buffer + "h" #'help-describe-mode + "?" #'help-describe-mode + "DEL" #'help-scroll-down-command + "SPC" #'help-scroll-down-command + "S-SPC" #'help-scroll-up-command + "RET" #'help-push-button + "TAB" #'help-forward-button + "C-M-i" #'help-backward-button + "" #'help-backward-button + "ESC TAB" #'help-backward-button) =20 (easy-menu-define help-mode-menu help-mode-map "Menu for Help mode." @@ -60,9 +75,9 @@ help-mode-menu ["Next Topic" help-go-forward :help "Go back to next topic in this help buffer" :active help-xref-forward-stack] - ["Move to Previous Button" backward-button + ["Move to Previous Button" help-backward-button :help "Move to the Previous Button in the help buffer"] - ["Move to Next Button" forward-button + ["Move to Next Button" help-forward-button :help "Move to the Next Button in the help buffer"] ["View Source" help-view-source :help "Go to the source file for the current help item"] @@ -150,6 +165,22 @@ help-mode-hook "Hook run by `help-mode'." :type 'hook :group 'help) + +;; with a little help from Helm +(defcustom help-mode-prefix-key "C-h M-h" + "`help-mode-map' prefix key for invocation from other buffers." + :version "30.1" + :type '(choice (string :tag "Key") (const :tag "no binding")) + :set (lambda (var key) + (when (and (boundp var) (symbol-value var)) + (define-key (current-global-map) + (read-kbd-macro (symbol-value var)) nil)) + (when key + (define-key (current-global-map) + (read-kbd-macro key) + help-mode-map)) + (set var key)) + :group 'help) =0C ;; Button types used by help =20 @@ -763,7 +794,127 @@ help-xref-on-pp =20 ;;;###autoload (define-obsolete-function-alias 'help-xref-interned #'describe-symbol "25.= 1") +=0C +;; commands from special-mode wrapped to work on help-mode only + +(defun help-quit-window () + "As `quit-window' but works only on *Help* buffer." + (interactive) + (let ((help-window (get-buffer-window (help-buffer)))) + (unless (window-live-p help-window) + (user-error "Help buffer is not currently visible.")) + (with-selected-window help-window + (quit-window nil help-window)))) + +(defun help-describe-mode () + "As `describe-mode' but for *Help* buffer only." + (interactive) + (let ((help-buffer (get-buffer (help-buffer)))) + (unless (window-live-p (get-buffer-window help-buffer)) + (user-error "Help buffer is not currently visible.")) + (describe-mode help-buffer))) + +(defun help-beginning-of-buffer () + "As `help-beginning-of-buffer' but for *Help* buffer only." + (interactive) + (let ((help-window (get-buffer-window (help-buffer)))) + (unless (window-live-p help-window) + (user-error "Help buffer is not currently visible.")) + (with-selected-window help-window + (goto-char (point-min))))) + +(defun help-end-of-buffer () + "As `help-end-of-buffer' but for *Help* buffer only." + (interactive) + (let ((help-window (get-buffer-window (help-buffer)))) + (unless (window-live-p help-window) + (user-error "Help buffer is not currently visible.")) + (with-selected-window help-window + (goto-char (point-max))))) +=0C +;; from files.el, for completeness and to eliminate potential confusion =20 +(defun help-revert-buffer () + "As `revert-buffer', but act on help buffer specifically." + (interactive) + (let ((help-window (get-buffer-window (help-buffer)))) + (unless (window-live-p help-window) + (user-error "Help buffer is not currently visible.")) + (with-selected-window help-window + (call-interactively #'revert-buffer)))) +=0C +;; Commands from button.el wrapped to work on help-mode only + +(defun help-forward-button () + (interactive) + (let ((help-window (get-buffer-window (help-buffer)))) + (unless (window-live-p help-window) + (user-error "Help buffer is not currently visible.")) + (with-selected-window help-window + (forward-button 1)))) + +(defun help-backward-button () + (interactive) + (let ((help-window (get-buffer-window (help-buffer)))) + (unless (window-live-p help-window) + (user-error "Help buffer is not currently visible.")) + (with-selected-window help-window + (backward-button 1)))) + +(defun help-button-describe () + (interactive) + (let ((help-window (get-buffer-window (help-buffer)))) + (unless (window-live-p help-window) + (user-error "Help buffer is not currently visible.")) + (with-selected-window help-window + (button-describe)))) + +(defun help-push-button () + (interactive) + (let ((help-window (get-buffer-window (help-buffer)))) + (unless (window-live-p help-window) + (error "Help buffer is not currently visible.")) + (with-selected-window help-window + (push-button)))) +=0C +;; Commands from window.el wrapped to work on help-mode only + +(defun help-scroll-up-command (&optional arg) + "As `scroll-up-command' but works only on *Help* buffer." + (interactive "^P") + (let ((help-window (get-buffer-window (help-buffer)))) + (unless (window-live-p help-window) + (error "Help buffer is not currently visible.")) + (with-selected-window help-window + (scroll-up-command arg)))) + +(defun help-scroll-down-command (&optional arg) + "As `scroll-down-command' but works only on *Help* buffer." + (interactive "^P") + (let ((help-window (get-buffer-window (help-buffer)))) + (unless (window-live-p help-window) + (user-error "Help buffer is not currently visible.")) + (with-selected-window help-window + (scroll-down-command arg)))) + +=0C + +(defvar help-jump nil) +(defun help-jump () + "Jump to and from *Help* window." + (interactive) + (let ((help-window (get-buffer-window (help-buffer))) + (current-window (selected-window))) + (cond + ((eq help-window current-window) + (unless help-jump + (user-error "No previously selected window to jump to.")) + (select-window help-jump)) + (t + (unless (window-live-p help-window) + (user-error "Help buffer is not currently visible.")) + (setq help-jump current-window) + (select-window help-window))))) =0C ;; Navigation/hyperlinking with xrefs =20 @@ -810,25 +961,37 @@ help-xref-go-forward (defun help-go-back () "Go back to previous topic in this help buffer." (interactive) - (if help-xref-stack - (help-xref-go-back (current-buffer)) - (user-error "No previous help buffer"))) + (let ((help-buffer (get-buffer (help-buffer)))) + (unless (window-live-p (get-buffer-window help-buffer)) + (user-error "Help buffer is not currently visible.")) + (with-current-buffer help-buffer + (if help-xref-stack + (help-xref-go-back (current-buffer)) + (user-error "No previous help buffer"))))) =20 (defun help-go-forward () "Go to the next topic in this help buffer." (interactive) - (if help-xref-forward-stack - (help-xref-go-forward (current-buffer)) - (user-error "No next help buffer"))) + (let ((help-buffer (get-buffer (help-buffer)))) + (unless (window-live-p (get-buffer-window help-buffer)) + (user-error "Help buffer is not currently visible.")) + (with-current-buffer help-buffer + (if help-xref-forward-stack + (help-xref-go-forward (current-buffer)) + (user-error "No next help buffer"))))) =20 (defun help-goto-next-page () "Go to the next page (if any) in the current buffer. The help buffers are divided into \"pages\" by the ^L character." (interactive nil help-mode) - (push-mark) - (forward-page) - (unless (eobp) - (forward-line 1))) + (let ((help-window (get-buffer-window (help-buffer)))) + (unless (window-live-p help-window) + (user-error "Help buffer is not currently visible.")) + (with-selected-window help-window + (push-mark) + (forward-page) + (unless (eobp) + (forward-line 1))))) =20 (defun help-goto-previous-page () "Go to the previous page (if any) in the current buffer. @@ -836,47 +999,68 @@ help-goto-previous-page =20 The help buffers are divided into \"pages\" by the ^L character." (interactive nil help-mode) - (push-mark) - (backward-page (if (looking-back "\f\n" (- (point) 5)) 2 1)) - (unless (bobp) - (forward-line 1))) + (let ((help-window (get-buffer-window (help-buffer)))) + (unless (window-live-p help-window) + (user-error "Help buffer is not currently visible.")) + (with-selected-window help-window + (push-mark) + (backward-page (if (looking-back "\f\n" (- (point) 5)) 2 1)) + (unless (bobp) + (forward-line 1))))) =20 (defun help-view-source () "View the source of the current help item." (interactive nil help-mode) - (unless (plist-get help-mode--current-data :file) - (error "Source file for the current help item is not defined")) - (help-function-def--button-function - (plist-get help-mode--current-data :symbol) - (plist-get help-mode--current-data :file) - (plist-get help-mode--current-data :type))) + (let ((help-buffer (get-buffer (help-buffer)))) + (unless (window-live-p (get-buffer-window help-buffer)) + (user-error "Help buffer is not currently visible.")) + (with-current-buffer help-buffer + (unless (plist-get help-mode--current-data :file) + (error "Source file for the current help item is not defined")) + (help-function-def--button-function + (plist-get help-mode--current-data :symbol) + (plist-get help-mode--current-data :file) + (plist-get help-mode--current-data :type))))) =20 (defun help-goto-info () "View the *info* node of the current help item." (interactive nil help-mode) - (unless help-mode--current-data - (error "No symbol to look up in the current buffer")) - (info-lookup-symbol (plist-get help-mode--current-data :symbol) - 'emacs-lisp-mode - help-window-keep-selected)) + (let ((help-buffer (get-buffer (help-buffer)))) + (unless (window-live-p (get-buffer-window help-buffer)) + (user-error "Help buffer is not currently visible.")) + (with-current-buffer help-buffer + (unless help-mode--current-data + (error "No symbol to look up in the current buffer")) + (with-selected-window (get-buffer-window help-buffer) + (info-lookup-symbol (plist-get help-mode--current-data :symbol) + 'emacs-lisp-mode + help-window-keep-selected))))) =20 (defun help-goto-lispref-info () "View the Emacs Lisp manual *info* node of the current help item." (interactive nil help-mode) - (unless help-mode--current-data - (error "No symbol to look up in the current buffer")) - (info-lookup-symbol (plist-get help-mode--current-data :symbol) - 'emacs-lisp-only)) + (let ((help-buffer (get-buffer (help-buffer)))) + (unless (window-live-p (get-buffer-window help-buffer)) + (user-error "Help buffer is not currently visible.")) + (with-current-buffer help-buffer + (unless help-mode--current-data + (error "No symbol to look up in the current buffer")) + (info-lookup-symbol (plist-get help-mode--current-data :symbol) + 'emacs-lisp-only)))) =20 (defun help-customize () "Customize variable or face whose doc string is shown in the current buf= fer." (interactive nil help-mode) - (let ((sym (plist-get help-mode--current-data :symbol))) - (unless (or (boundp sym) (facep sym)) - (user-error "No variable or face to customize")) - (cond - ((boundp sym) (customize-variable sym)) - ((facep sym) (customize-face sym))))) + (let ((help-buffer (get-buffer (help-buffer)))) + (unless (window-live-p (get-buffer-window help-buffer)) + (user-error "Help buffer is not currently visible.")) + (with-current-buffer help-buffer + (let ((sym (plist-get help-mode--current-data :symbol))) + (unless (or (boundp sym) (facep sym)) + (user-error "No variable or face to customize")) + (cond + ((boundp sym) (customize-variable sym)) + ((facep sym) (customize-face sym))))))) =20 (defun help-do-xref (_pos function args) "Call the help cross-reference function FUNCTION with args ARGS. diff --git a/lisp/info.el b/lisp/info.el index 035dff66e75..dcc6ed667d8 100644 --- a/lisp/info.el +++ b/lisp/info.el @@ -819,6 +819,22 @@ info-standalone (save-buffers-kill-emacs))) (info))) =0C +(defvar Info-jump nil) +(defun Info-jump () + "Jump to and from *info* window." + (interactive) + (let ((info-window (get-buffer-window "*info*")) + (current-window (selected-window))) + (cond + ((eq info-window (selected-window)) + (unless Info-jump + (error "No previously selected window to jump to.")) + (select-window Info-jump)) + (t + (when (window-live-p info-window) + (setq Info-jump current-window) + (select-window info-window)))))) +=0C ;; See if the accessible portion of the buffer begins with a node ;; delimiter, and the node header line which follows matches REGEXP. ;; Typically, this test will be followed by a loop that examines the @@ -831,12 +847,12 @@ info-standalone ;; The return value is the value of point at the beginning of matching ;; REGEXP, if the function succeeds, nil otherwise. (defun Info-node-at-bob-matching (regexp) - (and (bobp) ; are we at beginning of buffer? - (looking-at "\^_") ; does it begin with node delimiter? + (and (bobp) ; are we at beginning of buffer? + (looking-at "\^_") ; does it begin with node delimiter? (let (beg) (forward-line 1) (setq beg (point)) - (forward-line 1) ; does the line after delimiter match REGEXP? + (forward-line 1) ; does the line after delimiter match REGEXP? (re-search-backward regexp beg t)))) =20 (defun Info-find-file (filename &optional noerror no-pop-to-dir) @@ -2273,85 +2289,108 @@ Info-next "Go to the \"next\" node, staying on the same hierarchical level. This command doesn't descend into sub-nodes, like \\\\[Info= -forward-node] does." (interactive nil Info-mode) - ;; In case another window is currently selected - (save-window-excursion - (or (derived-mode-p 'Info-mode) (switch-to-buffer "*info*")) - (Info-goto-node (Info-extract-pointer "next")))) + (let ((info-window (get-buffer-window "*info*"))) + (unless (window-live-p info-window) + (user-error "There is no visible Info buffer.")) + ;; In case another window is currently selected + (save-window-excursion + (or (derived-mode-p 'Info-mode) (switch-to-buffer "*info*")) + (Info-goto-node (Info-extract-pointer "next"))))) =20 (defun Info-prev () "Go to the \"previous\" node, staying on the same hierarchical level. This command doesn't go up to the parent node, like \\\\[In= fo-backward-node] does." (interactive nil Info-mode) - ;; In case another window is currently selected - (save-window-excursion - (or (derived-mode-p 'Info-mode) (switch-to-buffer "*info*")) - (Info-goto-node (Info-extract-pointer "prev[ious]*" "previous")))) + (let ((info-window (get-buffer-window "*info*"))) + (unless (window-live-p info-window) + (user-error "There is no visible Info buffer.")) + ;; In case another window is currently selected + (save-window-excursion + (or (derived-mode-p 'Info-mode) (switch-to-buffer "*info*")) + (Info-goto-node (Info-extract-pointer "prev[ious]*" "previous")) + ;; for some reason my Emacs does not update correctly info buffer af= ter + ;; going back to a previous node, reverting buffer fixes it + (revert-buffer t t t)))) =20 (defun Info-up (&optional same-file) "Go to the superior node of this node. If SAME-FILE is non-nil, do not move to a different Info file." (interactive nil Info-mode) - ;; In case another window is currently selected - (save-window-excursion - (or (derived-mode-p 'Info-mode) (switch-to-buffer "*info*")) - (let ((old-node Info-current-node) - (old-file Info-current-file) - (node (Info-extract-pointer "up")) p) - (and same-file - (string-match "^(" node) - (error "Up node is in another Info file")) - (Info-goto-node node) - (setq p (point)) - (goto-char (point-min)) - (if (and (stringp old-file) - (search-forward "\n* Menu:" nil t) - (re-search-forward - (if (string-equal old-node "Top") - (concat "\n\\*[^:]+: +(" (file-name-nondirectory old-file) ")") - (concat "\n\\* +\\(" (regexp-quote old-node) - ":\\|[^:]+: +" (regexp-quote old-node) "\\)")) - nil t)) - (progn (beginning-of-line) (if (looking-at "^\\* ") (forward-char 2))) - (goto-char p) - (Info-restore-point Info-history)))) - ;; If scroll-conservatively is non-zero and less than 101, display - ;; as much of the superior node above the target line as possible. - (when (< 0 scroll-conservatively 101) - (recenter))) + (let ((info-window (get-buffer-window "*info*"))) + (unless (window-live-p info-window) + (user-error "There is no visible Info buffer.")) + (with-selected-window info-window + ;; In case another window is currently selected + (save-window-excursion + (or (derived-mode-p 'Info-mode) (switch-to-buffer "*info*")) + (let ((old-node Info-current-node) + (old-file Info-current-file) + (node (Info-extract-pointer "up")) p) + (and same-file + (string-match "^(" node) + (error "Up node is in another Info file")) + (Info-goto-node node) + (setq p (point)) + (goto-char (point-min)) + (if (and (stringp old-file) + (search-forward "\n* Menu:" nil t) + (re-search-forward + (if (string-equal old-node "Top") + (concat "\n\\*[^:]+: +(" (file-name-nondirectory o= ld-file) ")") + (concat "\n\\* +\\(" (regexp-quote old-node) + ":\\|[^:]+: +" (regexp-quote old-node) "\\)"= )) + nil t)) + (progn (beginning-of-line) (if (looking-at "^\\* ") (forward= -char 2))) + (goto-char p) + (Info-restore-point Info-history))) + ;; If scroll-conservatively is non-zero and less than 101, display + ;; as much of the superior node above the target line as possible. + ;; (when (< 0 scroll-conservatively 101) (recenter)) + ;; for some reason, in my Emacs, this is the only obe that actuall= y + ;; displays correctly, recentering leavs last line at the top + (revert-buffer t t t))))) =20 (defun Info-history-back () "Go back in the history to the last node visited." (interactive nil Info-mode) - (or Info-history - (user-error "This is the first Info node you looked at")) - (let ((history-forward - (cons (list Info-current-file Info-current-node (point)) - Info-history-forward)) - filename nodename opoint) - (setq filename (car (car Info-history))) - (setq nodename (car (cdr (car Info-history)))) - (setq opoint (car (cdr (cdr (car Info-history))))) - (setq Info-history (cdr Info-history)) - (Info-find-node filename nodename) - (setq Info-history (cdr Info-history)) - (setq Info-history-forward history-forward) - (goto-char opoint))) + (let ((info-window (get-buffer-window "*info*"))) + (unless (window-live-p info-window) + (user-error "There is no visible Info buffer.")) + (with-selected-window info-window + (or Info-history + (user-error "This is the first Info node you looked at")) + (let ((history-forward + (cons (list Info-current-file Info-current-node (point)) + Info-history-forward)) + filename nodename opoint) + (setq filename (car (car Info-history))) + (setq nodename (car (cdr (car Info-history)))) + (setq opoint (car (cdr (cdr (car Info-history))))) + (setq Info-history (cdr Info-history)) + (Info-find-node filename nodename) + (setq Info-history (cdr Info-history)) + (setq Info-history-forward history-forward) + (goto-char opoint))))) =20 (defalias 'Info-last 'Info-history-back) =20 (defun Info-history-forward () "Go forward in the history of visited nodes." (interactive nil Info-mode) - (or Info-history-forward - (user-error "This is the last Info node you looked at")) - (let ((history-forward (cdr Info-history-forward)) - filename nodename opoint) - (setq filename (car (car Info-history-forward))) - (setq nodename (car (cdr (car Info-history-forward)))) - (setq opoint (car (cdr (cdr (car Info-history-forward))))) - (Info-find-node filename nodename) - (setq Info-history-forward history-forward) - (goto-char opoint))) + (let ((info-window (get-buffer-window "*info*"))) + (unless (window-live-p info-window) + (user-error "There is no visible Info buffer.")) + (with-selected-window info-window + (or Info-history-forward + (user-error "This is the last Info node you looked at")) + (let ((history-forward (cdr Info-history-forward)) + filename nodename opoint) + (setq filename (car (car Info-history-forward))) + (setq nodename (car (cdr (car Info-history-forward)))) + (setq opoint (car (cdr (cdr (car Info-history-forward))))) + (Info-find-node filename nodename) + (setq Info-history-forward history-forward) + (goto-char opoint))))) =0C (add-to-list 'Info-virtual-files '("\\`dir\\'" @@ -2377,7 +2416,11 @@ Info-directory-find-node (defun Info-directory () "Go to the Info directory node." (interactive) - (Info-find-node "dir" "top")) + (let ((info-window (get-buffer-window "*info*"))) + (unless (window-live-p info-window) + (user-error "There is no visible Info buffer.")) + (with-selected-window info-window + (Info-find-node "dir" "top")))) =0C (add-to-list 'Info-virtual-files '("\\`\\*History\\*\\'" @@ -2416,9 +2459,13 @@ Info-history-find-node (defun Info-history () "Go to a node with a menu of visited nodes." (interactive nil Info-mode) - (Info-find-node "*History*" "Top") - (Info-next-reference) - (Info-next-reference)) + (let ((info-window (get-buffer-window "*info*"))) + (unless (window-live-p info-window) + (user-error "There is no visible Info buffer.")) + (with-selected-window info-window + (Info-find-node "*History*" "Top") + (Info-next-reference) + (Info-next-reference)))) =0C (add-to-list 'Info-virtual-nodes '("\\`\\*TOC\\*\\'" @@ -2453,12 +2500,16 @@ Info-toc "Go to a node with table of contents of the current Info file. Table of contents is created from the tree structure of menus." (interactive nil Info-mode) - (Info-find-node Info-current-file "*TOC*") - (let ((prev-node (nth 1 (car Info-history))) p) - (goto-char (point-min)) - (if (setq p (search-forward (concat "*Note " prev-node ":") nil t)) - (setq p (- p (length prev-node) 2))) - (goto-char (or p (point-min))))) + (let ((info-window (get-buffer-window "*info*"))) + (unless (window-live-p info-window) + (user-error "There is no visible Info buffer.")) + (with-selected-window info-window + (Info-find-node Info-current-file "*TOC*") + (let ((prev-node (nth 1 (car Info-history))) p) + (goto-char (point-min)) + (if (setq p (search-forward (concat "*Note " prev-node ":") nil t)= ) + (setq p (- p (length prev-node) 2))) + (goto-char (or p (point-min))))))) =20 (defun Info-toc-insert (nodes node-list level curr-file) "Insert table of contents with references to nodes." @@ -2798,38 +2849,45 @@ Info-menu new buffer." (interactive (let (;; If point is within a menu item, use that item as the default - (default nil) - (p (point)) - beg - (case-fold-search t)) + (default nil) + (p (point)) + beg + (case-fold-search t) + (info-window (get-buffer-window "*info*"))) + (unless (window-live-p info-window) + (user-error "There is no visible Info buffer.")) + (if (not (eq (selected-window) info-window)) + (Info-jump) + (setq Info-jump nil)) (save-excursion (goto-char (point-min)) (if (not (search-forward "\n* menu:" nil t)) - (user-error "No menu in this node")) + (user-error "No menu in this node")) (setq beg (point)) (and (< (point) p) - (save-excursion - (goto-char p) - (end-of-line) - (if (re-search-backward (concat "\n\\* +\\(" - Info-menu-entry-name-re - "\\):") + (save-excursion + (goto-char p) + (end-of-line) + (if (re-search-backward (concat "\n\\* +\\(" + Info-menu-entry-name-re + "\\):") beg t) - (setq default (match-string-no-properties 1)))))) + (setq default (match-string-no-properties 1)))))) (let ((item nil)) (while (null item) - (setq item (let ((completion-ignore-case t) - (Info-complete-menu-buffer (current-buffer))) - (completing-read (format-prompt "Menu item" default) - #'Info-complete-menu-item nil t nil nil + (setq item (let ((completion-ignore-case t) + (Info-complete-menu-buffer (current-buffer))) + (completing-read (format-prompt "Menu item" default) + #'Info-complete-menu-item nil t nil= nil default)))) (list item current-prefix-arg))) Info-mode) ;; there is a problem here in that if several menu items have the same ;; name you can only go to the node of the first with this command. (Info-goto-node (Info-extract-menu-item menu-item) - (and fork - (if (stringp fork) fork menu-item)))) + (and fork + (if (stringp fork) fork menu-item))) + (when Info-jump (Info-jump))) =20 (defun Info-extract-menu-item (menu-item) (setq menu-item (regexp-quote menu-item)) @@ -2869,32 +2927,44 @@ Info-nth-menu-item "Go to the node of the Nth menu item. N is the digit argument used to invoke this command." (interactive nil Info-mode) - (Info-goto-node - (Info-extract-menu-counting - (- (aref (this-command-keys) (1- (length (this-command-keys)))) ?0)))) + (let ((info-window (get-buffer-window "*info*"))) + (unless (window-live-p info-window) + (user-error "There is no visible Info buffer.")) + (with-selected-window info-window + (Info-goto-node + (Info-extract-menu-counting + (- (aref (this-command-keys) (1- (length (this-command-keys)))) ?0= )))))) =20 (defun Info-top-node () "Go to the Top node of this file." (interactive nil Info-mode) - (Info-goto-node "Top")) + (let ((info-window (get-buffer-window "*info*"))) + (unless (window-live-p info-window) + (user-error "There is no visible Info buffer.")) + (with-selected-window info-window + (Info-goto-node "Top")))) =20 (defun Info-final-node () "Go to the final node in this file." (interactive nil Info-mode) - (Info-goto-node "Top") - (let ((Info-history nil) - (case-fold-search t)) - ;; Go to the last node in the menu of Top. But don't delve into - ;; detailed node listings. - (Info-goto-node (Info-extract-menu-counting nil t)) - ;; If the last node in the menu is not last in pointer structure, - ;; move forward (but not down- or upward - see bug#1116) until we - ;; can't go any farther. - (while (Info-forward-node t t t) nil) - ;; Then keep moving down to last subnode, unless we reach an index. - (while (and (not (Info-index-node)) - (save-excursion (search-forward "\n* Menu:" nil t))) - (Info-goto-node (Info-extract-menu-counting nil))))) + (let ((info-window (get-buffer-window "*info*"))) + (unless (window-live-p info-window) + (user-error "There is no visible Info buffer.")) + (with-selected-window info-window + (Info-goto-node "Top") + (let ((Info-history nil) + (case-fold-search t)) + ;; Go to the last node in the menu of Top. But don't delve into + ;; detailed node listings. + (Info-goto-node (Info-extract-menu-counting nil t)) + ;; If the last node in the menu is not last in pointer structure, + ;; move forward (but not down- or upward - see bug#1116) until we + ;; can't go any farther. + (while (Info-forward-node t t t) nil) + ;; Then keep moving down to last subnode, unless we reach an index= . + (while (and (not (Info-index-node)) + (save-excursion (search-forward "\n* Menu:" nil t))) + (Info-goto-node (Info-extract-menu-counting nil))))))) =20 (defun Info-forward-node (&optional not-down not-up no-error) "Go forward one node, considering all nodes as forming one sequence. @@ -2905,66 +2975,76 @@ Info-forward-node NOT-UP non-nil means don't go to parent nodes, and NO-ERROR non-nil means don't signal a user-error if there's no node to go to." (interactive nil Info-mode) - (goto-char (point-min)) - (forward-line 1) - (let ((case-fold-search t)) - ;; three possibilities, in order of priority: - ;; 1. next node is in a menu in this node (but not in an index) - ;; 2. next node is next at same level - ;; 3. next node is up and next - (cond ((and (not not-down) - (save-excursion (search-forward "\n* menu:" nil t)) - (not (Info-index-node))) - (Info-goto-node (Info-extract-menu-counting 1)) - t) - ((save-excursion (search-backward "next:" nil t)) - (Info-next) - t) - ((and (not not-up) - (save-excursion (search-backward "up:" nil t)) - ;; Use string-equal, not equal, to ignore text props. - (not (string-equal (downcase (Info-extract-pointer "up")) - "top"))) - (let ((old-node Info-current-node)) - (Info-up) - (let ((old-history Info-history) - success) - (unwind-protect - (setq success (Info-forward-node t nil no-error)) - (or success (Info-goto-node old-node))) - (if Info-history-skip-intermediate-nodes - (setq Info-history old-history))))) - (no-error nil) - (t (user-error "No pointer forward from this node"))))) + (let ((info-window (get-buffer-window "*info*"))) + (unless (window-live-p info-window) + (user-error "There is no visible Info buffer.")) + (with-selected-window info-window + (goto-char (point-min)) + (forward-line 1) + (let ((case-fold-search t)) + ;; three possibilities, in order of priority: + ;; 1. next node is in a menu in this node (but not in an index= ) + ;; 2. next node is next at same level + ;; 3. next node is up and next + (cond ((and (not not-down) + (save-excursion (search-forward "\n* menu:" nil t)) + (not (Info-index-node))) + (Info-goto-node (Info-extract-menu-counting 1)) + t) + ((save-excursion (search-backward "next:" nil t)) + (Info-next) + t) + ((and (not not-up) + (save-excursion (search-backward "up:" nil t)) + ;; Use string-equal, not equal, to ignore text props. + (not (string-equal (downcase (Info-extract-pointer "up= ")) + "top"))) + (let ((old-node Info-current-node)) + (Info-up) + (let ((old-history Info-history) + success) + (unwind-protect + (setq success (Info-forward-node t nil no-error)) + (or success (Info-goto-node old-node))) + (if Info-history-skip-intermediate-nodes + (setq Info-history old-history))))) + (no-error nil) + (t (user-error "No pointer forward from this node"))))))) =20 (defun Info-backward-node () "Go backward one node, considering all nodes as forming one sequence. If the current node has a \"previous\" node, go to it, descending into its last sub-node, if any; otherwise go \"up\" to the parent node." (interactive nil Info-mode) - (let ((prevnode (Info-extract-pointer "prev[ious]*" t)) - (upnode (Info-extract-pointer "up" t)) - (case-fold-search t)) - (cond ((and upnode (string-search "(" upnode)) - (user-error "First node in file")) - ((and upnode (or (null prevnode) - ;; Use string-equal, not equal, - ;; to ignore text properties. - (string-equal (downcase prevnode) - (downcase upnode)))) - (Info-up)) - (prevnode - ;; If we move back at the same level, - ;; go down to find the last subnode*. - (Info-prev) - (let ((old-history Info-history)) - (while (and (not (Info-index-node)) - (save-excursion (search-forward "\n* Menu:" nil t))) - (Info-goto-node (Info-extract-menu-counting nil))) - (if Info-history-skip-intermediate-nodes - (setq Info-history old-history)))) - (t - (user-error "No pointer backward from this node"))))) + (let ((info-window (get-buffer-window "*info*"))) + (unless (window-live-p info-window) + (user-error "There is no visible Info buffer.")) + (with-selected-window info-window + (let ((prevnode (Info-extract-pointer "prev[ious]*" t)) + (upnode (Info-extract-pointer "up" t)) + (case-fold-search t)) + (cond ((and upnode (string-search "(" upnode)) + (user-error "First node in file")) + ((and upnode (or (null prevnode) + ;; Use string-equal, not equal, + ;; to ignore text properties. + (string-equal (downcase prevnode) + (downcase upnode)))) + (Info-up)) + (prevnode + ;; If we move back at the same level, + ;; go down to find the last subnode*. + (Info-prev) + (let ((old-history Info-history)) + (while (and (not (Info-index-node)) + (save-excursion (search-forward "\n* Menu:" n= il t))) + (Info-goto-node (Info-extract-menu-counting nil))) + (if Info-history-skip-intermediate-nodes + (setq Info-history old-history)))) + (t + (user-error "No pointer backward from this node"))) + ;; Emacs sometimes display just the very last line + (goto-char (point-min)))))) =20 (define-obsolete-function-alias 'Info-exit #'quit-window "27.1") =20 @@ -2972,31 +3052,39 @@ Info-next-menu-item "Go to the node of the next menu item." (interactive nil Info-mode) ;; Bind this in case the user sets it to nil. - (let* ((case-fold-search t) - (node - (save-excursion - (forward-line -1) - (search-forward "\n* menu:" nil t) - (and (search-forward "\n* " nil t) - (Info-extract-menu-node-name))))) - (if node (Info-goto-node node) - (user-error "No more items in menu")))) + (let ((info-window (get-buffer-window "*info*"))) + (unless (window-live-p info-window) + (user-error "There is no visible Info buffer.")) + (with-selected-window info-window + (let* ((case-fold-search t) + (node + (save-excursion + (forward-line -1) + (search-forward "\n* menu:" nil t) + (and (search-forward "\n* " nil t) + (Info-extract-menu-node-name))))) + (if node (Info-goto-node node) + (user-error "No more items in menu")))))) =20 (defun Info-last-menu-item () "Go to the node of the previous menu item." (interactive nil Info-mode) - (save-excursion - (forward-line 1) - ;; Bind this in case the user sets it to nil. - (let* ((case-fold-search t) - (beg (save-excursion - (and (search-backward "\n* menu:" nil t) - (point))))) - (or (and beg (search-backward "\n* " beg t)) - (user-error "No previous items in menu"))) - (Info-goto-node (save-excursion - (goto-char (match-end 0)) - (Info-extract-menu-node-name))))) + (let ((info-window (get-buffer-window "*info*"))) + (unless (window-live-p info-window) + (user-error "There is no visible Info buffer.")) + (with-selected-window info-window + (save-excursion + (forward-line 1) + ;; Bind this in case the user sets it to nil. + (let* ((case-fold-search t) + (beg (save-excursion + (and (search-backward "\n* menu:" nil t) + (point))))) + (or (and beg (search-backward "\n* " beg t)) + (user-error "No previous items in menu"))) + (Info-goto-node (save-excursion + (goto-char (match-end 0)) + (Info-extract-menu-node-name))))))) =20 (defmacro Info-no-error (&rest body) `(condition-case nil (progn ,@body t) (error nil))) @@ -3004,61 +3092,69 @@ Info-no-error (defun Info-next-preorder () "Go to the next subnode or the next node, or go up a level." (interactive nil Info-mode) - (cond ((Info-no-error (Info-next-menu-item))) - ((Info-no-error (Info-next))) - ((Info-no-error (Info-up t)) - ;; Since we have already gone thru all the items in this menu, - ;; go up to the end of this node. - (goto-char (point-max)) - ;; Since logically we are done with the node with that menu, - ;; move on from it. But don't add intermediate nodes - ;; to the history on recursive calls. - (let ((old-history Info-history)) - (Info-next-preorder) - (if Info-history-skip-intermediate-nodes - (setq Info-history old-history)))) - (t - (user-error "No more nodes")))) + (let ((info-window (get-buffer-window "*info*"))) + (unless (window-live-p info-window) + (user-error "There is no visible Info buffer.")) + (with-selected-window info-window + (cond ((Info-no-error (Info-next-menu-item))) + ((Info-no-error (Info-next))) + ((Info-no-error (Info-up t)) + ;; Since we have already gone thru all the items in this menu= , + ;; go up to the end of this node. + (goto-char (point-max)) + ;; Since logically we are done with the node with that menu, + ;; move on from it. But don't add intermediate nodes + ;; to the history on recursive calls. + (let ((old-history Info-history)) + (Info-next-preorder) + (if Info-history-skip-intermediate-nodes + (setq Info-history old-history)))) + (t + (user-error "No more nodes")))))) =20 (defun Info-last-preorder () "Go to the last node, popping up a level if there is none." (interactive nil Info-mode) - (cond ((and Info-scroll-prefer-subnodes - (Info-no-error - (Info-last-menu-item) - ;; If we go down a menu item, go to the end of the node - ;; so we can scroll back through it. - (goto-char (point-max)))) - ;; Keep going down, as long as there are nested menu nodes. - (let ((old-history Info-history)) - (while (Info-no-error - (Info-last-menu-item) - ;; If we go down a menu item, go to the end of the node - ;; so we can scroll back through it. - (goto-char (point-max)))) - (if Info-history-skip-intermediate-nodes - (setq Info-history old-history))) - (recenter -1)) - ((and (Info-no-error (Info-extract-pointer "prev")) - (not (equal (Info-extract-pointer "up") - (Info-extract-pointer "prev")))) - (Info-no-error (Info-prev)) - (goto-char (point-max)) - (let ((old-history Info-history)) - (while (Info-no-error - (Info-last-menu-item) - ;; If we go down a menu item, go to the end of the node - ;; so we can scroll back through it. - (goto-char (point-max)))) - (if Info-history-skip-intermediate-nodes - (setq Info-history old-history))) - (recenter -1)) - ((Info-no-error (Info-up t)) - (goto-char (point-min)) - (let ((case-fold-search t)) - (or (search-forward "\n* Menu:" nil t) - (goto-char (point-max))))) - (t (user-error "No previous nodes")))) + (let ((info-window (get-buffer-window "*info*"))) + (unless (window-live-p info-window) + (user-error "There is no visible Info buffer.")) + (with-selected-window info-window + (cond ((and Info-scroll-prefer-subnodes + (Info-no-error + (Info-last-menu-item) + ;; If we go down a menu item, go to the end of the node + ;; so we can scroll back through it. + (goto-char (point-max)))) + ;; Keep going down, as long as there are nested menu nodes. + (let ((old-history Info-history)) + (while (Info-no-error + (Info-last-menu-item) + ;; If we go down a menu item, go to the end of the = node + ;; so we can scroll back through it. + (goto-char (point-max)))) + (if Info-history-skip-intermediate-nodes + (setq Info-history old-history))) + (recenter -1)) + ((and (Info-no-error (Info-extract-pointer "prev")) + (not (equal (Info-extract-pointer "up") + (Info-extract-pointer "prev")))) + (Info-no-error (Info-prev)) + (goto-char (point-max)) + (let ((old-history Info-history)) + (while (Info-no-error + (Info-last-menu-item) + ;; If we go down a menu item, go to the end of the = node + ;; so we can scroll back through it. + (goto-char (point-max)))) + (if Info-history-skip-intermediate-nodes + (setq Info-history old-history))) + (recenter -1)) + ((Info-no-error (Info-up t)) + (goto-char (point-min)) + (let ((case-fold-search t)) + (or (search-forward "\n* Menu:" nil t) + (goto-char (point-max))))) + (t (user-error "No previous nodes")))))) =20 (defun Info-scroll-up () "Scroll one screenful forward in Info, considering all nodes as one sequ= ence. @@ -3075,23 +3171,27 @@ Info-scroll-up in other ways.)" =20 (interactive nil Info-mode) - (if (or (< (window-start) (point-min)) - (> (window-start) (point-max))) - (set-window-start (selected-window) (point))) - (let* ((case-fold-search t) - (virtual-end (save-excursion - (goto-char (point-min)) - (if (and Info-scroll-prefer-subnodes - (search-forward "\n* Menu:" nil t)) - (point) - (point-max))))) - (if (or (< virtual-end (window-start)) - (pos-visible-in-window-p virtual-end)) - (cond - (Info-scroll-prefer-subnodes (Info-next-preorder)) - ((Info-no-error (Info-goto-node (Info-extract-menu-counting 1)))) - (t (Info-next-preorder))) - (scroll-up)))) + (let ((info-window (get-buffer-window "*info*"))) + (unless (window-live-p info-window) + (user-error "There is no visible Info buffer.")) + (with-selected-window info-window + (if (or (< (window-start) (point-min)) + (> (window-start) (point-max))) + (set-window-start (selected-window) (point))) + (let* ((case-fold-search t) + (virtual-end (save-excursion + (goto-char (point-min)) + (if (and Info-scroll-prefer-subnodes + (search-forward "\n* Menu:" nil t)) + (point) + (point-max))))) + (if (or (< virtual-end (window-start)) + (pos-visible-in-window-p virtual-end)) + (cond + (Info-scroll-prefer-subnodes (Info-next-preorder)) + ((Info-no-error (Info-goto-node (Info-extract-menu-counting 1= )))) + (t (Info-next-preorder))) + (scroll-up)))))) =20 (defun Info-mouse-scroll-up (e) "Scroll one screenful forward in Info, using the mouse. @@ -3109,21 +3209,25 @@ Info-scroll-down beginning of a node, that goes to the previous node or back up to the parent node." (interactive nil Info-mode) - (if (or (< (window-start) (point-min)) - (> (window-start) (point-max))) - (set-window-start (selected-window) (point))) - (let* ((case-fold-search t) - (current-point (point)) - (virtual-end - (and Info-scroll-prefer-subnodes - (save-excursion - (setq current-point (line-beginning-position)) - (goto-char (point-min)) - (search-forward "\n* Menu:" current-point t))))) - (if (or virtual-end - (pos-visible-in-window-p (point-min) nil t)) - (Info-last-preorder) - (scroll-down)))) + (let ((info-window (get-buffer-window "*info*"))) + (unless (window-live-p info-window) + (user-error "There is no visible Info buffer.")) + (with-selected-window info-window + (if (or (< (window-start) (point-min)) + (> (window-start) (point-max))) + (set-window-start (selected-window) (point))) + (let* ((case-fold-search t) + (current-point (point)) + (virtual-end + (and Info-scroll-prefer-subnodes + (save-excursion + (setq current-point (line-beginning-position)) + (goto-char (point-min)) + (search-forward "\n* Menu:" current-point t))))) + (if (or virtual-end + (pos-visible-in-window-p (point-min) nil t)) + (Info-last-preorder) + (scroll-down)))))) =20 (defun Info-mouse-scroll-down (e) "Scroll one screenful backward in Info, using the mouse. @@ -3175,55 +3279,63 @@ Info-next-reference If COUNT is non-nil (interactively with a prefix arg), jump over COUNT cross-references." (interactive "i\np" Info-mode) - (unless count - (setq count 1)) - (if (< count 0) - (Info-prev-reference recur (- count)) - (while (unless (zerop count) (setq count (1- count))) - (let ((pat "\\*note[ \n\t]+\\([^:]+\\):\\|^\\* .*:\\|[hf]t?tps?://") - (old-pt (point)) - (case-fold-search t)) - (or (eobp) (forward-char 1)) - (or (Info-next-reference-or-link pat 'link) - (progn - (goto-char (point-min)) - (or (Info-next-reference-or-link pat 'link) - (progn - (goto-char old-pt) - (user-error "No cross references in this node"))))) - (if (looking-at "\\* Menu:") - (if recur - (user-error "No cross references in this node") - (Info-next-reference t)) - (if (looking-at "^\\* ") - (forward-char 2))))))) + (let ((info-window (get-buffer-window "*info*"))) + (unless (window-live-p info-window) + (user-error "There is no visible Info buffer.")) + (with-selected-window info-window + (unless count + (setq count 1)) + (if (< count 0) + (Info-prev-reference recur (- count)) + (while (unless (zerop count) (setq count (1- count))) + (let ((pat "\\*note[ \n\t]+\\([^:]+\\):\\|^\\* .*:\\|[hf]t?tps?:= //") + (old-pt (point)) + (case-fold-search t)) + (or (eobp) (forward-char 1)) + (or (Info-next-reference-or-link pat 'link) + (progn + (goto-char (point-min)) + (or (Info-next-reference-or-link pat 'link) + (progn + (goto-char old-pt) + (user-error "No cross references in this node"))))= ) + (if (looking-at "\\* Menu:") + (if recur + (user-error "No cross references in this node") + (Info-next-reference t)) + (if (looking-at "^\\* ") + (forward-char 2))))))))) =20 (defun Info-prev-reference (&optional recur count) "Move cursor to the previous cross-reference or menu item in the node. If COUNT is non-nil (interactively with a prefix arg), jump over COUNT cross-references." (interactive "i\np" Info-mode) - (unless count - (setq count 1)) - (if (< count 0) - (Info-next-reference recur (- count)) - (while (unless (zerop count) (setq count (1- count))) - (let ((pat "\\*note[ \n\t]+\\([^:]+\\):\\|^\\* .*:\\|[hf]t?tps?://") - (old-pt (point)) - (case-fold-search t)) - (or (Info-prev-reference-or-link pat 'link) - (progn - (goto-char (point-max)) - (or (Info-prev-reference-or-link pat 'link) - (progn - (goto-char old-pt) - (user-error "No cross references in this node"))))) - (if (looking-at "\\* Menu:") - (if recur - (user-error "No cross references in this node") - (Info-prev-reference t)) - (if (looking-at "^\\* ") - (forward-char 2))))))) + (let ((info-window (get-buffer-window "*info*"))) + (unless (window-live-p info-window) + (user-error "There is no visible Info buffer.")) + (with-selected-window info-window + (unless count + (setq count 1)) + (if (< count 0) + (Info-next-reference recur (- count)) + (while (unless (zerop count) (setq count (1- count))) + (let ((pat "\\*note[ \n\t]+\\([^:]+\\):\\|^\\* .*:\\|[hf]t?tps?:= //") + (old-pt (point)) + (case-fold-search t)) + (or (Info-prev-reference-or-link pat 'link) + (progn + (goto-char (point-max)) + (or (Info-prev-reference-or-link pat 'link) + (progn + (goto-char old-pt) + (user-error "No cross references in this node"))))= ) + (if (looking-at "\\* Menu:") + (if recur + (user-error "No cross references in this node") + (Info-prev-reference t)) + (if (looking-at "^\\* ") + (forward-char 2))))))))) =0C (defun Info-index-nodes (&optional file) "Return a list of names of all index nodes in Info FILE. @@ -3345,64 +3457,71 @@ Info-index Give an empty topic name to go to the Index node itself." (interactive (list - (let ((completion-ignore-case t) - (Info-complete-menu-buffer (clone-buffer)) - (Info-complete-nodes (Info-index-nodes)) - (Info-history-list nil)) - (info--ensure-not-in-directory-node) - (unwind-protect - (with-current-buffer Info-complete-menu-buffer - (Info-goto-index) - (completing-read "Index topic: " #'Info-complete-menu-item)) - (kill-buffer Info-complete-menu-buffer))))) + (let ((info-window (get-buffer-window "*info*"))) + (unless (window-live-p info-window) + (user-error "There is no visible Info buffer.")) + (if (not (eq (selected-window) info-window)) + (Info-jump) + (setq Info-jump nil)) + (let ((completion-ignore-case t) + (Info-complete-menu-buffer (clone-buffer)) + (Info-complete-nodes (Info-index-nodes)) + (Info-history-list nil)) + (info--ensure-not-in-directory-node) + (unwind-protect + (with-current-buffer Info-complete-menu-buffer + (Info-goto-index) + (completing-read "Index topic: " #'Info-complete-menu-item)) + (kill-buffer Info-complete-menu-buffer)))))) (info--ensure-not-in-directory-node) ;; Strip leading colon in topic; index format does not allow them. (if (and (stringp topic) - (> (length topic) 0) - (=3D (aref topic 0) ?:)) + (> (length topic) 0) + (=3D (aref topic 0) ?:)) (setq topic (substring topic 1))) (let ((orignode Info-current-node) - (pattern (format "\n\\* +\\([^\n]*\\(%s\\)[^\n]*\\):[ \t]+\\([^\n]*\\)\\.= \\(?:[ \t\n]*(line +\\([0-9]+\\))\\)?" - (regexp-quote topic))) - node (nodes (Info-index-nodes)) - (ohist-list Info-history-list) - (case-fold-search t)) + (pattern (format "\n\\* +\\([^\n]*\\(%s\\)[^\n]*\\):[ \t]+\\([^\n]= *\\)\\.\\(?:[ \t\n]*(line +\\([0-9]+\\))\\)?" + (regexp-quote topic))) + node (nodes (Info-index-nodes)) + (ohist-list Info-history-list) + (case-fold-search t)) (Info-goto-index) (or (equal topic "") - (let ((matches nil) - (exact nil) - ;; We bind Info-history to nil for internal node-switches so - ;; that we don't put junk in the history. In the first - ;; Info-goto-index call, above, we do update the history - ;; because that is what the user's previous node choice into it. - (Info-history nil) - found) - (while - (progn - (goto-char (point-min)) - (while (re-search-forward pattern nil t) - (let ((entry (match-string-no-properties 1)) - (nodename (match-string-no-properties 3)) - (line (string-to-number (concat "0" (match-string 4))))) - (add-text-properties - (- (match-beginning 2) (match-beginning 1)) - (- (match-end 2) (match-beginning 1)) - '(face info-index-match) entry) - (push (list entry nodename Info-current-node line) matches))) - (setq nodes (cdr nodes) node (car nodes))) - (Info-goto-node node)) - (or matches - (progn - (Info-goto-node orignode) - (user-error "No `%s' in index" topic))) - ;; Here it is a feature that assoc is case-sensitive. - (while (setq found (assoc topic matches)) - (setq exact (cons found exact) - matches (delq found matches))) + (let ((matches nil) + (exact nil) + ;; We bind Info-history to nil for internal node-switches so + ;; that we don't put junk in the history. In the first + ;; Info-goto-index call, above, we do update the history + ;; because that is what the user's previous node choice into= it. + (Info-history nil) + found) + (while + (progn + (goto-char (point-min)) + (while (re-search-forward pattern nil t) + (let ((entry (match-string-no-properties 1)) + (nodename (match-string-no-properties 3)) + (line (string-to-number (concat "0" (match-string = 4))))) + (add-text-properties + (- (match-beginning 2) (match-beginning 1)) + (- (match-end 2) (match-beginning 1)) + '(face info-index-match) entry) + (push (list entry nodename Info-current-node line) mat= ches))) + (setq nodes (cdr nodes) node (car nodes))) + (Info-goto-node node)) + (or matches + (progn + (Info-goto-node orignode) + (user-error "No `%s' in index" topic))) + ;; Here it is a feature that assoc is case-sensitive. + (while (setq found (assoc topic matches)) + (setq exact (cons found exact) + matches (delq found matches))) (setq Info-history-list ohist-list) - (setq Info-index-alternatives (nconc exact (nreverse matches)) + (setq Info-index-alternatives (nconc exact (nreverse matches)) Info--current-index-alternative 0) - (Info-index-next 0))))) + (Info-index-next 0)))) + (when Info-jump (select-window Info-jump))) =20 (defun Info-index-next (num) "Go to the next matching index item from the last \\\\[In= fo-index] command. @@ -3411,45 +3530,49 @@ Info-index-next =20 Also see the `Info-warn-on-index-alternatives-wrap' user option." (interactive "p" Info-mode) - (unless Info-index-alternatives - (user-error "No previous `i' command")) - (let ((index (+ Info--current-index-alternative num)) - (total (length Info-index-alternatives)) - (next-key (key-description (where-is-internal - 'Info-index-next overriding-local-map t)))) - (if (and Info-warn-on-index-alternatives-wrap - (> total 1) - (cond - ((< index 0) - (setq Info--current-index-alternative (- total 2)) - (message - "No previous matches, use `%s' to continue from end of lis= t" - next-key) - t) - ((>=3D index total) - (setq Info--current-index-alternative -1) - (message - "No previous matches, use `%s' to continue from start of l= ist" - next-key) - t))) - () ; Do nothing - (setq index (mod index total) - Info--current-index-alternative index) - (let ((entry (nth index Info-index-alternatives))) - (Info-goto-node (nth 1 entry)) - (if (> (nth 3 entry) 0) - ;; Forward 2 lines less because `Info-find-node-2' initially - ;; puts point to the 2nd line. - (forward-line (- (nth 3 entry) 2)) - (forward-line 3) ; don't search in headers - (Info-find-index-name (car entry))) - (message "Found `%s' in %s. %s" - (car entry) - (nth 2 entry) - (if (> total 1) - (format-message - "(%s total; use `%s' for next)" total next-key) - "(Only match)")))))) + (let ((info-window (get-buffer-window "*info*"))) + (unless (window-live-p info-window) + (user-error "There is no visible Info buffer.")) + (with-selected-window info-window + (unless Info-index-alternatives + (user-error "No previous `i' command")) + (let ((index (+ Info--current-index-alternative num)) + (total (length Info-index-alternatives)) + (next-key (key-description (where-is-internal + 'Info-index-next overriding-local-= map t)))) + (if (and Info-warn-on-index-alternatives-wrap + (> total 1) + (cond + ((< index 0) + (setq Info--current-index-alternative (- total 2)) + (message + "No previous matches, use `%s' to continue from end of= list" + next-key) + t) + ((>=3D index total) + (setq Info--current-index-alternative -1) + (message + "No previous matches, use `%s' to continue from start = of list" + next-key) + t))) + () ; Do nothing + (setq index (mod index total) + Info--current-index-alternative index) + (let ((entry (nth index Info-index-alternatives))) + (Info-goto-node (nth 1 entry)) + (if (> (nth 3 entry) 0) + ;; Forward 2 lines less because `Info-find-node-2' initial= ly + ;; puts point to the 2nd line. + (forward-line (- (nth 3 entry) 2)) + (forward-line 3) ; don't search in headers + (Info-find-index-name (car entry))) + (message "Found `%s' in %s. %s" + (car entry) + (nth 2 entry) + (if (> total 1) + (format-message + "(%s total; use `%s' for next)" total next-key) + "(Only match)")))))))) =20 (defun Info-find-index-name (name) "Move point to the place within the current node where NAME is defined." @@ -3534,32 +3657,39 @@ Info-virtual-index ;; `interactive' is a copy from `Info-index' (interactive (list - (let ((completion-ignore-case t) - (Info-complete-menu-buffer (clone-buffer)) - (Info-complete-nodes (Info-index-nodes)) - (Info-history-list nil)) - (info--ensure-not-in-directory-node) - (unwind-protect - (with-current-buffer Info-complete-menu-buffer - (Info-goto-index) - (completing-read "Index topic: " #'Info-complete-menu-item)) - (kill-buffer Info-complete-menu-buffer)))) + (let ((info-window (get-buffer-window "*info*"))) + (unless (window-live-p info-window) + (user-error "There is no visible Info buffer.")) + (if (not (eq (selected-window) info-window)) + (Info-jump) + (setq Info-jump nil)) + (let ((completion-ignore-case t) + (Info-complete-menu-buffer (clone-buffer)) + (Info-complete-nodes (Info-index-nodes)) + (Info-history-list nil)) + (info--ensure-not-in-directory-node) + (unwind-protect + (with-current-buffer Info-complete-menu-buffer + (Info-goto-index) + (completing-read "Index topic: " #'Info-complete-menu-item)) + (kill-buffer Info-complete-menu-buffer))))) Info-mode) (if (equal topic "") (Info-find-node Info-current-file "*Index*") (unless (assoc (cons Info-current-file topic) Info-virtual-index-nodes= ) (let ((orignode Info-current-node) - (ohist-list Info-history-list)) - ;; Reuse `Info-index' to set `Info-index-alternatives'. - (Info-index topic) - (push (cons (cons Info-current-file topic) Info-index-alternatives) - Info-virtual-index-nodes) - ;; Clean up unnecessary side-effects of `Info-index'. - (setq Info-history-list ohist-list) - (Info-goto-node orignode) - (message ""))) + (ohist-list Info-history-list)) + ;; Reuse `Info-index' to set `Info-index-alternatives'. + (Info-index topic) + (push (cons (cons Info-current-file topic) Info-index-alternatives= ) + Info-virtual-index-nodes) + ;; Clean up unnecessary side-effects of `Info-index'. + (setq Info-history-list ohist-list) + (Info-goto-node orignode) + (message ""))) (Info-find-node Info-current-file - (format "*Index for =E2=80=98%s=E2=80=99*" topic)))) + (format "*Index for =E2=80=98%s=E2=80=99*" topic))) + (when Info-jump (Info-jump))) =0C (add-to-list 'Info-virtual-files '("\\`\\*Apropos\\*\\'" @@ -3696,18 +3826,22 @@ info-apropos =20 Display a menu of the possible matches." (interactive "sIndex apropos: \nP") - (if (equal string "") - (Info-find-node Info-apropos-file "Top") - (let ((nodes Info-apropos-nodes) - nodename) - (while (and nodes (not (equal string (nth 1 (car nodes))))) - (setq nodes (cdr nodes))) - (if nodes - (Info-find-node Info-apropos-file (car (car nodes)) nil nil t) - (setq nodename (format "Index for =E2=80=98%s=E2=80=99" string)) - (push (list nodename string (Info-apropos-matches string regexp)) - Info-apropos-nodes) - (Info-find-node Info-apropos-file nodename))))) + (let ((info-window (get-buffer-window "*info*"))) + (unless (window-live-p info-window) + (user-error "There is no visible Info buffer.")) + (with-selected-window info-window + (if (equal string "") + (Info-find-node Info-apropos-file "Top") + (let ((nodes Info-apropos-nodes) + nodename) + (while (and nodes (not (equal string (nth 1 (car nodes))))) + (setq nodes (cdr nodes))) + (if nodes + (Info-find-node Info-apropos-file (car (car nodes)) nil nil = t) + (setq nodename (format "Index for =E2=80=98%s=E2=80=99" string= )) + (push (list nodename string (Info-apropos-matches string regex= p)) + Info-apropos-nodes) + (Info-find-node Info-apropos-file nodename))))))) =0C (add-to-list 'Info-virtual-files '("\\`\\*Finder.*\\*\\'" @@ -3842,17 +3976,29 @@ info-finder (interactive (when current-prefix-arg (require 'finder) - (list - (completing-read-multiple - "Keywords (separated by comma): " - (mapcar #'symbol-name (mapcar #'car (append finder-known-keywords - (finder-unknown-keyword= s)))) - nil t)))) + (let ((info-window (get-buffer-window "*info*"))) + (unless (window-live-p info-window) + (user-error "There is no visible Info buffer.")) + (if (not (eq (selected-window) info-window)) + (Info-jump) + (setq Info-jump nil)) + (list + (completing-read-multiple + "Keywords (separated by comma): " + (mapcar #'symbol-name (mapcar #'car (append finder-known-keywords + (finder-unknown-keywo= rds)))) + nil t))))) (require 'finder) - (if keywords - (Info-find-node Info-finder-file (mapconcat 'identity keywords ", ")= ) - (Info-find-node Info-finder-file "Top"))) - + (let ((info-window (get-buffer-window "*info*"))) + (unless (window-live-p info-window) + (user-error "There is no visible Info buffer.")) + (if (not (eq (selected-window) info-window)) + (Info-jump) + (setq Info-jump nil)) + (if keywords + (Info-find-node Info-finder-file (mapconcat 'identity keywords ", = ")) + (Info-find-node Info-finder-file "Top"))) + (when Info-jump (Info-jump))) =0C (defun Info-undefined () "Make command be undefined in Info." @@ -3871,22 +4017,26 @@ Info-help (defun Info-summary () "Display a brief summary of all Info commands." (interactive) - (save-window-excursion - (switch-to-buffer "*Help*") - (setq buffer-read-only nil) - (erase-buffer) - (insert (documentation 'Info-mode)) - (help-mode) - (goto-char (point-min)) - (let (ch flag) - (while (progn (setq flag (not (pos-visible-in-window-p (point-max)))= ) - (message (if flag "Type Space to see more" - "Type Space to return to Info")) - (if (not (eq ?\s (setq ch (read-event)))) - (progn (push ch unread-command-events) nil) - flag)) - (scroll-up))) - (bury-buffer "*Help*"))) + (let ((info-window (get-buffer-window "*info*"))) + (unless (window-live-p info-window) + (user-error "There is no visible Info buffer.")) + (with-selected-window info-window + (save-window-excursion + (switch-to-buffer "*Help*") + (setq buffer-read-only nil) + (erase-buffer) + (insert (documentation 'Info-mode)) + (help-mode) + (goto-char (point-min)) + (let (ch flag) + (while (progn (setq flag (not (pos-visible-in-window-p (point-ma= x)))) + (message (if flag "Type Space to see more" + "Type Space to return to Info")) + (if (not (eq ?\s (setq ch (read-event)))) + (progn (push ch unread-command-events) nil) + flag)) + (scroll-up))) + (bury-buffer "*Help*"))))) =0C (defun Info-get-token (pos start all &optional errorstring) "Return the token around POS. @@ -4038,7 +4188,7 @@ Info-mouse-follow-link (defvar Info-mode-map (let ((map (make-keymap))) (suppress-keymap map) - (define-key map "." 'beginning-of-buffer) + (define-key map "." 'Info-beginning-of-buffer) (define-key map " " 'Info-scroll-up) (define-key map [?\S-\ ] 'Info-scroll-down) (define-key map "\C-m" 'Info-follow-nearest-node) @@ -4060,10 +4210,10 @@ Info-mode-map (define-key map "[" 'Info-backward-node) (define-key map "<" 'Info-top-node) (define-key map ">" 'Info-final-node) - (define-key map "b" 'beginning-of-buffer) + (define-key map "b" 'Info-beginning-of-buffer) (put 'beginning-of-buffer :advertised-binding "b") (define-key map "d" 'Info-directory) - (define-key map "e" 'end-of-buffer) + (define-key map "e" 'Info-end-of-buffer) (define-key map "f" 'Info-follow-reference) (define-key map "g" 'Info-goto-node) (define-key map "G" 'Info-goto-node-web) @@ -4071,15 +4221,16 @@ Info-mode-map ;; This is for compatibility with standalone info (>~ version 5.2). ;; Though for some time, standalone info had H and h reversed. ;; See . - (define-key map "H" 'describe-mode) + (define-key map "H" 'Info-describe-mode) (define-key map "i" 'Info-index) + (define-key map "j" 'Info-jump) (define-key map "I" 'Info-virtual-index) (define-key map "l" 'Info-history-back) (define-key map "L" 'Info-history) (define-key map "m" 'Info-menu) (define-key map "n" 'Info-next) (define-key map "p" 'Info-prev) - (define-key map "q" 'quit-window) + (define-key map "q" 'Info-quit-window) (define-key map "r" 'Info-history-forward) (define-key map "s" 'Info-search) (define-key map "S" 'Info-search-case-sensitively) @@ -4104,6 +4255,21 @@ Info-mode-map map) "Keymap containing Info commands.") =20 +;; with a little help from Helm +(defcustom Info-mode-prefix-key "C-h M-i" + "`Info-mode-map' prefix key for invocation from other buffers." + :version "30.1" + :type '(choice (string :tag "Key") (const :tag "no binding")) + :set (lambda (var key) + (when (and (boundp var) (symbol-value var)) + (define-key (current-global-map) + (read-kbd-macro (symbol-value var)) nil)) + (when key + (define-key (current-global-map) + (read-kbd-macro key) + Info-mode-map)) + (set var key)) + :group 'info) =20 (defun Info-check-pointer (item) "Non-nil if ITEM is present in this node." @@ -4125,7 +4291,7 @@ Info-check-pointer :help "Go backward one node, considering all as a sequence"] ["Forward" Info-forward-node :help "Go forward one node, considering all as a sequence"] - ["Beginning" beginning-of-buffer + ["Beginning" Info-beginning-of-buffer :help "Go to beginning of this node"] ["Top" Info-top-node :help "Go to top node of file"] @@ -4323,20 +4489,24 @@ Info-copy-current-node-name The name of the Info file is prepended to the node name in parentheses. With a zero prefix arg, put the name inside a function call to `info'." (interactive "P" Info-mode) - (unless Info-current-node - (user-error "No current Info node")) - (let ((node (if (stringp Info-current-file) - (concat "(" (file-name-sans-extension - (file-name-nondirectory Info-current-file)) - ") " - Info-current-node)))) - (if (zerop (prefix-numeric-value arg)) - (setq node (concat "(info \"" node "\")"))) - (unless (stringp Info-current-file) - (setq node (format "(Info-find-node '%S '%S)" - Info-current-file Info-current-node))) - (kill-new node) - (message "%s" node))) + (let ((info-window (get-buffer-window "*info*"))) + (unless (window-live-p info-window) + (user-error "There is no visible Info buffer.")) + (with-selected-window info-window + (unless Info-current-node + (user-error "No current Info node")) + (let ((node (if (stringp Info-current-file) + (concat "(" (file-name-sans-extension + (file-name-nondirectory Info-current-file)) + ") " + Info-current-node)))) + (if (zerop (prefix-numeric-value arg)) + (setq node (concat "(info \"" node "\")"))) + (unless (stringp Info-current-file) + (setq node (format "(Info-find-node '%S '%S)" + Info-current-file Info-current-node))) + (kill-new node) + (message "%s" node))))) =20 =0C ;; Info mode is suitable only for specially formatted data. @@ -5509,6 +5679,42 @@ info--manual-names (apply-partially #'Info-read-node-name-2 Info-directory-list (mapcar #'car Info-suffix-list)))))))) +=0C +;; commands from special-mode wrapped to work on Info-mode only + +(defun Info-beginning-of-buffer () + "Move point to the beginning of *info* buffer." + (interactive) + (let ((info-window (get-buffer-window "*info*"))) + (unless (window-live-p info-window) + (user-error "There is no visible Info buffer.")) + (with-selected-window info-window + (goto-char (point-min))))) + +(defun Info-end-of-buffer () + "Move point to the beginning of *info* buffer." + (interactive) + (let ((info-window (get-buffer-window "*info*"))) + (unless (window-live-p info-window) + (user-error "There is no visible Info buffer.")) + (with-selected-window info-window + (goto-char (point-max))))) + +(defun Info-describe-mode () + "As `describe-mode' but for *Help* buffer only." + (interactive) + (let ((info-window (get-buffer "*info*"))) + (unless (window-live-p (get-buffer-window info-window)) + (user-error "There is no visible Info buffer.")) + (describe-mode info-window))) + +(defun Info-quit-window () + (interactive) + (let ((info-window (get-buffer-window "*info*"))) + (unless (window-live-p info-window) + (user-error "There is no visible Info buffer.")) + (with-selected-window info-window + (quit-window nil info-window)))) =20 (provide 'info) =20 --=20 2.40.1 --=-=-=--