Skip to content

Commit 90e1c80

Browse files
author
troiganto
committed
feat(properties): add setting org_use_property_inheritance
1 parent 5d9acf5 commit 90e1c80

File tree

9 files changed

+97
-8
lines changed

9 files changed

+97
-8
lines changed

Diff for: docs/configuration.org

+16
Original file line numberDiff line numberDiff line change
@@ -533,6 +533,22 @@ Prefix added to the generated id when [[#org_id_method][org_id_method]] is set t
533533
If =true=, generate ID with the Org ID module and append it to the
534534
headline as property. More info on [[#org_store_link][org_store_link]]
535535

536+
*** =org_use_property_inheritance=
537+
:PROPERTIES:
538+
:CUSTOM_ID: org_use_property_inheritance
539+
:END:
540+
- Type: =boolean | string | string[]=
541+
- Default: =false=
542+
Determine whether properties of one headline are inherited by sub-headlines.
543+
544+
- =false= - properties only pertain to the file or headline that defines them
545+
- =true= - properties of a headlines also pertain to all its sub-headlines
546+
- =string[]= - only the properties named in the given list are inherited
547+
- =string= - only properties matching the given regex are inherited
548+
549+
Note that for a select few properties, the inheritance behavior is hard-coded withing their special applications.
550+
See [[https://orgmode.org/manual/Property-Inheritance.html][Property Inheritance]] for details.
551+
536552
*** =org_babel_default_header_args=
537553
:PROPERTIES:
538554
:CUSTOM_ID: org_babel_default_header_args

Diff for: lua/orgmode/clock/init.lua

+1-1
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ function Clock:get_statusline()
118118
return ''
119119
end
120120

121-
local effort = self.clocked_headline:get_property('effort')
121+
local effort = self.clocked_headline:get_property('effort', false)
122122
local total = self.clocked_headline:get_logbook():get_total_with_active():to_string()
123123
if effort then
124124
return string.format('(Org) [%s/%s] (%s)', total, effort or '', self.clocked_headline:get_title())

Diff for: lua/orgmode/config/_meta.lua

+1
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,7 @@
235235
---@field org_id_method? 'uuid' | 'ts' | 'org' What method to use to generate ids via org.id module. Default: 'uuid'
236236
---@field org_id_prefix? string | nil Prefix to apply to id when `org_id_method = 'org'`. Default: nil
237237
---@field org_id_link_to_org_use_id? boolean If true, Storing a link to the headline will automatically generate ID for that headline. Default: false
238+
---@field org_use_property_inheritance boolean | string | string[] If true, properties are inherited by sub-headlines; may also be a regex or list of property names. Default: false
238239
---@field org_babel_default_header_args? table<string, string> Default header args for org-babel blocks: Default: { [':tangle'] = 'no', [':noweb'] = 'no' }
239240
---@field win_split_mode? 'horizontal' | 'vertical' | 'auto' | 'float' | string[] How to open agenda and capture windows. Default: 'horizontal'
240241
---@field win_border? 'none' | 'single' | 'double' | 'rounded' | 'solid' | 'shadow' | string[] Border configuration for `win_split_mode = 'float'`. Default: 'single'

Diff for: lua/orgmode/config/defaults.lua

+1
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ local DefaultConfig = {
6666
org_id_method = 'uuid',
6767
org_id_prefix = nil,
6868
org_id_link_to_org_use_id = false,
69+
org_use_property_inheritance = false,
6970
org_babel_default_header_args = {
7071
[':tangle'] = 'no',
7172
[':noweb'] = 'no',

Diff for: lua/orgmode/config/init.lua

+19
Original file line numberDiff line numberDiff line change
@@ -539,6 +539,25 @@ function Config:parse_header_args(args)
539539
return results
540540
end
541541

542+
---@param property_name string
543+
---@return boolean uses_inheritance
544+
function Config:use_property_inheritance(property_name)
545+
property_name = string.lower(property_name)
546+
547+
local use_inheritance = self.opts.org_use_property_inheritance or false
548+
549+
if type(use_inheritance) == 'table' then
550+
return vim.tbl_contains(use_inheritance, function(value)
551+
return vim.stricmp(value, property_name) == 0
552+
end, { predicate = true })
553+
elseif type(use_inheritance) == 'string' then
554+
local regex = vim.regex(use_inheritance)
555+
return regex:match_str(property_name) and true or false
556+
else
557+
return use_inheritance and true or false
558+
end
559+
end
560+
542561
---@type OrgConfig
543562
instance = Config:new()
544563
return instance

Diff for: lua/orgmode/files/headline.lua

+12-5
Original file line numberDiff line numberDiff line change
@@ -422,7 +422,7 @@ end
422422
function Headline:set_property(name, value)
423423
local bufnr = self.file:get_valid_bufnr()
424424
if not value then
425-
local existing_property, property_node = self:get_property(name)
425+
local existing_property, property_node = self:get_property(name, false)
426426
if existing_property and property_node then
427427
vim.fn.deletebufline(bufnr, property_node:start() + 1)
428428
end
@@ -443,7 +443,7 @@ function Headline:set_property(name, value)
443443
end
444444

445445
local property = (':%s: %s'):format(name, value)
446-
local existing_property, property_node = self:get_property(name)
446+
local existing_property, property_node = self:get_property(name, false)
447447
if existing_property then
448448
return self:_set_node_text(property_node, property)
449449
end
@@ -472,7 +472,10 @@ function Headline:add_note(note)
472472
end
473473

474474
---@param property_name string
475-
---@param search_parents? boolean
475+
---@param search_parents? boolean if true, search parent headlines;
476+
--- if false, only search this headline;
477+
--- if nil (default), check
478+
--- `org_use_property_inheritance`
476479
---@return string | nil, TSNode | nil
477480
function Headline:get_property(property_name, search_parents)
478481
local _, properties = self:get_properties()
@@ -486,6 +489,10 @@ function Headline:get_property(property_name, search_parents)
486489
end
487490
end
488491

492+
if search_parents == nil then
493+
search_parents = config:use_property_inheritance(property_name)
494+
end
495+
489496
if not search_parents then
490497
return nil, nil
491498
end
@@ -495,7 +502,7 @@ function Headline:get_property(property_name, search_parents)
495502
local headline_node = parent_section:field('headline')[1]
496503
if headline_node then
497504
local headline = Headline:new(headline_node, self.file)
498-
local property, property_node = headline:get_property(property_name)
505+
local property, property_node = headline:get_property(property_name, false)
499506
if property then
500507
return property, property_node
501508
end
@@ -918,7 +925,7 @@ function Headline:is_same(other_headline)
918925
end
919926

920927
function Headline:id_get_or_create()
921-
local id_prop = self:get_property('ID')
928+
local id_prop = self:get_property('ID', false)
922929
if id_prop then
923930
return vim.trim(id_prop)
924931
end

Diff for: lua/orgmode/org/hyperlinks/init.lua

+1-1
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ function Hyperlinks.as_custom_id_anchors(url)
5757
return function(headlines)
5858
return vim.tbl_map(function(headline)
5959
---@cast headline OrgHeadline
60-
local custom_id = headline:get_property('custom_id')
60+
local custom_id = headline:get_property('custom_id', false)
6161
return ('%s#%s'):format(prefix, custom_id)
6262
end, headlines)
6363
end

Diff for: lua/orgmode/org/links/types/custom_id.lua

+1-1
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ function OrgLinkCustomId:autocomplete(link)
6060
local prefix = opts.type == 'internal' and '' or opts.link_url:get_path_with_protocol() .. '::'
6161

6262
return vim.tbl_map(function(headline)
63-
local custom_id = headline:get_property('custom_id')
63+
local custom_id = headline:get_property('custom_id', false)
6464
return prefix .. '#' .. custom_id
6565
end, headlines)
6666
end

Diff for: tests/plenary/files/headline_spec.lua

+45
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
local helpers = require('tests.plenary.helpers')
2+
local config = require('orgmode.config')
23

34
describe('Headline', function()
45
describe('get_category', function()
@@ -54,6 +55,50 @@ describe('Headline', function()
5455
end)
5556
end)
5657

58+
describe('use_property_inheritance', function()
59+
local file = helpers.create_file_instance({
60+
'#+CATEGORY: file_category',
61+
'* Headline 1',
62+
':PROPERTIES:',
63+
':DIR: some/dir/',
64+
':THING: 0',
65+
':COLUMNS:',
66+
':END:',
67+
'** Headline 2',
68+
' some body text',
69+
}, 'category.org')
70+
after_each(function()
71+
config:extend({ org_use_property_inheritance = false })
72+
end)
73+
it('is false by default', function()
74+
assert.is.Nil(file:get_headlines()[2]:get_property('dir'))
75+
end)
76+
it('is active if true', function()
77+
config:extend({ org_use_property_inheritance = true })
78+
assert.are.same('some/dir/', file:get_headlines()[2]:get_property('dir'))
79+
assert.are.same('0', file:get_headlines()[2]:get_property('thing'))
80+
end)
81+
it('is selective if a list', function()
82+
config:extend({ org_use_property_inheritance = { 'dir' } })
83+
assert.are.same('some/dir/', file:get_headlines()[2]:get_property('dir'))
84+
assert.is.Nil(file:get_headlines()[2]:get_property('thing'))
85+
end)
86+
it('is selective if a regex', function()
87+
config:extend({ org_use_property_inheritance = '^di.$' })
88+
assert.are.same('some/dir/', file:get_headlines()[2]:get_property('dir'))
89+
assert.is.Nil(file:get_headlines()[2]:get_property('thing'))
90+
end)
91+
it('can be overridden with true', function()
92+
assert.is.Nil(file:get_headlines()[2]:get_property('dir'))
93+
assert.are.same('some/dir/', file:get_headlines()[2]:get_property('dir', true))
94+
end)
95+
it('can be overridden with false', function()
96+
config:extend({ org_use_property_inheritance = true })
97+
assert.are.same('some/dir/', file:get_headlines()[2]:get_property('dir'))
98+
assert.is.Nil(file:get_headlines()[2]:get_property('dir', false))
99+
end)
100+
end)
101+
57102
describe('get_all_dates', function()
58103
it('should properly parse dates from the headline and body', function()
59104
local file = helpers.create_file({

0 commit comments

Comments
 (0)