8

In Verilog/C/C++, comments can begin with //.

Here's an example comment, //This is a comment

I like to use the find-file-at-point feature. If my cursor is on the file name in `include "some_file.v".

But if my cursor is on the above example comment and if I hit C-x C-f, emacs tries to open a tentative path //This!

How do I selectively prevent find-file-at-point from activating? In this case, when the major mode is verilog-mode, how do I NOT do find-file-at-point when my cursor is on a line where the first 2 non-space characters are //?

  • I don't quite understand your usecase... Did you remap C-x C-f to ffap or to a wrapper around ffap? – T. Verron Sep 24 '14 at 5:18
  • Check the function mapped for your C-x C-f (by C-h k RET C-x C-f). It should say it "runs the command" find-file. – caisah Sep 24 '14 at 6:01
  • I can't reproduce this on my 24.3.1 GNU Emacs. Maybe this bug (as @Sigma notes) has already been solved? – remvee Sep 24 '14 at 7:44
  • 2
    @remvee The find-file-at-point feature is disabled by default. I have it enabled via ido. I have (setq ido-use-filename-at-point 'guess) in my config. – Kaushal Modi Sep 24 '14 at 16:12
9

This is a bit disppointing, because ffap.el has some code that should do just that:

 ;; Immediate rejects (/ and // and /* are too common in C/C++):
     ((member name '("" "/" "//" "/*" ".")) nil)

But unfortunately, it relies on there being a space after the comment separator.

It's also very disappointing, because comment markers should never be part of any string-at-point. So here's a patched version of ffap-string-at-point that tries to systematically ignore those markers

(require 'ffap)
(defun ffap-string-at-point (&optional mode)
  (let* ((args
      (cdr
       (or (assq (or mode major-mode) ffap-string-at-point-mode-alist)
           (assq 'file ffap-string-at-point-mode-alist))))
         next-comment
     (pt (point))
     (beg (if (use-region-p)
          (region-beginning)
        (save-excursion
          (skip-chars-backward (car args))
          (skip-chars-forward (nth 1 args) pt)
                  (save-excursion
                    (setq next-comment
                          (progn (comment-search-forward (line-end-position) t)
                                 (point))))
          (point))))
     (end (if (use-region-p)
          (region-end)
        (save-excursion
          (skip-chars-forward (car args))
          (skip-chars-backward (nth 2 args) pt)
          (point)))))
  (when (> end next-comment)
    (setq beg next-comment))
  (setq ffap-string-at-point
      (buffer-substring-no-properties
       (setcar ffap-string-at-point-region beg)
       (setcar (cdr ffap-string-at-point-region) end)))))

By side effect it fixes your problem, but it's much more general. I'm wondering if such a fix should be integrated upstream.

4

Thanks to the solution posted by @Sigma. I had that solution in my config for more than 2 years, and finally sent that as a patch to emacs upstream.

Commit in emacs master: e472cfe8


Here is what the patch effectively does:

(defun modi/ffap-string-at-point (&optional mode)
  "Return a string of characters from around point.

MODE (defaults to value of `major-mode') is a symbol used to look up
string syntax parameters in `ffap-string-at-point-mode-alist'.

If MODE is not found, we use `file' instead of MODE.

If the region is active,return a string from the region.

If the point is in a comment, ensure that the returned string does not contain
the comment start characters (especially for major modes that have '//' as
comment start characters). https://debbugs.gnu.org/cgi/bugreport.cgi?bug=24057

|-----------------------------------+---------------------------------|
| Example string in `c-mode' buffer | Returned `ffap-string-at-point' |
|-----------------------------------+---------------------------------|
| ▮//tmp                            | tmp                             |
| //▮tmp                            | tmp                             |
| ▮///tmp                           | /tmp                            |
| //▮/tmp                           | /tmp                            |
| ▮////tmp                          | //tmp                           |
| ////▮tmp                          | //tmp                           |
| ▮// //tmp                         | (empty string) \"\"             |
| // ▮/tmp                          | /tmp                            |
| // ▮//tmp                         | //tmp                           |
|-----------------------------------+---------------------------------|

Set the variables `ffap-string-at-point' and `ffap-string-at-point-region'.

When the region is active and larger than `ffap-max-region-length',
return an empty string, and set `ffap-string-at-point-region' to '(1 1)."
  (let* ((args
          (cdr
           (or (assq (or mode major-mode) ffap-string-at-point-mode-alist)
               (assq 'file ffap-string-at-point-mode-alist))))
         (region-selected (use-region-p))
         (pt (point))
         (beg (if region-selected
                  (region-beginning)
                (save-excursion
                  (skip-chars-backward (car args))
                  (skip-chars-forward (nth 1 args) pt)
                  (point))))
         (end (if region-selected
                  (region-end)
                (save-excursion
                  (skip-chars-forward (car args))
                  (skip-chars-backward (nth 2 args) pt)
                  (point))))
         (region-len (- (max beg end) (min beg end))))

    ;; If the initial characters of the to-be-returned string are the
    ;; current major mode's comment starter characters, *and* are
    ;; not part of a comment, remove those from the returned string
    ;; (Bug#24057).
    ;; Example comments in `c-mode' (which considers lines beginning
    ;; with "//" as comments):
    ;;  //tmp - This is a comment. It does not contain any path reference.
    ;;  ///tmp - This is a comment. The "/tmp" portion in that is a path.
    ;;  ////tmp - This is a comment. The "//tmp" portion in that is a path.
    (when (and
           ;; Proceed if no region is selected by the user.
           (null region-selected)
           ;; Check if END character is part of a comment.
           (save-excursion
             (nth 4 (syntax-ppss end))))
      ;; Move BEG to beginning of comment (after the comment start
      ;; characters), or END, whichever comes first.
      (save-excursion
        (let ((state (syntax-ppss beg)))
          ;; (nth 4 (syntax-ppss)) will be nil for comment start chars
          (unless (nth 4 state)
            (parse-partial-sexp beg end nil nil state :commentstop)
            (setq beg (point))))))

    (if (and (natnump ffap-max-region-length)
             (< region-len ffap-max-region-length)) ; Bug#25243.
        (setf ffap-string-at-point-region (list beg end)
              ffap-string-at-point
              (buffer-substring-no-properties beg end))
      (setf ffap-string-at-point-region (list 1 1)
            ffap-string-at-point ""))))
(advice-add 'ffap-string-at-point :override #'modi/ffap-string-at-point)
2

I think hacking find-file-at-point is easy, you can use defadvice on find-file-at-point.

The key point is detecting whether the cursor is in a comment. I had a similar issue when developing evil-nerd-commenter. Here is the function you can re-use. The trick is to detect the current font face.

(defun evilnc--in-comment-p (pos)
  (interactive)
  (let ((fontfaces (get-text-property pos 'face)))
    (when (not (listp fontfaces))
      (setf fontfaces (list fontfaces)))
    (delq nil
      (mapcar #'(lambda (f)
              ;; learn this trick from flyspell
              (or (eq f 'font-lock-comment-face)
              (eq f 'font-lock-comment-delimiter-face)))
          fontfaces))))
0

I know that this is not exactly addressing what the OP asked, but a simple way to get ffap to do what you want is to give it just a little advice.

(defun delp--ffap-string-at-point-filter (s)
  "Remove long stretches of /////'s from `ffap-string-at-point' return value."
  (interactive "sTest string: ")
  (if (string-match-p "^//" s)
      ""
    s))

(advice-add 'ffap-string-at-point :filter-return 'delp--ffap-string-at-point-filter)

Edit: fixed incorrect lambda quote (#' => just ') I understand that modern emacsen prefer '# but the ones that do not prefer it, don't understand it.

For me, this worked. I really appreciated the insights of Kaushal Modi, Sigma, Chen bin, and Giles.

I use extended strings of ////'s to break up the page, and I am often in the header when trying to find either the current directory or a file therein. I know that this advice will not serve for all; I put it here because a search on ffap brought me here. Others might have different personal advice to provide the function. Based on what I read here, I wrote the code above.

I have been using Emacs since 1984, and some of the new features do not get on my radar until I see some code. I recommend the Info section on advice. Or in emacs (Info-goto-node "(elisp)Advising Functions").

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.