From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.io!.POSTED.blaine.gmane.org!not-for-mail From: Yuri Khan Newsgroups: gmane.emacs.help Subject: Re: bmenu for recentf (or abstracting-away bmenu) Date: Thu, 16 Nov 2023 14:03:55 +0700 Message-ID: References: <87pm0afkr5.fsf@laptop.lockywolf.net> Mime-Version: 1.0 Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable Injection-Info: ciao.gmane.io; posting-host="blaine.gmane.org:116.202.254.214"; logging-data="37048"; mail-complaints-to="usenet@ciao.gmane.io" Cc: "help-gnu-emacs@gnu.org" To: Lockywolf Original-X-From: help-gnu-emacs-bounces+geh-help-gnu-emacs=m.gmane-mx.org@gnu.org Thu Nov 16 08:04:54 2023 Return-path: Envelope-to: geh-help-gnu-emacs@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 1r3WQm-0009IL-UN for geh-help-gnu-emacs@m.gmane-mx.org; Thu, 16 Nov 2023 08:04:53 +0100 Original-Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1r3WQ9-0007Kl-Mo; Thu, 16 Nov 2023 02:04:13 -0500 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 1r3WQ7-0007KI-O1 for help-gnu-emacs@gnu.org; Thu, 16 Nov 2023 02:04:11 -0500 Original-Received: from mail-ua1-x931.google.com ([2607:f8b0:4864:20::931]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1r3WQ5-0007P3-Jd for help-gnu-emacs@gnu.org; Thu, 16 Nov 2023 02:04:11 -0500 Original-Received: by mail-ua1-x931.google.com with SMTP id a1e0cc1a2514c-7b9ff2b6f9bso194518241.3 for ; Wed, 15 Nov 2023 23:04:08 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1700118247; x=1700723047; darn=gnu.org; h=content-transfer-encoding:cc:to:subject:message-id:date:from :in-reply-to:references:mime-version:from:to:cc:subject:date :message-id:reply-to; bh=36DoyzxHYj3/FlDSAKY/rQDLYNByyz4n7gDqFPbG5rw=; b=Nmwnd2HsjOIBXYRzZ8ENvDNYZeUCSL0NNPgq8btlP5YwmNr60qDTEMqs5SnlRSCHwt JZX0yeLZ8XV3SllAqEJSIQUdmkQmK0fCl8I+P9IlDuTDtIpV7f4WMrItPo0Ei4reqSpe 8hwuPcP/Qund9FWNoMvHNdZkwagUjp2WVqioafPXtPH7y1xuhvoov0ZbOvKdzT5waTfq 5t8XNXkAXmjI4EDVcPtMqRe3SgSjtMCU0qn7TcWspGLB5xWlgvx4obFFAaNK/mcb2Pjd ST4aTlStpgq2S44U0RWMcIOc9USuoXlAyq4/c1Q34URoA1Cw5JEDo32X5RVLhGSDJ90T Ia1Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1700118247; x=1700723047; h=content-transfer-encoding:cc:to:subject:message-id:date:from :in-reply-to:references:mime-version:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=36DoyzxHYj3/FlDSAKY/rQDLYNByyz4n7gDqFPbG5rw=; b=pMYH3bu79JBArOKoEqjZggrOXNQpQBMKcs2cpa5r4Jms2c3UpcYp3R9PGBcU2BU/Yx eOjzn9gO7URednMfw6hJ5tzwsBLAxEkYgjEiNpAbZhf6xX/8AVg4DnvXGUsmQhtW0WsU QFNNAsKGRE7fM/sMXhDgk9u1cf9EGQV8OWCMCrCAPvtOyoWYM3dBKv0O135peARDjIag d4XyMZYlHlMQPbd2OcwNsiFjrGIP+WKLluJMZ1rmRbQFH9vecXLhH7ddp2uYB98SOvbJ n5j+KZCkhNB+QdQN5eY1WkWX8G04xdBArX385rpnKy9SvLymBgKvxB+QZmi3GS6eUV8H 0pXw== X-Gm-Message-State: AOJu0Yzkf80B2PaNvqrN05T4nhkiFxwj/U6x+oPf520kLVJcLLNbRZrv c2JWcAJTWP48qmcIxJrhuwlwWGvw+vtdOCEa7FYXzj2vbnY= X-Google-Smtp-Source: AGHT+IHQXLvr1L0UgspRu3n/wZnxhr9vTQn3HDbPAOh4AhzSOFUb53ePB5jqLE59MQmsJDt51/Y1/G4n8nKuLnvSQIs= X-Received: by 2002:a05:6122:1163:b0:496:2e22:29e3 with SMTP id q3-20020a056122116300b004962e2229e3mr14642555vko.1.1700118247347; Wed, 15 Nov 2023 23:04:07 -0800 (PST) In-Reply-To: <87pm0afkr5.fsf@laptop.lockywolf.net> Received-SPF: pass client-ip=2607:f8b0:4864:20::931; envelope-from=yurivkhan@gmail.com; helo=mail-ua1-x931.google.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, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, T_SCC_BODY_TEXT_LINE=-0.01 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: help-gnu-emacs@gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Users list for the GNU Emacs text editor List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: help-gnu-emacs-bounces+geh-help-gnu-emacs=m.gmane-mx.org@gnu.org Original-Sender: help-gnu-emacs-bounces+geh-help-gnu-emacs=m.gmane-mx.org@gnu.org Xref: news.gmane.io gmane.emacs.help:145543 Archived-At: On Thu, 16 Nov 2023 at 12:54, Lockywolf wrote: > I want to have a buffer-menu for recent files, in a way similar to > bookmarks. > > In fact, the bmenu code in bookmarks.el is just about 700 lines, not > very huge, so I considered writing it myself. However, later I thought > that this would mean effectively duplicating a lot of code which might > happen to be useful in other packages too. > > Are there some plans on creating a common bmenu library for Emacs? > Perhaps it could also integrate with the menu-bar mode somehow (which > would be less surprising for the users used to the Windows way of > self-discovery). Or maybe such a library already exists? The abstraction behind bmenu is called tabulated-list-mode. Here=E2=80=99s = a simplified extract from my config implementing a recent files navigator. The core functionality is just about 50 lines of code and all of it is domain-specific (cannot be abstracted). You start with a top-level command to conjure a buffer and put it into a major mode derived from =E2=80=98tabulated-list-mode=E2=80=99: (defun yk-recentf-show () "Show a list of recently opened files." (interactive) (with-current-buffer (get-buffer-create " *Recentf*") (yk-recentf-show-mode) ;; see below (tabulated-list-revert) (pop-to-buffer (current-buffer)))) That mode needs to set a few buffer-local variables to teach the base mode how to display entries and how to revert itself on =E2=80=98g=E2=80=99= : (define-derived-mode yk-recentf-show-mode tabulated-list-mode "Recentf" "Major mode for `yk-recentf-show' buffers." (setq tabulated-list-format [("M" 1 nil) ("Filename" 20 nil) ("Directory" -1 nil)]) (setq-local tabulated-list-revert-hook 'yk-recentf-show--revert) ;; see below (tabulated-list-init-header)) Your revert hook needs to populate the buffer-local =E2=80=98tabulated-list-entries=E2=80=99 with a list whose each element is = a list of an ID and a vector of cell values: (defun yk-recentf-show--revert () "Re-populate the buffer listing recently opened files." (setq tabulated-list-entries (mapcar (lambda (filename) `(,filename ["" ,(file-name-nondirectory filename) ,(file-name-directory filename)])) recentf-list))) That=E2=80=99s basically all you need for the displaying part. Now for the acting part. You define a keymap for the derived mode (named by =E2=80=98*-map=E2=80=99 convention so the mode uses it automatically): (defvar yk-recentf-show-mode-map (let ((map (make-sparse-keymap))) (define-key map (kbd "RET") 'yk-recentf-show-find-file) (define-key map (kbd "o") 'yk-recentf-show-find-file-other-window) map)) The handlers are simple wrappers that get the ID of the entry at point and pass it to whatever command: (defun yk-recentf-show-find-file () "Visit the file at point." (interactive) (find-file (tabulated-list-get-id))) (defun yk-recentf-show-find-file-other-window () "Visit the file at point in other window." (interactive) (find-file-other-window (tabulated-list-get-id))) All that is left now is to bind the top-level command to some easy key. I use Alt+F11 in memory of a certain blue-and-cyan text mode file manager on a popular non-free platform. (global-set-key (kbd "M-") 'yk-recentf-show) One thing I found lacking in the base tabulated-list-mode is generic support for marks and deletion. Like, in Dired, Bmenu, and Ibuffer you can type =E2=80=98D=E2=80=99 to delete an entry at point immediately (possi= bly after confirmation), =E2=80=98d=E2=80=99 to mark an entry for deletion, =E2=80=98= x=E2=80=99 to delete all entries marked thus, =E2=80=98m=E2=80=99 to mark for other operations, and = =E2=80=98u=E2=80=99 to unmark. With tabulated-list-mode, you have to program all that for every derived mode. It could be abstracted by defining a buffer-local variable that would point to a mode-specific deletion function (taking an ID); all the marking mechanics could be implemented generically. As for menu bar integration, I=E2=80=99m not interested. Emacs tends to accumulate buffers, bookmarks, and recent files in numbers that are unwieldy in a pull-down menu but work just well in a tabulated-list buffer.