all messages for Emacs-related lists mirrored at yhetil.org
 help / color / mirror / code / Atom feed
* jumping through ifdefs
@ 2008-06-13 15:01 Rafal Kurcz
  2008-06-13 15:52 ` Phil Carmody
  0 siblings, 1 reply; 2+ messages in thread
From: Rafal Kurcz @ 2008-06-13 15:01 UTC (permalink / raw
  To: help-gnu-emacs

Hello
I have the following code:

#ifdef LINUX

"another nested ifdefs"

#else //LINUX

"another nested ifdefs"

#endif //LINUX

I would like to cycle through "LINUX" ifdefs by single emacs command.
I tried forward-ifdef, down-ifdef and next-ifdef from hide-ifdef-mode
but they don't work as I expect.
They either omit the "else" statement or stop at nested ifdefs.

Thank You for help


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

* Re: jumping through ifdefs
  2008-06-13 15:01 jumping through ifdefs Rafal Kurcz
@ 2008-06-13 15:52 ` Phil Carmody
  0 siblings, 0 replies; 2+ messages in thread
From: Phil Carmody @ 2008-06-13 15:52 UTC (permalink / raw
  To: help-gnu-emacs

Rafal Kurcz <pawlaczus@yahoo.com> writes:

> Hello
> I have the following code:
>
> #ifdef LINUX
>
> "another nested ifdefs"
>
> #else //LINUX
>
> "another nested ifdefs"
>
> #endif //LINUX
>
> I would like to cycle through "LINUX" ifdefs by single emacs command.
> I tried forward-ifdef, down-ifdef and next-ifdef from hide-ifdef-mode
> but they don't work as I expect.
> They either omit the "else" statement or stop at nested ifdefs.
>
> Thank You for help


I wrote this about 15 years ago (emacs 18?), and it still 
seems to work (in 22 and 21 at least). Or at least the stuff
I've needed to use recently still works. There's a whole pile 
of stuff you haven't asked for, and a fair chunk of it is 
now in the core emacs lisp libraries, so really ought to 
be purged. However, if it's useful, feel free to prune it 
and use it.


--- 8< ---

;;; BEGIN Short-cuts for preprocessor stuff in C mode

;; We use symbolic names for the 4 types of directive we can find
(defconst pc-hash-if 1)
(defconst pc-hash-else 2)
(defconst pc-hash-elif 3)
(defconst pc-hash-endif 4)

(defvar pc-auto-modernise-ifdef nil "non nil will cause modernisation to update a buffer") 
(defvar pc-last-directive-type nil "type of last directive found")
(defvar pc-last-directive-deftype nil "n?def iff last type was #(el)?ifn?def, else nil")
(defvar pc-last-directive-cond nil "condition string of last directive")


(defun pc-get-directive-type (&optional quick)
  "Get the directive type and any associated condition
Return by default a list of an enumeration for the preprocessor directive
which point is on, a string if it is an old-style ifn?def, and finally any
associated condition on the line. Store these for later use in the variables
pc-last-directive-type, pc-last-directive-deftype and pc-last-directive-cond.
If optional parameter QUICK is non-null, then do not try to extract the
condition, and do not set the variables, and return only the atom denoting
which type of directive was found."

  (interactive "p")
  (save-excursion
    (let* ((not-at-bol (not (bolp)))
           (l-end (if not-at-bol nil
                    (end-of-line) 
                    (point)))
           (l-beg (if not-at-bol nil
                    (beginning-of-line)
                    (point)))
           (d-end (if not-at-bol nil
                    ;; 3 of the below will be nil, one will be a match
                    (re-search-forward "^#\\s *\\(\\(if\\)\\|\\(else\\)\\|\\(elif\\)\\|\\(endif\\)\\)"
                                       l-end t nil))))
      ;; (message "%S %S %S %S" not-at-bol l-end l-beg d-end)      
      (if (null d-end)
          (progn 
            (message (if not-at-bol
                         "Not at beginning of line"
                       "No directive found at point"))
            nil)
        (let* ((which-type (cond ((not (null (match-string 2))) 'pc-hash-if)
                                 ((not (null (match-string 3))) 'pc-hash-else)
                                 ((not (null (match-string 4))) 'pc-hash-elif)
                                 ((not (null (match-string 5))) 'pc-hash-endif)
                                 (t nil))))
          (if (not (null quick))
              ;; should we return an atom or a cons cell?
              which-type ; nothing more to do, return the number
            
            (set 'pc-last-directive-type which-type)
            
            (if (or (eq which-type 'pc-hash-else) 
                    (eq which-type 'pc-hash-endif))
                ;; No condition required - return value
                (progn 
                  (message "else or endif")
                  (list which-type
                        (set 'pc-last-directive-deftype nil)
                        (set 'pc-last-directive-cond nil)))
                  
              ;; else it's an if or an elif
              (let* ((ndef-bit (and (not (eq which-type 'pc-hash-elif))
                                    (looking-at "n?def")
                                    (match-string 0)))
                     ;; c-beg is the beginning of a comment, or null
                     (c-beg (search-forward "/*" l-end t nil))
                     ;; a-end i either the end of line or before the comment
                     (a-end (if (null c-beg) l-end (- c-beg 2)))
                     ;; aux is the condition of the directive
                     (aux (progn (goto-char d-end)
                                 ;; skip over leading whitespace 
                                 ;; and ignore trailing whitespace
                                 (re-search-forward "\\s *\\(.*\\S \\)"
                                                    a-end t nil)
                                 (match-string 1))))
                (message "ndef-bit = %S" ndef-bit)
                (message "c-beg = %S" c-beg)
                (message "a-end = %S" a-end)
                (message "aux = %S" aux)
                
                (list which-type 
                      (set 'pc-last-directive-deftype ndef-bit)
                      (set 'pc-last-directive-cond aux))
                ) ; end let
              ) ; endif condition required
            ) ; endif null quick 
          ) ; end let
        ) ; endif not null d-end
      ) ; end let
    ) ; end save excursion
  ) ; end defun


(defun pc-preprocessor-enclosure (&optional forward skip-elses)
  "Finds the beginning or end of the enclosing preprocessor block.
Defaults - search forward, and ignore #else and #elif entirely.
Optional parameter FORWARD, if null means search forwards.
Optional parameter SKIP-ELSES, can be t, null or something else.
If null one can be enclosed within a #if #el*, or #el* #endif block.
If t all #elses or #elifs are skipped. Any other value skips #else 
but not #elif.
"
  (interactive "p") 
  ;;  (message "dir=%s elses=%s"
  ;;           (if forward "forward" "back")
  ;;           (if skip-elses "skip" "handle"))
  (let* ((on-if (if forward 1 -1))
         (search-fn (if forward 
                        're-search-forward 
                      're-search-backward))
         (search-str (concat "#\\s *\\(if" 
                             (cond ((eq skip-elses t) "") ; skip all elses
                                   ((null skip-elses) "\\|elif\\|else")
                                   (t "\\|elif"))
                             "\\|endif\\)\\(.*\\)"))
         (depth 0)
         (tmp-dtype nil)
         found)
    (save-excursion
      ;; if at beginning of line, move to previous line. this stops repeats
      (and forward (bolp) (forward-line)) 
      (while (and (not found)
                  (funcall search-fn search-str nil t))
        ;;        (message "%d) %s" depth (match-string 0))
        (beginning-of-line)
        (or (and (set 'tmp-dtype (pc-get-directive-type t))
                 ;;                 (message "dtype OK %S" tmp-dtype)
                 (cond ((eq tmp-dtype 'pc-hash-if) ; decrease depth, to -1?
                        (eq (setq depth (+ depth on-if)) -1))

                       ((eq tmp-dtype 'pc-hash-endif) ; increase depth, to -1?
                        (eq (setq depth (- depth on-if)) -1))
                       
                       ;; We can't match anything else unless we are
                       ;; not skipping elses.
                       ;; i.e. is this bit not needed?
                       ((or (eq skip-elses t) ; skip all elses
                            (and skip-elses (eq tmp-dtype 'pc-hash-else)))
                        nil)
                       
                       ;; We don't change the depth, but this would
                       ;; have taken us to -1 - so DO IT!
                       ;; either set depth to -1 (a lie) or set found (better)
                       ((equal depth 0) t)

                       ;; Catch-all - not found
                       (t nil))
                 (set 'found (point)))  ; end and (set and cond)
            
            ;; we didn't find a match, then get to depth -1,
            ;; get the hell off this line!
            (and (message "skip to next") forward (forward-line))))
      
      ;;      (message "Terminated while with depth %d found=%s" 
      ;;             depth
      ;;           (if (null found) "nil" "found"))
      )                                 ; end save excursion
    (if found 
        (goto-char found)
      (message "Couldn't find a match")
      nil)))


(defun pc-preprocessor-to-if () (interactive) (pc-preprocessor-enclosure nil t))
(defun pc-preprocessor-to-endif () (interactive) (pc-preprocessor-enclosure t t))
(defun pc-preprocessor-up-to-else () (interactive) (pc-preprocessor-enclosure nil nil))
(defun pc-preprocessor-down-to-else () (interactive) (pc-preprocessor-enclosure t nil))
(defun pc-preprocessor-up-to-el_if () (interactive) (pc-preprocessor-enclosure t 1))


(defun pc-modernise-ifdef (&optional my-hash)
  "Modern ANSI-ify #ifdef and #ifndef into #if defined and #if !defined\
It returns if a replacement was made."
  (interactive)
  (save-excursion
    (beginning-of-line)
    (message "my-hash = %S" my-hash)
    (let* ((this-hash (if (null my-hash) (pc-get-directive-type nil) my-hash))
           (oldtype (and this-hash (nth 1 this-hash)))
           (oldtype-n (and oldtype (string-equal oldtype "ndef"))))
      (message "this-hash = %S" this-hash)
      (message "oldtype = %S" oldtype)
      (message "oldtype-n = %S" oldtype-n)
      (if (null oldtype)
          (progn (message (if (null this-hash)
                            "Not on a directive"
                              "Not an old-style #ifn?def"))
                 nil)
        (search-forward oldtype nil t)
        (replace-match (if (string-equal oldtype "def") " defined" " !defined") t t)
        (message "replacing old-style #if%s %s" oldtype (nth 2 this-hash))
        t)
      nil)))


(defun pc-electric-endif (&optional nocomment)
  "Insert an endif and a comment describing the condition which has ended.
The comment will not be inserted if optional NOCOMMENT is t."
  (interactive "p")
  (let ((insertion 
         (if (eq nocomment t)
             "#endif"
           (save-excursion
             (let* ((conditional (pc-preprocessor-enclosure nil nil))
                    (cond-exp (and conditional (pc-get-directive-type)))
                    (negate-cond nil))
               (message "Conditional is %S" cond-exp)
               (if (not (eq (car cond-exp) 'pc-hash-else))
                   nil ; (message "Phew! not an else")
                 (message "Is an else - finding and negating the condition")
                 (set 'negate-cond t)
                 (set 'conditional (pc-preprocessor-enclosure nil 1)) ; skip elses
                 (set 'cond-exp (pc-get-directive-type))
                 ;; get the matching if or elif
                 (message "Conditional is now NOT %S" cond-exp))

               ;; we are now at an if or an elif
               (if (null conditional)
                   (progn 
                     (message "No #if or #elif found")
                     nil)
                 (if (not (and pc-auto-modernise-ifdef
                               (nth 1 cond-exp)))
                     (message "Not #if, not #ifn?def, or not auto-correcting")
                   ;; We must correct this
                   (pc-modernise-ifdef cond-exp)
                   (set 'cond-exp (pc-get-directive-type))
                   (message "Modernised ifdef"))
                 (format "#endif /* end %s %s */" 
                         (if negate-cond "else not" "if")
                         (nth 2 cond-exp))))))))

    (if (null insertion)
        (progn 
          (message "No modification made")
          nil)
      (pc-ensure-blank-line)
      (insert insertion)
      t)))

(defun pc-electric-else (&optional nocomment) 
  "Insert an else and a comment describing the condition which has ended.
The comment will not be inserted if optional NOCOMMENT is t."
  (interactive "p")
  (let ((insertion 
         (if (eq nocomment t)
             "#else"
           (save-excursion
             (let* ((conditional (pc-preprocessor-enclosure nil 1))
                    (cond-exp (and conditional (pc-get-directive-type))))
               (message "Conditional is %S" cond-exp)

               ;; we are now at an if or an elif
               (if (null conditional)
                   (progn 
                     (message "No #if or #elif found")
                     nil)
                 (if (not (and pc-auto-modernise-ifdef
                               (nth 1 cond-exp)))
                     (message "Not #if, not #ifn?def, or not auto-correcting")
                   ;; We must correct this
                   (pc-modernise-ifdef cond-exp)
                   (set 'cond-exp (pc-get-directive-type))
                   (message "Modernised ifdef"))
                 (format "#else /* %s not %s */" 
                         (if (eq (car cond-exp) 'pc-hash-if) "else" "elif")
                         (nth 2 cond-exp))))))))

    (if (null insertion)
        (progn 
          (message "No modification made")
          nil)
      (pc-ensure-blank-line)
      (insert insertion)
      t)))

(defun pc-hashout-region (start end)
  "Use #if 0 to remove a chunk of code."
  (interactive "r")
  (let ((opoint (point))
        (beg start)
        (fin end))
    (goto-char end)
    (pc-ensure-blank-line)
    (insert (format "#endif /* #endif 0 (auto-hashout %d-%d) */" beg fin))
    (goto-char start)
    (pc-ensure-blank-line)
    (insert (format "#if 0 /* (auto-hashout %d-%d) */" beg fin))))


(defun pc-unhashout-smallest-enclosure ()
  "If strictly between a #if and a #endif, remove those directives"
  (interactive)
  (save-excursion
    (let* ((top (pc-preprocessor-enclosure t nil))
           (topif (and top (pc-get-directive-type t)))
           (bot (and (eq topif 'pc-hash-if)
                     (pc-preprocessor-enclosure nil nil)))
           (botendif (and bot (pc-get-directive-type t))))
      (and botendif
           (progn
             (goto-char top)
             (re-search-forward ".*\n")
             (replace-match ""))))))

(defun pc-preprocessor-h-exclude ()
  "If in a .h file, insert #if/#endif exclusions"
  (interactive)
  (save-excursion
    (let* ((fname buffer-file-name)
           (ucfname (upcase fname)))
      (beginning-of-buffer)
      ;; A rudimentary check for comments at the top of the file
      (if (looking-at "/*")
          (search-forward "*/"))
      (message ucfname)
      (pc-ensure-blank-line)
      (insert ucfname)
      (beginning-of-line)
      (re-search-forward "^/.*/")
      (replace-match "#if !defined(_" t nil)
      (re-search-forward "[.]H$")
      (replace-match "_H_INCLUDED)" t nil)
      (end-of-buffer)
      (pc-electric-endif))))


(progn
  ;; Why do these not work - perhaps I should move them?
  (defvar pc-c-hash-map nil "map for C preprocessor functions")
  (define-prefix-command 'pc-c-hash-map)

  (define-key pc-c-hash-map [?0] 'pc-hashout-region)

  ;; toggle auto-modernise
  (define-key
    pc-c-hash-map [?a] 
    '(lambda () 
       (interactive)
       (message "%sautomatically modernising #ifdef"
                (if (set 'pc-auto-modernise-ifdef 
                         (null pc-auto-modernise-ifdef))
                    ""
                  "Not "))))
  (define-key pc-c-hash-map [?m] 'pc-modernise-ifdef)
  (define-key pc-c-hash-map [?f] 'pc-electric-endif)
  (define-key pc-c-hash-map [?e] 'pc-electric-else)
  (define-key pc-c-hash-map [?l] 'pc-electric-elif)  
  (define-key pc-c-hash-map [?h] 'pc-preprocessor-h-exclude)
  (define-key pc-c-hash-map [down] 'pc-preprocessor-to-endif)
  (define-key pc-c-hash-map [up] 'pc-preprocessor-to-if)
  (define-key pc-c-hash-map [C-down] 'pc-preprocessor-down-to-else)
  (define-key pc-c-hash-map [M-down] 'pc-preprocessor-down-to-else)
  (define-key pc-c-hash-map [C-up] 'pc-preprocessor-up-to-else))

;;; END Short-cuts for preprocessor stuff in C mode


--- 8< ---

-- 
Dear aunt, let's set so double the killer delete select all.
-- Microsoft voice recognition live demonstration


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

end of thread, other threads:[~2008-06-13 15:52 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-06-13 15:01 jumping through ifdefs Rafal Kurcz
2008-06-13 15:52 ` Phil Carmody

Code repositories for project(s) associated with this external index

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

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.