From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mp1.migadu.com ([2001:41d0:303:e16b::]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) by ms13.migadu.com with LMTPS id aHlqE4Eta2c1GQAA62LTzQ:P1 (envelope-from ) for ; Tue, 24 Dec 2024 21:54:09 +0000 Received: from aspmx1.migadu.com ([2001:41d0:303:e16b::]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) by mp1.migadu.com with LMTPS id aHlqE4Eta2c1GQAA62LTzQ (envelope-from ) for ; Tue, 24 Dec 2024 22:54:09 +0100 X-Envelope-To: larch@yhetil.org Authentication-Results: aspmx1.migadu.com; dkim=pass header.d=outlook.com header.s=selector1 header.b=RFt0v7hi; 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=pass (policy=none) header.from=outlook.com; arc=pass ("microsoft.com:s=arcselector10001:i=1") ARC-Message-Signature: i=2; a=rsa-sha256; c=relaxed/relaxed; d=yhetil.org; s=key1; t=1735077248; 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:in-reply-to:in-reply-to: references:references:list-id:list-help:list-unsubscribe: list-subscribe:list-post:dkim-signature; bh=TthmTenCp8nqgrqHwpHJ4jbiI3F36eE6EcVxUqZ2Ras=; b=Ey+Eb3kO7C9lq5L78MyzcI40h7g2g+fZlY96+uw+xDKIQXzaDFIl5h4rAXT29vsceLlwYm hhQ9c9IFkwUfW5k8q5dtuR5Dy2rz3nkYQyai0IvtkKTxPGiRjg1F63Y1lnFnFvbLKAaLJj SQ8gf/kmPNfLVOkdi9E3k24b9lEiL2B9m5wsLGUCJ7dXxRpHCDdjraBB+ftfTjkaFU+gjG 6i7k+RnmjzOjueYDPw4ryKdD/ZoLnpQIzDirvwuxxkTpePNCmuDQhHmU6tf4GUltmtnlWB +5VpifCgsY+ZQZ9hnOUIHD+tpOex1VCGQg/QrkGH3TmCKgRgMnRmT3PTt1IEeQ== ARC-Authentication-Results: i=2; aspmx1.migadu.com; dkim=pass header.d=outlook.com header.s=selector1 header.b=RFt0v7hi; 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=pass (policy=none) header.from=outlook.com; arc=pass ("microsoft.com:s=arcselector10001:i=1") ARC-Seal: i=2; s=key1; d=yhetil.org; t=1735077248; a=rsa-sha256; cv=pass; b=XfsT8bki6y7/RCTXemjIh4Yq+oQ2aPra/vJlvMiCK2kTtG1mWyAiVuui7KbmT+Xaj4QXCr F58l2SPpxS38gJLqNj73b1uORQQ8bvHK76xIijvxoJ2sM4cqVyG9OwawdpJitk2qOlYxsM qRGvfOtXMCV9FbkPZXzvlMckF9zQZzrqAdtiqtRDGiLuAU6MiAOcMQ0tKN/pfDT3C/H+/i JAufO4loct8yt9PZiIM4Ip5+aZkoJ9dB+APe/dZej48S2kyvomyzu9xTTSbowoy1GBZSDR SU/Wonr42Tjg0v6KLJ3SuRlM09cxcToKdpGaxD7G8eOD5dvc9R9UITCDLXKQ4Q== 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 999BA7BA6 for ; Tue, 24 Dec 2024 22:54:08 +0100 (CET) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1tQCq6-0007uc-2t; Tue, 24 Dec 2024 16:53:18 -0500 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 1tQCq4-0007uB-AB for emacs-orgmode@gnu.org; Tue, 24 Dec 2024 16:53:16 -0500 Received: from mail-mw2nam10olkn2081e.outbound.protection.outlook.com ([2a01:111:f403:2c12::81e] helo=NAM10-MW2-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 1tQCq1-0006K9-MI for emacs-orgmode@gnu.org; Tue, 24 Dec 2024 16:53:15 -0500 ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=VXzY5Y/wiVhXjkQrf6lPNgFdWgknZMukifT2mqFyS3rJb4MLj8gKuqyenixEn1ao5kDg6yukQIfNYqKP4F2srvWVvhGhVT026eIRl/BeRRXd0QxmFLrrqokikNVRHI5BxO+jTR3Ob7jswoJzfxLAzqw45e/osl+nYSZbBgJLiitd9VO+2c9JekMObjeHSiNGVlAn6C9DIVohf9xvjcb1s4cW8oBkJsc2fWFLK7+Fsrbx1JfvIlHvgAcbfD7oaUJ6NS8uXd7YkRqfDLWMMbNlBtYuG4fDuOjzAf9WQLUPe/5WdGHOG02VDh1gucaHyp7iHpxUhGrWKAQcxFTk8aSqOQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector10001; 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=TthmTenCp8nqgrqHwpHJ4jbiI3F36eE6EcVxUqZ2Ras=; b=U6KF5G/lVKKZ48B9Aw4heCO/BpO4VFnLzlizp5DpK82hmY7XPEUzFA8T4/e1ooadTccg21LN7nFxis6e72EAqTsuCOqYwWgqLqBy7R8nEr2pUFjGR+Llk5764A7t9iIf45tFTpjDLugWH7sGaYzwyw9DxFi3ZfyCTx7rdFwPrsEpHe8gHHxCdJp1e/VUtCPhK1kEM4ZLORF2hZkl5sjjI7BBKiyed6lzMT3VhPFgtQ1KBK0ZIiApG9t7pir5G1c8KGRj4u/bx+fvt+j9/gPkY0K2rz4MtwQcx4xDB0U3O0XfNinG0+fKbm5RCYNyEgPMBqvdpTetbFwH6OpZ9keceA== 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=TthmTenCp8nqgrqHwpHJ4jbiI3F36eE6EcVxUqZ2Ras=; b=RFt0v7hiCWxjWoY74+6CMWqj5dOdoJU4CXwwrIa/R788TtwEP75ipshNKCSksieAt4jerJe/QD80P/0tG4VATJkpt4KSrM8c+MMa3UCPMeNZ0EM3qfs7SReZPwRFHCC69cy9PniPnQTdqxoZ8bn06zR9V/rtP6qL316DugWFXCZ2C8HUe4TxOVtFTmUWcM4pkF0OiltYsVESOu9DXcbGyu/YYkEmFGQHiAwwdmKDgd+pCL8H1UaZml4SKQUcc0nHDjkYlTXde0PF/CIwgJA0aiBh+3nzsBbF8FCziaoTu2MLYN8SLI+9ZN2d7Sn7cFfDZ9ZVkHktGDWqDPYvMCLKkg== Received: from CH3PR84MB3424.NAMPRD84.PROD.OUTLOOK.COM (2603:10b6:610:1c4::17) by SJ0PR84MB1409.NAMPRD84.PROD.OUTLOOK.COM (2603:10b6:a03:419::8) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.8293.14; Tue, 24 Dec 2024 21:48:06 +0000 Received: from CH3PR84MB3424.NAMPRD84.PROD.OUTLOOK.COM ([fe80::5c77:7a58:48ed:9aef]) by CH3PR84MB3424.NAMPRD84.PROD.OUTLOOK.COM ([fe80::5c77:7a58:48ed:9aef%4]) with mapi id 15.20.8272.013; Tue, 24 Dec 2024 21:48:06 +0000 From: Morgan Smith To: Ihor Radchenko Cc: emacs-orgmode@gnu.org Subject: Re: [PATCH] lisp/org.el: Add ability to sort tags by hierarchy In-Reply-To: <874j9ugu57.fsf@localhost> (Ihor Radchenko's message of "Sat, 15 Jun 2024 14:25:40 +0000") References: <874j9ugu57.fsf@localhost> X-Hashcash: 1:20:241224:emacs-orgmode@gnu.org::M+hnPrU+clCWdwDF:06w0 X-Hashcash: 1:20:241224:yantar92@posteo.net::retyL0pP0Oa3gLEI:6YBA Date: Tue, 24 Dec 2024 16:48:12 -0500 Message-ID: User-Agent: Gnus/5.13 (Gnus v5.13) Content-Type: multipart/mixed; boundary="=-=-=" X-ClientProxiedBy: YQBPR0101CA0144.CANPRD01.PROD.OUTLOOK.COM (2603:10b6:c01:e::17) To CH3PR84MB3424.NAMPRD84.PROD.OUTLOOK.COM (2603:10b6:610:1c4::17) X-Microsoft-Original-Message-ID: <87jzboeorn.fsf@outlook.com> MIME-Version: 1.0 X-MS-Exchange-MessageSentRepresentingType: 1 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: CH3PR84MB3424:EE_|SJ0PR84MB1409:EE_ X-MS-Office365-Filtering-Correlation-Id: 215753d6-75a1-4d7b-cd68-08dd2464a675 X-Microsoft-Antispam: BCL:0; ARA:14566002|6092099012|15080799006|8060799006|7092599003|461199028|5072599009|19110799003|13095399003|3412199025|440099028; X-Microsoft-Antispam-Message-Info: =?us-ascii?Q?GqBvyyiYDk06qxWzRbU2S6En4Cev5S2jYEt2rZ2VYoCnRF1AYvVoK/ocNUKC?= =?us-ascii?Q?ztL+kRmMiWUuhyovkVCiHfSX91NiNwzS1jH5qEWicwfgHwRYBhtojgXzGTt7?= =?us-ascii?Q?Fwu68Sx8pxHPJw6eu3lCfRsVcea9bIQyIKLf9s5cEQairj5QRIzl8p/w9fwK?= =?us-ascii?Q?s1vKQy2s8lFaj37PE1RRe7l6ZCrIhGT5mEjyQt+gsO15KxloA8UTIJQUA9mN?= =?us-ascii?Q?f8dqnZb01ZspUFI4CJn+ssK+WqDLK9OWTPgNdbf6759kD8mjsMUStf0wvj+T?= =?us-ascii?Q?puTaMmMEFxZXs5t1oTJ7usETJUaW/2RY2l/MGahtS1D69UR+2GYb1QsxJdr+?= =?us-ascii?Q?vRjsoqmTkPHYc5LEmjF2OEgWVH/8DbuPXXpdDe02PqBwPWB45HhfCL+1IOZU?= =?us-ascii?Q?pj6Ka51LSGN32dfSngxgMm1P+H5jcKJUhS3UgPW0oLt3dPLLHAalHfvRf5Ki?= =?us-ascii?Q?bGxcUDp104jUkL00oRPGZ9aS1ED413aewsSzLO8ghKa0XfjDWITaNeeUzp+9?= =?us-ascii?Q?iOeqeLO/0IScNNrfSoj8MuJDA/gOAznQLHfFEX6OiMB9F+E//xOuFeio3Pb8?= =?us-ascii?Q?GvzlClY/kDB+74wxQsonul8FnA8yRfqOdqf62t/rLml5xDkP5XjavosPxjRE?= =?us-ascii?Q?HhSRHROzIazVcpfEPU5G9KfgHNLbuSOsgdnkcsOuxPyyt3Iy6rq/NQfOfP/H?= =?us-ascii?Q?l+Vaw71Zj+PFsDe4jlKRGtUFEJcEEQPZqWi7EwtnuIHwZUOg6GIyFmUKLNEP?= =?us-ascii?Q?VQG2SLn90XQSRtoQbS8bPXkUBJdHhDNiXpBn1Uzb8A9AlWyzc2uvmf3HJz+x?= =?us-ascii?Q?o/K2k7UMvb//K2VBWqfACKnt86T54+uQt9jBRWE5kaiwVoTaNgqM3jj7VW+7?= =?us-ascii?Q?Q9hknVcArGeZnvYLgMgJPonQ8H9qEeh9dd/eYXF+dsCq2HiSkVTP+w6+BEeF?= =?us-ascii?Q?I23rluCv72e5KiKQphezrDZZqysByttbsNnThdLhEovX1IDTWKA8d/xD6bc3?= =?us-ascii?Q?D9n5chTa6/BpQ/x2C4SBwoj3kvvtaEtWAbOkn8bqpvCL3X1/8TSCnVpJblm5?= =?us-ascii?Q?I2rdNdsmpgOtPaY8oUawuOm1j2QhW8Q+Qd4b7pelT2FQfAWbPCWFpvGth06K?= =?us-ascii?Q?/X9q6AbkvKWCvEyHxlg42AisX/xQa/aYPg=3D=3D?= X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: =?us-ascii?Q?iwie6czBZ3/J8UnDQlUvjPpzaI+WvgpOaHPHVcMlWW+qjBxCnEZQYXtBp8nE?= =?us-ascii?Q?DhnJoHQXmTdtM/ejSNrLV35iUY0TjoEfduOlNWJmnfhDCxhNCsD1bwqEeS0x?= =?us-ascii?Q?MOELwrmGK/QoC1DncXvVqVqTwYeSE0j/HbvGUH+qviYT7FFzD6PK+VYLQcNR?= =?us-ascii?Q?QvYL4q88CJEKzoJL+9iuNOFjIbQh+6GGBglL83NftniiFIv9fDoJ9qPn1bib?= =?us-ascii?Q?Y6e/ggWbQ7j0CLZXhStpgkp7K3KyTDJLDsrrOPeY6dZJiSyZbZfMIx5RhGwG?= =?us-ascii?Q?haGxQBye6P4n1q6pJTyl9AC1wC3xQzYHpsNsRFYEMJxofcQqOiUHXVHgFpoF?= =?us-ascii?Q?9ujDKhS+5EyQ8cX3XKOoZukvOTSBsuVQEWvkEPSu44dVCwQ6Fmx1ao3wxzoL?= =?us-ascii?Q?A9LB67SyC9OvOEy9bCjke3AnKDC5n9kfr0rlRQ2Ia/6ZbHy866P0K0ZOMbqd?= =?us-ascii?Q?+edlGt0kauVtskc+qbyClfZ5HC9kZcUPYGrLvZBqvvKgK9TtbXKoXiFGgKg5?= =?us-ascii?Q?YncLQNctE0Zcv6ev1V0joKFK7P6t+d2X4aigg2fmN2S0DlY5Jejf0r6DbGvy?= =?us-ascii?Q?0gqHoYpLSTO8IucI5VVIZyQkb8jMhdDNUHpX9cZB42NvKM/LpqKCEyIFsHCo?= =?us-ascii?Q?nWX/bdcLtz1/OMBNfOHSOoK0aod530XfY561Ln9PrpP0W8cBtJVK9ixeBLNI?= =?us-ascii?Q?5Hp/M4BDmWYOinJ+hO6sKiwTVDZIjz2O4im0FY8TAfjDDd/SJgivgLiV3YNJ?= =?us-ascii?Q?04hFVpCkT5hx89iHWZAc9oXPmpDu/vCVHjktPJEyZSKxpvjeWSXXFoDYrk0/?= =?us-ascii?Q?VL9xSvnsXrmy7Nf9fRvMh3Y1zP1l1TPAE83QIteNNw+JfmE2WJ4OSW3Bsfl4?= =?us-ascii?Q?TYRgvrqat2d3Mg8tqCSrYu5LBgJHHMtddyCyJTPMxgeFA30PbEhIge9mT5oA?= =?us-ascii?Q?vkJRiEoboYhDXRXHtkjR1tt0qu0ivriYJcksk/iuyuqSRAJW8E2dkgW/nyur?= =?us-ascii?Q?67h3mLoIkFa/9vMiacoH08jtOTDDtiQHn8cAjFC/qKFIwSilepk7XACQuLEC?= =?us-ascii?Q?0+2BP3DSbZyaYVUxyAQN4FpW/vSGEEL5UTesUKnt/rvlRp1hF/rmjBCjt+tl?= =?us-ascii?Q?3nfjNLmzXSjUMhYkYa1pFsZqTdpbVNWsYDvR4L7CsccnGh7XVprnGfWKV+eH?= =?us-ascii?Q?ON3WjdYKRXSyxV6HXPEikpe3u3jZKPgYZj76Lo4rEVCQ9ShON6qiIiX1Tnsj?= =?us-ascii?Q?LBwXQVJPe+PLhW5ljTWSTiBlRHG8hyyZx0YTJK2nfWrYtE8kSRuIgOF7QqJB?= =?us-ascii?Q?S3NYiQjIvJ4xResPFP9X+MH6?= X-OriginatorOrg: outlook.com X-MS-Exchange-CrossTenant-Network-Message-Id: 215753d6-75a1-4d7b-cd68-08dd2464a675 X-MS-Exchange-CrossTenant-AuthSource: CH3PR84MB3424.NAMPRD84.PROD.OUTLOOK.COM X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 24 Dec 2024 21:48:06.7798 (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: SJ0PR84MB1409 Received-SPF: pass client-ip=2a01:111:f403:2c12::81e; envelope-from=morgan.j.smith@outlook.com; helo=NAM10-MW2-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, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001 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-Migadu-Queue-Id: 999BA7BA6 X-Migadu-Scanner: mx13.migadu.com X-Migadu-Spam-Score: -5.40 X-Spam-Score: -5.40 X-TUID: NDtXcwyttA58 --=-=-= Content-Type: text/plain Hello! I have updated the patch with many improvements and tests. Ihor Radchenko writes: > Morgan Smith writes: > >> * 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. > >> + "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'." > > Thanks for the patch, but may you please elaborate what kind of sorting > order does your function imply? In particular, I am wondering about the > ordering of tags from different groups? Hopefully the test case I added shows how the sorting work. The tags end up sorted as this: ("group_a" "tag_a_1" "tag_a_2" "groupless" "lonely" "tag_b_1" "tag_b_2") Things end up with other things of their group. Things higher in the heirarchy end up earlier. When things are on the same level (like tag_a_1 and tag_a_2 or group_a and groupless) they are sorted using `org-sort-function'. > Also, what happens when there > are no tag groups defined? (These questions should be answered in the > docstring and the etc/ORG-NEWS entry you need to add, announcing the new > feature) Fallback to `org-sort-function'. > Also, sorting may not be alphabetical, depending on the value of > `org-sort-function'. I have corrected this in the documentation. --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=0001-lisp-org.el-Add-ability-to-sort-tags-by-hierarchy.patch >From 3f25374bbfd4134cea6ce0708633d500e8b41a89 Mon Sep 17 00:00:00 2001 From: Morgan Smith Date: Fri, 14 Jun 2024 17:38:41 -0400 Subject: [PATCH] lisp/org.el: Add ability to sort tags by hierarchy * lisp/org.el (org-tags-sort-hierarchy): New function. (org-tags-sort-function): Add new function to type. * testing/lisp/test-org-agenda.el (test-org-agenda/tags-sorting): Test sorting with a value of 'org-tags-sort-hierarchy. * etc/ORG-NEWS: Announce the new feature. --- etc/ORG-NEWS | 7 ++++++ lisp/org.el | 41 ++++++++++++++++++++++++++++++++- testing/lisp/test-org-agenda.el | 32 ++++++++++++++++++++----- 3 files changed, 73 insertions(+), 7 deletions(-) diff --git a/etc/ORG-NEWS b/etc/ORG-NEWS index 4c41f981c..62e8bb4ca 100644 --- a/etc/ORG-NEWS +++ b/etc/ORG-NEWS @@ -196,6 +196,13 @@ English. The default value is ~t~ as the CSL standard assumes that English titles are specified in sentence-case but the bibtex bibliography format requires them to be written in title-case. +*** New tags sorting function ~org-tags-sort-hierarchy~ + +By setting ~org-tags-sort-function~ to ~org-tags-sort-hierarchy~, tags +are sorted taking their hierarchy into account. See ~org-tag-alist~ +for how to set up a tag hierarchy. Secondary sorting is done using +~org-sort-function~. + ** New functions and changes in function arguments # This also includes changes in function behavior from Elisp perspective. diff --git a/lisp/org.el b/lisp/org.el index 748f258a2..6f5bf066d 100644 --- a/lisp/org.el +++ b/lisp/org.el @@ -231,6 +231,7 @@ Stars are put in group 1 and the trimmed body in group 2.") (defvar org-element--timestamp-regexp) (defvar org-indent-indentation-per-level) (defvar org-radio-target-regexp) +(defvar org-sort-function) (defvar org-target-link-regexp) (defvar org-target-regexp) (defvar org-id-overriding-file-name) @@ -2966,7 +2967,8 @@ default." (const :tag "Default sorting" nil) (const :tag "Alphabetical" org-string<) (const :tag "Reverse alphabetical" org-string>) - (function :tag "Custom function" nil))) + (const :tag "Sort by hierarchy" org-tags-sort-hierarchy) + (function :tag "Custom function" nil))) (defvar org-tags-history nil "History of minibuffer reads for tags.") @@ -4275,6 +4277,43 @@ 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. +See `org-tag-alist' for how to set up a tag hierarchy. Secondary +sorting is done using `org-sort-function'. This function is intended to +be a value of `org-tags-sort-function'." + (let ((group-alist (or org-tag-groups-alist-for-agenda + org-tag-groups-alist))) + (if (not (and org-group-tags + group-alist)) + (funcall org-sort-function 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) + ;; Prevent infinite loop + (not (member tag (cdr result)))) + 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 and does not allow us to use + ;; `org-sort-function'. + ;; (value< tag1-path tag2-path) + (catch :result + (dotimes (n (min (length tag1-path) (length tag2-path))) + ;; find the first difference and sort on that + (unless (string-equal (nth n tag1-path) (nth n tag2-path)) + (throw :result (funcall org-sort-function (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-agenda.el b/testing/lisp/test-org-agenda.el index 06d5abc43..d623389d4 100644 --- a/testing/lisp/test-org-agenda.el +++ b/testing/lisp/test-org-agenda.el @@ -663,18 +663,34 @@ Sunday 7 January 2024 (org-agenda-overriding-header "") (org-agenda-prefix-format "") (org-agenda-remove-tags t) - (org-agenda-sorting-strategy '(tag-up))))))) + (org-agenda-sorting-strategy '(tag-up)))))) + (org-tag-alist + '((:startgrouptag) + ("group_a") + (:grouptags) + ("tag_a_1") + ("tag_a_2") + ("group_a") ;; try to create infinite loop + (:endgrouptag) + (:startgroup) + ("tag_b_1") + ("tag_b_1") ;; duplicated + ("tag_b_2") + (:endgroup) + ("groupless") + ("lonely")))) (org-test-agenda-with-agenda (string-join '("* TODO group_a :group_a:" - "* TODO tag_a_1 :tag_a_1:" + "* TODO groupless :groupless:" "* TODO tag_a_2 :tag_a_2:" - "* TODO tag_b_1 :tag_b_1:" "* TODO tag_b_2 :tag_b_2:" - "* TODO groupless :groupless:" + "* TODO tag_a_1 :tag_a_1:" + "* TODO tag_b_1 :tag_b_1:" "* TODO lonely :lonely:") "\n") - (dolist (org-tags-sort-function '(nil org-string< org-string> ignore)) + (dolist (org-tags-sort-function '(nil org-string< org-string> + ignore org-tags-sort-hierarchy)) (should (string-equal (string-trim @@ -685,7 +701,7 @@ Sunday 7 January 2024 ;; Not sorted ('ignore (string-join - '("group_a" "tag_a_1" "tag_a_2" "tag_b_1" "tag_b_2" "groupless" "lonely") + '("group_a" "groupless" "tag_a_2" "tag_b_2" "tag_a_1" "tag_b_1" "lonely") "\n")) ((or 'nil 'org-string<) (string-join @@ -694,6 +710,10 @@ Sunday 7 January 2024 ('org-string> (string-join '("tag_b_2" "tag_b_1" "tag_a_2" "tag_a_1" "lonely" "groupless" "group_a") + "\n")) + ('org-tags-sort-hierarchy + (string-join + '("group_a" "tag_a_1" "tag_a_2" "groupless" "lonely" "tag_b_1" "tag_b_2") "\n"))))))))) (ert-deftest test-org-agenda/goto-date () -- 2.47.1 --=-=-=--