From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mp2.migadu.com ([2001:41d0:403:4876::]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) by ms13.migadu.com with LMTPS id qDeyIu6XbWZD0AAAe85BDQ:P1 (envelope-from ) for ; Sat, 15 Jun 2024 13:32:30 +0000 Received: from aspmx1.migadu.com ([2001:41d0:403:4876::]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) by mp2.migadu.com with LMTPS id qDeyIu6XbWZD0AAAe85BDQ (envelope-from ) for ; Sat, 15 Jun 2024 15:32:30 +0200 X-Envelope-To: larch@yhetil.org Authentication-Results: aspmx1.migadu.com; dkim=fail ("body hash did not verify") header.d=outlook.com header.s=selector1 header.b=LAaeOVNE; spf=pass (aspmx1.migadu.com: domain of "emacs-orgmode-bounces+larch=yhetil.org@gnu.org" designates 209.51.188.17 as permitted sender) smtp.mailfrom="emacs-orgmode-bounces+larch=yhetil.org@gnu.org"; dmarc=fail reason="SPF not aligned (relaxed)" header.from=outlook.com (policy=none); arc=reject ("signature check failed: fail, {[1] = sig:microsoft.com:reject}") ARC-Message-Signature: i=2; a=rsa-sha256; c=relaxed/relaxed; d=yhetil.org; s=key1; t=1718458350; h=from:from:sender:sender:reply-to:subject:subject:date:date: message-id:message-id:to:to:cc:cc:mime-version:mime-version: content-type:content-type: content-transfer-encoding:content-transfer-encoding:list-id:list-help: list-unsubscribe:list-subscribe:list-post:dkim-signature; bh=Kxr8F+G9a7WpNJtRa1Y/62iuGTmVmx1ci6cANyg8BgE=; b=Lf0BBINstasGju/8SWAo6SUfGA/LXut70/CoFvJr+Ib/HFLWJXHLJhSuTKAsCdXZT5nJKJ d8WXk6l/02uCVtCl05acuA+YQHToT5P+PEHVf8f5lZB/ThpuES1G5V7ELD4cefliMfcmBL PmfgfsamVO0xsfLJFnw7Kguh8iJw4T7OquPY5l8nvvGLDoEyYKKpsyhdUf5cM7mv4YLdrM Hkoq7FMP7S2bgDy2KJDJmVscE0PxkL7J5x41+Mf7Rt5IMaI8rjz9/CXHKnShNRCxJ8dmMO VTPra9Mv590f/rVpFF4CmWZHJMK4raS4Qc7NqeyYp8proP6x9QJNVHHjTcVT2A== ARC-Authentication-Results: i=2; aspmx1.migadu.com; dkim=fail ("body hash did not verify") header.d=outlook.com header.s=selector1 header.b=LAaeOVNE; spf=pass (aspmx1.migadu.com: domain of "emacs-orgmode-bounces+larch=yhetil.org@gnu.org" designates 209.51.188.17 as permitted sender) smtp.mailfrom="emacs-orgmode-bounces+larch=yhetil.org@gnu.org"; dmarc=fail reason="SPF not aligned (relaxed)" header.from=outlook.com (policy=none); arc=reject ("signature check failed: fail, {[1] = sig:microsoft.com:reject}") ARC-Seal: i=2; s=key1; d=yhetil.org; t=1718458350; a=rsa-sha256; cv=fail; b=CqbxEjPZUWjIa0wIHDLVH8Vtc0U3g6QpIR+z4Dxb9fi8wlY4R0oic0zoQ+577N9jXmmcVc fm3ZmOtaw8Pw+sSV2ayMpvXmopK0n4TXpicsJJr8G5XaBlCjWrd9RwblhmoMAf0zh7DGw5 MYyidmgpzSEnKLZsnXAo7t5/SHWWdiXz/9w7ZDerTFFgY9gmNUS2LZts+grjg/3Rt+QFwA /dDFNuDxvukYe0vj/RDDwMmQ8ZKcN2HlyZyfs7dbdl8w+TDrsqdOCeAd2rf+53hqKfyNEU uLIg891lmHfmoA29zQ/WiSy4rRch4hErBiyPimNPBvBxmJNPmAX/EesC5vU4lw== Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by aspmx1.migadu.com (Postfix) with ESMTPS id 5EF60853D for ; Sat, 15 Jun 2024 15:32:30 +0200 (CEST) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1sISmO-000136-Q2; Sat, 15 Jun 2024 08:45:12 -0400 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 1sISmM-000129-DN for emacs-orgmode@gnu.org; Sat, 15 Jun 2024 08:45:10 -0400 Received: from mail-sn1nam02olkn2084.outbound.protection.outlook.com ([40.92.44.84] helo=NAM02-SN1-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 1sISmK-0003iy-GW for emacs-orgmode@gnu.org; Sat, 15 Jun 2024 08:45:10 -0400 ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=hV4ijqafDlWNu84gFRarTzZgkZg4mbIP8w8+VBL7XmOZqOhWvlJTmk/uUqif3qmYM5TmqqCe9JfOrjSczr2CJGFBUUNG5+kOM5lTmRaSZowa0nWEeuOKFqrmmiSbbYnoTzp6XhWQZztJgjlSscB247JxtqZgvaoAO2kPn0XwT73vXys8I+NBvAI5AJHiTSrtQ9V6k3N5Tax1HySqmEOy0e8BgahUbjrlTAD3WGSghvGfWfkMo/cXAjmT91bwAtyhf3S6G8DxQwoWoLOuYPzN5KExZn80oA1wIMJ832DUKb2iyY03ODK9a27YzRQ7kDPcttmGdXPenEQXBJ3a/zOzOA== 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=MAuenkjt91hhQMNUvqaFSo4DTx6Y7RSKjQAUSnsh22Y=; b=RP3Ul373/n4Ki8dD7ran0wK4cT1n9OklkttRSzGLfBjTT9OshgV6h4O6cfvIm7zkcXYlIzDCcD2qzzHJra7Rt0xQu+7W90D7gM7u/+OcT9E71ntV4okiFRcLYaQcNxS/ryXIiAqYdidzjutaL9K1IaO1yVhOWZrnSJ1cR1INf4q2bs/lbb4jAz1a2zZYcwIsWiOF8lFcrc6DLSJmfvyBqrXPohqBJlTFJ1CZdaCgG76tXua9RXmZFYxMZlE8LAKVEotrFrUA1bsxuPJtLfewCdZ8MSFTom7i3tsT4899miTaMXVbyz4mPDFhE2hQ0c+WX8xeBF6mrBG/jvgkLEmKFQ== 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=outlook.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=MAuenkjt91hhQMNUvqaFSo4DTx6Y7RSKjQAUSnsh22Y=; b=LAaeOVNEO9GmTTyv/0RMFsvOnk8lXu/jbYySYjDs1un87XAWAgIfzH/+dw1lXfl+ZoRkHoAh3ZZDuWk85fC/Wja21b+CMWoe+8vTM2gQE/Q4nH2NhaQoqdimPldTexwLrG294A3NDy5zizRFqcZogXqoxVMQQ86CZZlS9W7h26LRyHrLK4aO6su3JErnJ/OvWc6one/cxWJXprDdIaX7NYiqO3eoJRvyQeu4rmy+XlGwMgILxUWht2rCJQ8EKPHlYBm4U3P3vJgSBDezR6hJv4cPp+sIRCbpS0KuqicGi8HpxtUQcwd/PUdtzlD6fXvUgVD2BE30IRgNpcs1osrN4A== Received: from CH3PR84MB3424.NAMPRD84.PROD.OUTLOOK.COM (2603:10b6:610:1c4::17) by PH0PR84MB2028.NAMPRD84.PROD.OUTLOOK.COM (2603:10b6:510:163::21) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.7677.28; Sat, 15 Jun 2024 12:40:01 +0000 Received: from CH3PR84MB3424.NAMPRD84.PROD.OUTLOOK.COM ([fe80::5c77:7a58:48ed:9aef]) by CH3PR84MB3424.NAMPRD84.PROD.OUTLOOK.COM ([fe80::5c77:7a58:48ed:9aef%3]) with mapi id 15.20.7677.027; Sat, 15 Jun 2024 12:40:00 +0000 From: Morgan Smith To: emacs-orgmode@gnu.org Cc: Morgan Smith Subject: [PATCH] lisp/org.el: Add ability to sort tags by hierarchy Date: Sat, 15 Jun 2024 08:35:46 -0400 Message-ID: X-Mailer: git-send-email 2.45.1 Content-Transfer-Encoding: 8bit Content-Type: text/plain X-TMN: [f19GdUJEItUlWGeJ6HrVEEGNBY3+omTv] X-ClientProxiedBy: YT4PR01CA0254.CANPRD01.PROD.OUTLOOK.COM (2603:10b6:b01:10f::9) To CH3PR84MB3424.NAMPRD84.PROD.OUTLOOK.COM (2603:10b6:610:1c4::17) X-Microsoft-Original-Message-ID: <20240615123545.31426-2-Morgan.J.Smith@outlook.com> MIME-Version: 1.0 X-MS-Exchange-MessageSentRepresentingType: 1 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: CH3PR84MB3424:EE_|PH0PR84MB2028:EE_ X-MS-Office365-Filtering-Correlation-Id: 19f6cfb1-89b0-476b-c911-08dc8d38449e X-Microsoft-Antispam: BCL:0; ARA:14566002|461199025|440099025|3412199022|1710799023; X-Microsoft-Antispam-Message-Info: vSN/TuhCGzaIL0Lt1g+/TpKoCj2mUnRn6D/kP3L4kuHMMTF246cdg7655haedkirOzM6UaK2JIX0tJm517cM94uoY+n2SabonIol7xWKVMEgzmopijQfEXskBEkJrfdW6Z74tyQIS6MR9NYlRLaxldI4RhNe064RjuayaBKNA5pEr8sXH8P0UCvzrOv6Msgr8tXFiGJc3XWcHWgwr7DLgZz0UUBZj2WR1gm7zHk8MHgW9+rhqkw72oOCMRr36EfbNPN46CaURKp9LQoczCSW5mFFz5nZMjsmxAEgxXdGEyxyL+I6aQZ4mwcrElqnxJg3ec5mGmJFE9so3MwoM5Ww+ns5GPnVcbclsiRlMicsj0XjCKwRSayHzDKzmxpBYEdbRtvrSq+/lhypGudYXWybL1bZqb1WCU3K56AaehyEfJuLkRU6i9Z1FnZM+jqiiCNY11aKdArzPV1A6QzH+m26cIrUUM608bIWTcKuerY8NoDzVI+6DtKo6YctblBO1IcWGwRHLkxpvODWMZFInUwOQLx7d83ifNDsKLNrEQFOEnpxXof21CC3ICTzUBWPhOxyFKrePwI+gtSMWCWvuCQFmVTu645D7JSdcRyM4lpiNpc= X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: =?us-ascii?Q?JMzKsIT0CbArwnFmJAt1ubwntZt3SW2sxSMKYGmH+xbYZZ9el/8u2AuhMOFv?= =?us-ascii?Q?Z6fXoMLhwD3xAoBckv5UJHx9CEoWR4zUgaXLNlr/7+5lIMSzEDpoqWVN1qLo?= =?us-ascii?Q?7VJoZxjeNqTEECWRiwbD0AaYtnuKKgi+HIGbgmUWjq1y1Ma/nqFyJKmybq/Q?= =?us-ascii?Q?316Rstil+xbx+QXKvr2oxm4hAq2wgqlbRzPNHkjLWhfHshKaJEWgNA5EwRjw?= =?us-ascii?Q?yRa55vNr0nz8EQQm0xoIZqkp6ZJTxnSsQJSc5mQlIKxWIbuH58AMb96WA3nv?= =?us-ascii?Q?TaH+6V6eYaRBJtPNk5ZJZa+FIgGZdaNk9CtVDA9Qx5OsMf04TALpju6cMToP?= =?us-ascii?Q?1GYSvmy6gBx/DSA9pY1vUiUZaefnf8iLs2yGNJT4qj0jWar0hCtaTPjCs+wR?= =?us-ascii?Q?x2hTVy0KU08+/enfchy7EDM2+ltoXywGmtmC9awVkPxIsoqf2zypRUSmxYiF?= =?us-ascii?Q?oj1hwUggadPWye2PWVNYJlX1MKbBQP6W10QnN4hgMDO4UjBDsohQyoNAwpzg?= =?us-ascii?Q?ZOHlp10lc85pFQUeo/sQ9/FxIBQheBSKotQvLIpMjlZfPj5VmNFS24Icb/CW?= =?us-ascii?Q?ZUg+BMWVdS4nHGeE4kUWuNMpMslDGoMPwcDWEG4VaZLfUGUieVPPPsC3ZNjU?= =?us-ascii?Q?7l6qyeT3nxXCLMu4onD03Z3zzGGjOKNKq0rnN+YS1T//bL+53IcaNu10N2Ty?= =?us-ascii?Q?lx1AsxogsUwAsAyzPvMsSpYCWGLd0QmZvsx6oeyauMXP43f+9Rv81XFkbiC5?= =?us-ascii?Q?LSFuoN7J8zdJnc7sk2J27ZCPb7C9UMEklNwZwaByN66BNbSJSUZ3I6RcHVuz?= =?us-ascii?Q?luKvrT1aFlRXF1HLmaB3TPJOhtCap9Q1owzmUphBHaiFncb91qP3zlLQWG25?= =?us-ascii?Q?eUGx0g42NVvuzo098jree+PS9uOPQYhi+eqQGpC9v1L8Ir4owqkqmSWQ+JzP?= =?us-ascii?Q?BgM5lgp6zm04YtdwOPtVsUjURNUKzxZykANL6eruHLuKgvv/CreTiSMKpwcg?= =?us-ascii?Q?FTfeKSv5g6ZK3TKu/dH9mxHCoPzQTM4Z1PE2Fl04ZaojkaqcovDTZUWB6edb?= =?us-ascii?Q?aM2lKSjiCA2E94vUnv3SHTkv3y3AisaDlxLwzokTMpZRuAcBq0g2ZiEAc5Z5?= =?us-ascii?Q?ECiT/x7Vx1f62U8yEGrg4CjXGkQ5kpuaxjs+SyLxYnNIOFJRHEnVSF4oiHwY?= =?us-ascii?Q?Mjs9B15PhmM4NhyNWJ1l/DHTCRayDA8p2eNSWDtzL62/POuPDK9IBT5yIyIH?= =?us-ascii?Q?+Bq3xjqZC7Qeoj13Ge5k?= X-OriginatorOrg: outlook.com X-MS-Exchange-CrossTenant-Network-Message-Id: 19f6cfb1-89b0-476b-c911-08dc8d38449e X-MS-Exchange-CrossTenant-AuthSource: CH3PR84MB3424.NAMPRD84.PROD.OUTLOOK.COM X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 15 Jun 2024 12:40:00.2028 (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: PH0PR84MB2028 Received-SPF: pass client-ip=40.92.44.84; envelope-from=Morgan.J.Smith@outlook.com; helo=NAM02-SN1-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-BeenThere: emacs-orgmode@gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: "General discussions about Org-mode." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: emacs-orgmode-bounces+larch=yhetil.org@gnu.org Sender: emacs-orgmode-bounces+larch=yhetil.org@gnu.org X-Migadu-Country: US X-Migadu-Flow: FLOW_IN X-Spam-Score: 4.15 X-Migadu-Queue-Id: 5EF60853D X-Migadu-Scanner: mx10.migadu.com X-Migadu-Spam-Score: 4.15 X-TUID: w6uV+jys0hco * lisp/org.el (org-tags-sort-hierarchy): New function. (org-tags-sort-function): Add new function to type. * testing/lisp/test-org.el (test-org/tags-sort-hierarchy): New test --- This is one of those things that I thought would be easy but then ended up hard. I wrote this so that items in my agenda would sort nicely. Items tagged in the same hierarchy would end up next to each other. lisp/org.el | 38 +++++++++++++++++++++++++++++++++++++- testing/lisp/test-org.el | 19 +++++++++++++++++++ 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/lisp/org.el b/lisp/org.el index 750b060f3..b828f4127 100644 --- a/lisp/org.el +++ b/lisp/org.el @@ -2955,7 +2955,8 @@ is better to limit inheritance to certain tags using the variables (const :tag "No sorting" nil) (const :tag "Alphabetical" org-string<) (const :tag "Reverse alphabetical" org-string>) - (function :tag "Custom function" nil))) + (const :tag "Hierarchy" org-tags-sort-hierarchy) + (function :tag "Custom function" nil))) (defvar org-tags-history nil "History of minibuffer reads for tags.") @@ -4262,6 +4263,41 @@ See `org-tag-alist' for their structure." ;; Preserve order of ALIST1. (append (nreverse to-add) alist2))))) +(defun org-tags-sort-hierarchy (tag1 tag2) + "Sort tags TAG1 and TAG2 by the tag hierarchy. +Sorting is done alphabetically. This function is intended to be a value +of `org-tags-sort-function'." + (let ((sort-func #'org-string<) + (group-alist (or org-tag-groups-alist-for-agenda + org-tag-groups-alist))) + (if (not (and org-group-tags + group-alist)) + (funcall sort-func tag1 tag2) + (let* ((tag-path-function + ;; Returns a list of tags describing the tag path + ;; ex: '("top level tag" "second level" "tag") + (lambda (tag) + (let ((result (list tag))) + (while (setq tag + (map-some + (lambda (key tags) + (when (and (member tag tags) + ;; infinite loop (only catches the trivial case) + (not (string-equal tag key))) + key)) + group-alist)) + (push tag result)) + result))) + (tag1-path (funcall tag-path-function tag1)) + (tag2-path (funcall tag-path-function tag2))) + ;; value< was added in Emacs 30 + ;; (value< tag1-path tag2-path) + (catch :result + (dotimes (n (min (length tag1-path) (length tag2-path))) + (unless (string-equal (nth n tag1-path) (nth n tag2-path)) + (throw :result (funcall sort-func (nth n tag1-path) (nth n tag2-path))))) + (< (length tag1-path) (length tag2-path))))))) + (defun org-priority-to-value (s) "Convert priority string S to its numeric value." (or (save-match-data diff --git a/testing/lisp/test-org.el b/testing/lisp/test-org.el index f21e52bfd..59b16a62a 100644 --- a/testing/lisp/test-org.el +++ b/testing/lisp/test-org.el @@ -8508,6 +8508,25 @@ Paragraph" (org-mode-restart) (let ((org-tag-alist-for-agenda nil)) (org-tags-expand "{A+}")))))) +(ert-deftest test-org/tags-sort-hierarchy () + "Test `org-tags-sort-hierarchy' specifications." + (let ((org-tag-groups-alist-for-agenda + '(("A" "B" "D" "z" "zz") + ("B" "y") + ("C" "x") + ("D" "w") + ("E" "C" "v"))) + (test-list '("v" "w" "x" "y" "zz" "z" "E" "D" "C" "B" "A"))) + (should (equal + '("A" "B" "y" "D" "w" "z" "zz" "E" "C" "x" "v") + (sort test-list #'org-tags-sort-hierarchy)))) + ;; infinite loop (tag "A" should not be in the "A" group) + (let ((org-tag-groups-alist-for-agenda + '(("A" "A" "B"))) + (test-list '("B" "A"))) + (should (equal + '("A" "B") + (sort test-list #'org-tags-sort-hierarchy))))) ;;; TODO keywords -- 2.45.1