Browse Source

Support for Muse-like structure tags.

Carsten Dominik 17 năm trước cách đây
mục cha
commit
f3e075ed62
6 tập tin đã thay đổi với 295 bổ sung178 xóa
  1. 60 16
      ORGWEBPAGE/Changes.org
  2. 184 147
      contrib/lisp/org-mtags.el
  3. 1 1
      doc/org.texi
  4. 4 0
      lisp/ChangeLog
  5. 2 1
      lisp/org-exp.el
  6. 44 13
      lisp/org.el

+ 60 - 16
ORGWEBPAGE/Changes.org

@@ -16,23 +16,24 @@
 ** Overview
 
    - Description lists are now supported natively
-   - Blockquotes for export
+   - Block quotes for export
    - Fontified code examples in HTML export
    - Include files for export
-   - The text before the first headline is now exported by default
+   - Text before the first headline is now exported by default
    - In-buffer options may now be collected in an external file
-   - The in-buffer settings keywords may now be upper or lower case
+   - The in-buffer settings keywords may now be lower case
+   - Completion of structure elements
    - Startup visibility can now be influenced by properties
-   - Clock task history, and moving entries with the running clock
+   - Clock task history, moving entries with the running clock
    - BBDB anniversaries much faster
-   - New file in the contrib directory: org-eval.el
+   - New contrib files: org-eval.el and org-mtags.el
 
 ** Incompatible changes
 
 - The text before the first headline is now exported by default
 
   Previously, the default was to not include text in an org-mode
-  buffer before the first headine.  From now on, the default it to
+  buffer before the first headline.  From now on, the default it to
   include it.  If you like the old default better, customize the
   variable =org-export-skip-text-before-1st-heading= or set the
   value on a per-file basis with
@@ -71,7 +72,7 @@
      - IDLWAVE   :: The Emacs modes for editing and
                     running IDL and WAVE CL files.
 
-*** Blockquotes for export
+*** Block quotes for export
 
     For quoting an entire paragraph as a citation, use
 
@@ -92,7 +93,7 @@ but not any simpler -- Albert Einstein
 *** Fontified code examples in HTML export
 
     You can now get code examples fontified like they would be
-    fontified in an emacs Buffer, and export the result to HTML.
+    fontified in an Emacs Buffer, and export the result to HTML.
     To do so, wrap the code examples into the following
     structure:
 
@@ -133,7 +134,7 @@ but not any simpler -- Albert Einstein
     : #+INCLUDE: "file" markup lang
 
     will lead to the inclusion of the contents of FILE at the moment
-    of publishing.  FILE ahould be surrounded by double quotes, this
+    of publishing.  FILE should be surrounded by double quotes, this
     is obligatory if it contains space characters.  The parameters
     MARKUP and LANG are optional.  MARKUP can be "example", "quote",
     or "src".  If it is "src", LANG should be the name of the Emacs
@@ -145,7 +146,7 @@ but not any simpler -- Albert Einstein
 *** The text before the first headline is now exported by default
 
     Previously, the default was to not include text in an org-mode
-    buffer before the first headine.  From now on, the default it to
+    buffer before the first headline.  From now on, the default it to
     include it.  If you like the old default better, customize the
     variable =org-export-skip-text-before-1st-heading= or set the
     value on a per-file basis with
@@ -171,6 +172,28 @@ but not any simpler -- Albert Einstein
     =#+startup=, to make these lines less imposing.  Similarly for all
     other in-buffer keywords.
 
+*** Completion of structure elements
+    As a new experimental feature, Org now supports completion of
+    structural elements like =#+BEGIN_EXAMPLE= in a special way.
+    It work by typing, for example "<e" and then pressing TAB, on
+    an otherwise empty line.  "<e" will expand into a complete
+    EXAMPLE template, with the cursor positioned in the middle.
+    Currently supported templates are:
+
+    : <s   #+begin_src
+    : <e   #+begin_example
+    : <q   #+begin_quote
+    : <v   #+begin_verse
+    : <l   #+begin_latex
+    : <L   #+latex:
+    : <h   #+begin_html
+    : <H   #+html:
+    : <a   #+begin_ascii
+    : <i   #+include:
+
+    This is an experimental feature, please comment!  See also
+    below under /org-mtags.el/.
+
 *** Startup visibility can now be influenced by properties
 
     When Emacs opens an Org mode buffer, the outline visibility
@@ -238,12 +261,33 @@ but not any simpler -- Albert Einstein
     approach using a hash for birthdays.  Thanks to Thomas
     Baumann for a patch to this effect.
 
-*** New file in the contrib directory: org-eval.el
-
-    The new module module /org-eval.el/ allowes to include the
-    result of the evaluation of Lisp code (and other scripting
-    languages) into the buffer, similar to the =<lisp>= tag of
-    [[http://mwolson.org/static/doc/emacs-wiki.html#Lisp-Tricks][Emacs Wiki]] and [[http://mwolson.org/static/doc/muse/Embedded-Lisp.html#Embedded-Lisp][Muse]].
+*** New files in the contrib directory
+
+    Do people think any of these should become core?
+
+    - org-eval.el :: This new module allows to include the result
+         of the evaluation of Lisp code (and other scripting
+         languages) into the buffer, similar to the =<lisp>= tag
+         of [[http://mwolson.org/static/doc/emacs-wiki.html#Lisp-Tricks][Emacs Wiki]] and [[http://mwolson.org/static/doc/muse/Embedded-Lisp.html#Embedded-Lisp][Muse]].
+    - org-mtags.el :: This new modules allows you to use
+         Muse-like tags for some structure definitions in Org.
+         For example, you can use
+         :<example>
+	 :...
+	 :</example>
+         instead of
+	 :#+BEGIN_EXAMPLE
+	 :...
+	 :#+END_EXAMPLE
+	 In fact, I myself find these easier to type and to look
+         at.  Also, it will allow you to more easily move text
+         and files back and forth between Org and Muse.  For a
+         list of supported structure elements, see the commentary
+         in the file /org-mtags.el/.
+
+	 If you load this module and use the "<i" etc completion
+         described above, the Muse form will automatically be
+         inserted.
 
 *** Bug fixes
     Many bug fixes again.  Will this ever stop?

+ 184 - 147
contrib/lisp/org-mtags.el

@@ -27,158 +27,195 @@
 ;;; Commentary:
 ;;
 ;; This modules implements some of the formatting tags available in
-;; Emacs Muse.  The goal of this devellopment is to  make it easier for
-;; people to move between both worlds, and eventually to allow Org files
-;; to be published as parts of the Muse environment.
+;; Emacs Muse.  This is not a way if adding new functionality, but just
+;; a different way to write some formatting directives.  The advantage is
+;; that files written in this way can be read by Muse reasonably well,
+;; and that this provides an alternative way of writing formatting
+;; directives in Org, a way that some might find more pleasant to type
+;; and look at that the Org's #+BEGIN..#+END notation.
+
+;; The goal of this development is to make it easier for people to
+;; move between both worlds as they see fit for different tasks.
+
+;; The following muse tags will be translated during export into their
+;; native Org equivalents:
+;;
+;;   <br>
+;;        Needs to be at the end of a line.  Will be translated to "\\".
+;;
+;;   <example>
+;;        Needs to be on a line by itself, similarly the </example> tag.
+;;        Will be translated into Org's #+BEGIN_EXAMPLE construct.
+;;
+;;   <quote>
+;;        Needs to be on a line by itself, similarly the </quote> tag.
+;;        Will be translated into Org's #+BEGIN_QUOTE construct.
+;;
+;;   <comment>
+;;        Needs to be on a line by itself, similarly the </comment> tag.
+;;        Will be translated into Org's #+BEGIN_COMMENT construct.
+;;
+;;   <verse>
+;;        Needs to be on a line by itself, similarly the </verse> tag.
+;;        Will be translated into Org's #+BEGIN_VERSE construct.
+;;
+;;   <content>
+;;        This gets translated into "[TABLE-OF-CONTENTS]".  It will not
+;;        trigger the production of a table of contents - that is done
+;;        in Org with the "#+OPTIONS: toc:t" setting.  But it will define
+;;        the location where the TOC will be placed.
+;;
+;;   <literal style="STYLE">    ;; only latex and html supported in Org
+;;        Needs to be on a line by itself, similarly the </literal> tag.
+;;
+;;   <src lang="LANG">
+;;        Needs to be on a line by itself, similarly the </src> tag.
+;;        Will be translated into Org's BEGIN_SRC construct.
+;;
+;;   <include file="FILE" markup="MARKUP" lang="LANG">
+;;        Needs to be on a line by itself.
+;;        Will be translated into Org's #+INCLUDE construct.
+;;
+;; The lisp/perl/ruby/python tags can be implemented using the
+;; `org-eval.el' module, which see.
 
 (require 'org)
 
 ;;; Customization
 
-(defgroup org-eval nil
-  "Options concerning including output from commands into the Org-mode buffer."
-  :tag "Org Eval"
+(defgroup org-mtags nil
+  "Options concerning Muse tags in Org mode."
+  :tag "Org Muse Tags"
   :group 'org)
 
-(defface org-eval
-  (org-compatible-face nil
-    '((((class color grayscale) (min-colors 88) (background light))
-       (:foreground "grey40"))
-      (((class color grayscale) (min-colors 88) (background dark))
-       (:foreground "grey60"))
-      (((class color) (min-colors 8) (background light))
-       (:foreground "green"))
-      (((class color) (min-colors 8) (background dark))
-       (:foreground "yellow"))))
-  "Face for command output that is included into an Org-mode buffer."
-  :group 'org-eval
-  :group 'org-faces
-  :version "22.1")
-
-(defvar org-eval-regexp nil)
-
-(defun org-eval-set-interpreters (var value)
-  (set-default var value)
-  (setq org-eval-regexp
-	(concat "<\\("
-		(mapconcat 'regexp-quote value "\\|")
-		"\\)"
-		"\\([^>]\\{0,50\\}?\\)>"
-		"\\([^\000]+?\\)</\\1>")))
-
-(defcustom org-eval-interpreters '("lisp")
-  "Interpreters allows for evaluation tags.
-This is a list of program names (as strings) that can evaluate code and
-insert the output into an Org-mode buffer.  Valid choices are 
-
-lisp    Interpret Emacs Lisp code and display the result
-shell   Pass command to the shell and display the result
-perl    The perl interpreter
-python  Thy python interpreter
-ruby    The ruby interpreter"
-  :group 'org-eval
-  :set 'org-eval-set-interpreters
-  :type '(set :greedy t
-	      (const "lisp")
-	      (const "perl")
-	      (const "python")
-	      (const "ruby")
-	      (const "shell")))
-  
-(defun org-eval-handle-snippets (limit &optional replace)
-  "Evaluate code nisppets and display the results as display property.
-When REPLACE is non-nil, replace the code region with the result (used
-for export)."
-  (let (a)
-    (while (setq a (text-property-any (point) (or limit (point-max))
-				      'org-eval t))
-      (remove-text-properties
-       a (next-single-property-change a 'org-eval nil limit)
-       '(display t intangible t org-eval t))))
-  (while (re-search-forward org-eval-regexp limit t)
-    (let* ((beg (match-beginning 0))
-	   (end (match-end 0))
-	   (kind (match-string 1))
-	   (attr (match-string 2))
-	   (code (match-string 3))
-	   (value (org-eval-code kind code))
-	   markup lang)
-      (if replace
-	  (progn
-	    (setq attr (save-match-data (org-eval-get-attributes attr))
-		  markup (cdr (assoc "markup" attr))
-		  lang  (cdr (assoc "lang" attr)))
-	    (replace-match
-	     (concat (if markup (format "#+BEGIN_%s" (upcase markup)))
-		     (if (and markup (equal (downcase markup) "src"))
-			 (concat " " (or lang "fundamental")))
-		     "\n"
-		     value
-		     (if markup (format "\n#+END_%s\n" (upcase markup))))
-	     t t))
-	(add-text-properties
-	 beg end
-	 (list 'display value 'intangible t 'font-lock-multiline t
-	       'face 'org-eval
-	       'org-eval t))))))
-
-(defun org-eval-replace-snippts ()
-  "Replace EVAL snippets in the entire buffer.
-This should go into the `org-export-preprocess-hook'."
-  (goto-char (point-min))
-  (org-eval-handle-snippets nil 'replace))
-
-(add-hook 'org-export-preprocess-hook 'org-eval-replace-snippts)
-(add-hook 'org-font-lock-hook 'org-eval-handle-snippets)
-
-(defun org-eval-get-attributes (str)
-  (let ((start 0) key value rtn)
-    (while (string-match "\\<\\([a-zA-Z]+\\)\\>=\"\\([^\"]+\\)\"" str start)
-      (setq key (match-string 1 str)
-	    value (match-string 2 str)
-	    start (match-end 0))
-      (push (cons key value) rtn))
-    rtn))
-
-(defun org-eval-code (interpreter code)
-  (cond
-   ((equal interpreter "lisp")
-    (org-eval-lisp (concat "(progn\n" code "\n)")))
-   ((equal interpreter "shell")
-    (shell-command-to-string code))
-   ((member interpreter '("perl" "python" "ruby"))
-    (org-eval-run (executable-find interpreter) code))
-   (t (error "Cannot evaluate code type %s" interpreter))))
-
-(defun org-eval-lisp (form)
-  "Evaluate the given form and return the result as a string."
-  (require 'pp)
-  (save-match-data
-    (condition-case err
-        (let ((object (eval (read form))))
-          (cond
-           ((stringp object) object)
-           ((and (listp object)
-                 (not (eq object nil)))
-            (let ((string (pp-to-string object)))
-              (substring string 0 (1- (length string)))))
-           ((numberp object)
-            (number-to-string object))
-           ((eq object nil) "")
-           (t
-            (pp-to-string object))))
-      (error
-       (org-display-warning (format "%s: Error evaluating %s: %s"
-                                     "???" form err))
-       "; INVALID LISP CODE"))))
-
-(defun org-eval-run (cmd code)
-  (with-temp-buffer
-    (insert code)
-    (shell-command-on-region (point-min) (point-max) cmd nil 'replace)
-    (buffer-string)))  
-
-(provide 'org-eval)
-
-;;; org-eval.el ends here
+(defcustom org-mtags-prefere-muse-templates t
+  "Non-nil means, prefere Muse tags for structure elements.
+This is relevane when expanding the templates defined in the variable
+`org-structure-templates'."
+  :group 'org-mtags
+  :type 'boolean)
+
+(defconst org-mtags-supported-tags
+  '("example" "quote" "comment" "verse" "content" "literal" "src" "include")
+  "The tags that are supported by org-mtags.el for conversion.
+In addition to this list, the <br> tag is supported as well.")
+
+(defconst org-mtags-fontification-re
+  (concat 
+   "^[ \t]*</?\\("
+   (mapconcat 'identity org-mtags-supported-tags "\\|")
+   "\\)\\>[^>]*>\\|<br>[ \t]*$")
+  "Regular expression used for fontifying muse tags.")
+
+(defun org-mtags-replace ()
+  "Replace Muse-like tags with the appropriate Org constructs.
+The is done in the entire buffer."
+  (interactive) ;; FIXME
+  (let ((re (concat "^[ \t]*\\(</?\\("
+		    (mapconcat 'identity org-mtags-supported-tags "\\|")
+		    "\\)\\)"))
+	info tag rpl style markup lang file)
+    ;; First, do the <br> tag
+    (goto-char (point-min))
+    (while (re-search-forward "<br>[ \t]*$" nil t)
+      (replace-match "\\\\" t t))
+    ;; Now, all the other tags
+    (goto-char (point-min))
+    (while (re-search-forward re nil t)
+      (goto-char (match-beginning 1))
+      (setq info (org-mtags-get-tag-and-attributes))
+      (if (not info)
+	  (end-of-line 1)
+	(setq tag (plist-get info :tag))
+	(cond
+	 ((equal tag "content")
+	  (setq rpl "[TABLE-OF-CONTENTS]")
+	  ;; FIXME: also trigger TOC in options-plist?????
+	  )
+	 ((member tag '("example" "quote" "comment" "verse"))
+	  (if (plist-get info :closing)
+	      (setq rpl (format "#+END_%s" (upcase tag)))
+	    (setq rpl (format "#+BEGIN_%s" (upcase tag)))))
+	 ((equal tag "literal")
+	  (setq style (plist-get info :style))
+	  (and style (setq style (downcase style)))
+	  (if (plist-get info :closing)
+	      (setq rpl (cond
+			 ((member style '("latex"))
+			  "#+END_LaTeX")
+			 ((member style '("html"))
+			  "#+END_HTML")
+			 ((member style '("ascii"))
+			  "#+END_ASCII")))
+	    (setq rpl (cond
+		       ((member style '("latex"))
+			"#+BEGIN_LaTeX")
+		       ((member style '("html"))
+			"#+BEGIN_HTML")
+		       ((member style '("ascii"))
+			"#+BEGIN_ASCII")))))
+	 ((equal tag "src")
+	  (if (plist-get info :closing)
+	      (setq rpl "#+END_SRC")
+	    (setq rpl "#+BEGIN_SRC")
+	    (when (setq lang (plist-get info :lang))
+	      (setq rpl (concat rpl " " lang)))))
+	 ((equal tag "include")
+	  (setq file (plist-get info :file)
+		markup (downcase (plist-get info :markup))
+		lang (plist-get info :lang))
+	  (setq rpl "#+INCLUDE")
+	  (when markup
+	    (setq rpl (concat rpl " " markup))
+	    (when (and (equal markup "src") lang)
+	      (setq rpl (concat rpl " " lang))))))
+	(when rpl
+	  (goto-char (plist-get info :match-beginning))
+	  (delete-region (point-at-bol) (plist-get info :match-end))
+	  (insert rpl))))))
+
+(defun org-mtags-get-tag-and-attributes ()
+  "Parse a Muse-like tag at point ant rturn the information about it.
+The return value is a property list which contains all the attributes
+with string values.  In addition, it reutnrs the following properties:
+
+:tag              The tag as a string.
+:match-beginning  The beginning of the match, just before \"<\".
+:match-end        The end of the match, just after \">\".
+:closing          t when the tag starts with \"</\"."
+  (when (looking-at "<\\(/\\)?\\([a-zA-Z]+\\>\\)\\([^>]*\\)>")
+    (let ((start 0)
+	  tag rest prop attributes)
+      (setq tag (org-match-string-no-properties 2)
+	    endp (match-end 1)
+	    rest (and (match-end 3)
+		      (org-match-string-no-properties 3))
+	    attributes (list :tag tag
+			     :match-beginning (match-beginning 0)
+			     :match-end (match-end 0)
+			     :closing endp))
+      (when rest
+	(while (string-match "\\([a-zA-Z]+\\)=\\([^ \t\n>]+\\|\"[^>]+\"\\)"
+			     rest start)
+	  (setq start (match-end 0)
+		prop (org-match-string-no-properties 1 rest)
+		val (org-remove-double-quotes
+		     (org-match-string-no-properties 2 rest)))
+	  (setq attributes (plist-put attributes
+				      (intern (concat ":" prop)) val))))
+      attributes)))
+
+(defun org-mtags-fontify-tags (limit)
+  "Fontify the muse-like tags."
+  (while (re-search-forward org-mtags-fontification-re limit t)
+    (add-text-properties (match-beginning 0) (match-end 0)
+			 '(face shadow font-lock-multiline t
+				font-lock-fontified t))))
+
+(add-hook 'org-export-preprocess-hook 'org-mtags-replace)
+(add-hook 'org-font-lock-hook 'org-mtags-fontify-tags)
+
+(provide 'org-mtags)
+
+;;; org-mtags.el ends here
 

+ 1 - 1
doc/org.texi

@@ -7268,7 +7268,7 @@ case.  All it then takes to make use of the program is adding a single line
 to the Org file:
 
 @example
-#+INFOSJ_OPT: view:info toc:nil
+#+INFOJS_OPT: view:info toc:nil
 @end example
 
 @noindent

+ 4 - 0
lisp/ChangeLog

@@ -1,3 +1,7 @@
+2008-05-14  Carsten Dominik  <dominik@science.uva.nl>
+
+	* org.el (org-try-structure-completion): New function.
+
 2008-05-13  Carsten Dominik  <dominik@science.uva.nl>
 
 	* org.el (org-set-font-lock-defaults): Improve fontification of

+ 2 - 1
lisp/org-exp.el

@@ -2853,7 +2853,8 @@ lang=\"%s\" xml:lang=\"%s\">
 		  (setq item-type "d"
 			item-tag (match-string 1 line)
 			line (substring line (match-end 0))))
-	      (unless (string-match "[^ \t]" line)
+	      (when (and (not (equal item-type "d"))
+			 (not (string-match "[^ \t]" line)))
 		;; empty line.  Pretend indentation is large.
 		(setq ind (if org-empty-line-terminates-plain-lists
 			      0

+ 44 - 13
lisp/org.el

@@ -3926,9 +3926,11 @@ If KWD is a number, get the corresponding match group."
 	  (setq org-cycle-subtree-status 'folded)
 	  (run-hook-with-args 'org-cycle-hook 'folded)))))
 
-     ;; TAB emulation
+     ;; TAB emulation and template completion
      (buffer-read-only (org-back-to-heading))
 
+     ((org-try-structure-completion))
+
      ((org-try-cdlatex-tab))
 
      ((and (eq org-cycle-emulate-tab 'exc-hl-bol)
@@ -7693,31 +7695,60 @@ This function can be used in a hook."
 
 (defcustom org-structure-template-alist
   '(
-    ("s" "#+begin_src ?\n\n#+end_src")
-    ("e" "#+begin_example\n?\n#+end_example")
-    ("q" "#+begin_quote\n?\n#+end_quote")
-    ("v" "#+begin_verse\n?\n#+end_verse")
-    ("l" "#+begin_latex\n?\n#+end_latex")
-    ("L" "#+latex: ")
-    ("h" "#+begin_html\n?\n#+end_html")
-    ("H" "#+html: ")
+    ("s" "#+begin_src ?\n\n#+end_src" 
+         "<src lang=\"?\">\n\n</src>")
+    ("e" "#+begin_example\n?\n#+end_example"
+         "<example>\n?\n</example>")
+    ("q" "#+begin_quote\n?\n#+end_quote"
+         "<quote>\n?\n</quote>")
+    ("v" "#+begin_verse\n?\n#+end_verse"
+         "<verse>\n?\n/verse>")
+    ("l" "#+begin_latex\n?\n#+end_latex"
+         "<literal style=\"latex\">\n?\n</literal>")
+    ("L" "#+latex: "
+         "<literal style=\"latex\">?</literal>")
+    ("h" "#+begin_html\n?\n#+end_html"
+         "<literal style=\"html\">\n?\n</literal>")
+    ("H" "#+html: "
+         "<literal style=\"html\">?</literal>")
     ("a" "#+begin_ascii\n?\n#+end_ascii")
     ("A" "#+ascii: ")
-    ("i" "#+include: %file ?")
+    ("i" "#+include: %file ?"
+         "<include file=%file markup=\"?\">")
     )
   "Structure completion elements.
 This is a list of abbreviation keys and values.  The value gets inserted
 it you type @samp{.} followed by the key and then the completion key,
 usually `M-TAB'.  %file will be replaced by a file name after prompting
 for the file uning completion.
+There are two templates for each key, the first uses the original Org syntax,
+the second uses Emacs Muse-like syntax tags.  These Muse-like tags become
+the default when the /org-mtags.el/ module has been loaded. See also the
+variable `org-mtags-prefere-muse-templates'.
 This is an experimental feature, it is undecided if it is going to stay in."
   :group 'org-completion
   :type '(repeat
-	  (string :tag "Key") (string :tag "Template")))
+	  (string :tag "Key")
+	  (string :tag "Template")
+	  (string :tag "Muse Template")))
+
+(defun org-try-structure-completion ()
+  "Try to complete a structure template before point.
+This looks for strings like \"<e\" on an otherwise empty line and
+expands them."
+  (let ((l (buffer-substring (point-at-bol) (point)))
+	a)
+    (when (and (looking-at "[ \t]*$")
+	       (string-match "^[ \t]*<\\([a-z]+\\)$"l)
+	       (setq a (assoc (match-string 1 l) org-structure-template-alist)))
+      (org-complete-expand-structure-template (+ -1 (point-at-bol)
+						 (match-beginning 1)) a)
+      t)))
 
 (defun org-complete-expand-structure-template (start cell)
   "Expand a structure template."
-  (let ((rpl (nth 1 cell)))
+  (let* ((musep (org-bound-and-true-p org-mtags-prefere-muse-templates))
+	 (rpl (nth (if musep 2 1) cell)))
     (delete-region start (point))
     (when (string-match "\\`#\\+" rpl)
       (cond
@@ -7763,7 +7794,7 @@ At all other locations, this simply calls the value of
 	    (confirm (lambda (x) (stringp (car x))))
 	    (searchhead (equal (char-before beg) ?*))
 	    (struct
-	     (when (and (equal (char-before beg1) ?.)
+	     (when (and (member (char-before beg1) '(?. ?<))
 			(setq a (assoc (buffer-substring beg1 (point))
 				       org-structure-template-alist)))
 	       (org-complete-expand-structure-template (1- beg1) a)