From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.io!.POSTED.blaine.gmane.org!not-for-mail From: Matthias Meulien Newsgroups: gmane.emacs.bugs Subject: bug#51809: 29.0.50; [PATCH] Support for outline default state in Diff buffers Date: Sun, 26 Dec 2021 17:05:25 +0100 Message-ID: <87r19zs662.fsf@gmail.com> References: <87lf1sw6ji.fsf@gmail.com> <86h7cgdk4v.fsf@mail.linkov.net> <87ee7kvshn.fsf@gmail.com> <87a6i7x5iq.fsf@gmail.com> <86k0hbam7r.fsf@mail.linkov.net> <878rxrmy7q.fsf@gmail.com> <86y256uc0v.fsf@mail.linkov.net> <87r1ajknsr.fsf@gmail.com> <86ilvu6w6k.fsf@mail.linkov.net> <871r2hc51a.fsf@gmail.com> 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="10088"; mail-complaints-to="usenet@ciao.gmane.io" User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/29.0.50 (gnu/linux) Cc: 51809@debbugs.gnu.org To: Juri Linkov Original-X-From: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane-mx.org@gnu.org Sun Dec 26 17:06:24 2021 Return-path: Envelope-to: geb-bug-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 1n1W2S-0002Ue-KN for geb-bug-gnu-emacs@m.gmane-mx.org; Sun, 26 Dec 2021 17:06:24 +0100 Original-Received: from localhost ([::1]:52598 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1n1W2Q-0004H1-SS for geb-bug-gnu-emacs@m.gmane-mx.org; Sun, 26 Dec 2021 11:06:22 -0500 Original-Received: from eggs.gnu.org ([209.51.188.92]:60160) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1n1W28-0004GO-Bt for bug-gnu-emacs@gnu.org; Sun, 26 Dec 2021 11:06:04 -0500 Original-Received: from debbugs.gnu.org ([209.51.188.43]:58027) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1n1W26-000371-UR for bug-gnu-emacs@gnu.org; Sun, 26 Dec 2021 11:06:04 -0500 Original-Received: from Debian-debbugs by debbugs.gnu.org with local (Exim 4.84_2) (envelope-from ) id 1n1W26-0001mU-GE for bug-gnu-emacs@gnu.org; Sun, 26 Dec 2021 11:06:02 -0500 X-Loop: help-debbugs@gnu.org Resent-From: Matthias Meulien Original-Sender: "Debbugs-submit" Resent-CC: bug-gnu-emacs@gnu.org Resent-Date: Sun, 26 Dec 2021 16:06:02 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: followup 51809 X-GNU-PR-Package: emacs X-GNU-PR-Keywords: patch Original-Received: via spool by 51809-submit@debbugs.gnu.org id=B51809.16405347376802 (code B ref 51809); Sun, 26 Dec 2021 16:06:02 +0000 Original-Received: (at 51809) by debbugs.gnu.org; 26 Dec 2021 16:05:37 +0000 Original-Received: from localhost ([127.0.0.1]:41340 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1n1W1g-0001lb-9o for submit@debbugs.gnu.org; Sun, 26 Dec 2021 11:05:36 -0500 Original-Received: from mail-wm1-f53.google.com ([209.85.128.53]:38722) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1n1W1e-0001lN-6k for 51809@debbugs.gnu.org; Sun, 26 Dec 2021 11:05:34 -0500 Original-Received: by mail-wm1-f53.google.com with SMTP id p1-20020a1c7401000000b00345c2d068bdso7221889wmc.3 for <51809@debbugs.gnu.org>; Sun, 26 Dec 2021 08:05:34 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:to:cc:subject:references:date:in-reply-to:message-id :user-agent:mime-version; bh=IvjGxsGNHYA1R3qbbbxA5SE3zYTrkq2nJbOel6YB1zg=; b=cVow58PlrCbFe9q1VPa9+j5C6X5uPiQBCrmIxgeBiudz1AuI1OOCL+COqe4zVI2OVG 0A4m2b4OxMmI8cje7HyEMUr6h+rOubloVYxV55BrG0wDRM3knAc/2oQMnA4XeJ1pjMoM iraLunP9slzKFzwNzRAVf3zK2QZF/FfDLGRcR3XsnAfHJ1xsFuEF2D/s/VmEMzfWSubF U5Ia2qtte8KzXQPzdSW5dOQTeJTAAWrOStFFdFjUYebK5BKto1XO99KzVcHjQRu1sT9G XfDJyVX2LzllgCX7Yg2uZ1BkBoZjlXm1VtRpTzjWm+9YB+zt39QVKqtbyuhOGWvZrGYz CypQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:references:date:in-reply-to :message-id:user-agent:mime-version; bh=IvjGxsGNHYA1R3qbbbxA5SE3zYTrkq2nJbOel6YB1zg=; b=T2A45uaBXW/JMuHRMb5dwvAS2elAKUv9LiRBPKMZQxY/pxGco4NOM/WsJheSBRoPPo hGwWjYLkt11oPH4gkNtBxwTsPX2t7o6kCQYO8hm/i+cxRYCwKS1KB39jx5usgpcC50fs DiCG/guL7RvowCZ3eocSxX/6H0Fksv/uTpX1CicYVVxgJ6sIrxKELhY4/loGpbUHmIhj kWwEVo8b6zX/f7JgwHo5oblrOXp6QsEzAvRrO2vzV0CkaTp1f2ERCKJ1bXqieeH02Zjo pIbKFlCs0BF2NJBezpVS16YC82CQEfUFwwZZWVogTTPd8M27iSnuAzcalykfsKFasxE8 waHw== X-Gm-Message-State: AOAM530FCAGdjUmRo1ocxK55d4g/bNQhYycqC7a7nIKmK5ZW7WrZ7bdX DBeOuPDhYdcM7CZzfC91wrWxUSPeR2Y= X-Google-Smtp-Source: ABdhPJyrWmnyJ7+fA641VPNkGGqjsefOwc8x5K2iIgXuFrGogtADU4oRaMxZdgUxFkEretXwiKtHug== X-Received: by 2002:a1c:8002:: with SMTP id b2mr10628226wmd.2.1640534728436; Sun, 26 Dec 2021 08:05:28 -0800 (PST) Original-Received: from carbon.localdomain ([2a01:e0a:245:c850:98f5:429a:aa8e:95bb]) by smtp.gmail.com with ESMTPSA id m17sm12953105wrz.91.2021.12.26.08.05.27 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 26 Dec 2021 08:05:27 -0800 (PST) In-Reply-To: <871r2hc51a.fsf@gmail.com> (Matthias Meulien's message of "Mon, 13 Dec 2021 08:55:45 +0100") X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list X-BeenThere: bug-gnu-emacs@gnu.org List-Id: "Bug reports for GNU Emacs, the Swiss army knife of text editors" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane-mx.org@gnu.org Original-Sender: "bug-gnu-emacs" Xref: news.gmane.io gmane.emacs.bugs:223150 Archived-At: --=-=-= Content-Type: text/plain Matthias Meulien writes: > (...) Thanks for reading the patch! I'll send another one, hopefully > taking your remarks into account, when support of local variables is > fixed. Here is an updated patch implementing a default state for Outline mode and Outline minor mode: --=-=-= Content-Type: text/x-diff Content-Disposition: inline; filename=0001-Extend-Outline-mode-with-default-visibility-state.patch >From db0cf942950c7e997d2701742ce16c8385f452e0 Mon Sep 17 00:00:00 2001 From: Matthias Meulien Date: Wed, 8 Dec 2021 22:35:42 +0100 Subject: [PATCH] Extend Outline mode with default visibility state * lisp/outline.el (outline-mode, outline-minor-mode): Ensure default visibility state is applied (outline-hide-sublevels): Add optional argument for function to call on each heading (outline-default-state): Define the default visibility state (outline-apply-default-state): Apply default visibility state --- lisp/outline.el | 183 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 180 insertions(+), 3 deletions(-) diff --git a/lisp/outline.el b/lisp/outline.el index 2ede4e23ea..c52b9cd4e7 100644 --- a/lisp/outline.el +++ b/lisp/outline.el @@ -353,7 +353,9 @@ outline-mode '(outline-font-lock-keywords t nil nil backward-paragraph)) (setq-local imenu-generic-expression (list (list nil (concat "^\\(?:" outline-regexp "\\).*$") 0))) - (add-hook 'change-major-mode-hook #'outline-show-all nil t)) + (add-hook 'change-major-mode-hook #'outline-show-all nil t) + (add-hook 'hack-local-variables-hook + #'outline-apply-default-state)) (defvar outline-minor-mode-map) @@ -436,7 +438,9 @@ outline-minor-mode nil t) (setq-local line-move-ignore-invisible t) ;; Cause use of ellipses for invisible text. - (add-to-invisibility-spec '(outline . t))) + (add-to-invisibility-spec '(outline . t)) + (add-hook 'hack-local-variables-hook + #'outline-apply-default-state)) (when (or outline-minor-mode-cycle outline-minor-mode-highlight) (if font-lock-fontified (font-lock-remove-keywords nil outline-font-lock-keywords)) @@ -1093,7 +1097,7 @@ outline-hide-sublevels (outline-map-region (lambda () (if (<= (funcall outline-level) levels) - (outline-show-heading))) + (outline-show-heading))) beg end) ;; Finally unhide any trailing newline. (goto-char (point-max)) @@ -1307,6 +1311,179 @@ outline-headers-as-kill (insert "\n\n")))))) (kill-new (buffer-string))))))) +(defcustom outline-default-state nil + "If non-nil, some headings are initially outlined. + +If equal to `outline-show-all', all text of buffer is shown. + +If equal to `outline-show-only-headings', only headings are shown. + +If equal to a number, show only headings up to the corresponding +level. See `outline-default-state-subtree-visibility' to +customize visibility of the subtree at the choosen level. + +If equal to a lambda function or function name, this function is +expected to toggle headings visibility, and will be called after +the mode is enabled." + :version "29.1" + :type '(choice (const :tag "Disabled" nil) + (const :tag "Show all" outline-show-all) + (const :tag "Only headings" outline-show-only-headings) + (natnum :tag "Show headings up to level" :value 1) + (function :tag "Custom function"))) + +(defcustom outline-default-state-subtree-visibility nil + "Defines visibility of subtree starting at level defined by `outline-default-state'. + +When nil, the subtree is hidden unconditionally. + +When equal to a list, each element is expected to equal one of: + +- A cons cell with CAR `match-regexp' and CDR a regexp, the + subtree will be hidden when the outline heading match the + regexp. + +- `subtree-has-long-lines' to only show the heading branches when + long lines are detected in its subtree (see + `outline-long-line-threshold' for the definition of long + lines). + +- `subtree-is-long' to only show the heading branches when its + subtree contains more than `outline-line-count-threshold' + lines. + +- A lambda function or function name which will be evaluated with + point at the beginningg of the heading and the match data set + appropriately, the function being expected to toggle the + heading visibility." + :version "29.1" + :type '(choice (const :tag "Hide subtree" nil) + (set :tag "Show subtree unless" + (cons :tag "Heading match regexp" + (const match-regexp) string) + (const :tag "Body has long lines" + subtree-has-long-lines) + (const :tag "Body is long" + subtree-is-long) + (cons :tag "Custom function" + (const custom-function) function)))) + +(defcustom outline-long-line-threshold 1000 + "Minimal number of characters in a line for a heading to be outlined." + :version "29.1" + :type '(natnum :tag "Number of lines")) + +(defcustom outline-line-count-threshold 50 + "Minimal number of lines for a heading to be outlined." + :version "29.1" + :type '(natnum :tag "Number of lines")) + +(defun outline-apply-default-state () + "Apply the outline state defined by `outline-default-state'." + (interactive) + (cond + ((integerp outline-default-state) + (outline--show-headings-up-to-level outline-default-state)) + ((when (functionp outline-default-state) + (funcall outline-default-state))))) + +(defun outline-show-only-headings () + "Show only headings." + (interactive) + (outline-show-all) + (outline-hide-region-body (point-min) (point-max))) + +(eval-when-compile (require 'so-long)) +(autoload 'so-long-detected-long-line-p "so-long") +(defvar so-long-skip-leading-comments) +(defvar so-long-threshold) +(defvar so-long-max-lines) + +(defun outline--show-headings-up-to-level (level) + "Show only headings up to a LEVEL level and call FUN on the leaves. + +Like `outline-hide-sublevels' but but call +`outline-default-state-subtree-visibility' for each heading at +level equal to LEVEL." + (if (not outline-default-state-subtree-visibility) + (outline-hide-sublevels level) + (if (< level 1) + (error "Must keep at least one level of headers")) + (save-excursion + (let* (outline-view-change-hook + (beg (progn + (goto-char (point-min)) + ;; Skip the prelude, if any. + (unless (outline-on-heading-p t) (outline-next-heading)) + (point))) + (end (progn + (goto-char (point-max)) + ;; Keep empty last line, if available. + (if (bolp) (1- (point)) (point)))) + (heading-regexp + (cdr-safe + (assoc 'match-regexp + outline-default-state-subtree-visibility))) + (check-line-count + (memq 'subtree-is-long + outline-default-state-subtree-visibility)) + (check-long-lines + (memq 'subtree-has-long-lines + outline-default-state-subtree-visibility)) + (custom-function + (cdr-safe + (assoc 'custom-function + outline-default-state-subtree-visibility)))) + (if (< end beg) + (setq beg (prog1 end (setq end beg)))) + ;; First hide everything. + (outline-hide-sublevels level) + ;; Then unhide the top level headers. + (outline-map-region + (lambda () + (let ((current-level (outline-level))) + (when (< current-level level) + (outline-show-heading) + (outline-show-entry)) + (when (= current-level level) + (cond + ((and heading-regexp + (let ((beg (point)) + (end (progn (outline-end-of-heading) (point)))) + (string-match-p heading-regexp (buffer-substring beg end)))) + ;; hide entry when heading match regexp + (outline-hide-entry)) + ((and check-line-count + (save-excursion + (let* ((beg (point)) + (end (progn (outline-end-of-subtree) (point))) + (line-count (count-lines beg end))) + (< outline-line-count-threshold line-count)))) + ;; show only branches when line count of subtree > + ;; threshold + (outline-show-branches)) + ((and check-long-lines + (save-excursion + (let ((beg (point)) + (end (progn (outline-end-of-subtree) (point)))) + (save-restriction + (narrow-to-region beg end) + (let ((so-long-skip-leading-comments nil) + (so-long-threshold outline-long-line-threshold) + (so-long-max-lines nil)) + (so-long-detected-long-line-p)))))) + ;; show only branches when long lines are detected + ;; in subtree + (outline-show-branches)) + (custom-function + ;; call custom function if defined + (funcall custom-function)) + (t + ;; if no previous clause succeeds, show subtree + (outline-show-subtree)))))) + beg end))) + (run-hooks 'outline-view-change-hook))) + (defun outline--cycle-state () "Return the cycle state of current heading. Return either 'hide-all, 'headings-only, or 'show-all." -- 2.30.2 --=-=-= Content-Type: text/plain Here is a file used to test this feature: --=-=-= Content-Type: text/plain Content-Disposition: inline; filename=test.outline # -*- mode: outline; -*- Help to test implementation of outline default state. * Heading 1 Preambule ** Heading with long lines 1.1 With looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong line ** Heading 1.2 Some text ** Heading 1.3 A first paragraph followed by a second paragraph but with less interesting text. To be discussed. ** Heading with not so long line 1.4 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa * Heading 2 Preamble to a heading with many lines. ** Heading with many lines 2.1 Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines Many lines ** Heading 2.2 Many lines * Heading 3 Preamble ** Heading matching regex 3.1 TOHIDE Hidden body *** Heading 3.1.1 Body of hidden parent **** Heading 3.1.1.1 *** Heading 3.1.2 Still in a hidden parent * Heading 4 Last body and nothing else but those three lines # Local Variables: # outline-default-state: 2 # outline-default-state-subtree-visibility: ((match-regexp . "TOHIDE") subtree-has-long-lines subtree-is-long) # outline-long-line-threshold: 200 # outline-line-count-threshold: 100 # End: --=-=-= Content-Type: text/plain There's a bug when used with diff-mode (where `outline-level' returns unexpected values), the starting point of that thread! I'll try to study this in the forthcoming days. --=-=-=--