In Emacs use a Lisp function to run the Java program the current file correspond to.
(defun java-run-current-file ()
"Runs the java program the current file correspond to"
(interactive)
(shell-command
(concat "java "
(file-name-sans-extension
(file-name-nondirectory (buffer-file-name))))))
It works by stripping the current file name of its path and extension and using it as an argument to java
which is run from the path where the file is at. The problem with this approach is that if the current file is part of a package then
- the argument to
java
has to be prefixed with the package name and a dot, and java
has to be run from the directory containing the package.
So for example if the file is file.java and the package name is pkg java
is called as java pkg.file
from the directory containing the directory pkg (the parent directory of the directory where file.java is placed).
How can I modify the function to be aware of packages and construct the argument to java
accordingly? I guess one might solve this by searching the current file for a package declaration, such as
package pkg;
and if it finds one it uses that package name to call java
appropriately.
Try the following code to search the current file for a package declaration:
(save-excursion
(goto-char (point-min))
(when (re-search-forward "^\\s *package\\s +\\(.*\\);" (point-max) t)
(match-string 1)))
This will return the value between a package
declaration and the semi-colon. It will return nil
if no such declaration is found.
(Beware that this might fail if there is a commented-out package
declaration somewhere before the actual package declaration, in a C-style, multi-line comment (/* ... */
).)
In order to change the directory from which a shell command is run, use the cd
function. Since in Java the package structure should reflect the directory structure, you can use the package information determined by the above code to figure out the base directory of your source code:
(let ((directory (file-name-directory (buffer-file-name)))
(sub-dirs (reverse (split-string package "\\."))))
(while sub-dirs
(if (string-match (concat "^\\(.*/\\)" (regexp-quote (car sub-dirs)) "/$") directory)
(setq directory (match-string 1 directory)
sub-dirs (cdr sub-dirs))
(error "Package does not match directory structure")))
(cd directory))
You may use this code to extend your function like so:
(defun java-run-current-file ()
"Runs the java program the current file corresponds to"
(interactive)
(let* ((package (save-excursion
(goto-char (point-min))
(when (re-search-forward "^\\s *package\\s +\\(.*\\);" (point-max) t)
(match-string 1))))
(directory (file-name-directory (buffer-file-name)))
sub-dirs)
(if directory
(setq directory (file-truename directory))
(error "Current buffer is not visiting a file"))
(when package
(setq sub-dirs (reverse (split-string package "\\.")))
(while sub-dirs
(if (string-match (concat "^\\(.*/\\)" (regexp-quote (car sub-dirs)) "/$") directory)
(setq directory (match-string 1 directory)
sub-dirs (cdr sub-dirs))
(error "Package does not match directory structure"))))
(cd directory)
(shell-command
(concat "java "
(if package (concat package ".") "")
(file-name-sans-extension
(file-name-nondirectory (buffer-file-name)))))))
Either use a proper Java IDE (you will eventually if you do this on a regular basis) or read the file to parse the package line.
Also note that you will most likely also need to specify the compilation root folder (or cd to it) to be able to set the class path properly.
For deriving the actual class name, reading the file and searching for "package (.*);" (matching newlines too) would most likely be good enough.
To be frank, when I need to work like this, I write a small script (bat-file under WIndows) which does what I need. The reason is that I usually need a lot of jarfiles on the classpath too, which eventually boils down to me needing to specify the whole command line anyway.
来源:https://stackoverflow.com/questions/12546437/how-to-make-a-lisp-function-call-java-while-being-aware-of-packages