* [dev] Implement "ref" link types @ 2012-02-19 18:08 Nicolas Goaziou 2012-02-19 19:28 ` Christian Moe ` (2 more replies) 0 siblings, 3 replies; 19+ messages in thread From: Nicolas Goaziou @ 2012-02-19 18:08 UTC (permalink / raw) To: Org Mode List [-- Attachment #1: Type: text/plain, Size: 1383 bytes --] Hello, I'd like to introduce a new type of internal links, namely "ref" links. Since any element can now have a "#+name: something" affiliated keyword, it would be practical to have a way to go straight to that name, from anywhere in the buffer. The following patch implements a [[ref:something]] syntax, or even [[ref:something][text]] to do so. The problem that I see here is that is breaks a bit syntax for internal links ("protocol:path" is usually for external links). Another solution would be to make them more hermetic: [[!something]]. On the export side, a "ref" link should be replaced by its description, if it has any, or by the sequence number of the matching element among elements of the same type. With the new exporter and the following buffer: #+begin_src org #+name: tab:letters #+caption: Letters | a | b | | c | d | #+name: tab:numbers #+caption: Numbers | 1 | | 2 | In tableau [[ref:tab:numbers]] we can see... Yet, in [[ref:tab:numbers][the same table]], ... #+end_src the last sentence, in ascii, becomes: In tableau 2 we can see... Yet, in the same table, ... Even if it's not a perfect replacement for "\ref{something}" commands, it should be sufficient for all major back-ends to provide an approximate functionality. And we gain interactivity in the Org buffer. What do you think? Regards, -- Nicolas Goaziou [-- Warning: decoded text below may be mangled, UTF-8 assumed --] [-- Attachment #2: patch for ref links --] [-- Type: text/x-patch, Size: 1263 bytes --] From f92e12a9482613d4b1d27090fac8d2667b094fdb Mon Sep 17 00:00:00 2001 From: Nicolas Goaziou <n.goaziou@gmail.com> Date: Sun, 19 Feb 2012 18:48:41 +0100 Subject: [PATCH] Implement "ref" internal link type in Org buffers * lisp/org.el (org-link-search): Handle "ref" internal link types. A [[ref:some-name]] link points to a "#+name: some-name" in the same buffer. --- lisp/org.el | 10 ++++++++++ 1 files changed, 10 insertions(+), 0 deletions(-) diff --git a/lisp/org.el b/lisp/org.el index a81f7fc..ea06863 100644 --- a/lisp/org.el +++ b/lisp/org.el @@ -9875,6 +9875,16 @@ visibility around point, thus ignoring ;; First check if there are any special search functions ((run-hook-with-args-until-success 'org-execute-file-search-functions s)) ;; Now try the builtin stuff + ;; Ref internal link. + ((and (string-match "^ref:\\(.*\\)" s0) + (let ((name (org-trim (match-string 1 s0)))) + (save-excursion + (goto-char (point-min)) + (and (re-search-forward + (format "^[ \t]*#\\+name: %s" name) nil t) + (setq type 'dedicated pos (match-beginning 0)))))) + (goto-char pos)) + ;; Custom-id ((and (equal (string-to-char s0) ?#) (> (length s0) 1) (save-excursion -- 1.7.9.1 ^ permalink raw reply related [flat|nested] 19+ messages in thread
* Re: [dev] Implement "ref" link types 2012-02-19 18:08 [dev] Implement "ref" link types Nicolas Goaziou @ 2012-02-19 19:28 ` Christian Moe 2012-02-19 19:41 ` Nicolas Goaziou 2012-02-19 19:41 ` Samuel Wales 2012-02-19 20:10 ` Carsten Dominik 2 siblings, 1 reply; 19+ messages in thread From: Christian Moe @ 2012-02-19 19:28 UTC (permalink / raw) To: Nicolas Goaziou; +Cc: Org Mode List Hi, I think it's a great idea, as I've wanted to do it myself :-) , but I'm glad it's in competent hands instead. Me, I don't see any problem with a [[ref:something]] syntax. It's the obvious org-native, cross-backend replacement for \ref. The [[protocol:something]] syntax already widens the notion of link to shell: and elisp: links, so I wouldn't worry about breaking conventions. Using e.g. [[!something]] instead would introduce a brand new bit of syntax. Suggestion: On export, how about enabling automatic element descriptions for references following the type:name convention, so that e.g. just : in [[ref:tab:numbers]] we can see... would expand to in Table 2 we can see... If implemented, this should be user-customizable e.g. through an alist like (("fig" . "Figure") ("tab" . "Table") ("map" . "Map")) Yours, Christian On 2/19/12 7:08 PM, Nicolas Goaziou wrote: > Hello, > > I'd like to introduce a new type of internal links, namely "ref" links. > > Since any element can now have a "#+name: something" affiliated keyword, > it would be practical to have a way to go straight to that name, from > anywhere in the buffer. The following patch implements > a [[ref:something]] syntax, or even [[ref:something][text]] to do so. > > The problem that I see here is that is breaks a bit syntax for internal > links ("protocol:path" is usually for external links). Another solution > would be to make them more hermetic: [[!something]]. > > On the export side, a "ref" link should be replaced by its description, > if it has any, or by the sequence number of the matching element among > elements of the same type. With the new exporter and the following > buffer: > > #+begin_src org > #+name: tab:letters > #+caption: Letters > | a | b | > | c | d | > > #+name: tab:numbers > #+caption: Numbers > | 1 | > | 2 | > > > In tableau [[ref:tab:numbers]] we can see... Yet, in > [[ref:tab:numbers][the same table]], ... > #+end_src > > the last sentence, in ascii, becomes: > > In tableau 2 we can see... Yet, in the same table, ... > > > Even if it's not a perfect replacement for "\ref{something}" commands, > it should be sufficient for all major back-ends to provide an > approximate functionality. And we gain interactivity in the Org buffer. > > What do you think? > > > Regards, > ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [dev] Implement "ref" link types 2012-02-19 19:28 ` Christian Moe @ 2012-02-19 19:41 ` Nicolas Goaziou 2012-02-19 20:11 ` Toby Cubitt 2012-02-19 20:20 ` Christian Moe 0 siblings, 2 replies; 19+ messages in thread From: Nicolas Goaziou @ 2012-02-19 19:41 UTC (permalink / raw) To: mail; +Cc: Org Mode List Hello, Christian Moe <mail@christianmoe.com> writes: > Me, I don't see any problem with a [[ref:something]] syntax. It's the > obvious org-native, cross-backend replacement for \ref. The > [[protocol:something]] syntax already widens the notion of link to > shell: and elisp: links, so I wouldn't worry about breaking > conventions. Using e.g. [[!something]] instead would introduce a brand > new bit of syntax. Not really brand new, since there already are [[#custom-id]] and [[*headline]]. Though, I'd favour [[ref:name]], too. > Suggestion: On export, how about enabling automatic element > descriptions for references following the type:name convention, so > that e.g. just > > : in [[ref:tab:numbers]] we can see... > > would expand to > > in Table 2 we can see... > > If implemented, this should be user-customizable e.g. through an alist > like > > (("fig" . "Figure") ("tab" . "Table") ("map" . "Map")) That's another possibility, but I'd rather follow LaTeX usage. I think it gives user more latitude in the end. Indeed, You don't have to think about a name prefix ; you can also have constructs like "Tables [[ref:table1]], [[ref:table2]] and [[ref:table3]]" for "Tables 1, 2 and 3", etc. Note that the behaviour you suggest can easily be implemented using filters in the new exporter. Regards, -- Nicolas Goaziou ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [dev] Implement "ref" link types 2012-02-19 19:41 ` Nicolas Goaziou @ 2012-02-19 20:11 ` Toby Cubitt 2012-02-19 20:20 ` Christian Moe 1 sibling, 0 replies; 19+ messages in thread From: Toby Cubitt @ 2012-02-19 20:11 UTC (permalink / raw) To: emacs-orgmode On Sun, Feb 19, 2012 at 08:41:45PM +0100, Nicolas Goaziou wrote: > > Suggestion: On export, how about enabling automatic element > > descriptions for references following the type:name convention, so > > that e.g. just > > > > : in [[ref:tab:numbers]] we can see... > > > > would expand to > > > > in Table 2 we can see... > > > > If implemented, this should be user-customizable e.g. through an alist > > like > > > > (("fig" . "Figure") ("tab" . "Table") ("map" . "Map")) > > That's another possibility, but I'd rather follow LaTeX usage. I think > it gives user more latitude in the end. Indeed, You don't have to think > about a name prefix ; you can also have constructs like "Tables > [[ref:table1]], [[ref:table2]] and [[ref:table3]]" for "Tables 1, 2 and > 3", etc. > > Note that the behaviour you suggest can easily be implemented using > filters in the new exporter. Note that for LaTeX export, there's no need to explicitly add names to references -- LaTeX can figure them out for itself from the label alone, with a little of help. There are various CTAN packages implementing this, such as cleveref, hyperref's \autoref command, varioref's \labelformat command... Cleveref is arguably the most powerful of these, as you can just chuck references into a \cref command (which replaces \ref): \cref{figure1,table1,table3,table2}, and it sorts them all out for you, to produce "Figure~1, and Tables~1 to~3". (Disclaimer: I'm the author of cleveref.) Toby -- Dr T. S. Cubitt Mathematics and Quantum Information group Department of Mathematics Complutense University Madrid, Spain email: tsc25@cantab.net web: www.dr-qubit.org ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [dev] Implement "ref" link types 2012-02-19 19:41 ` Nicolas Goaziou 2012-02-19 20:11 ` Toby Cubitt @ 2012-02-19 20:20 ` Christian Moe 1 sibling, 0 replies; 19+ messages in thread From: Christian Moe @ 2012-02-19 20:20 UTC (permalink / raw) To: Nicolas Goaziou; +Cc: Org Mode List On 2/19/12 8:41 PM, Nicolas Goaziou wrote: > > That's another possibility, but I'd rather follow LaTeX usage. I think > it gives user more latitude in the end. Indeed, You don't have to think > about a name prefix ; you can also have constructs like "Tables > [[ref:table1]], [[ref:table2]] and [[ref:table3]]" for "Tables 1, 2 and > 3", etc. Good points. > Note that the behaviour you suggest can easily be implemented using > filters in the new exporter. Right. I regret not having had the time to play with the new exporter and give feedback, it sounds very smart. Christian ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [dev] Implement "ref" link types 2012-02-19 18:08 [dev] Implement "ref" link types Nicolas Goaziou 2012-02-19 19:28 ` Christian Moe @ 2012-02-19 19:41 ` Samuel Wales 2012-02-19 20:02 ` Nicolas Goaziou 2012-02-19 20:10 ` Carsten Dominik 2 siblings, 1 reply; 19+ messages in thread From: Samuel Wales @ 2012-02-19 19:41 UTC (permalink / raw) To: Nicolas Goaziou; +Cc: Org Mode List I think it's a good idea, and would suggest this: * If it is not going to have features added, then [[ref:asdf]] is OK * If it /is/ going to have features added, then I recommend ES/US ES/US is extensible syntax / universal syntax, a specific proposal for an orthogonal and future-proof syntax for new capabilities like this. For example, you might want to put the target anywhere, not just where there are elements. The ID marker idea implements this with ES/US. In this case, we'd simply allow element names in addition with the same source syntax. Of course, you might decide that nobody will ever want to add new syntax to those links, in which case a simple new link type would make sense. Samuel -- The Kafka Pandemic: http://thekafkapandemic.blogspot.com ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [dev] Implement "ref" link types 2012-02-19 19:41 ` Samuel Wales @ 2012-02-19 20:02 ` Nicolas Goaziou 2012-02-19 20:48 ` Samuel Wales 0 siblings, 1 reply; 19+ messages in thread From: Nicolas Goaziou @ 2012-02-19 20:02 UTC (permalink / raw) To: Samuel Wales; +Cc: Org Mode List Hello, Samuel Wales <samologist@gmail.com> writes: > I think it's a good idea, and would suggest this: > > * If it is not going to have features added, then [[ref:asdf]] is OK > * If it /is/ going to have features added, then I recommend ES/US > > ES/US is extensible syntax / universal syntax, a specific proposal for > an orthogonal and future-proof syntax for new capabilities like this. Ok, I found the thread[1] about extensible syntax for links. I don't think that it would be a good idea to use a completely different syntax for just one type of link. Either we change the whole link system into the extensible syntax proposal, or we don't change it at all. I don't mind either way, but that's orthogonal to the problem at hand. For now, I'll assume we keep the classical link syntax. > For example, you might want to put the target anywhere, not just where > there are elements. Org already has targets for that: <<anywhere>> and [[anywhere]]. The [[ref:element]] link is more interesting for its export effect. Moving back and forth between it and targeted element is a bonus. Regards, [1] http://thread.gmane.org/gmane.emacs.orgmode/11896 -- Nicolas Goaziou ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [dev] Implement "ref" link types 2012-02-19 20:02 ` Nicolas Goaziou @ 2012-02-19 20:48 ` Samuel Wales 0 siblings, 0 replies; 19+ messages in thread From: Samuel Wales @ 2012-02-19 20:48 UTC (permalink / raw) To: Nicolas Goaziou; +Cc: Org Mode List On 2012-02-19, Nicolas Goaziou <n.goaziou@gmail.com> wrote: > Ok, I found the thread[1] about extensible syntax for links. Again this isn't just for links and if your syntax does all you ever anticipate, then I like it. I am talking about the future and the difficulty of adding ad-hoc syntax fore new features /later/. There are a few other threads (just FYI). I might have them listed someplace. Overview (also FYI): one concern is syntax creep in Org for new features. This affects external parsers and overall complexity and nonorthogonality, and introduces parsing risk, in which there are many gotchas -- such as inability to escape, quote, nest, pretty-print, etc. ES/US solves all of that in a single, simple way. If all you're doing is the one set of features now, then your syntax is great. But I'm talking about the future. > I don't think that it would be a good idea to use a completely different > syntax for just one type of link. Either we change the whole link system No, it's not for just one type of link. It applies to new features also, not just for ref links but for ID markers (links to anything, even words, using Org ID), very fancy dates that have features that are too difficult to include in existing date syntax, annotation of words or paragraphs or elements, maybe fancy detangling, browser-like link coloring by expiry and cached links, digraphs, undirected graphs including bidirectional links, super-fancy footnotes, any new feature we do not currently anticipate for the ref syntax or any other feature that would create nonorthogonality and complexity in existing syntax, and many new features we haven't thought of. It won't replace existing syntax. It's for new features. One point is that when we address something (such as nestability) for one new feature, it works for all others automatically. > into the extensible syntax proposal, or we don't change it at > all. I don't mind either way, but that's orthogonal to the problem at > hand. Again I think your syntax is great if that is all it is going to do. All I am doing is raising a possible alternative way of doing it. If you don't like it, that's fine. But my point is that if we only look at the problem at hand, we get syntax creep -- because new features are not at hand. Think of how dates now have to deal with deadlines, repeaters, etc. That's OK, but it's going to get harder to add new features to them. I'm not saying to replace dates, but for many new features, we might want to try something more orthogonal and extensible and universal. >> For example, you might want to put the target anywhere, not just where >> there are elements. > > Org already has targets for that: <<anywhere>> and [[anywhere]]. The Those do not use the Org ID mechanism, so they are brittle and don't operate across files in the same way Org IDs can (IIRC -- do they?). Again it's an example of how the syntax can add features without making parsing difficult. This all concerns future features, not anything today. Just something to consider. -- The Kafka Pandemic: http://thekafkapandemic.blogspot.com ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [dev] Implement "ref" link types 2012-02-19 18:08 [dev] Implement "ref" link types Nicolas Goaziou 2012-02-19 19:28 ` Christian Moe 2012-02-19 19:41 ` Samuel Wales @ 2012-02-19 20:10 ` Carsten Dominik 2012-02-20 0:51 ` Nicolas Goaziou 2 siblings, 1 reply; 19+ messages in thread From: Carsten Dominik @ 2012-02-19 20:10 UTC (permalink / raw) To: Nicolas Goaziou; +Cc: Org Mode List On 19.2.2012, at 19:08, Nicolas Goaziou wrote: > Hello, > > I'd like to introduce a new type of internal links, namely "ref" links. > > Since any element can now have a "#+name: something" affiliated keyword, > it would be practical to have a way to go straight to that name, from > anywhere in the buffer. The following patch implements > a [[ref:something]] syntax, or even [[ref:something][text]] to do so. > > The problem that I see here is that is breaks a bit syntax for internal > links ("protocol:path" is usually for external links). Another solution > would be to make them more hermetic: [[!something]]. > > On the export side, a "ref" link should be replaced by its description, > if it has any, or by the sequence number of the matching element among > elements of the same type. With the new exporter and the following > buffer: > > #+begin_src org > #+name: tab:letters > #+caption: Letters > | a | b | > | c | d | > > #+name: tab:numbers > #+caption: Numbers > | 1 | > | 2 | > > > In tableau [[ref:tab:numbers]] we can see... Yet, in > [[ref:tab:numbers][the same table]], ... > #+end_src > > the last sentence, in ascii, becomes: > > In tableau 2 we can see... Yet, in the same table, ... > > > Even if it's not a perfect replacement for "\ref{something}" commands, > it should be sufficient for all major back-ends to provide an > approximate functionality. And we gain interactivity in the Org buffer. > > What do you think? Love it! Why are you saying it is not a full replacement for \ref{something}? It is better than that because it will work in more backends.... - Carsten > > > Regards, > > -- > Nicolas Goaziou > From f92e12a9482613d4b1d27090fac8d2667b094fdb Mon Sep 17 00:00:00 2001 > From: Nicolas Goaziou <n.goaziou@gmail.com> > Date: Sun, 19 Feb 2012 18:48:41 +0100 > Subject: [PATCH] Implement "ref" internal link type in Org buffers > > * lisp/org.el (org-link-search): Handle "ref" internal link types. > > A [[ref:some-name]] link points to a "#+name: some-name" in the same > buffer. > --- > lisp/org.el | 10 ++++++++++ > 1 files changed, 10 insertions(+), 0 deletions(-) > > diff --git a/lisp/org.el b/lisp/org.el > index a81f7fc..ea06863 100644 > --- a/lisp/org.el > +++ b/lisp/org.el > @@ -9875,6 +9875,16 @@ visibility around point, thus ignoring > ;; First check if there are any special search functions > ((run-hook-with-args-until-success 'org-execute-file-search-functions s)) > ;; Now try the builtin stuff > + ;; Ref internal link. > + ((and (string-match "^ref:\\(.*\\)" s0) > + (let ((name (org-trim (match-string 1 s0)))) > + (save-excursion > + (goto-char (point-min)) > + (and (re-search-forward > + (format "^[ \t]*#\\+name: %s" name) nil t) > + (setq type 'dedicated pos (match-beginning 0)))))) > + (goto-char pos)) > + ;; Custom-id > ((and (equal (string-to-char s0) ?#) > (> (length s0) 1) > (save-excursion > -- > 1.7.9.1 > ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [dev] Implement "ref" link types 2012-02-19 20:10 ` Carsten Dominik @ 2012-02-20 0:51 ` Nicolas Goaziou 2012-02-20 7:09 ` Carsten Dominik 0 siblings, 1 reply; 19+ messages in thread From: Nicolas Goaziou @ 2012-02-20 0:51 UTC (permalink / raw) To: Carsten Dominik; +Cc: Org Mode List Hello, Carsten Dominik <carsten.dominik@gmail.com> writes: > Why are you saying it is not a full replacement for \ref{something}? There are still a few limitations. For example, you cannot reference a precise list item since items do not accept affiliated keywords. > It is better than that because it will work in more backends.... Yes, this is a big advantage. Regards, -- Nicolas Goaziou ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [dev] Implement "ref" link types 2012-02-20 0:51 ` Nicolas Goaziou @ 2012-02-20 7:09 ` Carsten Dominik 2012-02-20 10:59 ` Nicolas Goaziou 0 siblings, 1 reply; 19+ messages in thread From: Carsten Dominik @ 2012-02-20 7:09 UTC (permalink / raw) To: Nicolas Goaziou; +Cc: Org Mode List On Feb 20, 2012, at 1:51 AM, Nicolas Goaziou wrote: > Hello, > > Carsten Dominik <carsten.dominik@gmail.com> writes: > >> Why are you saying it is not a full replacement for \ref{something}? > > There are still a few limitations. For example, you cannot reference > a precise list item since items do not accept affiliated keywords. Ah, yes, this is right. Regards - Carsten > >> It is better than that because it will work in more backends.... > > Yes, this is a big advantage. > > > Regards, > > -- > Nicolas Goaziou - Carsten ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [dev] Implement "ref" link types 2012-02-20 7:09 ` Carsten Dominik @ 2012-02-20 10:59 ` Nicolas Goaziou 2012-02-20 22:06 ` Nicolas Goaziou 2012-03-05 9:37 ` Jambunathan K 0 siblings, 2 replies; 19+ messages in thread From: Nicolas Goaziou @ 2012-02-20 10:59 UTC (permalink / raw) To: Carsten Dominik; +Cc: Org Mode List Hello, Carsten Dominik <carsten.dominik@gmail.com> writes: > On Feb 20, 2012, at 1:51 AM, Nicolas Goaziou wrote: >> There are still a few limitations. For example, you cannot reference >> a precise list item since items do not accept affiliated keywords. > Ah, yes, this is right. Thinking about it, there may exist a better alternative to [[ref:name]] links: fuzzy links. In other words, "#+name: something" could be made an alternate of "<<something>>", with a lower priority. On the Org side, when a link like [[something]] or [[something][text]] is encountered in a buffer, the search would go on like this: 1. Search any "<<something>>" or "#+target: something"[1]. 2. If none is found, search any "#+name: something". 3. If it fails, try to find the headline "* something". 4. Eventually offer to create such headline. This step doesn't apply during export. On the export side, it depends on the description part of the link: - If there's a description (i.e. [[something][description]]), display it and link to target if possible, whatever that target is. - If there's no description: 1. A link to an invisible target will be replaced with _nothing_ (that's the point of being invisible). 2. A link to a target (i.e. <<something>>) will be replaced with the sequence number of the closest item or headline[2]. Examples: #+begin_src org - item one - item two - <<here>> item two dot one #+end_src Any link like [[here]] will be replaced with "2.1" during export. #+begin_src org * Headline one * Headline two * Headline two dot one Some paragraph. Another paragraph <<warning>>. #+end_src Here [[warning]] will also be replaced with "2.1" during export. Note that [[Headline two dot one]] would also be replaced with "2.1". 3. An link to an element (i.e. "#+name: something") would return the sequence number of that element among elements of the same type with a caption, a name affiliated keyword, or both. Example: #+begin_src org #+name: letters #+caption: I know my alphabet. |a|b|c| |foo|bar| #+name: numbers |1|2|3| #+end_src Here, a [[numbers]] link would be replaced with 2, since middle table has no name nor caption. To sum it up, at a quick glance, I can see the following: - Pros :: + No new syntax, + Possibly number every element, including items. - Cons :: + Fuzzy links are a bit overloaded, but, on the other hand, linking to headlines is not very useful since custom-id and id implementation. + There is more documentation to write. Again, what do you think? Regards, [1] This is the replacement for invisible targets, since they cannot live in comments anymore. [2] If headlines are not numbered (i.e. num:nil), replace link with headline's title instead. -- Nicolas Goaziou ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [dev] Implement "ref" link types 2012-02-20 10:59 ` Nicolas Goaziou @ 2012-02-20 22:06 ` Nicolas Goaziou 2012-02-21 1:26 ` Thomas S. Dye 2012-02-21 5:14 ` David Maus 2012-03-05 9:37 ` Jambunathan K 1 sibling, 2 replies; 19+ messages in thread From: Nicolas Goaziou @ 2012-02-20 22:06 UTC (permalink / raw) To: Carsten Dominik; +Cc: Org Mode List [-- Attachment #1: Type: text/plain, Size: 4946 bytes --] Completing myself, here is a patch implementing the previous suggestion, along with example output obtained with it. You may need to (fmakunbound 'org-e-ascii-target) to avoid an error, since this patch removes the function. First, the test buffer. #+begin_src org #+TITLE: Cross-references #+LANGUAGE: en * First headline. Here we demonstrate cross-references to items. 1. A first item in a list. 2. Another item 1. With three subparts. This one. 2. Another one. 3. <<itm:last>> And the last subpart. We end the list at item [[itm:last]]. Before we continue, here is a rather trivial equation, assuming base isn't too low[fn:1]. #+name: eq:trivial \begin{equation} 1 + 1 = 2 \end{equation} We can verify it with the following code: #+name: oneplusone #+caption: Shortly after the beginning of arithmetic. #+BEGIN_SRC emacs-lisp :exports code (+ 1 1) #+END_SRC ** Sub-topic Here we demonstrate cross-references to tables.<<sec:tables>> #+name: tab:numbers #+caption: Test | one | 1 | | two | 2 | #+name: tab:letters #+caption: Letters | a | aille | | b | bi | In table [[tab:numbers]] we can only see two numbers. Yet, in [[tab:numbers][the very same table]], we can already tell the radix used isn't too low (remember footnote [[fn:basetwo]]?). On the other hand, table [[tab:letters]] shows letters, but that's clearly uninteresting. #+name: eq:euler \begin{equation} e^{i\pi} + 1 = 0 \end{equation} Equation [[eq:euler]], also known as Euler's equation, is remarkable unlike to the equation [[eq:trivial]], which is boring (except the elisp part, in listing [[oneplusone]]). #+target: end This is the end of section [[sec:tables]]. [[end][Invisible link to line above]] * Footnotes [fn:1] <<fn:basetwo>> That is strictly greater than two. #+end_src Then the ASCII output. #+begin_src text 1 First headline. ================= Here we demonstrate cross-references to items. 1. A first item in a list. 2. Another item 1. With three subparts. This one. 2. Another one. 3. And the last subpart. We end the list at item 2.3. Before we continue, here is a rather trivial equation, assuming base isn't too low[1]. \begin{equation} 1 + 1 = 2 \end{equation} We can verify it with the following code: ,---- | (+ 1 1) `---- Listing 1: Shortly after the beginning of arithmetic. 1.1 Sub-topic ~~~~~~~~~~~~~ Here we demonstrate cross-references to tables. one 1 two 2 Table 1: Test a aille b bi Table 2: Letters In table 1 we can only see two numbers. Yet, in the very same table, we can already tell the radix used isn't too low (remember footnote 1?). On the other hand, table 2 shows letters, but that's clearly uninteresting. \begin{equation} e^{i\pi} + 1 = 0 \end{equation} Equation 2, also known as Euler's equation, is remarkable unlike to the equation 1, which is boring (except the elisp part, in listing 1). This is the end of section 1.1. #+end_src And with LaTeX. #+begin_src latex \section{First headline.} \label{sec-1} Here we demonstrate cross-references to items. \begin{enumerate} \item A first item in a list. \item Another item \begin{enumerate} \item With three subparts. This one. \item Another one. \item \label{itm:last} And the last subpart. \end{enumerate} \end{enumerate} We end the list at item \ref{itm:last}. Before we continue, here is a rather trivial equation, assuming base isn't too low\footnote{\label{fn:basetwo} That is strictly greater than two.}. \begin{equation} \label{eq:trivial} 1 + 1 = 2 \end{equation} We can verify it with the following code: \begin{figure}[H] \caption{\label{oneplusone}Shortly after the beginning of arithmetic.} \begin{verbatim} (+ 1 1) \end{verbatim} \end{figure} \subsection{Sub-topic} \label{sec-1-1} Here we demonstrate cross-references to tables.\label{sec:tables} \begin{table}[htb] \caption{\label{tab:numbers}Test} \begin{center} \begin{tabular}{lr} one & 1 \\ two & 2 \\ \end{tabular} \end{center} \end{table} \begin{table}[htb] \caption{\label{tab:letters}Letters} \begin{center} \begin{tabular}{ll} a & aille \\ b & bi \\ \end{tabular} \end{center} \end{table} In table \ref{tab:numbers} we can only see two numbers. Yet, in \hyperref[tab:numbers]{the very same table}, we can already tell the radix used isn't too low (remember footnote \ref{fn:basetwo}?). On the other hand, table \ref{tab:letters} shows letters, but that's clearly uninteresting. \begin{equation} \label{eq:euler} e^{i\pi} + 1 = 0 \end{equation} Equation \ref{eq:euler}, also known as Euler's equation, is remarkable unlike to the equation \ref{eq:trivial}, which is boring (except the elisp part, in listing \ref{oneplusone}). This is the end of section \ref{sec:tables}. #+end_src Regards, -- Nicolas Goaziou [-- Warning: decoded text below may be mangled, UTF-8 assumed --] [-- Attachment #2: cross-reference numbers --] [-- Type: text/x-patch, Size: 22154 bytes --] From dcae2d1015f958dcb1ed3c92349ad0c2e18a1219 Mon Sep 17 00:00:00 2001 From: Nicolas Goaziou <n.goaziou@gmail.com> Date: Mon, 20 Feb 2012 22:24:38 +0100 Subject: [PATCH] Implement cross-references numbers * lisp/org.el (org-link-search): Search for #+name affiliated keywords and invisible targets. * contrib/lisp/org-element.el (org-element-link-parser): Remove "ref" links relative part. (org-element-target-parser): Move property name from `:raw-value' to `:value'. (org-element-recursive-objects): Remove targets from tables. Cells are not parsed unless explicitely asked by back-end developer, too late. A target wouldn't be noticed in time. One solution could be to parse every table, but that's time consumming. (org-element-object-restrictions): Target are not recursive anymore. * contrib/lisp/org-export.el (org-export-resolve-fuzzy-link): Find elements with a matching "#+name: path" affiliated keyword. (org-export-get-ordinal): Make special cases for headlines, items, footnotes definitions and references. (org-export-resolve-ref-link): Removed function. * EXPERIMENTAL/org-e-latex.el (org-e-latex-link): Handle cross-reference numbers. (org-e-latex-target): Targets have no contents. * EXPERIMENTAL/org-e-ascii.el (org-e-ascii--describe-links): Ignore fuzzy links in link description at the end of the section. (org-e-ascii-link): Handle cross-reference numbers. --- EXPERIMENTAL/org-e-ascii.el | 80 ++++++++----------- EXPERIMENTAL/org-e-latex.el | 48 ++++------- contrib/lisp/org-element.el | 20 ++--- contrib/lisp/org-export.el | 185 ++++++++++++++++++++++++++---------------- lisp/org.el | 16 ++++ 5 files changed, 187 insertions(+), 162 deletions(-) diff --git a/EXPERIMENTAL/org-e-ascii.el b/EXPERIMENTAL/org-e-ascii.el index ef1ca1c..1361e23 100644 --- a/EXPERIMENTAL/org-e-ascii.el +++ b/EXPERIMENTAL/org-e-ascii.el @@ -826,28 +826,24 @@ channel." (org-element-get-property :raw-link link) (org-export-secondary-string desc 'e-ascii info))))) (cond - ;; Coderefs, radio links and ref links are ignored. - ((member type '("coderef" "radio" "ref")) nil) - ;; Id, custom-id and fuzzy links (with the exception of - ;; targets): Headlines refer to their numbering. - ((member type '("custom-id" "fuzzy" "id")) - (let ((destination (if (string= type "fuzzy") - (org-export-resolve-fuzzy-link link info) - (org-export-resolve-id-link link info)))) - (unless (eq (car destination) 'target) - (concat - (org-e-ascii--fill-string - (format - "[%s] %s" - anchor - (if (not destination) - (org-e-ascii--translate "Unknown reference" info) - (format - (org-e-ascii--translate "See section %s" info) - (mapconcat 'number-to-string - (org-export-get-headline-number destination info) - ".")))) - width info) "\n\n")))) + ;; Coderefs, radio links and fuzzy links are ignored. + ((member type '("coderef" "radio" "fuzzy")) nil) + ;; Id and custom-id links: Headlines refer to their numbering. + ((member type '("custom-id" "id")) + (let ((destination (org-export-resolve-id-link link info))) + (concat + (org-e-ascii--fill-string + (format + "[%s] %s" + anchor + (if (not destination) + (org-e-ascii--translate "Unknown reference" info) + (format + (org-e-ascii--translate "See section %s" info) + (mapconcat 'number-to-string + (org-export-get-headline-number destination info) + ".")))) + width info) "\n\n"))) ;; Do not add a link that cannot be resolved and doesn't have ;; any description: destination is already visible in the ;; paragraph. @@ -1392,29 +1388,23 @@ INFO is a plist holding contextual information." (org-element-get-property :path link) (cdr (assq 'radio-target org-element-object-restrictions))) 'e-ascii info)) - ;; Ref link: If there's no description (DESC, return link's - ;; destination sequence number among elements of same - ;; type. Otherwise, use DESC. - ((string= type "ref") - (if (org-string-nw-p desc) desc - (format "%d" - (org-export-get-ordinal - (org-export-resolve-ref-link link info) - info nil nil - (lambda (el) (or (org-element-get-property :caption el) - (org-element-get-property :name el))))))) ;; Do not apply a special syntax on fuzzy links pointing to ;; targets. - ((and (string= type "fuzzy") - (let ((path (org-element-get-property :path link))) - (loop for target in (plist-get info :target-list) - thereis (string= - (org-element-get-property :raw-value target) - path)))) - (if (org-string-nw-p desc) desc raw-link)) + ((string= type "fuzzy") + (let ((destination (org-export-resolve-fuzzy-link link info))) + ;; Ignore invisible "#+target: path". + (unless (eq (car destination) 'keyword) + (if (org-string-nw-p desc) desc + (when destination + (let ((number (org-export-get-ordinal destination info))) + (when number + (if (atom number) (number-to-string number) + (mapconcat 'number-to-string number "."))))))))) (t - (concat (format "[%s]" (if (org-string-nw-p desc) desc raw-link)) - (unless org-e-ascii-links-to-notes (format " (%s)" raw-link))))))) + (if (not (org-string-nw-p desc)) (format "[%s]" raw-link) + (concat + (format "[%s]" desc) + (unless org-e-ascii-links-to-notes (format " (%s)" raw-link)))))))) ;;;; Macro @@ -1852,11 +1842,7 @@ INFO is a plist used as a communication channel." ;;;; Target -(defun org-e-ascii-target (target contents info) - "Transcode a TARGET object from Org to ASCII. -CONTENTS is the contents of the target. INFO is a plist holding -contextual information." - contents) +;; Targets are invisible. ;;;; Time-stamp diff --git a/EXPERIMENTAL/org-e-latex.el b/EXPERIMENTAL/org-e-latex.el index eec331a..75b5a9b 100644 --- a/EXPERIMENTAL/org-e-latex.el +++ b/EXPERIMENTAL/org-e-latex.el @@ -1266,8 +1266,8 @@ CONTENTS is nil. INFO is a plist holding contextual information." (cond ((string= key "latex") value) ((string= key "index") (format "\\index{%s}" value)) - ((string= key "target") - (format "\\label{%s}" (org-export-solidify-link-text value))) + ;; Invisible targets. + ((string= key "target") nil) ((string= key "toc") (let ((value (downcase value))) (cond @@ -1426,32 +1426,26 @@ INFO is a plist holding contextual information. See (org-element-parse-secondary-string path (cdr (assq 'radio-target org-element-object-restrictions))) 'e-latex info))) - ;; Ref link: If no description is provided, reference label PATH - ;; and display table number. Otherwise move to label but display - ;; description instead. - ((string= type "ref") - (if (not desc) (format "\\ref{%s}" path) - (format "\\hyperref[%s]{%s}" path desc))) ;; Links pointing to an headline: Find destination and build ;; appropriate referencing command. ((member type '("custom-id" "fuzzy" "id")) (let ((destination (if (string= type "fuzzy") (org-export-resolve-fuzzy-link link info) (org-export-resolve-id-link link info)))) - ;; Fuzzy link points to a target. Do as above. (case (car destination) - (target - (format "\\hyperref[%s]{%s}" - (org-export-solidify-link-text - (org-element-get-property :raw-value destination)) + ;; Fuzzy link points nowhere. + ('nil + (format "\\texttt{%s}" (or desc (org-export-secondary-string (org-element-get-property :raw-link link) 'e-latex info)))) - ;; Fuzzy link points to an headline. If headlines are - ;; numbered and the link has no description, display - ;; headline's number. Otherwise, display description or - ;; headline's title. + ;; Fuzzy link points to an invisible target. + (keyword nil) + ;; LINK points to an headline. If headlines are numbered + ;; and the link has no description, display headline's + ;; number. Otherwise, display description or headline's + ;; title. (headline (let ((label (format "sec-%s" @@ -1466,13 +1460,10 @@ INFO is a plist holding contextual information. See (org-export-secondary-string (org-element-get-property :title destination) 'e-latex info)))))) - ;; Fuzzy link points nowhere. + ;; Fuzzy link points to a target. Do as above. (otherwise - (format "\\texttt{%s}" - (or desc - (org-export-secondary-string - (org-element-get-property :raw-link link) - 'e-latex info))))))) + (if (not desc) (format "\\ref{%s}" path) + (format "\\hyperref[%s]{%s}" path desc)))))) ;; Coderef: replace link with the reference name or the ;; equivalent line number. ((string= type "coderef") @@ -1945,14 +1936,11 @@ CONTENTS is nil. INFO is a plist holding contextual information." ;;;; Target -(defun org-e-latex-target (target text info) +(defun org-e-latex-target (target contents info) "Transcode a TARGET object from Org to LaTeX. -TEXT is the text of the target. INFO is a plist holding -contextual information." - (format "\\label{%s}%s" - (org-export-solidify-link-text - (org-element-get-property :raw-value target)) - text)) +CONTENTS is nil. INFO is a plist holding contextual +information." + (format "\\label{%s}" (org-element-get-property :value target))) ;;;; Time-stamp diff --git a/contrib/lisp/org-element.el b/contrib/lisp/org-element.el index 5b61942..554537e 100644 --- a/contrib/lisp/org-element.el +++ b/contrib/lisp/org-element.el @@ -1961,9 +1961,6 @@ Assume point is at the beginning of the link." ;; Explicit type (http, irc, bbdb...). See `org-link-types'. ((string-match org-link-re-with-space3 link) (setq type (match-string 1 link) path (match-string 2 link))) - ;; Ref type: PATH is the name of the target element. - ((string-match "^ref:\\(.*\\)" link) - (setq type "ref" path (org-trim (match-string 1 link)))) ;; Id type: PATH is the id. ((string-match "^id:\\([-a-f0-9]+\\)" link) (setq type "id" path (match-string 1 link))) @@ -2265,25 +2262,21 @@ CONTENTS is the contents of the object." "Parse target at point. Return a list whose car is `target' and cdr a plist with -`:begin', `:end', `:contents-begin', `:contents-end', `raw-value' -and `:post-blank' as keywords. +`:begin', `:end', `:contents-begin', `:contents-end', `value' and +`:post-blank' as keywords. Assume point is at the target." (save-excursion (looking-at org-target-regexp) (let ((begin (point)) - (contents-begin (match-beginning 1)) - (contents-end (match-end 1)) - (raw-value (org-match-string-no-properties 1)) + (value (org-match-string-no-properties 1)) (post-blank (progn (goto-char (match-end 0)) (skip-chars-forward " \t"))) (end (point))) `(target (:begin ,begin :end ,end - :contents-begin ,contents-begin - :contents-end ,contents-end - :raw-value ,raw-value + :value ,value :post-blank ,post-blank))))) (defun org-element-target-interpreter (target contents) @@ -2470,7 +2463,7 @@ Sharing the same successor comes handy when, for example, the regexp matching one object can also match the other object.") (defconst org-element-recursive-objects - '(emphasis link macro subscript superscript target radio-target) + '(emphasis link macro subscript superscript radio-target) "List of recursive object types.") (defconst org-element-non-recursive-block-alist @@ -2540,8 +2533,7 @@ This list is checked after translations have been applied. See (subscript entity export-snippet inline-babel-call inline-src-block latex-fragment sub/superscript text-markup) (superscript entity export-snippet inline-babel-call inline-src-block - latex-fragment sub/superscript text-markup) - (target entity export-snippet latex-fragment sub/superscript text-markup)) + latex-fragment sub/superscript text-markup)) "Alist of recursive objects restrictions. CAR is a recursive object type and CDR is a list of successors diff --git a/contrib/lisp/org-export.el b/contrib/lisp/org-export.el index 34f290e..aa6ca52 100644 --- a/contrib/lisp/org-export.el +++ b/contrib/lisp/org-export.el @@ -1278,7 +1278,13 @@ Following tree properties are set: `(:parse-tree ,data :target-list - ,(org-element-map data 'target (lambda (target local) target) info) + ,(org-element-map + data '(keyword target) + (lambda (blob local) + (when (or (eq (car blob) 'target) + (string= (downcase (org-element-get-property :key blob)) + "target")) + blob)) info) :headline-numbering ,(org-export-collect-headline-numbering data info) :back-end ,backend) info)) @@ -2579,8 +2585,11 @@ INFO is a plist holding contextual information. Return value can be an object, an element, or nil: -- If LINK path exactly matches any target, return the target - object. +- If LINK path matches a target object (i.e. <<path>>) or + element (i.e. \"#+target: path\"), return it. + +- If LINK path exactly matches the name affiliated keyword + \(i.e. #+name: path) of an element, return that element. - If LINK path exactly matches any headline name, return that element. If more than one headline share that name, priority @@ -2591,39 +2600,53 @@ Return value can be an object, an element, or nil: Assume LINK type is \"fuzzy\"." (let ((path (org-element-get-property :path link))) - ;; Link points to a target: return it. - (or (loop for target in (plist-get info :target-list) - when (string= (org-element-get-property :raw-value target) path) - return target) - ;; Link either points to an headline or nothing. Try to find - ;; the source, with priority given to headlines with the closest - ;; common ancestor. If such candidate is found, return its - ;; beginning position as an unique identifier, otherwise return - ;; nil. - (let ((find-headline - (function - ;; Return first headline whose `:raw-value' property - ;; is NAME in parse tree DATA, or nil. - (lambda (name data) - (org-element-map - data 'headline - (lambda (headline local) - (when (string= - (org-element-get-property :raw-value headline) - name) - headline)) - info 'first-match))))) - ;; Search among headlines sharing an ancestor with link, - ;; from closest to farthest. - (or (catch 'exit - (mapc - (lambda (parent) - (when (eq (car parent) 'headline) - (let ((foundp (funcall find-headline path parent))) - (when foundp (throw 'exit foundp))))) - (plist-get info :genealogy)) nil) - ;; No match with a common ancestor: try the full parse-tree. - (funcall find-headline path (plist-get info :parse-tree))))))) + (cond + ;; First try to find a matching "<<path>>" unless user specified + ;; he was looking for an headline (path starts with a * + ;; character). + ((and (not (eq (substring path 0 1) ?*)) + (loop for target in (plist-get info :target-list) + when (string= (org-element-get-property :value target) path) + return target))) + ;; Then try to find an element with a matching "#+name: path" + ;; affiliated keyword. + ((and (not (eq (substring path 0 1) ?*)) + (org-element-map + (plist-get info :parse-tree) org-element-all-elements + (lambda (el local) + (when (string= (org-element-get-property :name el) path) el)) + info 'first-match))) + ;; Last case: link either points to an headline or to + ;; nothingness. Try to find the source, with priority given to + ;; headlines with the closest common ancestor. If such candidate + ;; is found, return its beginning position as an unique + ;; identifier, otherwise return nil. + (t + (let ((find-headline + (function + ;; Return first headline whose `:raw-value' property is + ;; NAME in parse tree DATA, or nil. + (lambda (name data) + (org-element-map + data 'headline + (lambda (headline local) + (when (string= + (org-element-get-property :raw-value headline) + name) + headline)) + info 'first-match))))) + ;; Search among headlines sharing an ancestor with link, from + ;; closest to farthest. + (or (catch 'exit + (mapc + (lambda (parent) + (when (eq (car parent) 'headline) + (let ((foundp (funcall find-headline path parent))) + (when foundp (throw 'exit foundp))))) + (plist-get info :genealogy)) nil) + ;; No match with a common ancestor: try the full + ;; parse-tree. + (funcall find-headline path (plist-get info :parse-tree)))))))) (defun org-export-resolve-id-link (link info) "Return headline referenced as LINK destination. @@ -2641,20 +2664,6 @@ is either \"id\" or \"custom-id\"." headline)) info 'first-match))) -(defun org-export-resolve-ref-link (link info) - "Return element referenced as LINK destination. - -INFO is a plist used as a communication channel. - -Assume LINK type is \"ref\" and. Return value is the first -element whose `:name' property matches LINK's `:path', or nil." - (let ((name (org-element-get-property :path link))) - (org-element-map - (plist-get info :parse-tree) org-element-all-elements - (lambda (el local) - (when (string= (org-element-get-property :name el) name) el)) - info 'first-match))) - (defun org-export-resolve-coderef (ref info) "Resolve a code reference REF. @@ -2745,27 +2754,61 @@ Optional argument PREDICATE is a function returning a non-nil value if the current element or object should be counted in. It accepts one argument: the element or object being considered. This argument allows to count only a certain type of objects, -like inline images, which are a subset of links \(in that case, -`org-export-inline-image-p' might be an useful predicate\)." - (let ((counter 0) - ;; Determine if search should apply to current section, in - ;; which case it should be retrieved first, or to full parse - ;; tree. As a special case, an element or object without - ;; a parent headline will also trigger a full search, - ;; notwithstanding WITHIN-SECTION value. - (data - (if (not within-section) (plist-get info :parse-tree) - (or (org-export-get-parent-headline element info) - (plist-get info :parse-tree))))) - ;; Increment counter until ELEMENT is found again. - (org-element-map - data (or types (car element)) - (lambda (el local) - (cond - ((equal element el) (1+ counter)) - ((not predicate) (incf counter) nil) - ((funcall predicate el) (incf counter) nil))) - info 'first-match))) +like inline images, which are a subset of links (in that case, +`org-export-inline-image-p' might be an useful predicate). + +Return value is a list of numbers if ELEMENT is an headline or an +item. It is nil for keywords. It represents the footnote number +for footnote definitions and footnote references. If ELEMENT is +a target, return the same value as if ELEMENT was the closest +table, item or headline containing the target. In any other +case, return the sequence number of ELEMENT among elements or +objects of the same type." + ;; A target keyword, representing an invisible target, never has + ;; a sequence number. + (unless (eq (car element) 'keyword) + ;; Ordinal of a target object is the ordinal of the closest table, + ;; item, or headline containing the object. + (when (eq (car element) 'target) + (setq element + (loop for parent in (org-export-get-genealogy element info) + when + (memq + (car parent) + '(footnote-definition footnote-reference headline item table)) + return parent))) + (case (car element) + ;; Special case 1: An headline returns its number as a list. + (headline (org-export-get-headline-number element info)) + ;; Special case 2: An item returns its number as a list. + (item (let ((struct (org-element-get-property :structure element))) + (org-list-get-item-number + (org-element-get-property :begin element) + struct + (org-list-prevs-alist struct) + (org-list-parents-alist struct)))) + ((footnote definition footnote-reference) + (org-export-get-footnote-number element info)) + (otherwise + (let ((counter 0) + ;; Determine if search should apply to current section, + ;; in which case it should be retrieved first, or to full + ;; parse tree. As a special case, an element or object + ;; without a parent headline will also trigger a full + ;; search, notwithstanding WITHIN-SECTION value. + (data + (if (not within-section) (plist-get info :parse-tree) + (or (org-export-get-parent-headline element info) + (plist-get info :parse-tree))))) + ;; Increment counter until ELEMENT is found again. + (org-element-map + data (or types (car element)) + (lambda (el local) + (cond + ((equal element el) (1+ counter)) + ((not predicate) (incf counter) nil) + ((funcall predicate el) (incf counter) nil))) + info 'first-match)))))) ;;;; For Src-Blocks diff --git a/lisp/org.el b/lisp/org.el index a81f7fc..6708801 100644 --- a/lisp/org.el +++ b/lisp/org.el @@ -9896,6 +9896,22 @@ visibility around point, thus ignoring pos (match-beginning 0)))) ;; There is an exact target for this (goto-char pos)) + ((save-excursion + (goto-char (point-min)) + (and + (re-search-forward + (format "^[ \t]*#\\+target: %s" (regexp-quote s0)) nil t) + (setq type 'dedicated pos (match-beginning 0)))) + ;; Found an invisible target. + (goto-char pos)) + ((save-excursion + (goto-char (point-min)) + (and + (re-search-forward + (format "^[ \t]*#\\+name: %s" (regexp-quote s0)) nil t) + (setq type 'dedicated pos (match-beginning 0)))) + ;; Found an element with a matching name. + (goto-char pos)) ((and (string-match "^(\\(.*\\))$" s0) (save-excursion (goto-char (point-min)) -- 1.7.9.1 ^ permalink raw reply related [flat|nested] 19+ messages in thread
* Re: [dev] Implement "ref" link types 2012-02-20 22:06 ` Nicolas Goaziou @ 2012-02-21 1:26 ` Thomas S. Dye 2012-02-21 5:14 ` David Maus 1 sibling, 0 replies; 19+ messages in thread From: Thomas S. Dye @ 2012-02-21 1:26 UTC (permalink / raw) To: Nicolas Goaziou; +Cc: Org Mode List, Carsten Dominik Nice! Nicolas Goaziou <n.goaziou@gmail.com> writes: > Completing myself, here is a patch implementing the previous suggestion, > along with example output obtained with it. You may need to > (fmakunbound 'org-e-ascii-target) to avoid an error, since this patch > removes the function. > > > First, the test buffer. > > > #+begin_src org > #+TITLE: Cross-references > #+LANGUAGE: en > > * First headline. > > Here we demonstrate cross-references to items. > > 1. A first item in a list. > 2. Another item > 1. With three subparts. This one. > 2. Another one. > 3. <<itm:last>> And the last subpart. > > We end the list at item [[itm:last]]. > > Before we continue, here is a rather trivial equation, assuming base > isn't too low[fn:1]. > > #+name: eq:trivial > \begin{equation} > 1 + 1 = 2 > \end{equation} > > We can verify it with the following code: > > #+name: oneplusone > #+caption: Shortly after the beginning of arithmetic. > #+BEGIN_SRC emacs-lisp :exports code > (+ 1 1) > #+END_SRC > > ** Sub-topic > > Here we demonstrate cross-references to tables.<<sec:tables>> > > #+name: tab:numbers > #+caption: Test > | one | 1 | > | two | 2 | > > #+name: tab:letters > #+caption: Letters > | a | aille | > | b | bi | > > In table [[tab:numbers]] we can only see two numbers. Yet, in [[tab:numbers][the very > same table]], we can already tell the radix used isn't too low > (remember footnote [[fn:basetwo]]?). On the other hand, table > [[tab:letters]] shows letters, but that's clearly uninteresting. > > #+name: eq:euler > \begin{equation} > e^{i\pi} + 1 = 0 > \end{equation} > > Equation [[eq:euler]], also known as Euler's equation, is remarkable > unlike to the equation [[eq:trivial]], which is boring (except the > elisp part, in listing [[oneplusone]]). > > #+target: end > This is the end of section [[sec:tables]]. [[end][Invisible link to line above]] > > * Footnotes > > [fn:1] <<fn:basetwo>> That is strictly greater than two. > #+end_src > > > > Then the ASCII output. > > > > #+begin_src text > 1 First headline. > ================= > > Here we demonstrate cross-references to items. > > 1. A first item in a list. > 2. Another item > 1. With three subparts. This one. > 2. Another one. > 3. And the last subpart. > > We end the list at item 2.3. > > Before we continue, here is a rather trivial equation, assuming base > isn't too low[1]. > > \begin{equation} > 1 + 1 = 2 > \end{equation} > > We can verify it with the following code: > > ,---- > | (+ 1 1) > `---- > Listing 1: Shortly after the beginning of arithmetic. > > > 1.1 Sub-topic > ~~~~~~~~~~~~~ > > Here we demonstrate cross-references to tables. > > one 1 > two 2 > Table 1: Test > > a aille > b bi > Table 2: Letters > > In table 1 we can only see two numbers. Yet, in the very same table, > we can already tell the radix used isn't too low (remember footnote > 1?). On the other hand, table 2 shows letters, but that's clearly > uninteresting. > > \begin{equation} > e^{i\pi} + 1 = 0 > \end{equation} > > Equation 2, also known as Euler's equation, is remarkable unlike to > the equation 1, which is boring (except the elisp part, in listing 1). > > This is the end of section 1.1. > #+end_src > > > > And with LaTeX. > > > > #+begin_src latex > \section{First headline.} > \label{sec-1} > > Here we demonstrate cross-references to items. > > \begin{enumerate} > \item A first item in a list. > \item Another item > \begin{enumerate} > \item With three subparts. This one. > \item Another one. > \item \label{itm:last} And the last subpart. > \end{enumerate} > \end{enumerate} > > We end the list at item \ref{itm:last}. > > Before we continue, here is a rather trivial equation, assuming base > isn't too low\footnote{\label{fn:basetwo} That is strictly greater than two.}. > > \begin{equation} > \label{eq:trivial} > 1 + 1 = 2 > \end{equation} > > We can verify it with the following code: > > \begin{figure}[H] > \caption{\label{oneplusone}Shortly after the beginning of arithmetic.} > \begin{verbatim} > (+ 1 1) > \end{verbatim} > \end{figure} > > \subsection{Sub-topic} > \label{sec-1-1} > > Here we demonstrate cross-references to tables.\label{sec:tables} > > \begin{table}[htb] > \caption{\label{tab:numbers}Test} > \begin{center} > \begin{tabular}{lr} > one & 1 \\ > two & 2 \\ > \end{tabular} > \end{center} > \end{table} > > \begin{table}[htb] > \caption{\label{tab:letters}Letters} > \begin{center} > \begin{tabular}{ll} > a & aille \\ > b & bi \\ > \end{tabular} > \end{center} > \end{table} > > In table \ref{tab:numbers} we can only see two numbers. Yet, in \hyperref[tab:numbers]{the very > same table}, we can already tell the radix used isn't too low > (remember footnote \ref{fn:basetwo}?). On the other hand, table > \ref{tab:letters} shows letters, but that's clearly uninteresting. > > \begin{equation} > \label{eq:euler} > e^{i\pi} + 1 = 0 > \end{equation} > > Equation \ref{eq:euler}, also known as Euler's equation, is remarkable > unlike to the equation \ref{eq:trivial}, which is boring (except the > elisp part, in listing \ref{oneplusone}). > > This is the end of section \ref{sec:tables}. > #+end_src > > > Regards, > > -- > Nicolas Goaziou > From dcae2d1015f958dcb1ed3c92349ad0c2e18a1219 Mon Sep 17 00:00:00 2001 > From: Nicolas Goaziou <n.goaziou@gmail.com> > Date: Mon, 20 Feb 2012 22:24:38 +0100 > Subject: [PATCH] Implement cross-references numbers > > * lisp/org.el (org-link-search): Search for #+name affiliated keywords > and invisible targets. > * contrib/lisp/org-element.el (org-element-link-parser): Remove "ref" > links relative part. > (org-element-target-parser): Move property name from `:raw-value' to > `:value'. > (org-element-recursive-objects): Remove targets from tables. Cells > are not parsed unless explicitely asked by back-end developer, too > late. A target wouldn't be noticed in time. One solution could be to > parse every table, but that's time consumming. > (org-element-object-restrictions): Target are not recursive anymore. > * contrib/lisp/org-export.el (org-export-resolve-fuzzy-link): Find > elements with a matching "#+name: path" affiliated keyword. > (org-export-get-ordinal): Make special cases for headlines, items, > footnotes definitions and references. > (org-export-resolve-ref-link): Removed function. > * EXPERIMENTAL/org-e-latex.el (org-e-latex-link): Handle > cross-reference numbers. > (org-e-latex-target): Targets have no contents. > * EXPERIMENTAL/org-e-ascii.el (org-e-ascii--describe-links): Ignore > fuzzy links in link description at the end of the section. > (org-e-ascii-link): Handle cross-reference numbers. > --- > EXPERIMENTAL/org-e-ascii.el | 80 ++++++++----------- > EXPERIMENTAL/org-e-latex.el | 48 ++++------- > contrib/lisp/org-element.el | 20 ++--- > contrib/lisp/org-export.el | 185 ++++++++++++++++++++++++++---------------- > lisp/org.el | 16 ++++ > 5 files changed, 187 insertions(+), 162 deletions(-) > > diff --git a/EXPERIMENTAL/org-e-ascii.el b/EXPERIMENTAL/org-e-ascii.el > index ef1ca1c..1361e23 100644 > --- a/EXPERIMENTAL/org-e-ascii.el > +++ b/EXPERIMENTAL/org-e-ascii.el > @@ -826,28 +826,24 @@ channel." > (org-element-get-property :raw-link link) > (org-export-secondary-string desc 'e-ascii info))))) > (cond > - ;; Coderefs, radio links and ref links are ignored. > - ((member type '("coderef" "radio" "ref")) nil) > - ;; Id, custom-id and fuzzy links (with the exception of > - ;; targets): Headlines refer to their numbering. > - ((member type '("custom-id" "fuzzy" "id")) > - (let ((destination (if (string= type "fuzzy") > - (org-export-resolve-fuzzy-link link info) > - (org-export-resolve-id-link link info)))) > - (unless (eq (car destination) 'target) > - (concat > - (org-e-ascii--fill-string > - (format > - "[%s] %s" > - anchor > - (if (not destination) > - (org-e-ascii--translate "Unknown reference" info) > - (format > - (org-e-ascii--translate "See section %s" info) > - (mapconcat 'number-to-string > - (org-export-get-headline-number destination info) > - ".")))) > - width info) "\n\n")))) > + ;; Coderefs, radio links and fuzzy links are ignored. > + ((member type '("coderef" "radio" "fuzzy")) nil) > + ;; Id and custom-id links: Headlines refer to their numbering. > + ((member type '("custom-id" "id")) > + (let ((destination (org-export-resolve-id-link link info))) > + (concat > + (org-e-ascii--fill-string > + (format > + "[%s] %s" > + anchor > + (if (not destination) > + (org-e-ascii--translate "Unknown reference" info) > + (format > + (org-e-ascii--translate "See section %s" info) > + (mapconcat 'number-to-string > + (org-export-get-headline-number destination info) > + ".")))) > + width info) "\n\n"))) > ;; Do not add a link that cannot be resolved and doesn't have > ;; any description: destination is already visible in the > ;; paragraph. > @@ -1392,29 +1388,23 @@ INFO is a plist holding contextual information." > (org-element-get-property :path link) > (cdr (assq 'radio-target org-element-object-restrictions))) > 'e-ascii info)) > - ;; Ref link: If there's no description (DESC, return link's > - ;; destination sequence number among elements of same > - ;; type. Otherwise, use DESC. > - ((string= type "ref") > - (if (org-string-nw-p desc) desc > - (format "%d" > - (org-export-get-ordinal > - (org-export-resolve-ref-link link info) > - info nil nil > - (lambda (el) (or (org-element-get-property :caption el) > - (org-element-get-property :name el))))))) > ;; Do not apply a special syntax on fuzzy links pointing to > ;; targets. > - ((and (string= type "fuzzy") > - (let ((path (org-element-get-property :path link))) > - (loop for target in (plist-get info :target-list) > - thereis (string= > - (org-element-get-property :raw-value target) > - path)))) > - (if (org-string-nw-p desc) desc raw-link)) > + ((string= type "fuzzy") > + (let ((destination (org-export-resolve-fuzzy-link link info))) > + ;; Ignore invisible "#+target: path". > + (unless (eq (car destination) 'keyword) > + (if (org-string-nw-p desc) desc > + (when destination > + (let ((number (org-export-get-ordinal destination info))) > + (when number > + (if (atom number) (number-to-string number) > + (mapconcat 'number-to-string number "."))))))))) > (t > - (concat (format "[%s]" (if (org-string-nw-p desc) desc raw-link)) > - (unless org-e-ascii-links-to-notes (format " (%s)" raw-link))))))) > + (if (not (org-string-nw-p desc)) (format "[%s]" raw-link) > + (concat > + (format "[%s]" desc) > + (unless org-e-ascii-links-to-notes (format " (%s)" raw-link)))))))) > > > ;;;; Macro > @@ -1852,11 +1842,7 @@ INFO is a plist used as a communication channel." > > ;;;; Target > > -(defun org-e-ascii-target (target contents info) > - "Transcode a TARGET object from Org to ASCII. > -CONTENTS is the contents of the target. INFO is a plist holding > -contextual information." > - contents) > +;; Targets are invisible. > > > ;;;; Time-stamp > diff --git a/EXPERIMENTAL/org-e-latex.el b/EXPERIMENTAL/org-e-latex.el > index eec331a..75b5a9b 100644 > --- a/EXPERIMENTAL/org-e-latex.el > +++ b/EXPERIMENTAL/org-e-latex.el > @@ -1266,8 +1266,8 @@ CONTENTS is nil. INFO is a plist holding contextual information." > (cond > ((string= key "latex") value) > ((string= key "index") (format "\\index{%s}" value)) > - ((string= key "target") > - (format "\\label{%s}" (org-export-solidify-link-text value))) > + ;; Invisible targets. > + ((string= key "target") nil) > ((string= key "toc") > (let ((value (downcase value))) > (cond > @@ -1426,32 +1426,26 @@ INFO is a plist holding contextual information. See > (org-element-parse-secondary-string > path (cdr (assq 'radio-target org-element-object-restrictions))) > 'e-latex info))) > - ;; Ref link: If no description is provided, reference label PATH > - ;; and display table number. Otherwise move to label but display > - ;; description instead. > - ((string= type "ref") > - (if (not desc) (format "\\ref{%s}" path) > - (format "\\hyperref[%s]{%s}" path desc))) > ;; Links pointing to an headline: Find destination and build > ;; appropriate referencing command. > ((member type '("custom-id" "fuzzy" "id")) > (let ((destination (if (string= type "fuzzy") > (org-export-resolve-fuzzy-link link info) > (org-export-resolve-id-link link info)))) > - ;; Fuzzy link points to a target. Do as above. > (case (car destination) > - (target > - (format "\\hyperref[%s]{%s}" > - (org-export-solidify-link-text > - (org-element-get-property :raw-value destination)) > + ;; Fuzzy link points nowhere. > + ('nil > + (format "\\texttt{%s}" > (or desc > (org-export-secondary-string > (org-element-get-property :raw-link link) > 'e-latex info)))) > - ;; Fuzzy link points to an headline. If headlines are > - ;; numbered and the link has no description, display > - ;; headline's number. Otherwise, display description or > - ;; headline's title. > + ;; Fuzzy link points to an invisible target. > + (keyword nil) > + ;; LINK points to an headline. If headlines are numbered > + ;; and the link has no description, display headline's > + ;; number. Otherwise, display description or headline's > + ;; title. > (headline > (let ((label > (format "sec-%s" > @@ -1466,13 +1460,10 @@ INFO is a plist holding contextual information. See > (org-export-secondary-string > (org-element-get-property :title destination) > 'e-latex info)))))) > - ;; Fuzzy link points nowhere. > + ;; Fuzzy link points to a target. Do as above. > (otherwise > - (format "\\texttt{%s}" > - (or desc > - (org-export-secondary-string > - (org-element-get-property :raw-link link) > - 'e-latex info))))))) > + (if (not desc) (format "\\ref{%s}" path) > + (format "\\hyperref[%s]{%s}" path desc)))))) > ;; Coderef: replace link with the reference name or the > ;; equivalent line number. > ((string= type "coderef") > @@ -1945,14 +1936,11 @@ CONTENTS is nil. INFO is a plist holding contextual information." > > ;;;; Target > > -(defun org-e-latex-target (target text info) > +(defun org-e-latex-target (target contents info) > "Transcode a TARGET object from Org to LaTeX. > -TEXT is the text of the target. INFO is a plist holding > -contextual information." > - (format "\\label{%s}%s" > - (org-export-solidify-link-text > - (org-element-get-property :raw-value target)) > - text)) > +CONTENTS is nil. INFO is a plist holding contextual > +information." > + (format "\\label{%s}" (org-element-get-property :value target))) > > > ;;;; Time-stamp > diff --git a/contrib/lisp/org-element.el b/contrib/lisp/org-element.el > index 5b61942..554537e 100644 > --- a/contrib/lisp/org-element.el > +++ b/contrib/lisp/org-element.el > @@ -1961,9 +1961,6 @@ Assume point is at the beginning of the link." > ;; Explicit type (http, irc, bbdb...). See `org-link-types'. > ((string-match org-link-re-with-space3 link) > (setq type (match-string 1 link) path (match-string 2 link))) > - ;; Ref type: PATH is the name of the target element. > - ((string-match "^ref:\\(.*\\)" link) > - (setq type "ref" path (org-trim (match-string 1 link)))) > ;; Id type: PATH is the id. > ((string-match "^id:\\([-a-f0-9]+\\)" link) > (setq type "id" path (match-string 1 link))) > @@ -2265,25 +2262,21 @@ CONTENTS is the contents of the object." > "Parse target at point. > > Return a list whose car is `target' and cdr a plist with > -`:begin', `:end', `:contents-begin', `:contents-end', `raw-value' > -and `:post-blank' as keywords. > +`:begin', `:end', `:contents-begin', `:contents-end', `value' and > +`:post-blank' as keywords. > > Assume point is at the target." > (save-excursion > (looking-at org-target-regexp) > (let ((begin (point)) > - (contents-begin (match-beginning 1)) > - (contents-end (match-end 1)) > - (raw-value (org-match-string-no-properties 1)) > + (value (org-match-string-no-properties 1)) > (post-blank (progn (goto-char (match-end 0)) > (skip-chars-forward " \t"))) > (end (point))) > `(target > (:begin ,begin > :end ,end > - :contents-begin ,contents-begin > - :contents-end ,contents-end > - :raw-value ,raw-value > + :value ,value > :post-blank ,post-blank))))) > > (defun org-element-target-interpreter (target contents) > @@ -2470,7 +2463,7 @@ Sharing the same successor comes handy when, for example, the > regexp matching one object can also match the other object.") > > (defconst org-element-recursive-objects > - '(emphasis link macro subscript superscript target radio-target) > + '(emphasis link macro subscript superscript radio-target) > "List of recursive object types.") > > (defconst org-element-non-recursive-block-alist > @@ -2540,8 +2533,7 @@ This list is checked after translations have been applied. See > (subscript entity export-snippet inline-babel-call inline-src-block > latex-fragment sub/superscript text-markup) > (superscript entity export-snippet inline-babel-call inline-src-block > - latex-fragment sub/superscript text-markup) > - (target entity export-snippet latex-fragment sub/superscript text-markup)) > + latex-fragment sub/superscript text-markup)) > "Alist of recursive objects restrictions. > > CAR is a recursive object type and CDR is a list of successors > diff --git a/contrib/lisp/org-export.el b/contrib/lisp/org-export.el > index 34f290e..aa6ca52 100644 > --- a/contrib/lisp/org-export.el > +++ b/contrib/lisp/org-export.el > @@ -1278,7 +1278,13 @@ Following tree properties are set: > `(:parse-tree > ,data > :target-list > - ,(org-element-map data 'target (lambda (target local) target) info) > + ,(org-element-map > + data '(keyword target) > + (lambda (blob local) > + (when (or (eq (car blob) 'target) > + (string= (downcase (org-element-get-property :key blob)) > + "target")) > + blob)) info) > :headline-numbering ,(org-export-collect-headline-numbering data info) > :back-end ,backend) > info)) > @@ -2579,8 +2585,11 @@ INFO is a plist holding contextual information. > > Return value can be an object, an element, or nil: > > -- If LINK path exactly matches any target, return the target > - object. > +- If LINK path matches a target object (i.e. <<path>>) or > + element (i.e. \"#+target: path\"), return it. > + > +- If LINK path exactly matches the name affiliated keyword > + \(i.e. #+name: path) of an element, return that element. > > - If LINK path exactly matches any headline name, return that > element. If more than one headline share that name, priority > @@ -2591,39 +2600,53 @@ Return value can be an object, an element, or nil: > > Assume LINK type is \"fuzzy\"." > (let ((path (org-element-get-property :path link))) > - ;; Link points to a target: return it. > - (or (loop for target in (plist-get info :target-list) > - when (string= (org-element-get-property :raw-value target) path) > - return target) > - ;; Link either points to an headline or nothing. Try to find > - ;; the source, with priority given to headlines with the closest > - ;; common ancestor. If such candidate is found, return its > - ;; beginning position as an unique identifier, otherwise return > - ;; nil. > - (let ((find-headline > - (function > - ;; Return first headline whose `:raw-value' property > - ;; is NAME in parse tree DATA, or nil. > - (lambda (name data) > - (org-element-map > - data 'headline > - (lambda (headline local) > - (when (string= > - (org-element-get-property :raw-value headline) > - name) > - headline)) > - info 'first-match))))) > - ;; Search among headlines sharing an ancestor with link, > - ;; from closest to farthest. > - (or (catch 'exit > - (mapc > - (lambda (parent) > - (when (eq (car parent) 'headline) > - (let ((foundp (funcall find-headline path parent))) > - (when foundp (throw 'exit foundp))))) > - (plist-get info :genealogy)) nil) > - ;; No match with a common ancestor: try the full parse-tree. > - (funcall find-headline path (plist-get info :parse-tree))))))) > + (cond > + ;; First try to find a matching "<<path>>" unless user specified > + ;; he was looking for an headline (path starts with a * > + ;; character). > + ((and (not (eq (substring path 0 1) ?*)) > + (loop for target in (plist-get info :target-list) > + when (string= (org-element-get-property :value target) path) > + return target))) > + ;; Then try to find an element with a matching "#+name: path" > + ;; affiliated keyword. > + ((and (not (eq (substring path 0 1) ?*)) > + (org-element-map > + (plist-get info :parse-tree) org-element-all-elements > + (lambda (el local) > + (when (string= (org-element-get-property :name el) path) el)) > + info 'first-match))) > + ;; Last case: link either points to an headline or to > + ;; nothingness. Try to find the source, with priority given to > + ;; headlines with the closest common ancestor. If such candidate > + ;; is found, return its beginning position as an unique > + ;; identifier, otherwise return nil. > + (t > + (let ((find-headline > + (function > + ;; Return first headline whose `:raw-value' property is > + ;; NAME in parse tree DATA, or nil. > + (lambda (name data) > + (org-element-map > + data 'headline > + (lambda (headline local) > + (when (string= > + (org-element-get-property :raw-value headline) > + name) > + headline)) > + info 'first-match))))) > + ;; Search among headlines sharing an ancestor with link, from > + ;; closest to farthest. > + (or (catch 'exit > + (mapc > + (lambda (parent) > + (when (eq (car parent) 'headline) > + (let ((foundp (funcall find-headline path parent))) > + (when foundp (throw 'exit foundp))))) > + (plist-get info :genealogy)) nil) > + ;; No match with a common ancestor: try the full > + ;; parse-tree. > + (funcall find-headline path (plist-get info :parse-tree)))))))) > > (defun org-export-resolve-id-link (link info) > "Return headline referenced as LINK destination. > @@ -2641,20 +2664,6 @@ is either \"id\" or \"custom-id\"." > headline)) > info 'first-match))) > > -(defun org-export-resolve-ref-link (link info) > - "Return element referenced as LINK destination. > - > -INFO is a plist used as a communication channel. > - > -Assume LINK type is \"ref\" and. Return value is the first > -element whose `:name' property matches LINK's `:path', or nil." > - (let ((name (org-element-get-property :path link))) > - (org-element-map > - (plist-get info :parse-tree) org-element-all-elements > - (lambda (el local) > - (when (string= (org-element-get-property :name el) name) el)) > - info 'first-match))) > - > (defun org-export-resolve-coderef (ref info) > "Resolve a code reference REF. > > @@ -2745,27 +2754,61 @@ Optional argument PREDICATE is a function returning a non-nil > value if the current element or object should be counted in. It > accepts one argument: the element or object being considered. > This argument allows to count only a certain type of objects, > -like inline images, which are a subset of links \(in that case, > -`org-export-inline-image-p' might be an useful predicate\)." > - (let ((counter 0) > - ;; Determine if search should apply to current section, in > - ;; which case it should be retrieved first, or to full parse > - ;; tree. As a special case, an element or object without > - ;; a parent headline will also trigger a full search, > - ;; notwithstanding WITHIN-SECTION value. > - (data > - (if (not within-section) (plist-get info :parse-tree) > - (or (org-export-get-parent-headline element info) > - (plist-get info :parse-tree))))) > - ;; Increment counter until ELEMENT is found again. > - (org-element-map > - data (or types (car element)) > - (lambda (el local) > - (cond > - ((equal element el) (1+ counter)) > - ((not predicate) (incf counter) nil) > - ((funcall predicate el) (incf counter) nil))) > - info 'first-match))) > +like inline images, which are a subset of links (in that case, > +`org-export-inline-image-p' might be an useful predicate). > + > +Return value is a list of numbers if ELEMENT is an headline or an > +item. It is nil for keywords. It represents the footnote number > +for footnote definitions and footnote references. If ELEMENT is > +a target, return the same value as if ELEMENT was the closest > +table, item or headline containing the target. In any other > +case, return the sequence number of ELEMENT among elements or > +objects of the same type." > + ;; A target keyword, representing an invisible target, never has > + ;; a sequence number. > + (unless (eq (car element) 'keyword) > + ;; Ordinal of a target object is the ordinal of the closest table, > + ;; item, or headline containing the object. > + (when (eq (car element) 'target) > + (setq element > + (loop for parent in (org-export-get-genealogy element info) > + when > + (memq > + (car parent) > + '(footnote-definition footnote-reference headline item table)) > + return parent))) > + (case (car element) > + ;; Special case 1: An headline returns its number as a list. > + (headline (org-export-get-headline-number element info)) > + ;; Special case 2: An item returns its number as a list. > + (item (let ((struct (org-element-get-property :structure element))) > + (org-list-get-item-number > + (org-element-get-property :begin element) > + struct > + (org-list-prevs-alist struct) > + (org-list-parents-alist struct)))) > + ((footnote definition footnote-reference) > + (org-export-get-footnote-number element info)) > + (otherwise > + (let ((counter 0) > + ;; Determine if search should apply to current section, > + ;; in which case it should be retrieved first, or to full > + ;; parse tree. As a special case, an element or object > + ;; without a parent headline will also trigger a full > + ;; search, notwithstanding WITHIN-SECTION value. > + (data > + (if (not within-section) (plist-get info :parse-tree) > + (or (org-export-get-parent-headline element info) > + (plist-get info :parse-tree))))) > + ;; Increment counter until ELEMENT is found again. > + (org-element-map > + data (or types (car element)) > + (lambda (el local) > + (cond > + ((equal element el) (1+ counter)) > + ((not predicate) (incf counter) nil) > + ((funcall predicate el) (incf counter) nil))) > + info 'first-match)))))) > > > ;;;; For Src-Blocks > diff --git a/lisp/org.el b/lisp/org.el > index a81f7fc..6708801 100644 > --- a/lisp/org.el > +++ b/lisp/org.el > @@ -9896,6 +9896,22 @@ visibility around point, thus ignoring > pos (match-beginning 0)))) > ;; There is an exact target for this > (goto-char pos)) > + ((save-excursion > + (goto-char (point-min)) > + (and > + (re-search-forward > + (format "^[ \t]*#\\+target: %s" (regexp-quote s0)) nil t) > + (setq type 'dedicated pos (match-beginning 0)))) > + ;; Found an invisible target. > + (goto-char pos)) > + ((save-excursion > + (goto-char (point-min)) > + (and > + (re-search-forward > + (format "^[ \t]*#\\+name: %s" (regexp-quote s0)) nil t) > + (setq type 'dedicated pos (match-beginning 0)))) > + ;; Found an element with a matching name. > + (goto-char pos)) > ((and (string-match "^(\\(.*\\))$" s0) > (save-excursion > (goto-char (point-min)) -- Thomas S. Dye http://www.tsdye.com ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [dev] Implement "ref" link types 2012-02-20 22:06 ` Nicolas Goaziou 2012-02-21 1:26 ` Thomas S. Dye @ 2012-02-21 5:14 ` David Maus 2012-02-21 9:18 ` Nicolas Goaziou 1 sibling, 1 reply; 19+ messages in thread From: David Maus @ 2012-02-21 5:14 UTC (permalink / raw) To: Nicolas Goaziou; +Cc: Org Mode List, Carsten Dominik [-- Attachment #1: Type: text/plain, Size: 1095 bytes --] At Mon, 20 Feb 2012 23:06:32 +0100, Nicolas Goaziou wrote: > > Completing myself, here is a patch implementing the previous suggestion, > along with example output obtained with it. You may need to > (fmakunbound 'org-e-ascii-target) to avoid an error, since this patch > removes the function. I don't see why we should drop the link type in fuzzy links. After all they /are/ are special type of link. Without the link type we will run into trouble, won't we?. In the example file: ,---- | We end the list at item [[itm:last]]. `---- So, itm:last is a fuzzy link but it could as well be a "regular" link of type "itm" with a path component of "last" and no description. If we say that we use itm:last as a fuzzy link iff there is no registered link type "itm" we might put people into trouble if in some point in the future Org mode introduces a link of type "itm" and the fuzzy links stop working. Or is there any technical reason to use [[itm:last]] instead of [[ref:itm:last]]? Best, -- David -- OpenPGP... 0x99ADB83B5A4478E6 Jabber.... dmjena@jabber.org Email..... dmaus@ictsoc.de [-- Attachment #2: Type: application/pgp-signature, Size: 230 bytes --] ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [dev] Implement "ref" link types 2012-02-21 5:14 ` David Maus @ 2012-02-21 9:18 ` Nicolas Goaziou 2012-02-27 19:38 ` Nicolas Goaziou 2012-02-27 20:38 ` David Maus 0 siblings, 2 replies; 19+ messages in thread From: Nicolas Goaziou @ 2012-02-21 9:18 UTC (permalink / raw) To: David Maus; +Cc: Org Mode List, Carsten Dominik Hello, David Maus <dmaus@ictsoc.de> writes: > I don't see why we should drop the link type in fuzzy links. After all > they /are/ are special type of link. There is no link type in fuzzy links : [[something]] matches <<something>> in master. > Without the link type we will run into trouble, won't we?. > > In the example file: > > ,---- > | We end the list at item [[itm:last]]. > `---- > > So, itm:last is a fuzzy link but it could as well be a "regular" link > of type "itm" with a path component of "last" and no description. I realize my examples are confusing. I shouldn't have used colons. In fact, the output will be the same if the target is <<itm-last>>, <<table-last>> or even <<foo>>. In other words, the "itm:" part wasn't meant as a link type, but as a cosmetic part of the name. So the list example could as well be: #+begin_src org 1. A first item in a list. 2. Another item 1. With three subparts. This one. 2. Another one. 3. <<last>> And the last subpart. We end the list at item [[last]]. #+end_src > If we say that we use itm:last as a fuzzy link iff there is no > registered link type "itm" we might put people into trouble if in some > point in the future Org mode introduces a link of type "itm" and the > fuzzy links stop working. Certainly. I should have used another name for my examples. The point is that a fuzzy link [[foo]] will detect the context of its matching target (<<foo>>) or element (#+name: foo) and return an appropriate sequence number. The main difference with the current behaviour is that the target part is ignored. At the moment, in LaTeX, <<path>> becomes \label{path}path. It will be only \label{path} with the patch. This allows for more flexibility (note that other types of targets, like radio targets are unchanged). > Or is there any technical reason to use [[itm:last]] instead of > [[ref:itm:last]]? I won't answer this question, since it came out from a mistake of mine. Though, I'd answer another one: "Why do I prefer [[something]] over [[ref:something]]?". Thank you for asking this... Well, that's because Org also recognizes plain links, i.e. http//orgmode.org. And, for an internal link, I'd rather enforce the use of square brackets, as it is done actually. Also, we don't require extra syntax, unless we don't want to change behaviour of targets. Regards, -- Nicolas Goaziou ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [dev] Implement "ref" link types 2012-02-21 9:18 ` Nicolas Goaziou @ 2012-02-27 19:38 ` Nicolas Goaziou 2012-02-27 20:38 ` David Maus 1 sibling, 0 replies; 19+ messages in thread From: Nicolas Goaziou @ 2012-02-27 19:38 UTC (permalink / raw) To: David Maus; +Cc: Org Mode List, Carsten Dominik [-- Attachment #1: Type: text/plain, Size: 247 bytes --] Hello, Here is a new version of the patch built on top of master, along with test cases. If there is no objection, I'll push it to master in a couple of days. I really think that's a great feature to have in Org. Regards, -- Nicolas Goaziou [-- Warning: decoded text below may be mangled, UTF-8 assumed --] [-- Attachment #2: numbered cross references --] [-- Type: text/x-patch, Size: 27354 bytes --] From 2fdde87bb7f1241f3d24dbd8ae030a300fe8f0fc Mon Sep 17 00:00:00 2001 From: Nicolas Goaziou <n.goaziou@gmail.com> Date: Mon, 20 Feb 2012 22:24:38 +0100 Subject: [PATCH] Implement numbered cross-references * lisp/org.el (org-link-search): Search for #+name affiliated keywords and invisible targets. * contrib/lisp/org-element.el (org-element-link-parser): Remove "ref" links relative part. (org-element-target-parser): Move property name from `:raw-value' to `:value'. (org-element-recursive-objects): Remove targets from tables. Cells are not parsed unless explicitely asked by back-end developer, too late. A target wouldn't be noticed in time. One solution could be to parse every table, but that's time consumming. (org-element-object-restrictions): Target are not recursive anymore. * contrib/lisp/org-export.el (org-export-resolve-fuzzy-link): Find elements with a matching "#+name: path" affiliated keyword. (org-export-get-ordinal): Make special cases for headlines, items, footnotes definitions and references. (org-export-resolve-ref-link): Removed function. * EXPERIMENTAL/org-e-latex.el (org-e-latex-link): Handle cross-reference numbers. (org-e-latex-target): Targets have no contents. * EXPERIMENTAL/org-e-ascii.el (org-e-ascii--describe-links): Ignore fuzzy links in link description at the end of the section. (org-e-ascii-link): Handle cross-reference numbers. * testing/contrib/lisp/test-org-export.el: Add tests. * testing/lisp/test-org.el: Add tests. --- EXPERIMENTAL/org-e-ascii.el | 80 ++++++++---------- EXPERIMENTAL/org-e-latex.el | 50 +++++------ contrib/lisp/org-element.el | 20 ++--- contrib/lisp/org-export.el | 141 ++++++++++++++++++++----------- lisp/org.el | 16 ++++ testing/contrib/lisp/test-org-export.el | 91 ++++++++++++++++++++ testing/lisp/test-org.el | 41 +++++++++ 7 files changed, 299 insertions(+), 140 deletions(-) diff --git a/EXPERIMENTAL/org-e-ascii.el b/EXPERIMENTAL/org-e-ascii.el index 0eb547b..c9cca4a 100644 --- a/EXPERIMENTAL/org-e-ascii.el +++ b/EXPERIMENTAL/org-e-ascii.el @@ -825,28 +825,24 @@ channel." (if (not desc) (org-element-property :raw-link link) (org-export-secondary-string desc 'e-ascii info))))) (cond - ;; Coderefs, radio links and ref links are ignored. - ((member type '("coderef" "radio" "ref")) nil) - ;; Id, custom-id and fuzzy links (with the exception of - ;; targets): Headlines refer to their numbering. - ((member type '("custom-id" "fuzzy" "id")) - (let ((destination (if (string= type "fuzzy") - (org-export-resolve-fuzzy-link link info) - (org-export-resolve-id-link link info)))) - (unless (eq (org-element-type destination) 'target) - (concat - (org-e-ascii--fill-string - (format - "[%s] %s" - anchor - (if (not destination) - (org-e-ascii--translate "Unknown reference" info) - (format - (org-e-ascii--translate "See section %s" info) - (mapconcat 'number-to-string - (org-export-get-headline-number destination info) - ".")))) - width info) "\n\n")))) + ;; Coderefs, radio links and fuzzy links are ignored. + ((member type '("coderef" "radio" "fuzzy")) nil) + ;; Id and custom-id links: Headlines refer to their numbering. + ((member type '("custom-id" "id")) + (let ((destination (org-export-resolve-id-link link info))) + (concat + (org-e-ascii--fill-string + (format + "[%s] %s" + anchor + (if (not destination) + (org-e-ascii--translate "Unknown reference" info) + (format + (org-e-ascii--translate "See section %s" info) + (mapconcat 'number-to-string + (org-export-get-headline-number destination info) + ".")))) + width info) "\n\n"))) ;; Do not add a link that cannot be resolved and doesn't have ;; any description: destination is already visible in the ;; paragraph. @@ -1390,29 +1386,23 @@ INFO is a plist holding contextual information." (org-element-property :path link) (cdr (assq 'radio-target org-element-object-restrictions))) 'e-ascii info)) - ;; Ref link: If there's no description (DESC, return link's - ;; destination sequence number among elements of same - ;; type. Otherwise, use DESC. - ((string= type "ref") - (if (org-string-nw-p desc) desc - (format "%d" - (org-export-get-ordinal - (org-export-resolve-ref-link link info) - info nil nil - (lambda (el) (or (org-element-property :caption el) - (org-element-property :name el))))))) ;; Do not apply a special syntax on fuzzy links pointing to ;; targets. - ((and (string= type "fuzzy") - (let ((path (org-element-property :path link))) - (loop for target in (plist-get info :target-list) - thereis (string= - (org-element-property :raw-value target) - path)))) - (if (org-string-nw-p desc) desc raw-link)) + ((string= type "fuzzy") + (let ((destination (org-export-resolve-fuzzy-link link info))) + ;; Ignore invisible "#+target: path". + (unless (eq (org-element-type destination) 'keyword) + (if (org-string-nw-p desc) desc + (when destination + (let ((number (org-export-get-ordinal destination info))) + (when number + (if (atom number) (number-to-string number) + (mapconcat 'number-to-string number "."))))))))) (t - (concat (format "[%s]" (if (org-string-nw-p desc) desc raw-link)) - (unless org-e-ascii-links-to-notes (format " (%s)" raw-link))))))) + (if (not (org-string-nw-p desc)) (format "[%s]" raw-link) + (concat + (format "[%s]" desc) + (unless org-e-ascii-links-to-notes (format " (%s)" raw-link)))))))) ;;;; Macro @@ -1850,11 +1840,7 @@ INFO is a plist used as a communication channel." ;;;; Target -(defun org-e-ascii-target (target contents info) - "Transcode a TARGET object from Org to ASCII. -CONTENTS is the contents of the target. INFO is a plist holding -contextual information." - contents) +;; Targets are invisible. ;;;; Time-stamp diff --git a/EXPERIMENTAL/org-e-latex.el b/EXPERIMENTAL/org-e-latex.el index 43bbde7..01b8ee2 100644 --- a/EXPERIMENTAL/org-e-latex.el +++ b/EXPERIMENTAL/org-e-latex.el @@ -1291,8 +1291,8 @@ CONTENTS is nil. INFO is a plist holding contextual information." (cond ((string= key "latex") value) ((string= key "index") (format "\\index{%s}" value)) - ((string= key "target") - (format "\\label{%s}" (org-export-solidify-link-text value))) + ;; Invisible targets. + ((string= key "target") nil) ((string= key "toc") (let ((value (downcase value))) (cond @@ -1451,32 +1451,26 @@ INFO is a plist holding contextual information. See (org-element-parse-secondary-string path (cdr (assq 'radio-target org-element-object-restrictions))) 'e-latex info))) - ;; Ref link: If no description is provided, reference label PATH - ;; and display table number. Otherwise move to label but display - ;; description instead. - ((string= type "ref") - (if (not desc) (format "\\ref{%s}" path) - (format "\\hyperref[%s]{%s}" path desc))) ;; Links pointing to an headline: Find destination and build ;; appropriate referencing command. ((member type '("custom-id" "fuzzy" "id")) (let ((destination (if (string= type "fuzzy") (org-export-resolve-fuzzy-link link info) (org-export-resolve-id-link link info)))) - ;; Fuzzy link points to a target. Do as above. (case (org-element-type destination) - (target - (format "\\hyperref[%s]{%s}" - (org-export-solidify-link-text - (org-element-property :raw-value destination)) + ;; Fuzzy link points nowhere. + ('nil + (format "\\texttt{%s}" (or desc (org-export-secondary-string (org-element-property :raw-link link) 'e-latex info)))) - ;; Fuzzy link points to an headline. If headlines are - ;; numbered and the link has no description, display - ;; headline's number. Otherwise, display description or - ;; headline's title. + ;; Fuzzy link points to an invisible target. + (keyword nil) + ;; LINK points to an headline. If headlines are numbered + ;; and the link has no description, display headline's + ;; number. Otherwise, display description or headline's + ;; title. (headline (let ((label (format "sec-%s" @@ -1491,13 +1485,11 @@ INFO is a plist holding contextual information. See (org-export-secondary-string (org-element-property :title destination) 'e-latex info)))))) - ;; Fuzzy link points nowhere. + ;; Fuzzy link points to a target. Do as above. (otherwise - (format "\\texttt{%s}" - (or desc - (org-export-secondary-string - (org-element-property :raw-link link) - 'e-latex info))))))) + (let ((path (org-export-solidify-link-text path))) + (if (not desc) (format "\\ref{%s}" path) + (format "\\hyperref[%s]{%s}" path desc))))))) ;; Coderef: replace link with the reference name or the ;; equivalent line number. ((string= type "coderef") @@ -1970,14 +1962,12 @@ CONTENTS is nil. INFO is a plist holding contextual information." ;;;; Target -(defun org-e-latex-target (target text info) +(defun org-e-latex-target (target contents info) "Transcode a TARGET object from Org to LaTeX. -TEXT is the text of the target. INFO is a plist holding -contextual information." - (format "\\label{%s}%s" - (org-export-solidify-link-text - (org-element-property :raw-value target)) - text)) +CONTENTS is nil. INFO is a plist holding contextual +information." + (format "\\label{%s}" + (org-export-solidify-link-text (org-element-property :value target)))) ;;;; Time-stamp diff --git a/contrib/lisp/org-element.el b/contrib/lisp/org-element.el index 4e5e7fd..de5d4ea 100644 --- a/contrib/lisp/org-element.el +++ b/contrib/lisp/org-element.el @@ -1967,9 +1967,6 @@ Assume point is at the beginning of the link." ;; Explicit type (http, irc, bbdb...). See `org-link-types'. ((string-match org-link-re-with-space3 link) (setq type (match-string 1 link) path (match-string 2 link))) - ;; Ref type: PATH is the name of the target element. - ((string-match "^ref:\\(.*\\)" link) - (setq type "ref" path (org-trim (match-string 1 link)))) ;; Id type: PATH is the id. ((string-match "^id:\\([-a-f0-9]+\\)" link) (setq type "id" path (match-string 1 link))) @@ -2269,25 +2266,21 @@ CONTENTS is the contents of the object." "Parse target at point. Return a list whose car is `target' and cdr a plist with -`:begin', `:end', `:contents-begin', `:contents-end', `raw-value' -and `:post-blank' as keywords. +`:begin', `:end', `:contents-begin', `:contents-end', `value' and +`:post-blank' as keywords. Assume point is at the target." (save-excursion (looking-at org-target-regexp) (let ((begin (point)) - (contents-begin (match-beginning 1)) - (contents-end (match-end 1)) - (raw-value (org-match-string-no-properties 1)) + (value (org-match-string-no-properties 1)) (post-blank (progn (goto-char (match-end 0)) (skip-chars-forward " \t"))) (end (point))) `(target (:begin ,begin :end ,end - :contents-begin ,contents-begin - :contents-end ,contents-end - :raw-value ,raw-value + :value ,value :post-blank ,post-blank))))) (defun org-element-target-interpreter (target contents) @@ -2481,7 +2474,7 @@ regexp matching one object can also match the other object.") "Complete list of object types.") (defconst org-element-recursive-objects - '(emphasis link macro subscript superscript target radio-target) + '(emphasis link macro subscript superscript radio-target) "List of recursive object types.") (defconst org-element-non-recursive-block-alist @@ -2551,8 +2544,7 @@ This list is checked after translations have been applied. See (subscript entity export-snippet inline-babel-call inline-src-block latex-fragment sub/superscript text-markup) (superscript entity export-snippet inline-babel-call inline-src-block - latex-fragment sub/superscript text-markup) - (target entity export-snippet latex-fragment sub/superscript text-markup)) + latex-fragment sub/superscript text-markup)) "Alist of recursive objects restrictions. CAR is a recursive object type and CDR is a list of successors diff --git a/contrib/lisp/org-export.el b/contrib/lisp/org-export.el index b809758..e7cca50 100644 --- a/contrib/lisp/org-export.el +++ b/contrib/lisp/org-export.el @@ -1289,7 +1289,13 @@ Following tree properties are set: `(:parse-tree ,data :target-list - ,(org-element-map data 'target 'identity info) + ,(org-element-map + data '(keyword target) + (lambda (blob) + (when (or (eq (org-element-type blob) 'target) + (string= (upcase (org-element-property :key blob)) + "TARGET")) + blob)) info) :headline-numbering ,(org-export-collect-headline-numbering data info) :back-end ,backend) info)) @@ -2697,8 +2703,11 @@ INFO is a plist holding contextual information. Return value can be an object, an element, or nil: -- If LINK path exactly matches any target, return the target - object. +- If LINK path matches a target object (i.e. <<path>>) or + element (i.e. \"#+target: path\"), return it. + +- If LINK path exactly matches the name affiliated keyword + \(i.e. #+name: path) of an element, return that element. - If LINK path exactly matches any headline name, return that element. If more than one headline share that name, priority @@ -2709,16 +2718,29 @@ Return value can be an object, an element, or nil: Assume LINK type is \"fuzzy\"." (let ((path (org-element-property :path link))) - ;; Link points to a target: return it. - (or (loop for target in (plist-get info :target-list) - when (string= (org-element-property :raw-value target) path) - return target) - ;; Link either points to an headline or nothing. Try to find - ;; the source, with priority given to headlines with the closest - ;; common ancestor. If such candidate is found, return its - ;; beginning position as an unique identifier, otherwise return - ;; nil. - (let ((find-headline + (cond + ;; First try to find a matching "<<path>>" unless user specified + ;; he was looking for an headline (path starts with a * + ;; character). + ((and (not (eq (substring path 0 1) ?*)) + (loop for target in (plist-get info :target-list) + when (string= (org-element-property :value target) path) + return target))) + ;; Then try to find an element with a matching "#+name: path" + ;; affiliated keyword. + ((and (not (eq (substring path 0 1) ?*)) + (org-element-map + (plist-get info :parse-tree) org-element-all-elements + (lambda (el) + (when (string= (org-element-property :name el) path) el)) + info 'first-match))) + ;; Last case: link either points to an headline or to + ;; nothingness. Try to find the source, with priority given to + ;; headlines with the closest common ancestor. If such candidate + ;; is found, return its beginning position as an unique + ;; identifier, otherwise return nil. + (t + (let ((find-headline (function ;; Return first headline whose `:raw-value' property ;; is NAME in parse tree DATA, or nil. @@ -2741,7 +2763,7 @@ Assume LINK type is \"fuzzy\"." (when foundp (throw 'exit foundp))))) (org-export-get-genealogy link info)) nil) ;; No match with a common ancestor: try the full parse-tree. - (funcall find-headline path (plist-get info :parse-tree))))))) + (funcall find-headline path (plist-get info :parse-tree)))))))) (defun org-export-resolve-id-link (link info) "Return headline referenced as LINK destination. @@ -2759,20 +2781,6 @@ is either \"id\" or \"custom-id\"." headline)) info 'first-match))) -(defun org-export-resolve-ref-link (link info) - "Return element referenced as LINK destination. - -INFO is a plist used as a communication channel. - -Assume LINK type is \"ref\" and. Return value is the first -element whose `:name' property matches LINK's `:path', or nil." - (let ((name (org-element-property :path link))) - (org-element-map - (plist-get info :parse-tree) org-element-all-elements - (lambda (el) - (when (string= (org-element-property :name el) name) el)) - info 'first-match))) - (defun org-export-resolve-coderef (ref info) "Resolve a code reference REF. @@ -2863,27 +2871,62 @@ Optional argument PREDICATE is a function returning a non-nil value if the current element or object should be counted in. It accepts one argument: the element or object being considered. This argument allows to count only a certain type of objects, -like inline images, which are a subset of links \(in that case, -`org-export-inline-image-p' might be an useful predicate\)." - (let ((counter 0) - ;; Determine if search should apply to current section, in - ;; which case it should be retrieved first, or to full parse - ;; tree. As a special case, an element or object without - ;; a parent headline will also trigger a full search, - ;; notwithstanding WITHIN-SECTION value. - (data - (if (not within-section) (plist-get info :parse-tree) - (or (org-export-get-parent-headline element info) - (plist-get info :parse-tree))))) - ;; Increment counter until ELEMENT is found again. - (org-element-map - data (or types (org-element-type element)) - (lambda (el) - (cond - ((equal element el) (1+ counter)) - ((not predicate) (incf counter) nil) - ((funcall predicate el) (incf counter) nil))) - info 'first-match))) +like inline images, which are a subset of links (in that case, +`org-export-inline-image-p' might be an useful predicate). + +Return value is a list of numbers if ELEMENT is an headline or an +item. It is nil for keywords. It represents the footnote number +for footnote definitions and footnote references. If ELEMENT is +a target, return the same value as if ELEMENT was the closest +table, item or headline containing the target. In any other +case, return the sequence number of ELEMENT among elements or +objects of the same type." + ;; A target keyword, representing an invisible target, never has + ;; a sequence number. + (unless (eq (org-element-type element) 'keyword) + ;; Ordinal of a target object refer to the ordinal of the closest + ;; table, item, or headline containing the object. + (when (eq (org-element-type element) 'target) + (setq element + (loop for parent in (org-export-get-genealogy element info) + when + (memq + (org-element-type parent) + '(footnote-definition footnote-reference headline item + table)) + return parent))) + (case (org-element-type element) + ;; Special case 1: An headline returns its number as a list. + (headline (org-export-get-headline-number element info)) + ;; Special case 2: An item returns its number as a list. + (item (let ((struct (org-element-property :structure element))) + (org-list-get-item-number + (org-element-property :begin element) + struct + (org-list-prevs-alist struct) + (org-list-parents-alist struct)))) + ((footnote definition footnote-reference) + (org-export-get-footnote-number element info)) + (otherwise + (let ((counter 0) + ;; Determine if search should apply to current section, + ;; in which case it should be retrieved first, or to full + ;; parse tree. As a special case, an element or object + ;; without a parent headline will also trigger a full + ;; search, notwithstanding WITHIN-SECTION value. + (data + (if (not within-section) (plist-get info :parse-tree) + (or (org-export-get-parent-headline element info) + (plist-get info :parse-tree))))) + ;; Increment counter until ELEMENT is found again. + (org-element-map + data (or types (org-element-type element)) + (lambda (el) + (cond + ((equal element el) (1+ counter)) + ((not predicate) (incf counter) nil) + ((funcall predicate el) (incf counter) nil))) + info 'first-match)))))) ;;;; For Src-Blocks diff --git a/lisp/org.el b/lisp/org.el index a81f7fc..b8dd292 100644 --- a/lisp/org.el +++ b/lisp/org.el @@ -9896,6 +9896,22 @@ visibility around point, thus ignoring pos (match-beginning 0)))) ;; There is an exact target for this (goto-char pos)) + ((save-excursion + (goto-char (point-min)) + (and + (re-search-forward + (format "^[ \t]*#\\+TARGET: %s" (regexp-quote s0)) nil t) + (setq type 'dedicated pos (match-beginning 0)))) + ;; Found an invisible target. + (goto-char pos)) + ((save-excursion + (goto-char (point-min)) + (and + (re-search-forward + (format "^[ \t]*#\\+NAME: %s" (regexp-quote s0)) nil t) + (setq type 'dedicated pos (match-beginning 0)))) + ;; Found an element with a matching #+name affiliated keyword. + (goto-char pos)) ((and (string-match "^(\\(.*\\))$" s0) (save-excursion (goto-char (point-min)) diff --git a/testing/contrib/lisp/test-org-export.el b/testing/contrib/lisp/test-org-export.el index c9923d4..f791391 100644 --- a/testing/contrib/lisp/test-org-export.el +++ b/testing/contrib/lisp/test-org-export.el @@ -369,3 +369,94 @@ body\n"))) ;; Both footnotes should be seen. (should (= (length (org-export-collect-footnote-definitions tree info)) 2)))))) + +(ert-deftest test-org-export/fuzzy-links () + "Test fuzz link export specifications." + ;; 1. Links to invisible (keyword) targets should be ignored. + (org-test-with-temp-text + "Paragraph.\n#+TARGET: Test\n[[Test]]" + (let* ((tree (org-element-parse-buffer)) + (info (org-combine-plists (org-export-initial-options)))) + (setq info (org-combine-plists + info (org-export-collect-tree-properties tree info 'test))) + (should-not + (org-element-map + tree 'link + (lambda (link) + (org-export-get-ordinal + (org-export-resolve-fuzzy-link link info) info)) info)))) + ;; 2. Link to an headline should return headline's number. + (org-test-with-temp-text + "Paragraph.\n* Head1\n* Head2\n* Head3\n[[Head2]]" + (let* ((tree (org-element-parse-buffer)) + (info (org-combine-plists (org-export-initial-options)))) + (setq info (org-combine-plists + info (org-export-collect-tree-properties tree info 'test))) + (should + ;; Note: Headline's number is in fact a list of numbers. + (equal '(2) + (org-element-map + tree 'link + (lambda (link) + (org-export-get-ordinal + (org-export-resolve-fuzzy-link link info) info)) info t))))) + ;; 3. Link to a target in an item should return item's number. + (org-test-with-temp-text + "- Item1\n - Item11\n - <<test>>Item12\n- Item2\n\n\n[[test]]" + (let* ((tree (org-element-parse-buffer)) + (info (org-combine-plists (org-export-initial-options)))) + (setq info (org-combine-plists + info (org-export-collect-tree-properties tree info 'test))) + (should + ;; Note: Item's number is in fact a list of numbers. + (equal '(1 2) + (org-element-map + tree 'link + (lambda (link) + (org-export-get-ordinal + (org-export-resolve-fuzzy-link link info) info)) info t))))) + ;; 4. Link to a target in a footnote should return footnote's + ;; number. + (org-test-with-temp-text + "Paragraph[1][2][fn:lbl3:C<<target>>][[test]][[target]]\n[1] A\n\n[2] <<test>>B" + (let* ((tree (org-element-parse-buffer)) + (info (org-combine-plists (org-export-initial-options)))) + (setq info (org-combine-plists + info (org-export-collect-tree-properties tree info 'test))) + (should + (equal '(2 3) + (org-element-map + tree 'link + (lambda (link) + (org-export-get-ordinal + (org-export-resolve-fuzzy-link link info) info)) info))))) + ;; 5. Link to a named element should return sequence number of that + ;; element. + (org-test-with-temp-text + "#+NAME: tbl1\n|1|2|\n#+NAME: tbl2\n|3|4|\n#+NAME: tbl3\n|5|6|\n[[tbl2]]" + (let* ((tree (org-element-parse-buffer)) + (info (org-combine-plists (org-export-initial-options)))) + (setq info (org-combine-plists + info (org-export-collect-tree-properties tree info 'test))) + (should + (= 2 + (org-element-map + tree 'link + (lambda (link) + (org-export-get-ordinal + (org-export-resolve-fuzzy-link link info) info)) info t))))) + ;; 6. Link to a target not within an item, a table, a footnote + ;; reference or definition should return section number. + (org-test-with-temp-text + "* Head1\n* Head2\nParagraph<<target>>\n* Head3\n[[target]]" + (let* ((tree (org-element-parse-buffer)) + (info (org-combine-plists (org-export-initial-options)))) + (setq info (org-combine-plists + info (org-export-collect-tree-properties tree info 'test))) + (should + (equal '(2) + (org-element-map + tree 'link + (lambda (link) + (org-export-get-ordinal + (org-export-resolve-fuzzy-link link info) info)) info t)))))) diff --git a/testing/lisp/test-org.el b/testing/lisp/test-org.el index 4fc9ac9..3639367 100644 --- a/testing/lisp/test-org.el +++ b/testing/lisp/test-org.el @@ -98,6 +98,47 @@ http://article.gmane.org/gmane.emacs.orgmode/21459/" (org-babel-next-src-block) (should (equal '(2 1) (org-babel-execute-src-block))))) + +\f +;;; Links + +;;;; Fuzzy links + +;; Fuzzy links [[text]] encompass links to a target (<<text>>), to +;; a target keyword (aka an invisible target: #+TARGET: text), to +;; a named element (#+name: text) and to headlines (* Text). + +(ert-deftest test-org-export/fuzzy-links () + "Test fuzzy links specifications." + ;; 1. Fuzzy link goes in priority to a matching target. + (org-test-with-temp-text + "#+TARGET: Test\n#+NAME: Test\n|a|b|\n<<Test>>\n* Test\n[[Test]]" + (goto-line 4) + (org-open-at-point) + (should (looking-at "<<Test>>"))) + ;; 2. Fuzzy link should then go to a matching target keyword. + (org-test-with-temp-text + "#+NAME: Test\n|a|b|\n#+TARGET: Test\n* Test\n[[Test]]" + (goto-line 4) + (org-open-at-point) + (should (looking-at "#\\+TARGET: Test"))) + ;; 3. Then fuzzy link points to an element with a given name. + (org-test-with-temp-text "Test\n#+NAME: Test\n|a|b|\n* Test\n[[Test]]" + (goto-line 5) + (org-open-at-point) + (should (looking-at "#\\+NAME: Test"))) + ;; 4. A target still lead to a matching headline otherwise. + (org-test-with-temp-text "* Head1\n* Head2\n*Head3\n[[Head2]]" + (goto-line 4) + (org-open-at-point) + (should (looking-at "\\* Head2"))) + ;; 5. With a leading star in link, enforce heading match. + (org-test-with-temp-text "#+TARGET: Test\n* Test\n<<Test>>\n[[*Test]]" + (goto-line 4) + (org-open-at-point) + (should (looking-at "\\* Test")))) + + (provide 'test-org) ;;; test-org.el ends here -- 1.7.9.2 ^ permalink raw reply related [flat|nested] 19+ messages in thread
* Re: [dev] Implement "ref" link types 2012-02-21 9:18 ` Nicolas Goaziou 2012-02-27 19:38 ` Nicolas Goaziou @ 2012-02-27 20:38 ` David Maus 1 sibling, 0 replies; 19+ messages in thread From: David Maus @ 2012-02-27 20:38 UTC (permalink / raw) To: Nicolas Goaziou; +Cc: David Maus, Org Mode List, Carsten Dominik [-- Attachment #1: Type: text/plain, Size: 1084 bytes --] At Tue, 21 Feb 2012 10:18:00 +0100, Nicolas Goaziou wrote: > > Hello, > > David Maus <dmaus@ictsoc.de> writes: > > > I don't see why we should drop the link type in fuzzy links. After all > > they /are/ are special type of link. > > There is no link type in fuzzy links : [[something]] matches > <<something>> in master. > > > Without the link type we will run into trouble, won't we?. > > > > In the example file: > > > > ,---- > > | We end the list at item [[itm:last]]. > > `---- > > > > So, itm:last is a fuzzy link but it could as well be a "regular" link > > of type "itm" with a path component of "last" and no description. > > I realize my examples are confusing. I shouldn't have used colons. In > fact, the output will be the same if the target is <<itm-last>>, > <<table-last>> or even <<foo>>. > > In other words, the "itm:" part wasn't meant as a link type, but as > a cosmetic part of the name. So the list example could as well be: Thanks for the clarification. Best, -- David -- OpenPGP... 0x99ADB83B5A4478E6 Jabber.... dmjena@jabber.org Email..... dmaus@ictsoc.de [-- Attachment #2: Type: application/pgp-signature, Size: 230 bytes --] ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [dev] Implement "ref" link types 2012-02-20 10:59 ` Nicolas Goaziou 2012-02-20 22:06 ` Nicolas Goaziou @ 2012-03-05 9:37 ` Jambunathan K 1 sibling, 0 replies; 19+ messages in thread From: Jambunathan K @ 2012-03-05 9:37 UTC (permalink / raw) To: Nicolas Goaziou; +Cc: Org Mode List > On the Org side, when a link like [[something]] or [[something][text]] > is encountered in a buffer, the search would go on like this: > > 1. Search any "<<something>>" or "#+target: something"[1]. > 1. A link to an invisible target will be replaced with _nothing_ > (that's the point of being invisible). What led you to come up with this interpretation? [3]. A target *reference* will *always* export and create a clickable link irrespective how the target is *defined*. Read on... > [1] This is the replacement for invisible targets, since they cannot > live in comments anymore. I think there is an element of confusion about what invisible target is. After some digging, I realize that they were originally called as invisible anchors. The manual [1] has the following note: ,---- | * Matt Lundin has proposed last-row references for table formulas | and named invisible anchors. `---- The original post [2] from Matt says ,---- | # <<radiotarget>> | | should become | | <a name="radiotarget"></a> `---- Now the question is what is invisible? The "description" in <a ...> </a> becomes invisible. Why was the term invisible chosen in the first place. For this one has to look at the the "default" behaviour for targets which is to export with *both* the anchor name and anchor description. So, I think a correction is in order. Footnotes: [1] (info "(org) History and Acknowledgments") [2] http://lists.gnu.org/archive/html/emacs-orgmode/2008-11/msg00327.html [3] If I export the following unit test snippet, the produced output from LaTeX/PDF has *just* "Paragraph" and nothing else. ,---- | Paragraph. | #+TARGET: Test | [[Test]] `---- -- ^ permalink raw reply [flat|nested] 19+ messages in thread
end of thread, other threads:[~2012-03-05 9:37 UTC | newest] Thread overview: 19+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2012-02-19 18:08 [dev] Implement "ref" link types Nicolas Goaziou 2012-02-19 19:28 ` Christian Moe 2012-02-19 19:41 ` Nicolas Goaziou 2012-02-19 20:11 ` Toby Cubitt 2012-02-19 20:20 ` Christian Moe 2012-02-19 19:41 ` Samuel Wales 2012-02-19 20:02 ` Nicolas Goaziou 2012-02-19 20:48 ` Samuel Wales 2012-02-19 20:10 ` Carsten Dominik 2012-02-20 0:51 ` Nicolas Goaziou 2012-02-20 7:09 ` Carsten Dominik 2012-02-20 10:59 ` Nicolas Goaziou 2012-02-20 22:06 ` Nicolas Goaziou 2012-02-21 1:26 ` Thomas S. Dye 2012-02-21 5:14 ` David Maus 2012-02-21 9:18 ` Nicolas Goaziou 2012-02-27 19:38 ` Nicolas Goaziou 2012-02-27 20:38 ` David Maus 2012-03-05 9:37 ` Jambunathan K
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).