简体   繁体   中英

Regexp Emacs for R comments

I would like to build a regexp in Emacs for cleaning up my R code.

One of the problems I ran into was that there are different types of comments: You have those with a certain amount of whitespace (1), eg:

        # This is a comment:
# This is also a comment

or you have situations like this (2):

require(lattice) # executable while the comment is informative

The idea is that I want to align the comments when they are of the second kind (after something that's executable), while excluding those of the first kind.

Ideally, it will align all the comments BETWEEN those of the first kind, but not those of the first kind.

Example:

funfun <- function(a, b) {
# This is a function
    if (a == b) { # if a equals b
      c <- 1 # c is 1 
    }
  }  
#

To:

funfun <- function(a, b) {
# This is a function
    if (a == b) { # if a equals b
      c <- 1      # c is 1 
    }
  }  
# 

I found a regexp to do a replacement for those of the first kind, so then I was able to align them per paragraph (mark-paragraph). That worked kind of well.

Problem is then the backsubstitution:

(replace-regexp "^\\s-+#+" "bla" nil (point-min) (point-max))

This replaces from the start of a line, with any amount of whitespace and any amount of comment characters like:

     #########   

into

     bla

The problem is that I would like to replace them back into what they are originally, so "bla" has to go back into the same amount of whitespace and same amount of #.

Hopefully someone understands what I am trying to do and has either a better idea for an approach or knows how to solve this regexp part.

在此处输入图片说明

Well, here's some crazy attempt at doing something I thought you were after. It seems to work, but it needs a lot of testing and polishing:

(defun has-face-at-point (face &optional position)
  (unless position (setq position (point)))
  (unless (consp face) (setq face (list face)))
  (let ((props (text-properties-at position)))
    (loop for (key value) on props by #'cddr
          do (when (and (eql key 'face) (member value face))
               (return t)))))

(defun face-start (face)
  (save-excursion
    (while (and (has-face-at-point face) (not (bolp)))
      (backward-char))
    (- (point) (save-excursion (move-beginning-of-line 1)) (if  (bolp) 0 -1))))

(defun beautify-side-comments ()
  (interactive)
  ;; Because this function does a lot of insertion, it would
  ;; be better to execute it in the temporary buffer, while
  ;; copying the original text of the file into it, such as
  ;; to prevent junk in the formatted buffer's history
  (let ((pos (cons (save-excursion
                     (beginning-of-line)
                     (count-lines (point-min) (point)))
                   (- (save-excursion (end-of-line) (point)) (point))))
        (content (buffer-string))
        (comments '(font-lock-comment-face font-lock-comment-delimiter-face)))
    (with-temp-buffer
      (insert content)
      (goto-char (point-min))
      ;; thingatpt breaks if there are overlays with their own faces
      (let* ((commentp (has-face-at-point comments))
             (margin
              (if commentp (face-start comments) 0))
             assumed-margin pre-comment commented-lines)
        (while (not (eobp))
          (move-end-of-line 1)
          (cond
           ((and (has-face-at-point comments)
                 commentp)            ; this is a comment continued from
                                        ; the previous line
            (setq assumed-margin (face-start comments)
                  pre-comment
                  (buffer-substring-no-properties
                   (save-excursion (move-beginning-of-line 1))
                   (save-excursion (beginning-of-line) 
                                   (forward-char assumed-margin) (point))))
            (if (every
                 (lambda (c) (or (char-equal c ?\ ) (char-equal c ?\t)))
                 pre-comment)
                ;; This is the comment preceded by whitespace
                (setq commentp nil margin 0 commented-lines 0)
              (if (<= assumed-margin margin)
                  ;; The comment found starts on the left of
                  ;; the margin of the comments found so far
                  (save-excursion
                    (beginning-of-line) 
                    (forward-char assumed-margin)
                    (insert (make-string (- margin assumed-margin) ?\ ))
                    (incf commented-lines))
                ;; This could be optimized by going forward and
                ;; collecting as many comments there are, but
                ;; it is simpler to return and re-indent comments
                ;; (assuming there won't be many such cases anyway.
                (setq margin assumed-margin)
                (move-end-of-line (1- (- commented-lines))))))
           ((has-face-at-point comments)
            ;; This is the fresh comment
            ;; This entire block needs refactoring, it is
            ;; a repetition of the half the previous blockp
            (setq assumed-margin (face-start comments)
                  pre-comment
                  (buffer-substring-no-properties
                   (save-excursion (move-beginning-of-line 1))
                   (save-excursion (beginning-of-line) 
                                   (forward-char assumed-margin) (point))))
            (unless (every
                     (lambda (c)
                       (or (char-equal c ?\ ) (char-equal c ?\t)))
                     pre-comment)
              (setq commentp t margin assumed-margin commented-lines 0)))
           (commentp
            ;; This is the line directly after a block of comments
            (setq commentp nil margin assumed-margin commented-lines 0)))
          (unless (eobp) (forward-char)))
        ;; Retrieve back the formatted contnent
        (setq content (buffer-string))))
    (erase-buffer)
    (insert content)
    (beginning-of-buffer)
    (forward-line (car pos))
    (end-of-line)
    (backward-char (cdr pos))))

I've also duplicated it on pastebin for better readability: http://pastebin.com/C2L9PRDM

EDIT: This should restore the mouse position but will not restore the scroll position (could be worked to, perhaps, I'd just need to look for how scrolling is stored).

align-regexp is the awesome bit of emacs magic you need:

(defun align-comments ()
  "align R comments depending on whether at start or in the middle."
  (interactive)
  (align-regexp (point-min) (point-max)  
    "^\\(\\s-*?\\)\\([^[:space:]]+\\)\\(\\s-+\\)#" 3 1 nil) ;type 2 regex
  (align-regexp (point-min) (point-max)  
    "^\\(\\s-*\\)\\(\\s-*\\)#" 2 0 nil))                    ;type 1 regex

before:

# a comment type 1
      ## another comment type 1
a=1 ###### and a comment type 2 with lots of #####'s
a.much.longer.variable.name=2          # and another, slightly longer type 2 comment    
      ## and a final type 1

after:

      # a comment type 1
      ## another comment type 1
a=1                           ###### and a comment type 2 with lots of #####'s
a.much.longer.variable.name=2 # and another, slightly longer type 2 comment    
      ## and a final type 1

Try

(replace-regexp "^\\(\\s-+\\)#" "\\1bla" nil (point-min) (point-max))

then

(replace-regexp "^\\(\\s-+\\)bla+" "\\1#" nil (point-min) (point-max))

but If I understood you well, I would probably do something like :

(align-string "\b\s-#" begin end)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM