views:

407

answers:

3

This question is related to another one, Emacs :TODO indicator at left side. I recently came across a minor mode I like a lot called FixmeMode. It supports auto highlighting of TODO marks, and navigating between them. However, I think it makes more sense to recognize the "TODO" strings only in comments, rather than polluting the whole file. Is it possible?

+1  A: 

It's possible but quite a bit trickier. Fixme mode uses font-lock to do its highlighting, so it works on an as-you-type basis to highlight the keywords. Font-lock hooks in at a very low level, basically running after every change is made to the buffer's contents. It is highly optimized, though, which allows it to appear instantaneous on modern computers.

The TODO indicator in the left fringe is static. Execute the function and all current TODO's are highlighted; change the buffer (adding or removing TODO's) does not change the fringe indicator; that's only changed when the function runs again.

Your approach would have to get into syntax tables, determining first when you're in a comment and then looking for the keywords. The tricky part comes in doing this interactively (i.e. as you type). You should be able to hook into the font-lock constructs to do this, but the function you provide to search for the comment syntax table and then for the keywords better be very efficient, as it will be run each and every time a buffer changes (though it will only run on the changed region, I think). You would want to stuff all of this in font-lock-syntactic-keywords rather than font-lock-keywords because the syntactic-keyword pass happens before the syntactic pass (which happens before the keyword pass), and you need to set TODO inside comments before comments themselves are set.

Sorry it's not a full working-code answer.....

Joe Casadonte
Wow. That's a lot trickier that I would've thought.
Wei Hu
It's both easier and trickier than I thought. Easier in the sense that the whole font-lock hook and tie-in weren't that hard, but way, way harder because it's really tricky to find out if you're in a comment. At least I haven't been able to figure it out so far. So no code example yet.....
Joe Casadonte
+1  A: 

Maybe this will help: there's a fn c-in-literal in cc-mode, and a similar csharp-in-literal in csharp mode. The return value is c if in a C-style comment, c++ if in a C++ style comment. You could add that to the code at http://stackoverflow.com/questions/2242572/emacs-todo-indicator-at-left-side to get what you want.

(defun annotate-todo ()
   "put fringe marker on TODO: lines in the curent buffer"
  (interactive)
  (let (lit)
  (save-excursion
    (goto-char (point-min))
    (while (re-search-forward "TODO:" nil t)
      (progn
        (setq lit (c-in-literal)) ;; or csharp-in-literal
        (if (or (eq lit 'c) (eq lit 'c++))
            (let ((overlay (make-overlay (- (point) 5) (point))))
              (overlay-put overlay 'before-string
                           (propertize "A"
                                       'display
                                       '(left-fringe   ;; right
                                         horizontal-bar
                                         better-fringes-important-bitmap))))))))))
Cheeso
Thanks for your answer. I would really like an answer that improves on the FixmeMode, because it works on an as-you-type basis, as @Joe has said.
Wei Hu
Yep, that makes sense. As a side point, I think highlighting the things that are already on screen, is not as useful as providing a visual indication of the approximate location of the FIXME in the source code. visual diff tools do this, as does Eclipse when showing compile errors, and Visual Studio, as well, I think. I asked about that separately: http://stackoverflow.com/questions/2348679/emacs-todo-indicator-on-left-fringe-has-a-strange-side-effect-deleting-charact
Cheeso
+7  A: 

Check out the library fic-mode.el, it has been verified in C++ and Emacs-Lisp.

It was written specifically to answer this question.

The installation is like any standard package:

(require 'fic-mode)
(add-hook 'c++-mode-hook 'turn-on-fic-mode) 

Though Wei Hu did ask for an easy way to add it to multiple modes, so here goes:

(defun add-something-to-mode-hooks (mode-list something)
  "helper function to add a callback to multiple hooks"
  (dolist (mode mode-list)
    (add-hook (intern (concat (symbol-name mode) "-mode-hook")) something)))

(add-something-to-mode-hooks '(c++ tcl emacs-lisp) 'turn-on-fic-mode)
Trey Jackson
Nice work, thanks! Could you briefly explain how you managed to solved this task with so little code? Also, it would be nice to have a customizable variable `fic-modes` which can be used to define all enabled modes, instead of having to adding to mode hooks one by one.
Wei Hu
@WeiHu As to how it was solved with so little code, it's worth just reading the code - font-lock provides a rich enough framework to solve the problem. The real key was to use a font-lock setting that calls a routine (as opposed to just a regexp) which determines what strings to highlight (that would be `'fic-search-for-keyword`), and that routine just verifies that the string is in a comment/string.
Trey Jackson
So you're making use of the fact that strings and comments use special font faces. Could there be false positives?
Wei Hu
@WeiHu As the FIXME is only recognized when it's marked as a string/comment, the false positive would only happen when font-lock has a false positive on those types.
Trey Jackson