gmj-ifdef.org 7.3 KB

CAVEAT

This is probably obsolete for two reasons:

  • hide-ifdef-mode exists https://www.emacswiki.org/emacs/HideIfDef

  • Using real cpp(1) directly probably makes more sense than re-implementing it in elisp. e.g. something like

    cat resume-master.txt | cpp resume.tex | latex
    

    Not precice, but that's the idea. Need to figure out how org fits in into all this (export resume-master.org ot resume-master.txt before the start of this pipeline?)

What

Code to do cpp ifdef-like substitution on org-mode export.

Why

Because I wanted to ifdef my resume for different versoins (LinkedIn, etc)

How

Define functions to implmenet #define, #ifdef, etc. Add as an org-mode export hook. Define keybindings.

See sample text below.

Who

George Jones <gmj@pobox.com>

When

To run on the current buffer by hand NOW type C-x # b.

To run automatically AT EXPORT, just run org-mode export. The hook should fire.

Code

ifdef keybindings and function definitions


; May need to remap
;

  ; Getting this error as of Emacs 25
  ; Debugger entered--Lisp error: (error "Key sequence C-x # i starts with non-prefix key C-x #")  
  ;
  ;(global-set-key (kbd "C-x # i") 'ifdef-ifdef)
  ;(global-set-key (kbd "C-x # d") 'ifdef-define)
  ;(global-set-key (kbd "C-x # b") 'ifdef-buffer)

  ; define global config things for ifdef.
  ; I'm pretty sure global setq is not the right way...

  ; set one of these
  (setq ifdef-delete-chunks t) ;  delete non-matching chunks
  (setq ifdef-comment-chunks nil) ;  delete non-matching chunks
  (setq comment-start "#")

  (defun my-text-mode-hook ()
    (setq comment-start "#")
    ; (message "my-text-mode-hook ran.")
    )

  (defun my-ifdef-hook (backend)
    "Apply cpp(1) style ifdefs to buffer before org-mode export."
    (ifdef-org-export-hook))

  (add-hook 'org-export-before-parsing-hook 'my-ifdef-hook)


  (add-hook 'text-mode-hook 'my-text-mode-hook)

(defun ifdef-org-export-hook ()
  "Export hook to apply #ifdef logic to buffer during org-mode export"
  (interactive)
  (setq ifdef-delete-chunks t) ;  delete non-matching chunks
  (ifdef-buffer)
  (ifdef-delete-all-ifdef-lines)
)

(defun ifdef-buffer ()
  "Apply cpp-like #ifdef processing to current buffer."
  (interactive)

  (ifdef-define)
  (goto-char (point-min))
  (while (ifdef-ifdef) nil)
  (ifdef-delete-all-ifdef-lines)
)


(defun ifdef-delete-all-ifdef-lines ()
  "Delete all #define, #ifdef, and #endif lines in the current buffer"
  (interactive)

  (goto-char (point-min))

  (while (re-search-forward "^\\s\-\*#\\(ifdef\\|define\\|endif\\)" nil t)
    (setq pos1 (line-beginning-position) )
    (forward-line 1)
    (beginning-of-line)
    (setq pos2 (line-beginning-position) )
    (delete-region pos1 pos2)
  )
)

(defun ifdef-define ()
  "Find #ifdef FOO. (setq ifdef-condition FOO).  FOO can be an elisp regexp.

e.g.  #define \(interesting\|contributions\)"
  (interactive)
  (setq ifdef-condition nil)

  (let ((case-fold-search t))
    (save-excursion 
      (goto-char (point-min))
      ; first #define wins
      (if (search-forward-regexp "^\\s\-\*\\(#define\\) \\(.*$\\)" nil t) (setq ifdef-condition (match-string 2)))
    ) ; save-excursion
  ) ;let

   (message (concat (concat "ifdef-condition is \"" ifdef-condition) "\""))
  ifdef-condition
)

(defun ifdef-ifdef ()
  "Delete the next non-matching #ifdef section.

Find ifdef FOO. Cut it out if conditional not defined.
Return t if #ifdef section found (matching or not), nil if not found or no pattern."

  (interactive)
  (catch 'exit
    ; (message "ifdef-ifdef")
    (let ((case-fold-search t))
      (if (boundp 'ifdef-condition)
          (progn ; true
          (setq ifdef-look-for-this (concat "#ifdef " ifdef-condition))
          ; (message (concat "ifdef-look-for-this " ifdef-look-for-this))
          )
          (progn ; false
            (message "ifdef-condition not defined")
          (throw 'exit nil)
          )
      )
      (setq ifdef-found-ifdef-section nil)
  
      ; (message "ifdef-ifdef continue")
      (let (beg end (cnt 1) ifdefChunk)
  ;      (save-excursion
         (progn
           ; (message "ifdef-ifdef looking for chunk to delete")
  	(when (re-search-forward
  	       "^\\s\-\*#ifdef" nil t)
  
  	  (setq beg (match-beginning 0))
  	  (while (re-search-forward "^\\s\-\*#endif" nil t)
  	    (setq ifdef-chunk (buffer-substring beg (point)))
            ; (message (concat "ifdef-chunk-START>" ifdef-chunk "<ifdef-chunk-END"))
            ; (message (concat "ifdef-look-for-this " ifdef-look-for-this))
            (setq ifdef-found-ifdef-section t)
            (setq ifdef-delete-or-comment-this-chunk nil)

            (when (not (string-match ifdef-look-for-this ifdef-chunk))
              (setq ifdef-delete-or-comment-this-chunk t))
              ; (message "ifdef-delete-or-comment-this-chunk")
              (if ifdef-delete-or-comment-this-chunk
                  (if ifdef-delete-chunks ; if delete-chunks
                      (progn ; if delete chunks
                        ; (message "deleting chunk")
                        (delete-region beg (point))
                           ; now get rid of any resulting blank lines
  
                        (setq ifdef-this-line-is-blank (looking-at "[ \t]*$"))
                        (setq ifdef-this-line-is-blank t)
                        (if ifdef-this-line-is-blank
                            (delete-blank-lines); collapse surrounding bank lines to one
                        )  
                      )
                      (if ifdef-comment-chunks ; else if comment chunks
                          (progn 
                            (comment-region beg (point))
                          ) 
                      ) ; if commenting this chunk
                   ) ; if deleting this chunk
               ) ; if commenting or deleting this chunk

  	    (throw 'exit ifdef-found-ifdef-section)))
  	nil)))))
ifdef-ifdef

Some sample text

#deFine \(ALICE\|LARRY\)

The start of the story is the same.

#iFdef ALICE
Alice is special, this message is just for her.
#endif
#ifdef BOB
Bob gets a different story
#endif
#ifdef ALiCE
For Alice, the story continues.
#endif
#ifdef BOB
but Bob is left in the dark.
#endif
#ifdef LARRY
Larry Boy.  Lean and mean, green machine, outta sight, DYN-O-MITE !!!
Lorum ipsum.
Foo.
#endif

To Do List

Clean up symbols

  • Make everything ifdef-

  • Avoid globals (setq) whehn not neede

Check logic

  • correct placement after ifdef-ifdef ?

Think about name of ifdef-cut

  • should it be something like ifdef-ifdef ?

Think about doing it without regexps

Inhibit ifdef expansoin of example above on export

  • It would be more useful as an example if ifdef left it alone

  • Probably a conditional in the export hook. Look for this file name and skip ifdef processing, or find a way to quote the

TODO Seems to only work with one #define, fix this

TODO Add #else or #ifndef

Test

This is some text

foo - should see this

bar - should see this too, but seems not to work.