Highlighing of Fortran OpenACC directives in Emacs

Remark: THIS VERSION IS OBSOLETE. SEE MU OTHER POSTS BELOW FOR AN UPDATED VERSION

Here is how I configured my emacs to highlight OpenACC and OpenMP comments in both F90 and F77 files.

For OpenACC, the directives are highlighted only if the directive name is recognized.

For OpenMP, I only check the !omp prefix but adding a list of keywords should be relatively easy (though, there are a lot more commands).

(not thoroughly) Tested on Emacs 24.3.1 and 24.5.1

Simply add the code below to your .emacs file

;;;; 
;;;; Set font for OpenACC and OpenMP comments in fortran and f90 mode 
;;;;

(defface openacc-face
  '((t 
     :foreground "#ff43a4" 
     ;; :background "yellow"
     ;; :bold       t
     ))
  "Face for !$ACC comments"
  )

(defvar openacc-face 'openacc-face)

(defface openmp-face
  '((t 
     :foreground "#43ff84" 
     ;; :background "yellow"
     ;; :bold       t
     ))
  "Face for !$OMP comments"
  )

(defvar openmp-face 'openmp-face)

;; Regex describing all known OpenACC directive names 
(defvar acc-commands-regex 
  (concat 
   "\\("
   ;; ======== standalone directive names
   (regexp-opt '( "cache" 
                  "data"     
                  "declare"        
                  "host_data"
                  "init"
                  "kernels"     ;; will also catch "kernels loop"  
                  "loop"
                  "parallel"    ;; will also catch "parallel loop" 
                  "routine"
                  "set"
                  "shutdown"
                  "update"
                  "wait"
                 ))
   ;; ====== END ???? 
   "\\|end[ \t]+"    (regexp-opt  '( "parallel"  "kernels" "data" "host_data" "atomic" ))
   ;; ====== ATOMIC ???? 
   "\\|atomic[ \t]+" (regexp-opt  '( "read" "write" "update" "capture" )) 
   ;; ====== ENTER DATA and EXIT DATA 
   "\\|enter[ \t]+data"    
   "\\|exit[ \t]+data"   
   "\\)"
))  


;; For OpenACC, uncomment one of the two lines below to choose between a simple regexp 
;; that only matches the $ACC prefix or a more advanced version that also matches the directive name.
;; (defvar acc-regex "$acc[\t ].*")
(defvar acc-regex (concat "$acc[ \t]+" acc-commands-regex  "\\b.*" ))

;; TODO: make a version that matches all OpenMP command names
(defvar omp-regex "$omp[\t ].*")

;; Regex describing F77 and F90 comments containing an OpenACC or OpenMP directive
;;
;; Reminder: For F90 comments, the \\( ... \\)) indicates which part should be 
;; highlighted. Then the argument 1 should be passed instead of 0 to font-lock-add-keywords
;;
(defvar acc-regex-f90 (concat "^[ \t]*\\(!" acc-regex "\\)" ) )
(defvar acc-regex-f77 (concat "^[cC]"       acc-regex ) )
(defvar omp-regex-f90 (concat "^[ \t]*\\(!" omp-regex "\\)" ) )
(defvar omp-regex-f77 (concat "^[cC]"       omp-regex ) )

(font-lock-add-keywords 'f90-mode  
 ( list 
   ( list acc-regex-f90 1 'openacc-face t)
   ( list omp-regex-f90 1 'openmp-face  t)
  ))

(font-lock-add-keywords 'fortran-mode  
 (list 
   ( list acc-regex-f77 0 'openacc-face t)
   ( list acc-regex-f90 1 'openacc-face t)
   ( list omp-regex-f77 0 'openmp-face  t)
   ( list omp-regex-f90 1 'openmp-face  t)
  ))

[/quote]

The definition of acc-commands-regex below should produce equivalent result but it should be easier to maintain and to extend even for people not familiar with emacs regexps.



;; Regex describing all known OpenACC directive names 
(defvar acc-commands-regex  
  ;; Use regexp-opt to build the regex matching all command names 
  ;; and replace-regexp-in-string to allow any sequence of blank 
  ;; characters as separator between keywords 
  (replace-regexp-in-string " " "[ \t]+"   
       (regexp-opt '( "cache" 
                      "data"     
                      "declare"        
                      "host_data"
                      "init"
                      "kernels"     ;; will also catch "kernels loop"  
                      "loop"
                      "parallel"    ;; will also catch "parallel loop" 
                      "routine"
                      "set"
                      "shutdown"
                      "update"
                      "wait"
                      "end parallel"
                      "end kernels"
                      "end data"
                      "end host_data"
                      "end atomic"
                      "atomic read"
                      "atomic write"
                      "atomic update"
                      "atomic capture"
                      "enter data"
                      "exit data"
                      ))
       ))

Also, change the definition of acc-regex as follow to allow line continuations of the form "$acc & … " to be highlighted:

(defvar acc-regex (concat "$acc[ \t]+\\(" acc-commands-regex  "\\b\\|&\\).*" ))

Using the method from my previous post, creating a regexp that matches all OpenMP directive names is a lot easier so here is a full updated version:


;;;; 
;;;; Enable Highlighing of OpenACC and OpenMP directives in Fortan 
;;;;
(when (require 'font-lock nil 'noerror )

  (defface openacc-face
    '((t 
       :foreground "#ff43a4" 
       ;; :background "yellow"
       ;; :bold       t
       ))
    "Face for !$ACC comments"
    )

  (defvar openacc-face 'openacc-face)

  (defface openmp-face
    '((t 
       :foreground "#43ff84" 
       ;; :background "yellow"
       ;; :bold       t
       ))
    "Face for !$OMP comments"
    )

  (defvar openmp-face 'openmp-face)

  ;; Regex describing all known OpenACC directive names 
  (defvar acc-commands-regex  
    ;; Use regexp-opt to build the regex and replace-regexp-in-string 
    ;; to allow any sequence of blank characters as separator 
    (replace-regexp-in-string " " "[ \t]+"   
                              (regexp-opt '( "cache" 
                                             "data"     
                                             "declare"        
                                             "host_data"
                                             "init"
                                             "kernels"     ;; will also catch "kernels loop"  
                                             "loop"
                                             "parallel"    ;; will also catch "parallel loop" 
                                             "routine"
                                             "set"
                                             "shutdown"
                                             "update"
                                             "wait"
                                             "end parallel"
                                             "end kernels"
                                             "end data"
                                             "end host_data"
                                             "end atomic"
                                             "atomic read"
                                             "atomic write"
                                             "atomic update"
                                             "atomic capture"
                                             "enter data"
                                             "exit data"
                                             ))
                              ))

  ;; Regex describing all known OpenMP directive names (up to version 4.5)
  ;; Remark: Combined directives are not present since they are matched by they first kerword
  ;;         (e.g. "parallel" will also match "parallel do")
  (defvar omp-commands-regex  
    (replace-regexp-in-string " " "[ \t]+"   
                              (regexp-opt '( 
                                            "atomic"
                                            "barrier"
                                            "cancel"
                                            "cancellation point"
                                            "critical"
                                            "declare reduction"
                                            "declare simd"
                                            "declare target"
                                            "distribute"
                                            "do" 
                                            "end atomic"
                                            "end critical"
                                            "end distribute"
                                            "end do" 
                                            "end master"
                                            "end ordered"
                                            "end parallel"
                                            "end sections"
                                            "end simd"
                                            "end single"
                                            "end target"
                                            "end task"
                                            "end taskgroup"
                                            "end taskloop"
                                            "end teams"
                                            "end workshare"
                                            "flush"
                                            "master"
                                            "ordered"
                                            "parallel" 
                                            "section"
                                            "sections"
                                            "simd"
                                            "single"
                                            "target"
                                            "task"
                                            "taskgroup"
                                            "taskloop"
                                            "taskwait"
                                            "taskyield"
                                            "teams"
                                            "threadprivate"
                                            "workshare"
                                            ))
                              ))


  ;; For OpenACC, uncomment one of the two lines below to choose between a simple regexp 
  ;; that only matches the $ACC prefix or a more advanced version that also matches the directive name.
  ;; (defvar acc-regex "$acc[\t ].*")
  (defvar acc-regex (concat "$acc[ \t]+\\(" acc-commands-regex  "\\b\\|&\\).*" ))

  ;; Same for OpenMP
  ;;(defvar omp-regex "$omp[\t ].*")
  (defvar omp-regex (concat "$omp[ \t]+\\(" omp-commands-regex  "\\b\\|&\\).*" ))

  ;; Regex describing F77 and F90 comments containing an OpenACC or OpenMP directive
  ;;
  ;; Reminder: For F90 comments, the \\( ... \\)) indicates which part should be 
  ;; highlighted. Then the argument 1 should be passed instead of 0 to font-lock-add-keywords
  ;;
  (defvar acc-regex-f90 (concat "^[ \t]*\\(!" acc-regex "\\)" ) )
  (defvar acc-regex-f77 (concat "^[cC]"       acc-regex ) )
  (defvar omp-regex-f90 (concat "^[ \t]*\\(!" omp-regex "\\)" ) )
  (defvar omp-regex-f77 (concat "^[cC]"       omp-regex ) )

  (font-lock-add-keywords 'f90-mode  
                          ( list 
                            ( list acc-regex-f90 1 'openacc-face t)
                            ( list omp-regex-f90 1 'openmp-face  t)
                            ))

  (font-lock-add-keywords 'fortran-mode  
                          (list 
                           ( list acc-regex-f77 0 'openacc-face t)
                           ( list acc-regex-f90 1 'openacc-face t)
                           ( list omp-regex-f77 0 'openmp-face  t)
                           ( list omp-regex-f90 1 'openmp-face  t)
                           ))

)  ;;;;;; of Fortran OpenACC and OpenMP highlighting