2012-07-31

Switching to a Java package implementation in Emacs

There already quite a few methods to switch between header and implementation of C and C++ sources in Emacs. Maybe I will put one of those here as well later. But now I needed something different: In a big project, I would like to point at a line like import com.foo.tech.implementation.SomeClass; and Emacs automatically visits the file where this class has been implemented. I wrote two small Elisp functions to perform this:
(defun my-switch-to-java-implementation (packagename classname)
  "Tries to switch to the java source file containing the package packagename and
the class classname."
  (with-temp-buffer
    (shell-command-to-string
     (format "%s %s -iname '*.java' | xargs egrep -l '%s' | sort -u | xargs egrep -l 'class %s' | head -n 1" 
             "find"
             "path/to/your/java/sources"
             packagename
             classname
             )
     (current-buffer) )
    (if (= (count-lines (point-min) (point-max) ) 1)
        (progn
          (search-forward "\n")
          (replace-match "")
          (find-file (buffer-string))
          )
      (message "Could not find suitable implementation.")
      )
    )
  )

(defun my-switch-to-java-implementation-at-point ()
  "Tries to switch to the java source file which contains the package and class imported
in the current line."
  (interactive)
  (save-excursion
    (beginning-of-line)
    (if (looking-at "^import \\(.*\\)\\.\\(.+\\);$")
        (let ( (packagename (buffer-substring (match-beginning 1) (match-end 1) ) )
               (classname (buffer-substring (match-beginning 2) (match-end 2) ) ) )
          (my-switch-to-java-implementation packagename classname)
          )
      (message "This is not an import")
      )
    )
  )
You basically call my-switch-to-java-implementation-at-point (e.g. by binding it to a key) when you are on the import line. Emacs then launches find and grep to look for the definition of your class. Of course, using something like GNU Global would be faster, but also a bit more tricky, since it does not use the packagename, but rather only the class name, which might be used multiple times in your project.
Also note that you have to set the path to your Java source files. I suggest you make a customizable variable of that, or use your ede-project-root or something similar.

2012-07-17

Commenting out line or region in Emacs

What I always wanted: commenting or uncommenting a line or region using emacs. Of course, there is comment-region, but this is much nicer:

(defun comment-or-uncomment-line-or-region ()
  "Comments or uncomments the current line or region."
  (interactive)
  (if (region-active-p)
      (comment-or-uncomment-region (region-beginning) (region-end))
    (comment-or-uncomment-region (line-beginning-position) (line-end-position))
    )
  )

(define-key c-mode-base-map (kbd "C-/") 'comment-or-uncomment-line-or-region)
See also the corresponding Stackoverflow question.

2012-07-02

A nice C++ autocomplete configuration for Emacs

Update: I have written a new blog post about this, with updated information and using new and current packages.

Autocompletion for C++ in Emacs is a hot topic -- at least for Emacs users. At least for Emacs users who code C++... So here is my setup that I use, and which works reasonably well. I use the autocomplete package with the autocomplete-clang extension.



You can get the autocomplete package at github and the clang extension is available also on many repositories on github I used the one by brianjcj. Under OS X I use clang 3.1, under Linux I use clang 3.0. You may have to point autocomplete to the correct binary via the customizable variable ac-clang-executable.

So basically you have to clone the autocomplete package, clone the clang extension and copy the auto-complete-clang.el file into the same folder. I used ~/bin/emacs/auto-complete, but you can choose any directory. Maybe you have your own site-lisp folder.

(defcustom mycustom-system-include-paths '("./include/" "/opt/local/include" "/usr/include" )
  "This is a list of include paths that are used by the clang auto completion."
  :group 'mycustom
  :type '(repeat directory)
  )

(add-to-list 'load-path "~/bin/emacs/auto-complete")
(require 'auto-complete-config)
(add-to-list 'ac-dictionary-directories "~/bin/emacs/auto-complete/ac-dict")
(ac-config-default)
(require 'auto-complete-clang)
(setq clang-completion-suppress-error 't)
(setq ac-clang-flags
      (mapcar (lambda (item)(concat "-I" item))
              (append 
               mycustom-system-include-paths
               )
              )
      )

(defun my-ac-clang-mode-common-hook()
  (define-key c-mode-base-map (kbd "M-/") 'ac-complete-clang)
)

(add-hook 'c-mode-common-hook 'my-ac-clang-mode-common-hook)

The key binding to M-/ is useful on US-keyboard, you may change that to your liking. So to start autocompletion, just type something and hit M-/. Clang will try to parse your file up to point and give useful completions. If it cannot parse your source, the cursor at point will turn red, and the minibuffer will show the error message. Most likely you forgot to add some include paths to mycustom-system-include-paths, or you really do have a syntax error.

The customizable variable mycustom-system-include-paths can be named arbitrarily. Many thanks to the helpful people over at stackoverflow for supplying me with the answer how to make a directory list nicely customizable.