emacs-orgmode@gnu.org archives
 help / color / mirror / code / Atom feed
* Re: [RFC] Document level property drawer
@ 2019-10-02 20:29 Gustav Wikström
  0 siblings, 0 replies; 35+ messages in thread
From: Gustav Wikström @ 2019-10-02 20:29 UTC (permalink / raw)
  To: sebastian.miele@gmail.com; +Cc: emacs-orgmode@gnu.org

Hi Sebastian,

> From:	Sebastian Miele
> Subject:	Re: [O] [RFC] Document level property drawer
> Date:	Tue, 01 Oct 2019 12:38:12 +0000

> ...

> I would like to be able to make a clear distinction between properties
> that are visible by default and properties that are not. Maybe it would
> be possible to allow some #+.. syntax following headings for subtree
> properties that are visible by default. A requirement could be made that
> such property specifications always have to be followed by a property
> drawer, even if that is empty. Then everything #+.. that is before the
> property drawer would belong to the heading/subtree, and everything #+..
> that follows the drawer would be treated as it is until now.

That maps quite well to what I also had in mind initially. What I
called "Document property keywords" in [fn:1]:

  #+begin_quote
  I propose to allow properties to be defined also as document
  property keywords. All keywords in the top of a buffer, before any
  non-comment line, are document-level keywords. In effect, they are
  properties that apply in exactly the same way as properties defined
  in the property drawer. The only reason for using a document keyword
  instead of defining it inside the property drawer is to make it more
  visible. One example would be the title-keyword (#+TITLE: ...).
  #+end_quote

Although I didn't think of generalizing it to also work for the
outline nodes. Something that makes sense to do though, given the use
case you describe!

[fn:1] https://lists.gnu.org/archive/html/emacs-orgmode/2019-06/msg00000.html

FWIW
Gustav

^ permalink raw reply	[flat|nested] 35+ messages in thread
* Re: [RFC] Document level property drawer
@ 2019-10-24 22:29 Gustav Wikström
  0 siblings, 0 replies; 35+ messages in thread
From: Gustav Wikström @ 2019-10-24 22:29 UTC (permalink / raw)
  To: adam@alphapapa.net; +Cc: emacs-orgmode@gnu.org

Hi Adam,

Adam Porter <adam@alphapapa.net> writes:

> There are a lot of deprecation recommendations in your attached
> document:
> 
> > I propose to depricate property-keywords
> > I propose to depricate the Options-keyword
> > I propose to relabel these keywords as document keywords
> > I propose to depricate the #+CATEGORY syntax
> > I propose to depricate the #+FILETAGS syntax
> > I propose to depricate the #+COLUMNS syntax
> > I propose to depricate the #+ARCHIVE syntax
> > I propose to depricate the TODO-keywords
> > I propose to depricate the priorities-keyword
> > I propose to depricate the tags-keyword
> > I propose to depricate the link-keyword
> > I propose to depricate the constants-keyword
> > I propose to depricate the setupfile-keyword
> > I propose to depricate the Macro-keyword
> 
> The thoroughness of your investigation is admirable.

Thanks!

> However, I propose that we don't deprecate any of those.  Org has been
> around for over a decade now.  Such drastic changes would not serve
> users well.

I think you're right in the fact that Org mode will need to continue
to understand them. I'll say again that I wrote the document quite a
while ago. It's unedited and initially meant for my eyes only. So "to
deprecate" may be perceived to strong and I certainly didn't mean to
cut them out straight away. I don't think the diverse use of keywords
are good for the future of Org mode though, and I do think there is
value in trying to consolidate functionality and possibly promote
something that is more clear and easy to understand. Many of the
existing keywords have a corresponding property-syntax. For those I
think it would be good to start promoting using properties instead of
keywords (as I've written over and over again :) ). Other keywords
affect the Org mode configuration for the current buffer. They are
basically shortcuts to customization for the current buffer. Which led
me to proposing a settings drawer. It may be that it initially is
enough to just update the documentation with a chapter about keywords
and categorizing them in some groups based on intended purpose. I do
however still like the idea of collecting those keywords in a drawer
instead of having them spread out in the document.

> Note that I'm taking your use of the word "deprecate" to mean what
> it's expected to mean in this context: that the software developers
> recommend against using it, with the intention to eventually remove
> support for the feature.  We shouldn't be removing any such features
> from Org.
> 
> Not only would it not serve users well, but it would make the software
> much more complicated.  As it stands, finding, e.g. a #+CATEGORY:
> keyword and getting its value is as simple as:
> 
>     (save-excursion
>       (goto-char (point-min))
>       (when (re-search-forward (rx bol "#+CATEGORY:" (1+ blank)
>                                    (group (1+ nonl)))
>                                nil t)
>         (match-string 1)))
> 
> Hiding those keywords in drawers means that either:
> 
> a) Eligible drawers must be located, and then the desired
> property must be searched for inside of them.
> 
> b) Possibly valid properties must be located, and each one must be
> confirmed to be inside an eligible drawer.
> 
> What benefit would this added complexity serve?  To put the keywords
> in one place in the document?  There are already multiple ways to
> achieve that.

I don't agree here. Keywords today break the outline and have no
positional requirements. Both a property and a settings drawer would
have a fixed position to make it easy to locate, both programmatically,
visually and by search.

> I can't emphasize enough how important stability and consistency is
> for Org and its file formats right now.  As I've said, there are new
> implementations in development, which have the potential to bring a
> lot of publicity and new users to Org.  For example, this one was
> mentioned on a Hacker News post a few days ago:

There is always a balance between stability, backwards compatibility
and progression. I agree that backwards compatibility and stability is
important. But I also argue that progression is important. Good if Org
mode is gaining traction! But that doesn't mean we can't improve it
further, for it to gain even more traction! And in the larger scheme
of things, Org mode still is tiny. So let's not oversell ourselves
here. It's almost like a catch 22. But the largest hinderance for Org
mode to grow further is probably its ties with Emacs, the very thing
that makes Org mode into the powerhouse it is!

> https://github.com/mickael-kerjean/filestash
> 
> In the same HN post were examples of implementations for Vim and
> VSCode.  Already, especially in the VSCode ones, there were apparent
> incompatibilities in their implementations of the Org file format.

I've been a promoter of separating the Org mode syntax from Emacs for
a long time. I think I've written about it on this list previously as
well. So I know what you mean. That still isn't any argument for
stopping progress and improvement.

> As well, there are now parsers in JavaScript, Python, and Rust.
> 
> Markdown is by far the most popular plain-text format, and has been
> for years, but it has long suffered from competing, slightly
> incompatible flavors and implementations.  Reddit has theirs, GitHub
> has theirs, etc.
> 
> Org's file format may finally be gaining some momentum.  Let's not
> jeopardize Org's chances by making implementors' job more difficult
> than it already is.

I think that's a bit to defensive from Org mode's perspective. Yes, we
should promote other tools to start implementing Org mode parsers. No,
we shouldn't make it more difficult. But also let's not stop all
progress with the belief that it will make life easier. I believe that
a document property drawer that follows the syntax of the outline
drawer will make life easier for those you're talking about. Though in
the end it's always the end users we have to think most about.

I think your lobbying against this has had effect though, since the
property drawer already is out of 9.3 and in a separate branch.

Regards,
Gustav

^ permalink raw reply	[flat|nested] 35+ messages in thread
* Re: [RFC] Document level property drawer
@ 2019-10-20  2:28 Gustav Wikström
  2019-10-22 21:24 ` Marco Wahl
                   ` (2 more replies)
  0 siblings, 3 replies; 35+ messages in thread
From: Gustav Wikström @ 2019-10-20  2:28 UTC (permalink / raw)
  To: marcowahlsoft@gmail.com; +Cc: emacs-orgmode@gnu.org

[-- Attachment #1: Type: text/plain, Size: 3402 bytes --]

Hi!

I'll start with the most important info at the top. I've applied the
patch! But before anyone comes screaming I'll just say it's applied on
a separate branch. After consultation with Nicolas Goaziou that was
seen as the most reasonable thing to do. The idea is that it's high
time to start wrapping up 9.3 for a release and (even though I
personally would like to have this in there) since this patch might
continue to generate discussion and possibly bugs and fixes it's more
safe to let this come after the 9.3 release.

Sooo, a separate branch is created in the Org mode repository named
"next". I'm not entirely sure how we're supposed to work with it. But
I've anyways pushed my (non-breaking) patch there.

Comments on what you've written below!

Marco Wahl writes:

> > @Marco Wahl; As I understand you've applied the patch and tried it
> > out. Have you found any issues yet? What do you think of the patch
> > after having used it for a while?
> 
> Indeed I applied your patch and have it applied still.  Please note that
> I did nothing fancy and in particular I did not try to break the patch.

Got it.

> The patch works good for me.

Nice to hear!

> Noteworthy observations AFAICT:
> 
> 1. I could not translate my personal "#+TODO: . N ~ | x c g >" into a
> respective :TODO: property.

Yes, that's true. The reason is that there is no TODO-property that
fits in property drawers right now. I.e. special properties such as
TODO, TAGS, priority, scheduling and deadlines that have special
syntax for the outline still have no defined meaning for outline level
0. I ofc. think that's an oversight ;) But I may also be a bit crazy. 

A conclusion to draw from that, that may be worth writing more about,
is that the property drawer for node level 0 will not be able to
replace all file-level keywords that exist today.  Only properties that
currently can also be defined in property drawers in the outline will
work in the property drawer on level 0.  Makes sense?

The idea I had for all the other keywords that apply for the whole
file was to create another drawer, what I called a settings drawer.
Because the TODO-keyword you refer to above really is a setting that
you're making for the current file, much the same as when you make
changes in global, folder local or file local variables using the
standard emacs framework.

I've attached an investigation I did of the world of Org mode
keywords. It was done quite a while back and some things in there are
subjective and may not represent my current picture of the "ideal".
Nonetheless, maybe an interesting read for the ... other crazy people
out there?

> 2. With org-ids turned on and point before the first heading, function
> org-store-link creates an org-id property at the document level.
> 
> Regarding number 1. I think a list of document-level properties which
> don't behave the same when used in the document property drawer would be
> nice.  Ideally this list is empty AFAICT.  Maybe I overlook something.
> Is this an issue?

Ahh, see the attachment ;) Maybe can give answers to some of your
thoughts.

> I think observation 2. is just a little surprise but turns out to be
> natural when the document level property drawer is enabled.

Glad to hear!

> 
> 
> Still +1 for the inclusion of the patch and HTH,
> -- 

Thanks
Gustav

[-- Attachment #2: Investigation.org --]
[-- Type: application/octet-stream, Size: 12130 bytes --]

:PROPERTIES:
:ID:       ccbcc594-6ea0-42d4-af6d-62b93bb951d1
:COLUMNS:  %item %has-entry-syntax
:END:
#+TITLE: Investigation of keywords in org-mode

There are LOADS of them. They are used for configurations of various
kind. Many have ambigous names and it's not always clear what they're
for or in what context they're used. Some are document-level content.
Some change the emacs org-mode state for the buffer. This node sums up
my investigation of them.

* Startup
:PROPERTIES:
:has-entry-syntax: nil
:END:
Used to customize the org-mode configuration for the current buffer.

- Visibility :: [[info:org#Initial visibility][info:org#Initial visibility]]
- Blocks :: [[info:org#Blocks][info:org#Blocks]]
- Column Width and Alignment :: [[info:org#Column Width and Alignment][info:org#Column Width and Alignment]]
- Setting constants-unit-system :: [[info:org#References][info:org#References]]
- Closing items :: [[info:org#Closing items][info:org#Closing items]]
- Setting org-tag-persistent-alist :: [[info:org#Setting Tags][info:org#Setting Tags]]
- Setting logging when scheduling stuff :: [[info:org#Inserting deadline/schedule][info:org#Inserting deadline/schedule]]
- Setting logging for repeated tasks :: [[info:org#Repeated tasks][info:org#Repeated tasks]]
- Logging Clocking in/out :: [[info:org#Clocking commands][info:org#Clocking commands]]
- Logging refiling :: [[info:org#Refile and Copy][info:org#Refile and Copy]]
- Setting org-Pretty-entities :: [[info:org#Special Symbols][info:org#Special Symbols]]
- Previewing Latex :: [[info:org#Previewing LaTeX fragments][info:org#Previewing LaTeX fragments]]
- Inline images customization :: [[info:org#Images][info:org#Images]]
- Footnotes customization :: [[info:org#Creating Footnotes][info:org#Creating Footnotes]]
- Setting beamer minor mode :: [[info:org#Editing support][info:org#Editing support]]
- Displaying headline-stars :: [[info:org#Clean View][info:org#Clean View]]
- Various other customization :: [[info:org#In-buffer Settings][info:org#In-buffer Settings]]

To see all options, =M-C-i= (pcomplete) on the following row:
#+STARTUP:

or look at the constant =org-startup-options= defined in org.el.

Common for all startup-keywords is that they take no parameters. Each
value has a predefined configuration. For example hideblocks &
nohideblocks are two startup-options that both set
org-hide-block-startup. But to different values.

Startup modifies the state of the run-time.

One benefit of having them is for easy and concise user-configuration
inside each buffer. But it basically is the same as setting file local
variables for safe Org mode defcustoms. Why not just promote that
instead? Or provide syntactic sugar for it inside a "settings
drawer"?!... That would both reduce complexity and increase
configurability.

Making the setting-drawer work similar to property-drawers might allow
for a "Set Startup" interactive function that can take the same
arguments as are available for the Startup-keyword today. I'm sure
using startup has it's merits for those who are used to it, so
depricating it in favour of another more general solution is not an
option.

* Property
:PROPERTIES:
:has-entry-syntax: t
:END:
The property keyword is used to set properties for the whole document.
Main intention seems to be for inheritance-purposes. When working in a
multi-file project (not yet supported that well...) having
document-properties are meaningful also without inheritance. One can
imagine a set of org-mode files without headlines in a folder where
one would want to set ID's for example, and link between the files
using these ID's.

Property-keywords can be defined anywhere inside the file, even inside
headlines. The property that is set using the keyword-syntax always
applies to the whole document (unless overridden in headlines). If one
property is defined multiple times the last definition is used.

*I propose to depricate property-keywords* in its current form, and
introduce (1) a property-drawer for the document node and (2) a new
kind of property-keyword that is only valid at a certain point in the
buffer. Properties in property-drawers should override the existing
properties set by (the existing) property-keyword, as long as both
ways to define document-level properties are supported.

** New kind of property-keyword
The new kind of property-keyword would be on the form
=#+{propertyname}: {propertyvalue}= and would only be applicable if
entered directly at the top of the buffer, below potential
comment-lines but before any blank line and before the setting drawer
and the property drawer. Ideally a property-keyword shall override a
property set in the document property drawer, if the keyword is
defined in both places.

* Options
:PROPERTIES:
:HAS-ENTRY-SYNTAX: t
:END:
The options-keyword is used solemly when exporting. This is
unfortunately not obvious by the naming of the keyword.

The same functionality exist also for subtrees. Then the
export-options are specified as properties with the key
'EXPORT_OPTIONS'.

*I propose to depricate the Options-keyword* and start using the
already existing property-syntax instead inside the proposed
document-level property-drawer.

* Various Export-keywords
:PROPERTIES:
:HAS-ENTRY-SYNTAX: t
:END:
Keywords such as Title, Subtitle, Author, Date, Email are documented
as "export-keywords". Ref: [[info:org#Export Settings][info:org#Export Settings]]

*I propose to relabel these keywords as document keywords*. That label
is more fitting and does not provide any negative side-effects since
an export anyways is supposed to export org-mode information. If
anything, the relabeling promotes the keywords to first-class citizens
within org-mode.

I also propose to specify a fixed set of document-keywords that
org-mode support (i.e. document them) and provide a command to easily
insert them into the buffer. The location of these keywords should be
at the top of the buffer, after potential comment-lines but before the
document-level settings-drawer and properties-drawer.

Unsupported document-keywords can also be included but might not be
guaranteed to be exported, or function correctly from other
perspectives. But by their location in the top of the buffer, they
should be recognized as document-keywords non the less.

The reason for not wanting to depricate the keywords for these
properties and delegate them to the properties-drawer is that these
are keywords that makes sense to visually promote inside a buffer. I'm
for example quite often using a =#+TITLE:= line to declare the title
of the document. Hiding that line inside the properties-drawer is
counter-intuitive. I might however want to "demote" some of the
document-level keywords into a drawer for various reasons. So allowing
document-keywords to also be specified as keywords makes sense.

* Category
:PROPERTIES:
:HAS-ENTRY-SYNTAX: t
:END:

Can either be defined at document-level with =#+CATEGORY= or at
entry-level with a property named the same.

*I propose to depricate the #+CATEGORY syntax* in favour of a category
property inside the proposed document-level property-drawer.

* Filetags
:PROPERTIES:
:HAS-ENTRY-SYNTAX: normal tag inheritance
:END:

Filetags are tags set on document-level.

*I propose to depricate the #+FILETAGS syntax* in favour of a tags
property inside the proposed document-level property-drawer.

Now this might be a bit confusing since the Tags-keyword also exist on
document-level. More on that in it's own entry: [[id:ba02b48b-261a-4e65-b9a2-dfc57391ae41][Tags]].

* Columns
:PROPERTIES:
:has-entry-syntax: t
:END:

Can either be defined at document-level with =#+COLUMNS= or at
entry-level with a property named the same.

*I propose to depricate the #+COLUMNS syntax* in favour of a columns
property inside the proposed document-level property-drawer.

* Archive
:PROPERTIES:
:HAS-ENTRY-SYNTAX: t
:END:

Can either be defined at document-level with =#+ARCHIVE= or at
entry-level with a property named the same.

*I propose to depricate the #+ARCHIVE syntax* in favour of an archive
property inside the proposed document-level property-drawer.

* TODO, SEQ_TODO, TYP_TODO
:PROPERTIES:
:HAS-ENTRY-SYNTAX: nil
:END:
Setting the default allowed todo-values for a buffer (using the local
variable =org-todo-keywords-1=.

*I propose to depricate the TODO-keywords* and instead add
them as available keys in the proposed settings-drawer.

* Priorities
:PROPERTIES:
:HAS-ENTRY-SYNTAX: nil
:END:
Setting default priority-values for a buffer.

*I propose to depricate the priorities-keyword* and instead add
priorities as an available key in the proposed settings-drawer.

* Tags
:PROPERTIES:
:HAS-ENTRY-SYNTAX: nil
:ID:       ba02b48b-261a-4e65-b9a2-dfc57391ae41
:END:

Tags-keyword is used when setting the buffer-local variable
org-current-tag-alist. This defines the default tags available for
autocompletion within the buffer.

There can be more than one Tags-keyword per file and all rows
accumulate together into the final list of tags available.

*I propose to depricate the tags-keyword* and instead add tags as a
keyword available in the proposed settings-drawer.

* Link
:PROPERTIES:
:HAS-ENTRY-SYNTAX: nil
:END:
Link-abbreviations that can be used throughout the document.

*I propose to depricate the link-keyword* and instead add link as a
keyword available in the proposed settings-drawer.

* Constants
:PROPERTIES:
:HAS-ENTRY-SYNTAX: nil
:END:
buffer-global constans that can be used by tables and nothing else.

Set by =org-set-regexps-and-options=, using
=org--setup-collect-keywords=, into
=org-table-formula-constants-local=. As I understand it.

They fit closely together with link-abbreviations in how the keywords
are used to populate buffer-wide special key-value pairs. They differ
from properties since they are special-purpose.

*I propose to depricate the constants-keyword* and instead add
constants as a key available in the proposed settings-drawer.

* Setupfile
:PROPERTIES:
:HAS-ENTRY-SYNTAX: nil
:END:
Outsourcing of settings to another file (or url).

*I propose to depricate the setupfile-keyword* and instead add
setupfile as a key available in the proposed settings-drawer.

* Macro
:PROPERTIES:
:HAS-ENTRY-SYNTAX: nil
:END:
Text snippets.

*I propose to depricate the Macro-keyword* and instead add
macro as a key available in the proposed settings-drawer.

* Inline-keywords
:PROPERTIES:
:HAS-ENTRY-SYNTAX: n/a
:END:
Keywords which are used to provide content at a certain location in the
file.

Keywords: =INCLUDE=, =TOC=, =ASCII=, =HTML=, =LATEX=, =ODT=,
=TEXINFO=, =BEAMER=, =INDEX=

I have no issue with these keywords.

* Affiliated keywords
:PROPERTIES:
:HAS-ENTRY-SYNTAX: n/a
:END:
Keywords that relates to or adds information to objects within the
buffer. List of keywords is found in
=org-element-affiliated-keywords=. (Ex. NAME, CAPTION, RESULT, etc.)

These are not of my concern here, I haven't used them much but when I
have I haven't had any problems with them. The functionality is clear.

* Misc
:PROPERTIES:
:HAS-ENTRY-SYNTAX: nil
:END:
There are more keywords... But with my current understanding there
were no keywords that would make my suggestions invalid. I just
didn't work my way through them all since I thought of them as very
specific and of minor importance.

** Bind
Used for very advanced exporting. They seem to apply to the whole
document but only when the document is being exported. I'll call it a
special kind of export keyword for now...

** Specific export-backend keywords
I didn't work my way though all export-backend keywords. My guess is
that most of them could fit into either of the proposed drawer, the
document-keywords section (for example the Description-keyword that
can be used in HTML-export) or that they are inline or affiliate
keywords, which I'm not addressing here.

^ permalink raw reply	[flat|nested] 35+ messages in thread
* Re: [RFC] Document level property drawer
@ 2019-10-06  6:02 Gustav Wikström
  0 siblings, 0 replies; 35+ messages in thread
From: Gustav Wikström @ 2019-10-06  6:02 UTC (permalink / raw)
  To: moptop99@gmail.com; +Cc: emacs-orgmode@gnu.org

Hi Matt,

Thanks for your comment! I can assure you that you need not worry
about the propsed patch here in terms of your workflow. This is in no
way a hasty, sloppy work. Care has been taken when developing it to
not break anything existing. I hear your concerns on the larger topic
of keywords though. But that's not something that is changing in this
patch.

Some more comments below.

> I'd like to just quickly chime in in support of Adam's caution on this
> issue. I can absolutely see advantages to document level properties, I
> have written many code fragments that rely on the use of keywords and
> expect org filensyntax to be consistent with what actually exists. I
> use these code fragments to hold together a somewhat fragile workflow
> that allows me to use org in a work environment that is not especially
> receptive to simple text documents. I have invested a lot of time in
> making those systems run and sometimes even I don't entirely remember
> what I did to make them possible.
> 
> It would really, really suck to have those systems break. It would
> take me a lot of time to track down the causes and change what I
> needed to. VMs that currently pull in Emacs andnorg and my code would
> stop working. Old versions of my files would no longer render
> properly. My efforts to make my courses and other writings effectively
> reproducible by others would be significantly set back. Etc. I think
> these are the kinds of difficulties Adam means to describe.

Again, caution is ofc taken. In no way does this patch change any of
your existing properties. If something, it proposes a way for you to
simplify your setup if you at any time in the future choose to do
that.

The patch that is applied is tested and no existing functionailty is
changed. Ofc, if you have a critical flow I'd advice you to use the
stable branch of Org mode and not rely on the master branch, since
that branch from time to time will get bugs in it.

Regards
Gustav

^ permalink raw reply	[flat|nested] 35+ messages in thread
* Re: [RFC] Document level property drawer
@ 2019-10-06  5:35 Gustav Wikström
  0 siblings, 0 replies; 35+ messages in thread
From: Gustav Wikström @ 2019-10-06  5:35 UTC (permalink / raw)
  To: adam@alphapapa.net; +Cc: emacs-orgmode@gnu.org

[-- Attachment #1: Type: text/plain, Size: 8796 bytes --]

Hi Adam,



> > In no way is this a major, breaking change.  No document you have

> > today will break by the introduction of this.  The only thing changing

> > is if you *actively* create a document level property drawer and

> > choose to enter a property there that you already have defined in the

> > same document, using a property keyword.

>

> There is a bigger picture which you seem to be ignoring.



No I'm not ignoring any bigger picture. Hence the RFC which I'm asking

for comments about. So far a couple of positive remarks and your

comments on the other end of the spectrum.



> Org is distributed with Emacs itself.  Emacs versions are long-lived.

> People use old Emacs and Org versions for years, because few people

> build and install Emacs themselves, so most users use the versions

> included with their OS or other distributions.



Yes.



> Your proposed change would introduce a major change in the way document

> properties are interpreted.  This means that the same document, when

> used on Emacs/Org versions before and after this change, would be

> interpreted differently.  That's breaking forward-compatibility between

> versions, ones which will be in the wild for years.  That decision

> should not be taken lightly.



No, not really. Here we disagree again. I already interpret the

documents as an outline. And it seems others agree with me on that as

well. Your interpretation may be different but I fail to see where the

documentation agrees with your sentiment. The fact that some commands

that works for the outline doesn't work before the first headline has

been a shortcoming for a long time. I don't think that shortcoming is

something to continue to strive for.



Forward compatibility should indeed be taken into account. I don't

think the argument to stop this change due to breaking forward

compatibility is that strong though. If I as a user choose to use new

features, I agree to also use new software. As a package author you

can continue to rely on Org element and assume the user understands

that relation. If your packages creates property keywords today you

can continue to do so, or consider upgrading your package to depend on

Org mode 9.3 and start using the new feature to more easily insert

properties into a document level property drawer. If you do that you

of course have to mark your dependency to Org mode 9.3 as well.



> > Keywords that previously had file-wide effects will continue to have

> > that. That's not removed. So you must have misunderstood something

> > here.

>

> I did not misunderstand; I am looking from a different perspective.  In

> your proposal, line-based keywords can be overridden by document-level

> property drawers, while they currently are applied to the entire file

> regardless of any drawers.  That is a major change.



Ok, so correct me if I'm wrong here. The thing you object to in my

patch is the fact that the properties in the document level drawer

have a higher precedence over property keywords? Since it means that

outline level 0 have higher precedence than file level keywords? I've

already argued why I think that's natural. This order seems fine to

me: (from lowest to highest rank)

1. File level property keyword

2. Node level 0 property drawer property (file level property drawer)

3. Node level 1 property drawer property

4. Node level 2 property drawer property

5. ...and so on...



This feels less unintuitive and will be more difficult to explain to

new users:

1. Node level 0 property drawer property (file level property drawer)

2. File level property keyword

3. Node level 1 property drawer property

4. Node level 2 property drawer property

5. ...and so on...



I can't agree on changing to the second ranking only based on your

concerns. If more people were to back you up I think it's fair to have

that discussion though!



> >> What you're proposing is actually a fundamental change to the way Org

> >> documents are interpreted.  Org documents are not currently an

> >> outline, just a series of elements which may include an outline.

> >> Text and elements before a first heading are not part of a node,

> >> they're just text and elements in the document.

> >

> > I don't agree here. What I'm proposing in this patch doesn't change

> > the fundament of an Org mode document. I'd rather say it enhances the

> > fundament! Since the outline to a large extent is the fundament! The

> > following quote is from the documentation - Chapter 2, Document

> > Structure:

> >

> >   #+begin_quote Org is an outliner.  Outlines allow a document to be

> >   organized in a hierarchical structure, which, least for me, is the

> >   best representation of notes and thoughts.  #+end_quote

>

> Org is a collection of Emacs code built on top of Outline Mode, which

> interprets plain-text files in a certain way.  Since the beginning, such

> files have been interpreted such that content before the first heading

> is not part of an outline node.  Your proposal would change that.  For

> better or worse, it is a major change that has implications which you

> seem to be unconcerned with.



Of course I'm not unconcerned. I just don't know what you’re

trying to do here. Please take a look at your comment again and then

switch over to ORG-NEWS. Version 9.3 (I.e. a MINOR version) have 9

incompatible changes and 23 new features already pushed to master.

Those 32 changes all have implications for how Org mode documents are

interpreted. Your way of arguing in this thread could be done for many

of those changes. But then, why haven't you? Things change. Most

things to the better! This change being a big step to the better

according to me. And the change I'm proposing here isn't even a

breaking one. I get that it may have struck a nerve for you. But

please argue to the point of the issue instead of trying to blow this

thing up into some mayor breaking incompatible thing. Because it

isn't. It's a harmonization of a feature that already exist. To use it

you have to agree that things before the first headline symbolizes a

level-0 node. If you don't sympathize with that, then simply don't use

it. Your external packages won't need any modifications for this, Org

element already understands it.



> > Thus, saying Org documents are not currently an outline again feels

> > disingenuous and at this point I struggle to take your comments

> > seriously.

>

> As a fellow Org developer, I empathize with the work you have put into

> your proposal and its code.  However, it would be best if you would

> consider these issues impartially.



Which is what I'm doing. I got your comments and your sentiment now

Adam. If you have anything new to add then please do. But please stop

arguing that this change is a major breaking one. If you really still

think that then persuade other members of this community who share

your concern to make their voices heard. Only your comment is on the

side of reject due to major breaking change. The others are positive.

But still few comments all together.



> Like it or not, there is a wider "ecosystem" around Org today, and it is

> growing.  Your proposal would have effects rippling downstream for years

> to come, and other people would have to spend time making changes to

> accommodate them.  Thus, the wider implications of your proposal should

> be very carefully considered.



For this proposed addition to Org mode I don't see where any change is

needed in the wider ecosystem. I do however encourage such change. For

some packages it can resolve issues and simplify development. Because

after this patch is applied there is support for programmatic insertion

of properties that applies to whole files, without having to worry

about positional preferences of the user for property keywords. The

preference when there is a conflict for a property between the keyword

and the drawer is already discussed. That is not a breaking change. If

existing documents break, then please let me know where and how, so I

can try to fix it.



> Org is a big, and growing, project with thousands of users.  We have a

> duty to take these considerations into account, so major changes, like

> your proposal, should be taken very seriously.



Of course it is taken seriously. Your responding to the RFC on the

mailing list. And I'm taking comments on the patch just due to that

concern.



/Gustav

[-- Attachment #2: Type: text/html, Size: 21652 bytes --]

^ permalink raw reply	[flat|nested] 35+ messages in thread
* Re: [RFC] Document level property drawer
@ 2019-10-05 18:20 Gustav Wikström
  2019-10-06  0:51 ` Adam Porter
  0 siblings, 1 reply; 35+ messages in thread
From: Gustav Wikström @ 2019-10-05 18:20 UTC (permalink / raw)
  To: adam@alphapapa.net; +Cc: emacs-orgmode@gnu.org

Hi again Adam,

> IIUC, your proposal would work like this:
> 
> #+BEGIN_SRC org
>   :PROPERTIES:
>   :CATEGORY: Gamma
>   :END:
>   
>   # Category here is "Gamma"
> 
>   ,* Node 1
> 
>   # Category here is "Gamma"
> 
>   ,* Node 2
>   :PROPERTIES:
>   :CATEGORY: Beta
>   :END:
> 
>   # Category here is "Beta"
> 
>   ,#+CATEGORY: Alpha
> #+END_SRC

You understand correctly. In that case precedence would kick in since
a category is defined using both a category keyword and a category
inside the document property drawer. The example above is mostly
theoretical since there is no use-case for having category set on
document level using both conventions. If the example above was a real
document I'd suggest removing either the category keyword or the
category property from the document property drawer.

> In Org, some keywords are special, like #+CATEGORY.  For many years,
> such keywords have had file-wide effects regardless of their placement
> in the file.  IIUC, your proposal would change that, and that would
> still be a major, breaking change.

This seems disingenuous. In no way is this a major, breaking change.
No document you have today will break by the introduction of this.
The only thing changing is if you *actively* create a document level
property drawer and choose to enter a property there that you already
have defined in the same document, using a property keyword.

Keywords that previously had file-wide effects will continue to have
that. That's not removed. So you must have missunderstood something
here.

I understand you dislike the preference of letting the property drawer
have a higher precedance than property keywords, if the same property
is defined in both ways. I've already argued why I think that is the
sane choice to make. But having that precedance doesn't break anything
since you cannot define a property drawer on document level today.

> > If you think of the document as an outline, something Org mode is all
> > about, it makes sense to also think of things before the first
> > headline as "node level 0". And with that way of conceptually thinking
> > of the document it makes perfect sense to have a property drawer fixed
> > at the top - in the same way as it is required for all other node
> > levels.
> 
> What you're proposing is actually a fundamental change to the way Org
> documents are interpreted.  Org documents are not currently an outline,
> just a series of elements which may include an outline.  Text and
> elements before a first heading are not part of a node, they're just
> text and elements in the document.

I don't agree here. What I'm proposing in this patch doesn't change
the fundament of an Org mode document. I'd rather say it enhances the
fundament! Since the outline to a large extent is the fundament! The
following quote is from the documentation - Chapter 2, Document
Structure:

  #+begin_quote
  Org is an outliner.  Outlines allow a document to be organized in a
  hierarchical structure, which, least for me, is the best representation
  of notes and thoughts.
  #+end_quote

Thus, saying Org documents are not currently an outline again feels
disingenuous and at this point I struggle to take your comments
seriously.

Regards
Gustav

^ permalink raw reply	[flat|nested] 35+ messages in thread
* Re: [RFC] Document level property drawer
@ 2019-09-30 22:09 Gustav Wikström
  2019-10-03 18:31 ` Adam Porter
  0 siblings, 1 reply; 35+ messages in thread
From: Gustav Wikström @ 2019-09-30 22:09 UTC (permalink / raw)
  To: adam@alphapapa.net; +Cc: emacs-orgmode@gnu.org

Hi Adam,

> How does it differ from what was previously proposed?

It differs by not introducing the document concept in Org element.
Removing that means there is no reason to wait for another major
version of Org mode.

> What exactly does "will (shall)" mean?

You can ignore the inside of the parentheses. It carries no important
meaning.

> > 3) Properties defined in a property drawer will have precedence over
> >    properties defined as a property keyword, if the same property is
> >    defined using both conventions.
> 
> That protocol seems unnatural and confusing to me:
> 
> - If precedence were to be defined by something other than file-order,
>   it seems to me that those defined with #+ keywords should have
>   precedence, because they are more visible, while those in drawers are
>   hidden.
> - However, it seems to me that the simplest, most natural protocol would
>   be for later declarations to override earlier ones.

I'd argue that precedence already works that way. One has to take
inheritance into account. With inheritance turned on, tell me which
value for Property1 is used for the nodes in the following example:

#+begin_src org
  ,* Node 1
  ,* Node 2
  :PROPERTIES:
  :Property1: Value1
  :END:

  ,#+PROPERTY: Property1 Value2
#+end_src

As you'll see line number already isn't the deciding factor. 

With two ways to define properties it makes sense to first think of
which syntax to promote as "more important" and then to think of
precedence rules for duplicates within each syntax.

Having the same syntax for node level 0 as for regular nodes makes the
property functionality easy to understand and congruent. Something I
think is worth promoting by saying that property blocks on file-level
has precedence over the keyword syntax.

> > 4) The position for the document level property drawer is:
> >    - At the first line in a file that is not a comment or a keyword.
> >
> >      I.e. the following will work:
> >
> >      #+begin_src org
> >        # -*- mode: org -*-
> >        ,#+TITLE: Test
> >        :PROPERTIES:
> >        :CATEGORY: Test
> >        :END:
> >
> >        Preamble 
> >
> >        ,* Some heading
> >        Some content
> >      #+end_src
> >
> >
> >      but not this:
> >
> >      #+begin_src org
> >        Some comment and/or empty line
> >
> >        :PROPERTIES:
> >        :CATEGORY: Test
> >        :END:
> >
> >        ,* Some heading
> >        Some content
> >      #+end_src
> 
> That feels unintuitive to me.  Document-level property keywords may
> appear anywhere in a file, so it seems inconsistent for document-level
> property drawers to be limited in this way, as if there were an implied
> headline at the top of the file.  If it were so, I would expect to see
> many mailing list posts by users asking why the properties defined in
> their document-level property drawers aren't working.  Given that there
> is no enforcement in Org's UI to keep such drawers in certain places, I
> think the implementation should be tolerant of users' preferences and
> mistakes (cf. Postel's Law).

If you think of the document as an outline, something Org mode is all
about, it makes sense to also think of things before the first
headline as "node level 0". And with that way of conceptually thinking
of the document it makes perfect sense to have a property drawer fixed
at the top - in the same way as it is required for all other node
levels.

Regarding the placement of drawers, if you apply my patch on your end
to test it out you'll see that the built in functionality to define
properties creates the drawer for you. That's easy to do since it's
positional rule is easy to derive by the system. Try for example
org-set-property (C-c C-x p) and you'll get the drawer defined for
you. In exactly the same way as it already works when you're inside a
heading today.

The lack of posts asking why properties defined on their outline nodes
doesn't work tells me that positional requirements for property
drawers already is well understood.

Kind regards
Gustav

^ permalink raw reply	[flat|nested] 35+ messages in thread
* [RFC] Document level property drawer
@ 2019-09-29 10:27 Gustav Wikström
  2019-09-29 19:13 ` Marco Wahl
                   ` (2 more replies)
  0 siblings, 3 replies; 35+ messages in thread
From: Gustav Wikström @ 2019-09-29 10:27 UTC (permalink / raw)
  To: emacs-orgmode@gnu.org; +Cc: Nicolas Goaziou

[-- Attachment #1: Type: text/plain, Size: 1584 bytes --]

Hi,

This patch introduces a document level property drawer. 

This has been discussed previously in a larger context:
- https://lists.gnu.org/archive/html/emacs-orgmode/2019-06/msg00000.html
- https://lists.gnu.org/archive/html/emacs-orgmode/2019-08/msg00339.html 
- https://lists.gnu.org/archive/html/emacs-orgmode/2019-09/msg00010.html

The patch is a somewhat modified version of what was included in the third 
link above.

The following will be true for document level property drawers:
1) In the same way that one can have a property drawer for a heading, one
   can have a property drawer for a whole document.
2) All existing commands that can work with property drawers will
   (shall) work also on property drawers before the first heading.
3) Properties defined in a property drawer will have precedence over
   properties defined as a property keyword, if the same property is
   defined using both conventions.
4) The position for the document level property drawer is:
   - At the first line in a file that is not a comment or a keyword.

     I.e. the following will work:
     #+begin_src org
       # -*- mode: org -*-
       ,#+TITLE: Test
       :PROPERTIES:
       :CATEGORY: Test
       :END:

       Preamble 

       ,* Some heading
       Some content
     #+end_src

     but not this:
     #+begin_src org
       Some comment and/or empty line

       :PROPERTIES:
       :CATEGORY: Test
       :END:

       ,* Some heading
       Some content
     #+end_src

What do you say?

Regards
Gustav Wikström

[-- Attachment #2: 0001-Org-document-property-drawers.patch --]
[-- Type: application/octet-stream, Size: 45416 bytes --]

From 91dad61268b0c2a6efe9c9fa5cee868434d33b76 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gustav=20Wikstr=C3=B6m?= <gustav@whil.se>
Date: Sun, 26 May 2019 22:13:09 +0200
Subject: [PATCH] Org document property-drawers

Add functionality to define property-blocks on document level, in
addition to at headline level.

* doc/org-manual.org:
* etc/ORG-NEWS: Document new functionality.

* lisp/org.el (org-keyword-regexp): Define constant instead of
  hardcoding.
(org-file-properties): Renamed, see next line.
(org-keyword-properties): Renamed from above.  Due to the fact that
  properties can be defined for the whole document using property
  drawers this local variable needs a rename to make its name less
  ambigous.
(org-refresh-properties, org-refresh-property, org-entry-properties)
(org-refresh-category-properties, org-get-property-block)
(org-entry-get-with-inheritance, org-entry-put)
(org-insert-property-drawer, org-end-of-subtree): Made to work before
  first headline.
(org-at-property-block-p): New function to validate if point is at the
start of a property block.
(org-property-global-value): Renamed, see next line.
(org-property-global-or-keyword-value): Renamed from above to match
  its functionality better.
(org-back-to-heading-or-point-min): New function to make a document
  work as a level 0 node in the outline.
(org-at-keyword-p): Predicate function to answer to if we're currently
at a keyword line or not.
(org-up-heading-or-point-min): New function to make a document work as
a level 0 node in the outline.

* lisp/org-element.el (org-element--current-element): Can now detect
  property-blocks before first headline according to it's positional
  rules.

* lisp/org-attach.el (org-attach): Make it possible to call the
  attachment dispatcher also before the first headline, since document
  property drawers make attachments possible for the whole document
  now.

* lisp/org-capture.el: Modified only due to rename of function in
  org.el.

* lisp/org-compat.el (org-file-properties)
(org-property-global-value): Renamed functions declared obsolete.

* testing/lisp/test-org.el (org/insert-property-drawer)
(org/set-property, org/delete-property, org/delete-property-globally):
  Additions of tests to check if they work before first headline.
(org/at-property-p, org/at-property-block-p, org/get-property-block)
(org/entry-get, org/refresh-properties): New tests

* testing/examples/property-inheritance.org: Switch from
  property-keywords to a property-drawer in the testfile.
  Functionality should be the same, but now using a document drawer
  instead of property-keywords.

  Reason for switching is that I'd like us to slowly depricate
  property-keywords.

* testing/lisp/test-org-element.el:

* contrib/lisp/ox-taskjuggler.el: A comment is modified only due to
  rename of function in org.el.
---
 contrib/lisp/ox-taskjuggler.el            |   2 +-
 doc/org-manual.org                        |  27 +-
 etc/ORG-NEWS                              |  10 +-
 lisp/org-attach.el                        |   2 +-
 lisp/org-capture.el                       |   4 +-
 lisp/org-compat.el                        |   6 +
 lisp/org-element.el                       |   5 +-
 lisp/org.el                               | 268 ++++++++++++-------
 testing/examples/property-inheritance.org |   6 +-
 testing/lisp/test-org-element.el          |  21 ++
 testing/lisp/test-org.el                  | 305 +++++++++++++++++++++-
 11 files changed, 539 insertions(+), 117 deletions(-)

diff --git a/contrib/lisp/ox-taskjuggler.el b/contrib/lisp/ox-taskjuggler.el
index 16a6a3a74..bc90b93a4 100644
--- a/contrib/lisp/ox-taskjuggler.el
+++ b/contrib/lisp/ox-taskjuggler.el
@@ -137,7 +137,7 @@
 ;;   :END:
 ;;
 ;;;; * TODO
-;;   - Look at org-file-properties, org-global-properties and
+;;   - Look at org-keyword-properties, org-global-properties and
 ;;     org-global-properties-fixed
 ;;   - What about property inheritance and org-property-inherit-p?
 ;;   - Use TYPE_TODO as an way to assign resources
diff --git a/doc/org-manual.org b/doc/org-manual.org
index f2f059e77..8358620ca 100644
--- a/doc/org-manual.org
+++ b/doc/org-manual.org
@@ -4951,7 +4951,7 @@ with many examples, see [[*Matching tags and properties]].
 
 A property is a key-value pair associated with an entry.  Properties
 can be set so they are associated with a single entry, with every
-entry in a tree, or with every entry in an Org file.
+entry in a tree, or with the whole buffer.
 
 There are two main applications for properties in Org mode.  First,
 properties are like tags, but with a value.  Imagine maintaining
@@ -5015,8 +5015,13 @@ disks in a box like this:
   :END:
 #+end_example
 
-If you want to set properties that can be inherited by any entry in
-a file, use a line like:
+Properties can be inserted on buffer level.  That means they apply
+before the first headline and can be inherited by all entries in a
+file.  Property blocks defined before first headline needs to be
+located at the top of the buffer, allowing only comments and keywords
+above.
+
+Properties can also be defined using lines like:
 
 #+cindex: @samp{_ALL} suffix, in properties
 #+cindex: @samp{PROPERTY}, keyword
@@ -5081,7 +5086,9 @@ The following commands help to work with properties:
   #+findex: org-insert-drawer
   Insert a property drawer into the current entry.  The drawer is
   inserted early in the entry, but after the lines with planning
-  information like deadlines.
+  information like deadlines.  If before first headline the drawer is
+  inserted at the top of the drawer after any potential comments or
+  keywords.
 
 - {{{kbd(C-c C-c)}}} (~org-property-action~) ::
 
@@ -5304,11 +5311,6 @@ done by defining a column format line.
 :DESCRIPTION: Where defined, where valid?
 :END:
 
-To define a column format for an entire file, use a line like:
-
-#+cindex: @samp{COLUMNS}, keyword
-: #+COLUMNS: %25ITEM %TAGS %PRIORITY %TODO
-
 To specify a format that only applies to a specific tree, add
 a =COLUMNS= property to the top node of that tree, for example:
 
@@ -5319,6 +5321,13 @@ a =COLUMNS= property to the top node of that tree, for example:
    :END:
 #+end_example
 
+A =COLUMNS= property within a property drawer before first headline
+will apply to the entire file.  As an addition to property drawers,
+keywords can also be defined for an entire file using a line like:
+
+#+cindex: @samp{COLUMNS}, keyword
+: #+COLUMNS: %25ITEM %TAGS %PRIORITY %TODO
+
 If a =COLUMNS= property is present in an entry, it defines columns for
 the entry itself, and for the entire subtree below it.  Since the
 column definition is part of the hierarchical structure of the
diff --git a/etc/ORG-NEWS b/etc/ORG-NEWS
index 9fff4ad16..716818720 100644
--- a/etc/ORG-NEWS
+++ b/etc/ORG-NEWS
@@ -130,6 +130,14 @@ Export ignore done tasks with a deadline when
 Likewise, scheduled done tasks are also ignored when
 ~org-icalendar-use-scheduled~ contains the same symbol.
 
+*** Property drawers before first headline, outline  level 0
+Property drawers will now work before first headline and Org mode is
+moving more towards making things before the first headline behave
+just as if it was at outline level 0.  Inheritance for properties will
+work also for this level.  In other words; defining things in a
+property drawer before the first headline will make them "inheritable"
+for all headlines.
+
 *** Add split-window-right option for src block edit window placement
 Given the increasing popularity of wide screen monitors, splitting
 horizontally may make more sense than splitting vertically.  An
@@ -2165,7 +2173,7 @@ without changing the headline.
 
 *** Hierarchies of tags
 
-The functionality of nesting tags in hierarchies is added to org-mode.
+The functionality of nesting tags in hierarchies is added to Org mode.
 This is the generalization of what was previously called "Tag groups"
 in the manual.  That term is now changed to "Tag hierarchy".
 
diff --git a/lisp/org-attach.el b/lisp/org-attach.el
index 2138a2807..b2f948c2e 100644
--- a/lisp/org-attach.el
+++ b/lisp/org-attach.el
@@ -244,7 +244,7 @@ Shows a list of commands and prompts for another key to execute a command."
       (when marker
 	(set-buffer (marker-buffer marker))
 	(goto-char marker))
-      (org-back-to-heading t)
+      (org-back-to-heading-or-point-min t)
       (save-excursion
 	(save-window-excursion
 	  (unless org-attach-expert
diff --git a/lisp/org-capture.el b/lisp/org-capture.el
index 4f97e17ea..67343e02a 100644
--- a/lisp/org-capture.el
+++ b/lisp/org-capture.el
@@ -1736,11 +1736,11 @@ The template may still contain \"%?\" for cursor positioning."
 			 (_ (error "Invalid `org-capture--clipboards' value: %S"
 				   org-capture--clipboards)))))
 		    ("p"
-		     ;; We remove file properties inherited from
+		     ;; We remove keyword properties inherited from
 		     ;; target buffer so `org-read-property-value' has
 		     ;; a chance to find allowed values in sub-trees
 		     ;; from the target buffer.
-		     (setq-local org-file-properties nil)
+		     (setq-local org-keyword-properties nil)
 		     (let* ((origin (set-marker (make-marker)
 						(org-capture-get :pos)
 						(org-capture-get :buffer)))
diff --git a/lisp/org-compat.el b/lisp/org-compat.el
index 4446a169d..c167ba908 100644
--- a/lisp/org-compat.el
+++ b/lisp/org-compat.el
@@ -556,6 +556,12 @@ use of this function is for the stuck project list."
 (define-obsolete-function-alias 'org-make-link-regexps
   'org-link-make-regexps "Org 9.3")
 
+(define-obsolete-function-alias 'org-file-properties
+  'org-keyword-properties "Org 9.3")
+
+(define-obsolete-function-alias 'org-property-global-value
+  'org-property-global-or-keyword-value "Org 9.3")
+
 (define-obsolete-variable-alias 'org-angle-link-re
   'org-link-angle-re "Org 9.3")
 
diff --git a/lisp/org-element.el b/lisp/org-element.el
index 56b3cc413..110ff5624 100644
--- a/lisp/org-element.el
+++ b/lisp/org-element.el
@@ -3910,7 +3910,10 @@ element it has to parse."
 	     ;; LaTeX Environment.
 	     ((looking-at org-element--latex-begin-environment)
 	      (org-element-latex-environment-parser limit affiliated))
-	     ;; Drawer and Property Drawer.
+	     ;; Property drawer (before first headline, else it's catched above).
+	     ((org-at-property-block-p)
+	      (org-element-property-drawer-parser limit))
+	     ;; Drawer.
 	     ((looking-at org-drawer-regexp)
 	      (org-element-drawer-parser limit affiliated))
 	     ;; Fixed Width
diff --git a/lisp/org.el b/lisp/org.el
index cb83bef3b..cbc476644 100644
--- a/lisp/org.el
+++ b/lisp/org.el
@@ -350,6 +350,9 @@ FULL is given."
 
 \f
 ;;; Syntax Constants
+;;;; Keyword
+(defconst org-keyword-regexp "^[ \t]*#\\+\\(\\S-+?\\):[ \t]*\\(.*\\)$"
+  "Regular expression for keyword-lines")
 
 ;;;; Block
 
@@ -3182,8 +3185,13 @@ This list will be combined with the constant `org-global-properties-fixed'.
 The entries in this list are cons cells where the car is a property
 name and cdr is a string with the value.
 
-You can set buffer-local values for the same purpose in the variable
-`org-file-properties' this by adding lines like
+Buffer local properties are added either by a document property drawer
+
+:PROPERTIES:
+:NAME: VALUE
+:END:
+
+or by adding lines like
 
 #+PROPERTY: NAME VALUE"
   :group 'org-properties
@@ -3191,10 +3199,14 @@ You can set buffer-local values for the same purpose in the variable
 	  (cons (string :tag "Property")
 		(string :tag "Value"))))
 
-(defvar-local org-file-properties nil
+(defvar-local org-keyword-properties nil
   "List of property/value pairs that can be inherited by any entry.
-Valid for the current buffer.
-This variable is populated from #+PROPERTY lines.")
+Valid for the current buffer.  This variable is populated from
+#+PROPERTY lines.
+
+Note that properties are defined also in property drawers.
+Properties defined there will take precedence over properties
+defined as keywords.")
 
 (defgroup org-agenda nil
   "Options concerning agenda views in Org mode."
@@ -3203,11 +3215,18 @@ This variable is populated from #+PROPERTY lines.")
 
 (defvar-local org-category nil
   "Variable used by Org files to set a category for agenda display.
-Such files should use a file variable to set it, for example
+There are multiple ways to set the category.  One way is to set
+it in the document property drawer.  For example:
+
+:PROPERTIES:
+:CATEGORY: ELisp
+:END:
+
+Other ways to define it is as an emacs file variable, for example
 
 #   -*- mode: org; org-category: \"ELisp\"
 
-or contain a special line
+or for the file to contain a special line:
 
 #+CATEGORY: ELisp
 
@@ -4340,8 +4359,8 @@ related expressions."
       (setq org-tag-groups-alist
 	    (org-tag-alist-to-groups org-current-tag-alist))
       (unless tags-only
-	;; File properties.
-	(setq-local org-file-properties (cdr (assq 'property alist)))
+	;; Properties.
+	(setq-local org-keyword-properties (cdr (assq 'property alist)))
 	;; Archive location.
 	(let ((archive (cdr (assq 'archive alist))))
 	  (when archive (setq-local org-archive-location archive)))
@@ -4349,9 +4368,9 @@ related expressions."
 	(let ((cat (org-string-nw-p (cdr (assq 'category alist)))))
 	  (when cat
 	    (setq-local org-category (intern cat))
-	    (setq-local org-file-properties
+	    (setq-local org-keyword-properties
 			(org--update-property-plist
-			 "CATEGORY" cat org-file-properties))))
+			 "CATEGORY" cat org-keyword-properties))))
 	;; Columns.
 	(let ((column (cdr (assq 'columns alist))))
 	  (when column (setq-local org-columns-default-format column)))
@@ -8269,13 +8288,14 @@ the value of the drawer property."
 	 (inhibit-read-only t)
 	 (inherit? (org-property-inherit-p dprop))
 	 (property-re (org-re-property (concat (regexp-quote dprop) "\\+?") t))
-	 (global (and inherit? (org--property-global-value dprop nil))))
+	 (global-or-keyword (and inherit?
+				 (org--property-global-or-keyword-value dprop nil))))
     (with-silent-modifications
       (org-with-point-at 1
-	;; Set global values (e.g., values defined through
-	;; "#+PROPERTY:" keywords) to the whole buffer.
-	(when global (put-text-property (point-min) (point-max) tprop global))
-	;; Set local values.
+	;; Set global and keyword based values to the whole buffer.
+	(when global-or-keyword
+	  (put-text-property (point-min) (point-max) tprop global-or-keyword))
+	;; Set values based on property-drawers throughout the document.
 	(while (re-search-forward property-re nil t)
 	  (when (org-at-property-p)
 	    (org-refresh-property tprop (org-entry-get (point) dprop) inherit?))
@@ -8283,21 +8303,29 @@ the value of the drawer property."
 
 (defun org-refresh-property (tprop p &optional inherit)
   "Refresh the buffer text property TPROP from the drawer property P.
-The refresh happens only for the current headline, or the whole
-sub-tree if optional argument INHERIT is non-nil."
-  (unless (org-before-first-heading-p)
-    (save-excursion
-      (org-back-to-heading t)
-      (let ((start (point))
-	    (end (save-excursion
-		   (if inherit (org-end-of-subtree t t)
-		     (or (outline-next-heading) (point-max))))))
-	(if (symbolp tprop)
-	    ;; TPROP is a text property symbol.
-	    (put-text-property start end tprop p)
-	  ;; TPROP is an alist with (property . function) elements.
-	  (pcase-dolist (`(,prop . ,f) tprop)
-	    (put-text-property start end prop (funcall f p))))))))
+The refresh happens only for the current entry, or the whole
+sub-tree if optional argument INHERIT is non-nil.
+
+If point is before first headline, the function applies to the
+part before the first headline.  In that particular case, when
+optional argument INHERIT is non-nil, it refreshes properties for
+the whole buffer."
+  (save-excursion
+    (org-back-to-heading-or-point-min t)
+    (let ((start (point))
+	  (end (save-excursion
+		 (cond ((and inherit (org-before-first-heading-p))
+			(point-max))
+		       (inherit
+			(org-end-of-subtree t t))
+		       ((outline-next-heading))
+		       ((point-max))))))
+      (if (symbolp tprop)
+	  ;; TPROP is a text property symbol.
+	  (put-text-property start end tprop p)
+	;; TPROP is an alist with (property . function) elements.
+	(pcase-dolist (`(,prop . ,f) tprop)
+	  (put-text-property start end prop (funcall f p)))))))
 
 (defun org-refresh-category-properties ()
   "Refresh category text properties in the buffer."
@@ -8313,9 +8341,9 @@ sub-tree if optional argument INHERIT is non-nil."
 		(t org-category))))
     (with-silent-modifications
       (org-with-wide-buffer
-       ;; Set buffer-wide category.  Search last #+CATEGORY keyword.
-       ;; This is the default category for the buffer.  If none is
-       ;; found, fall-back to `org-category' or buffer file name.
+       ;; Set buffer-wide property from keyword.  Search last #+CATEGORY
+       ;; keyword.  If none is found, fall-back to `org-category' or
+       ;; buffer file name, or set it by the document property drawer.
        (put-text-property
 	(point-min) (point-max)
 	'org-category
@@ -8327,15 +8355,20 @@ sub-tree if optional argument INHERIT is non-nil."
 		(throw 'buffer-category
 		       (org-element-property :value element)))))
 	  default-category))
-       ;; Set sub-tree specific categories.
+       ;; Set categories from the document property drawer or
+       ;; property drawers in the outline.  If category is found in
+       ;; the property drawer for the whole buffer that value
+       ;; overrides the keyword-based value set above.
        (goto-char (point-min))
        (let ((regexp (org-re-property "CATEGORY")))
 	 (while (re-search-forward regexp nil t)
 	   (let ((value (match-string-no-properties 3)))
 	     (when (org-at-property-p)
 	       (put-text-property
-		(save-excursion (org-back-to-heading t) (point))
-		(save-excursion (org-end-of-subtree t t) (point))
+		(save-excursion (org-back-to-heading-or-point-min t))
+		(save-excursion (if (org-before-first-heading-p)
+				    (point-max)
+				  (org-end-of-subtree t t)))
 		'org-category
 		value)))))))))
 
@@ -12921,30 +12954,45 @@ Modifications are made by side-effect.  Return new alist."
 
 (defun org-get-property-block (&optional beg force)
   "Return the (beg . end) range of the body of the property drawer.
-BEG is the beginning of the current subtree, or of the part
-before the first headline.  If it is not given, it will be found.
-If the drawer does not exist, create it if FORCE is non-nil, or
-return nil."
+BEG is the beginning of the current subtree or the beginning of
+the document if before the first headline.  If it is not given,
+it will be found.  If the drawer does not exist, create it if
+FORCE is non-nil, or return nil."
   (org-with-wide-buffer
-   (when beg (goto-char beg))
-   (unless (org-before-first-heading-p)
-     (let ((beg (cond (beg)
+   (let ((beg (cond (beg (goto-char beg))
 		      ((or (not (featurep 'org-inlinetask))
 			   (org-inlinetask-in-task-p))
-		       (org-back-to-heading t))
-		      (t (org-with-limited-levels (org-back-to-heading t))))))
-       (forward-line)
-       (when (looking-at-p org-planning-line-re) (forward-line))
-       (cond ((looking-at org-property-drawer-re)
-	      (forward-line)
-	      (cons (point) (progn (goto-char (match-end 0))
-				   (line-beginning-position))))
-	     (force
-	      (goto-char beg)
-	      (org-insert-property-drawer)
-	      (let ((pos (save-excursion (search-forward ":END:")
-					 (line-beginning-position))))
-		(cons pos pos))))))))
+		       (org-back-to-heading-or-point-min t) (point))
+		      (t (org-with-limited-levels
+			  (org-back-to-heading-or-point-min t))
+			 (point)))))
+     ;; Move point to its position according to its positional rules.
+     (cond ((org-before-first-heading-p)
+	    (while (and (bolp) (or (org-at-keyword-p) (org-at-comment-p))) (forward-line)))
+	   (t (forward-line)
+	      (when (looking-at-p org-planning-line-re) (forward-line))))
+     (cond ((looking-at org-property-drawer-re)
+	    (forward-line)
+	    (cons (point) (progn (goto-char (match-end 0))
+				 (line-beginning-position))))
+	   (force
+	    (goto-char beg)
+	    (org-insert-property-drawer)
+	    (let ((pos (save-excursion (re-search-forward org-property-drawer-re)
+				       (line-beginning-position))))
+	      (cons pos pos)))))))
+
+(defun org-at-property-block-p ()
+  "Return t when point is at the first line of a property drawer.
+The property drawer is validated according to its positional
+rules using `org-get-property-block'."
+  (save-excursion
+    (beginning-of-line)
+    (and (looking-at org-property-start-re)
+	 (forward-line)
+	 (let ((property-drawer (org-get-property-block)))
+	   (and property-drawer
+		(= (point) (car property-drawer)))))))
 
 (defun org-at-property-p ()
   "Non-nil when point is inside a property drawer.
@@ -13029,7 +13077,7 @@ Return value is an alist.  Keys are properties, as upcased
 strings."
   (org-with-point-at pom
     (when (and (derived-mode-p 'org-mode)
-	       (ignore-errors (org-back-to-heading t)))
+	       (org-back-to-heading-or-point-min t))
       (catch 'exit
 	(let* ((beg (point))
 	       (specific (and (stringp which) (upcase which)))
@@ -13238,13 +13286,13 @@ unless LITERAL-NIL is non-nil."
 	;; Return final values.
 	(and (not (equal value '(nil))) (nreverse value))))))
 
-(defun org--property-global-value (property literal-nil)
-  "Return value for PROPERTY in current buffer.
+(defun org--property-global-or-keyword-value (property literal-nil)
+  "Return value for PROPERTY as defined by global properties or by keyword.
 Return value is a string.  Return nil if property is not set
-globally.  Also return nil when PROPERTY is set to \"nil\",
-unless LITERAL-NIL is non-nil."
+globally or by keyword.  Also return nil when PROPERTY is set to
+\"nil\", unless LITERAL-NIL is non-nil."
   (let ((global
-	 (cdr (or (assoc-string property org-file-properties t)
+	 (cdr (or (assoc-string property org-keyword-properties t)
 		  (assoc-string property org-global-properties t)
 		  (assoc-string property org-global-properties-fixed t)))))
     (if literal-nil global (org-not-nil global))))
@@ -13393,12 +13441,12 @@ However, if LITERAL-NIL is set, return the string value \"nil\" instead."
 			   value)))
 	   (cond
 	    ((car v)
-	     (org-back-to-heading t)
+	     (org-back-to-heading-or-point-min t)
 	     (move-marker org-entry-property-inherited-from (point))
 	     (throw 'exit nil))
-	    ((org-up-heading-safe))
+	    ((org-up-heading-or-point-min))
 	    (t
-	     (let ((global (org--property-global-value property literal-nil)))
+	     (let ((global (org--property-global-or-keyword-value property literal-nil)))
 	       (cond ((not global))
 		     (value (setq value (concat global " " value)))
 		     (t (setq value global))))
@@ -13430,8 +13478,8 @@ decreases scheduled or deadline date by one day."
 	 (user-error "Invalid property name: \"%s\"" property)))
   (org-with-point-at pom
     (if (or (not (featurep 'org-inlinetask)) (org-inlinetask-in-task-p))
-	(org-back-to-heading t)
-      (org-with-limited-levels (org-back-to-heading t)))
+	(org-back-to-heading-or-point-min t)
+      (org-with-limited-levels (org-back-to-heading-or-point-min t)))
     (let ((beg (point)))
       (cond
        ((equal property "TODO")
@@ -13567,19 +13615,26 @@ COLUMN formats in the current buffer."
 Do nothing if the drawer already exists.  The newly created
 drawer is immediately hidden."
   (org-with-wide-buffer
+   ;; Set point to the position where the drawer should be inserted.
    (if (or (not (featurep 'org-inlinetask)) (org-inlinetask-in-task-p))
-       (org-back-to-heading t)
-     (org-with-limited-levels (org-back-to-heading t)))
-   (forward-line)
-   (when (looking-at-p org-planning-line-re) (forward-line))
+       (org-back-to-heading-or-point-min t)
+     (org-with-limited-levels (org-back-to-heading-or-point-min t)))
+   (if (org-before-first-heading-p)
+       (while (and (bolp) (or (org-at-keyword-p) (org-at-comment-p))) (forward-line))
+     (progn
+       (forward-line)
+       (when (looking-at-p org-planning-line-re) (forward-line))))
    (unless (looking-at-p org-property-drawer-re)
      ;; Make sure we start editing a line from current entry, not from
      ;; next one.  It prevents extending text properties or overlays
      ;; belonging to the latter.
-     (when (bolp) (backward-char))
-     (let ((begin (1+ (point)))
+     (when (and (bolp) (> (point) (point-min))) (backward-char))
+     (let ((begin (if (= (point) (point-min))
+		      (point)
+		    (1+ (point))))
 	   (inhibit-read-only t))
-       (insert "\n:PROPERTIES:\n:END:")
+       (unless (= begin (point-min)) (insert "\n"))
+       (insert ":PROPERTIES:\n:END:")
        (org-flag-drawer t nil (line-end-position 0) (point))
        (when (eobp) (insert "\n"))
        (org-indent-region begin (point))))))
@@ -20484,6 +20539,15 @@ interactive command with similar behavior."
     (error (error "Before first headline at position %d in buffer %s"
 		  (point) (current-buffer)))))
 
+(defun org-back-to-heading-or-point-min (&optional invisible-ok)
+  "Go back to heading or first point in buffer.
+If point is before first heading go to first point in buffer
+instead of back to heading."
+  (condition-case nil
+      (outline-back-to-heading invisible-ok)
+    (error
+     (goto-char (point-min)))))
+
 (defun org-before-first-heading-p ()
   "Before first heading?"
   (org-with-limited-levels
@@ -20517,6 +20581,12 @@ unless optional argument NO-INHERITANCE is non-nil."
       (beginning-of-line)
       (looking-at "^[ \t]*# "))))
 
+(defun org-at-keyword-p nil
+  "Return t if cursor is at a keyword-line."
+  (save-excursion
+    (move-beginning-of-line 1)
+    (looking-at org-keyword-regexp)))
+
 (defun org-at-drawer-p nil
   "Return t if cursor is at a drawer keyword."
   (save-excursion
@@ -20564,6 +20634,17 @@ make a significant difference in outlines with very many siblings."
 	   (re-search-backward (format "^\\*\\{1,%d\\} " level-up) nil t)
 	   (funcall outline-level)))))
 
+(defun org-up-heading-or-point-min ()
+  "Move to the heading line of which the present is a subheading, or point-min.
+This version is needed to make point-min behave like a virtual
+heading of level 0 for property-inheritance.  It will return the
+level of the headline found (down to 0) or nil if already at a
+point before the first headline or at point-min."
+  (when (ignore-errors (org-back-to-heading t))
+    (if (< 1 (funcall outline-level))
+	(org-up-heading-safe)
+      (unless (= (point) (point-min)) (goto-char (point-min))))))
+
 (defun org-first-sibling-p ()
   "Is this heading the first child of its parents?"
   (interactive)
@@ -20664,28 +20745,31 @@ If there is no such heading, return nil."
 (defun org-end-of-subtree (&optional invisible-ok to-heading)
   "Goto to the end of a subtree."
   ;; This contains an exact copy of the original function, but it uses
-  ;; `org-back-to-heading', to make it work also in invisible
-  ;; trees.  And is uses an invisible-ok argument.
+  ;; `org-back-to-heading-or-point-min', to make it work also in invisible
+  ;; trees and before first headline.  And is uses an invisible-ok argument.
   ;; Under Emacs this is not needed, but the old outline.el needs this fix.
   ;; Furthermore, when used inside Org, finding the end of a large subtree
   ;; with many children and grandchildren etc, this can be much faster
   ;; than the outline version.
-  (org-back-to-heading invisible-ok)
+  (org-back-to-heading-or-point-min invisible-ok)
   (let ((first t)
 	(level (funcall outline-level)))
-    (if (and (derived-mode-p 'org-mode) (< level 1000))
-	;; A true heading (not a plain list item), in Org
-	;; This means we can easily find the end by looking
-	;; only for the right number of stars.  Using a regexp to do
-	;; this is so much faster than using a Lisp loop.
-	(let ((re (concat "^\\*\\{1," (int-to-string level) "\\} ")))
-	  (forward-char 1)
-	  (and (re-search-forward re nil 'move) (beginning-of-line 1)))
-      ;; something else, do it the slow way
-      (while (and (not (eobp))
-		  (or first (> (funcall outline-level) level)))
-	(setq first nil)
-	(outline-next-heading)))
+    (cond ((= level 0)
+	   (goto-char (point-max)))
+	  ((and (derived-mode-p 'org-mode) (< level 1000))
+	   ;; A true heading (not a plain list item), in Org
+	   ;; This means we can easily find the end by looking
+	   ;; only for the right number of stars.  Using a regexp to do
+	   ;; this is so much faster than using a Lisp loop.
+	   (let ((re (concat "^\\*\\{1," (int-to-string level) "\\} ")))
+	     (forward-char 1)
+	     (and (re-search-forward re nil 'move) (beginning-of-line 1))))
+	  (t
+	   ;; something else, do it the slow way
+	   (while (and (not (eobp))
+		       (or first (> (funcall outline-level) level)))
+	     (setq first nil)
+	     (outline-next-heading))))
     (unless to-heading
       (when (memq (preceding-char) '(?\n ?\^M))
 	;; Go to end of line before heading
diff --git a/testing/examples/property-inheritance.org b/testing/examples/property-inheritance.org
index 6979de595..9c0b7e662 100644
--- a/testing/examples/property-inheritance.org
+++ b/testing/examples/property-inheritance.org
@@ -1,5 +1,7 @@
-#+property: header-args :var  foo=1
-#+property: header-args+ :var bar=2
+:PROPERTIES:
+:header-args: :var foo=1
+:header-args+: :var bar=2
+:END:
 
 #+begin_src emacs-lisp
   (+ foo bar)
diff --git a/testing/lisp/test-org-element.el b/testing/lisp/test-org-element.el
index f2ab38031..1408e6be5 100644
--- a/testing/lisp/test-org-element.el
+++ b/testing/lisp/test-org-element.el
@@ -1925,6 +1925,15 @@ e^{i\\pi}+1=0
 	    (let ((element (org-element-at-point)))
 	      (list (org-element-property :key element)
 		    (org-element-property :value element))))))
+  ;; The insides of property blocks on document level are parsed the
+  ;; same way as headline property blocks.  I.e. the concept of
+  ;; `node-property' apply also for properties in those blocks.
+  (should
+   (equal '("abc" "value")
+	  (org-test-with-temp-text ":PROPERTIES:\n<point>:abc: value\n:END:"
+	    (let ((element (org-element-at-point)))
+	      (list (org-element-property :key element)
+		    (org-element-property :value element))))))
   ;; Value should be trimmed.
   (should
    (equal "value"
@@ -2111,6 +2120,18 @@ Outside list"
        (org-test-with-temp-text
 	   "* H\nDEADLINE: <2014-03-04 tue.>\n<point>:PROPERTIES:\n:prop: value\n:END:"
 	 (org-element-type (org-element-at-point)))))
+  (should
+   (eq 'property-drawer
+       (org-test-with-temp-text "<point>:PROPERTIES:\n:prop: value\n:END:"
+	 (org-element-type (org-element-at-point)))))
+  (should
+   (eq 'property-drawer
+       (org-test-with-temp-text "# C\n#+Key: V\n# C\n<point>:PROPERTIES:\n:prop: value\n:END:"
+	 (org-element-type (org-element-at-point)))))
+  (should-not
+   (eq 'property-drawer
+       (org-test-with-temp-text "\n<point>:PROPERTIES:\n:prop: value\n:END:"
+	 (org-element-type (org-element-at-point)))))
   ;; Allow properties without value and no property at all.
   (should
    (eq 'property-drawer
diff --git a/testing/lisp/test-org.el b/testing/lisp/test-org.el
index 215d8fdb0..1b3fb48bb 100644
--- a/testing/lisp/test-org.el
+++ b/testing/lisp/test-org.el
@@ -386,10 +386,116 @@
 \f
 ;;; Drawers
 
+(ert-deftest test-org/at-property-p ()
+  "Test `org-at-property-p' specifications."
+  (should
+   (equal 't
+	  (org-test-with-temp-text "* H\n:PROPERTIES:\n<point>:PROP: t\n:END:\n"
+	    (org-at-property-p))))
+  (should
+   (equal 't
+	  (org-test-with-temp-text ":PROPERTIES:\n<point>:PROP: t\n:END:\n"
+	    (org-at-property-p)))))
+
+(ert-deftest test-org/at-property-block-p ()
+  "Test `org-at-property-block-p' specifications."
+  (should
+   (equal 't
+	  (org-test-with-temp-text "* H\n<point>:PROPERTIES:\n:PROP: t\n:END:\n"
+	    (org-at-property-block-p))))
+  (should
+   (equal 't
+	  (org-test-with-temp-text ":PROPERTIES:\n:PROP: t\n:END:\n"
+	    (org-at-property-block-p))))
+  ;; The function only returns t if point is at the first line of a
+  ;; property block.
+  (should-not
+   (equal 't
+	  (org-test-with-temp-text ":PROPERTIES:\n<point>:PROP: t\n:END:\n"
+	    (org-at-property-block-p)))))
+
+(ert-deftest test-org/get-property-block ()
+  "Test `org-get-property-block' specifications."
+  (should
+   (equal '(14 . 14)
+	  (org-test-with-temp-text ":PROPERTIES:\n:END:\n* H\n"
+	    (org-get-property-block))))
+  (should
+   (equal '(14 . 14)
+	  (org-test-with-temp-text ":PROPERTIES:\n:END:\n"
+	    (org-get-property-block))))
+  ;; Comments above a document property block is ok.
+  (should
+   (equal '(18 . 18)
+	  (org-test-with-temp-text "# C\n:PROPERTIES:\n:END:\n"
+	    (org-get-property-block))))
+  ;; Keywords above a document property block is ok.
+  (should
+   (equal '(23 . 23)
+	  (org-test-with-temp-text "#+Key: v\n:PROPERTIES:\n:END:\n"
+	    (org-get-property-block))))
+  ;; Comments and keywords are allowed before a document property block.
+  (should
+   (equal '(27 . 36)
+	  (org-test-with-temp-text "# C\n#+key: v\n:PROPERTIES:\n:KEY: V:\n:END:\n"
+	    (org-get-property-block))))
+  (should
+   (equal '(36 . 45)
+	  (org-test-with-temp-text "#+Key: v\n# C\n#+Key: v\n:PROPERTIES:\n:KEY: V:\n:END:\n"
+	    (org-get-property-block))))
+  ;; A document property block will not be valid if there are lines
+  ;; with whitespace above it
+  (should-not
+   (org-test-with-temp-text "\n:PROPERTIES:\n:END:\n"
+     (org-get-property-block)))
+  (should
+   (equal '(18 . 18)
+	  (org-test-with-temp-text "* H\n:PROPERTIES:\n:END:\n<point>"
+	    (org-get-property-block))))
+  (should
+   (equal "* H\n:PROPERTIES:\n:END:\n"
+	  (org-test-with-temp-text "* H"
+	    (let ((org-adapt-indentation nil))
+	      (org-get-property-block nil 'force))
+	    (buffer-string))))
+  (should
+   (equal ":PROPERTIES:\n:END:\n"
+	  (org-test-with-temp-text ""
+	    (org-get-property-block nil 'force)
+	    (buffer-string))))
+  (should
+   (equal "* H1\n  :PROPERTIES:\n  :END:\n* H2"
+	  (org-test-with-temp-text "* H1\n* H2"
+	    (let ((org-adapt-indentation t))
+	      (org-get-property-block nil 'force))
+	    (buffer-string)))))
+
 (ert-deftest test-org/insert-property-drawer ()
   "Test `org-insert-property-drawer' specifications."
-  ;; Error before first headline.
-  (should-error (org-test-with-temp-text "" (org-insert-property-drawer)))
+  ;; Insert drawer in empty buffer
+  (should
+   (equal ":PROPERTIES:\n:END:\n"
+	  (org-test-with-temp-text ""
+	    (let ((org-adapt-indentation nil)) (org-insert-property-drawer))
+	    (buffer-string))))
+  ;; Insert drawer in document header with existing comment and
+  ;; keyword.
+  (should
+   (equal "# C\n#+TITLE: T\n:PROPERTIES:\n:END:\n"
+	  (org-test-with-temp-text "# C\n#+TITLE: T"
+	    (let ((org-adapt-indentation nil)) (org-insert-property-drawer))
+	    (buffer-string))))
+  (should
+   (equal ":PROPERTIES:\n:END:"
+	  (org-test-with-temp-text ":PROPERTIES:\n:END:"
+	    (let ((org-adapt-indentation nil)) (org-insert-property-drawer))
+	    (buffer-string))))
+  ;; Insert drawer in document header with one existing heading in buffer.
+  (should
+   (equal ":PROPERTIES:\n:END:\n* T\n"
+	  (org-test-with-temp-text "<point>\n* T\n"
+	    (let ((org-adapt-indentation nil)) (org-insert-property-drawer))
+	    (buffer-string))))
   ;; Insert drawer right after headline if there is no planning line,
   ;; or after it otherwise.
   (should
@@ -2178,19 +2284,19 @@ SCHEDULED: <2014-03-04 tue.>"
    (equal "foo=1"
 	  (org-test-with-temp-text "#+PROPERTY: var foo=1"
 	    (org-mode-restart)
-	    (cdr (assoc "var" org-file-properties)))))
+	    (cdr (assoc "var" org-keyword-properties)))))
   (should
    (equal
     "foo=1 bar=2"
     (org-test-with-temp-text "#+PROPERTY: var foo=1\n#+PROPERTY: var+ bar=2"
       (org-mode-restart)
-      (cdr (assoc "var" org-file-properties)))))
+      (cdr (assoc "var" org-keyword-properties)))))
   (should
    (equal
     "foo=1 bar=2"
     (org-test-with-temp-text "#+PROPERTY: var foo=1\n#+PROPERTY: VAR+ bar=2"
       (org-mode-restart)
-      (cdr (assoc "var" org-file-properties)))))
+      (cdr (assoc "var" org-keyword-properties)))))
   ;; ARCHIVE keyword.
   (should
    (equal "%s_done::"
@@ -2207,7 +2313,7 @@ SCHEDULED: <2014-03-04 tue.>"
    (equal "test"
 	  (org-test-with-temp-text "#+CATEGORY: test"
 	    (org-mode-restart)
-	    (cdr (assoc "CATEGORY" org-file-properties)))))
+	    (cdr (assoc "CATEGORY" org-keyword-properties)))))
   ;; COLUMNS keyword.
   (should
    (equal "%25ITEM %TAGS %PRIORITY %TODO"
@@ -2291,7 +2397,7 @@ SCHEDULED: <2014-03-04 tue.>"
 	  (org-test-with-temp-text
 	      (format "#+SETUPFILE: \"%s/examples/setupfile.org\"" org-test-dir)
 	    (org-mode-restart)
-	    (cdr (assoc "a" org-file-properties))))))
+	    (cdr (assoc "a" org-keyword-properties))))))
 
 
 \f
@@ -4941,6 +5047,66 @@ Paragraph<point>"
 	  (org-test-with-temp-text "* H\n:PROPERTIES:\n:A: 1\n:A+: 2\n:END:"
 	    (org-property-values "A")))))
 
+(ert-deftest test-org/set-property ()
+  "Test `org-set-property' specifications."
+  (should
+   (equal
+    ":PROPERTIES:\n:TEST: t\n:END:\n"
+    (org-test-with-temp-text ""
+      (let ((org-property-format "%s %s"))
+	(org-set-property "TEST" "t"))
+      (buffer-string))))
+  (should
+   (equal
+    "* H\n:PROPERTIES:\n:TEST: t\n:END:\n"
+    (org-test-with-temp-text "* H"
+      (let ((org-adapt-indentation nil)
+	    (org-property-format "%s %s"))
+	(org-set-property "TEST" "t"))
+      (buffer-string)))))
+
+(ert-deftest test-org/delete-property ()
+  "Test `org-delete-property' specifications."
+  (should
+   (equal
+    ""
+    (org-test-with-temp-text ":PROPERTIES:\n:TEST: t\n:END:\n"
+      (org-delete-property "TEST")
+      (buffer-string))))
+  (should
+   (equal
+    ":PROPERTIES:\n:TEST1: t\n:END:\n"
+    (org-test-with-temp-text ":PROPERTIES:\n:TEST1: t\n:TEST2: t\n:END:\n"
+      (org-delete-property "TEST2")
+      (buffer-string))))
+  (should
+   (equal
+    "* H\n"
+    (org-test-with-temp-text "* H\n:PROPERTIES:\n:TEST: t\n:END:\n"
+      (org-delete-property "TEST")
+      (buffer-string))))
+  (should
+   (equal
+    "* H\n:PROPERTIES:\n:TEST1: t\n:END:\n"
+    (org-test-with-temp-text "* H\n:PROPERTIES:\n:TEST1: t\n:TEST2: t\n:END:\n"
+      (org-delete-property "TEST2")
+      (buffer-string)))))
+
+(ert-deftest test-org/delete-property-globally ()
+  "Test `org-delete-property-global' specifications."
+  (should
+   (equal
+    ""
+    (org-test-with-temp-text ":PROPERTIES:\n:TEST: t\n:END:\n"
+      (org-delete-property-globally "TEST")
+      (buffer-string))))
+  (should
+   (equal
+    "* H\n"
+    (org-test-with-temp-text ":PROPERTIES:\n:TEST: t\n:END:\n* H\n:PROPERTIES:\n:TEST: nil\n:END:"
+      (org-delete-property-globally "TEST")
+      (buffer-string)))))
+
 (ert-deftest test-org/find-property ()
   "Test `org-find-property' specifications."
   ;; Regular test.
@@ -5022,6 +5188,10 @@ Paragraph<point>"
 (ert-deftest test-org/entry-get ()
   "Test `org-entry-get' specifications."
   ;; Regular test.
+  (should
+   (equal "1"
+	  (org-test-with-temp-text ":PROPERTIES:\n:A: 1\n:END:"
+	    (org-entry-get (point) "A"))))
   (should
    (equal "1"
 	  (org-test-with-temp-text "* H\n:PROPERTIES:\n:A: 1\n:END:"
@@ -5061,6 +5231,11 @@ Paragraph<point>"
      (org-entry-get (point) "B" nil t)))
   ;; Handle inheritance, when allowed.  Include extended values and
   ;; possibly global values.
+  (should
+   (equal
+    "1"
+    (org-test-with-temp-text ":PROPERTIES:\n:A: 1\n:END:\n* H"
+      (org-entry-get (point-max) "A" t))))
   (should
    (equal
     "1"
@@ -5076,12 +5251,30 @@ Paragraph<point>"
    (org-test-with-temp-text "* H\n:PROPERTIES:\n:A: 1\n:END:\n** H2"
      (let ((org-use-property-inheritance nil))
        (org-entry-get (point-max) "A" 'selective))))
+  (should
+   (equal
+    "1 2"
+    (org-test-with-temp-text
+	":PROPERTIES:\n:A: 1\n:END:\n* H\n:PROPERTIES:\n:A+: 2\n:END:"
+      (org-entry-get (point-max) "A" t))))
   (should
    (equal
     "1 2"
     (org-test-with-temp-text
 	"* H\n:PROPERTIES:\n:A: 1\n:END:\n** H2\n:PROPERTIES:\n:A+: 2\n:END:"
       (org-entry-get (point-max) "A" t))))
+  (should
+   (equal
+    "1 2"
+    (org-test-with-temp-text
+	":PROPERTIES:\n:A: 1\n:END:\n* H1\n* H2\n:PROPERTIES:\n:A+: 2\n:END:"
+      (org-entry-get (point-max) "A" t))))
+  (should
+   (equal
+    "1 2"
+    (org-test-with-temp-text
+	"* H1\n:PROPERTIES:\n:A: 1\n:END:\n* H2.1\n* H2.2\n:PROPERTIES:\n:A+: 2\n:END:"
+      (org-entry-get (point-max) "A" t))))
   (should
    (equal "1"
 	  (org-test-with-temp-text
@@ -5093,6 +5286,14 @@ Paragraph<point>"
 	  (org-test-with-temp-text
 	      "#+PROPERTY: A 0\n* H\n:PROPERTIES:\n:A+: 1\n:END:"
 	    (org-mode-restart)
+	    (org-entry-get (point-max) "A" t))))
+  ;; document level property-drawer has precedance over
+  ;; global-property by PROPERTY-keyword.
+  (should
+   (equal "0 2"
+	  (org-test-with-temp-text
+	      ":PROPERTIES:\n:A: 0\n:END:\n#+PROPERTY: A 1\n* H\n:PROPERTIES:\n:A+: 2\n:END:"
+	    (org-mode-restart)
 	    (org-entry-get (point-max) "A" t)))))
 
 (ert-deftest test-org/entry-properties ()
@@ -5416,8 +5617,44 @@ Paragraph<point>"
 	    (let ((org-use-property-inheritance t))
 	      (org-refresh-properties "A" 'org-test))
 	    (get-text-property (point) 'org-test))))
+  ;; When a document level property-drawer is used, those properties
+  ;; should work exactly like headline-properties as if at a
+  ;; headline-level 0.
+  (should
+   (equal "1"
+	  (org-test-with-temp-text
+	      ":PROPERTIES:\n:A: 1\n:END:\n"
+	    (org-mode-restart)
+	    (let ((org-use-property-inheritance t))
+	      (org-refresh-properties "A" 'org-test))
+	    (get-text-property (point) 'org-test))))
+  (should-not
+   (equal "1"
+	  (org-test-with-temp-text
+	      ":PROPERTIES:\n:A: 1\n:END:\n<point>* H1"
+	    (org-mode-restart)
+	    (let ((org-use-property-inheritance nil))
+	      (org-refresh-properties "A" 'org-test))
+	    (get-text-property (point) 'org-test))))
+  (should
+   (equal "1"
+	  (org-test-with-temp-text
+	      ":PROPERTIES:\n:A: 1\n:END:\n<point>* H1"
+	    (org-mode-restart)
+	    (let ((org-use-property-inheritance t))
+	      (org-refresh-properties "A" 'org-test))
+	    (get-text-property (point) 'org-test))))
+  (should
+   (equal "2"
+	  (org-test-with-temp-text
+	      ":PROPERTIES:\n:A: 1\n:END:\n<point>* H1\n:PROPERTIES:\n:A: 2\n:END:"
+	    (org-mode-restart)
+	    (let ((org-use-property-inheritance t))
+	      (org-refresh-properties "A" 'org-test))
+	    (get-text-property (point) 'org-test))))
   ;; When property is inherited, use global value across the whole
-  ;; buffer.  However local values have precedence.
+  ;; buffer.  However local values have precedence, as well as the
+  ;; document level property-drawer.
   (should-not
    (equal "1"
 	  (org-test-with-temp-text "#+PROPERTY: A 1\n<point>* H1"
@@ -5437,10 +5674,62 @@ Paragraph<point>"
 	  (org-test-with-temp-text
 	      "#+PROPERTY: A 1\n<point>* H\n:PROPERTIES:\n:A: 2\n:END:"
 	    (org-mode-restart)
+	    (let ((org-use-property-inheritance t))
+	      (org-refresh-properties "A" 'org-test))
+	    (get-text-property (point) 'org-test))))
+  ;; When both keyword-property and document-level property-block is
+  ;; defined, the property-block has precedance.
+  (should
+   (equal "1"
+	  (org-test-with-temp-text
+	      ":PROPERTIES:\n:A: 1\n:END:\n#+PROPERTY: A 2\n<point>* H1"
+	    (org-mode-restart)
 	    (let ((org-use-property-inheritance t))
 	      (org-refresh-properties "A" 'org-test))
 	    (get-text-property (point) 'org-test)))))
 
+(ert-deftest test-org/refresh-category-properties ()
+  "Test `org-refresh-category-properties' specifications"
+  (should
+   (equal "cat1"
+	  (org-test-with-temp-text
+	      ":PROPERTIES:\n:CATEGORY: cat1\n:END:"
+	    (org-refresh-category-properties)
+	    (get-text-property (point) 'org-category))))
+  (should
+   (equal "cat1"
+	  (org-test-with-temp-text
+	      "* H\n:PROPERTIES:\n:CATEGORY: cat1\n:END:"
+	    (org-refresh-category-properties)
+	    (get-text-property (point) 'org-category))))
+  ;; Even though property-inheritance is deactivated, category
+  ;; property should be inherited.  As described in
+  ;; `org-use-property-inheritance'.
+  (should
+   (equal "cat1"
+	  (org-test-with-temp-text
+	      ":PROPERTIES:\n:CATEGORY: cat1\n:END:\n<point>* H"
+	    (org-mode-restart)
+	    (let ((org-use-property-inheritance nil))
+	      (org-refresh-category-properties))
+	    (get-text-property (point) 'org-category))))
+  (should
+   (equal "cat1"
+	  (org-test-with-temp-text
+	      ":PROPERTIES:\n:CATEGORY: cat1\n:END:\n<point>* H"
+	    (org-mode-restart)
+	    (let ((org-use-property-inheritance t))
+	      (org-refresh-category-properties))
+	    (get-text-property (point) 'org-category))))
+  (should
+   (equal "cat2"
+	  (org-test-with-temp-text
+	      ":PROPERTIES:\n:CATEGORY: cat1\n:END:\n<point>* H\n:PROPERTIES:\n:CATEGORY: cat2\n:END:\n"
+	    (org-mode-restart)
+	    (let ((org-use-property-inheritance t))
+	      (org-refresh-category-properties))
+	    (get-text-property (point) 'org-category)))))
+
 \f
 ;;; Refile
 
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 35+ messages in thread

end of thread, other threads:[~2020-02-01 19:59 UTC | newest]

Thread overview: 35+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-10-02 20:29 [RFC] Document level property drawer Gustav Wikström
  -- strict thread matches above, loose matches on Subject: below --
2019-10-24 22:29 Gustav Wikström
2019-10-20  2:28 Gustav Wikström
2019-10-22 21:24 ` Marco Wahl
2019-10-23  8:43 ` Marco Wahl
2019-10-23  8:59   ` Gustav Wikström
2019-10-24 21:01   ` Gustav Wikström
2019-10-25 12:58     ` Marco Wahl
2019-10-23 16:08 ` Adam Porter
2019-10-06  6:02 Gustav Wikström
2019-10-06  5:35 Gustav Wikström
2019-10-05 18:20 Gustav Wikström
2019-10-06  0:51 ` Adam Porter
2019-09-30 22:09 Gustav Wikström
2019-10-03 18:31 ` Adam Porter
2019-10-04 10:38   ` Marco Wahl
2019-10-06  1:01     ` Adam Porter
2019-10-07  7:46       ` Marco Wahl
2019-09-29 10:27 Gustav Wikström
2019-09-29 19:13 ` Marco Wahl
2019-09-30 16:01 ` Adam Porter
2019-09-30 20:46   ` Marco Wahl
2019-10-01 12:38     ` Sebastian Miele
2020-01-13 21:52       ` Marco Wahl
2020-01-15  8:18         ` Sebastian Miele
2020-02-01 19:59           ` Marco Wahl
2019-10-01 13:55     ` Adam Porter
2019-10-02 10:29       ` Marco Wahl
2019-10-03 18:06         ` Adam Porter
2019-10-04 11:05           ` Marco Wahl
2019-10-06  1:05             ` Adam Porter
2019-10-06  5:10               ` Matt Price
2019-10-15 17:49 ` Gustav Wikström
2019-10-16  0:48   ` Adam Porter
2019-10-16  9:48   ` Marco Wahl

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs/org-mode.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).