7

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 //?

4
  • I don't quite understand your usecase... Did you remap C-x C-f to ffap or to a wrapper around ffap?
    – T. Verron
    Commented Sep 24, 2014 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
    Commented Sep 24, 2014 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
    Commented Sep 24, 2014 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. Commented Sep 24, 2014 at 16:12

4 Answers 4

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.

3
  • Added the (require 'ffap). I realized this patch wasn't being effective in emacs init as ffap probably gets autoloaded.. which doesn't happen until I interactively do find-file Commented Nov 3, 2014 at 14:36
  • I would welcome an upstream fix.
    – wasamasa
    Commented Nov 3, 2014 at 14:36
  • 2
    @wasamasa Done: git.savannah.gnu.org/cgit/emacs.git/commit/… Commented Mar 23, 2017 at 17:00
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 and acknowledge you have read our privacy policy.

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