├── .gitignore
├── emacs-eclim-pkg.el
├── .github
└── ISSUE_TEMPLATE.md
├── CONTRIBUTING.md
├── tests
├── maven-tests.el
├── parser-tests.el
├── command-tests.el
├── run-tests.el
├── company-emacs-eclim-tests.el
└── emacs-eclim-linter-init.el
├── .travis.yml
├── History.txt
├── eclim-maven.el
├── ac-emacs-eclim-source.el
├── eclim-ant.el
├── company-emacs-eclim.el
├── eclim-debug.el
├── eclimd.el
├── eclim-java-run.el
├── README.md
├── eclim-completion.el
├── eclim-project.el
├── eclim-problems.el
├── eclim.el
└── eclim-java.el
/.gitignore:
--------------------------------------------------------------------------------
1 | *.elc
2 | TAGS
3 |
--------------------------------------------------------------------------------
/emacs-eclim-pkg.el:
--------------------------------------------------------------------------------
1 | (define-package "emacs-eclim" "0.3"
2 | "An interface to the Eclipse IDE."
3 | '((dash "2.11.0")
4 | (json "1.2")
5 | (popup "0.5.2")
6 | (s "1.9.0")))
7 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | THIS REPOSITORY HAS MOVED TO:
2 |
3 | https://github.com/emacs-eclim/emacs-eclim
4 |
5 | Please ensure that you are using the latest version of Emacs Eclim from the new repository and then submit any issues to that repository.
6 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | The project is under active development and we are always looking for
2 | assistance. See the
3 | [Roadmap](http://wiki.github.com/senny/emacs-eclim/roadmap) wiki page
4 | for more information.
5 |
6 | 1. Fork emacs-eclim
7 | 2. Create a topic branch - `git checkout -b my_branch`
8 | 3. Make your changes and update the History.txt file
9 | 4. Push to your branch - `git push origin my_branch`
10 | 5. Rebase and squash commits
11 | 6. Send me a pull-request for your topic branch
12 | 7. That's it!
13 |
--------------------------------------------------------------------------------
/tests/maven-tests.el:
--------------------------------------------------------------------------------
1 | (require 'eclim)
2 |
3 | (ert-deftest regexp-match-filename-line-column-properly ()
4 | (let* ((error-string "[ERROR] /home/lklich/Dokumenty/Projekty/LibertyGlobal/eclim-test-mvn-project/lib-a/src/main/java/com/acme/liba/LibraryA.java:[22,12] method codePointAt in class java.lang.String cannot be applied to given types;")
5 | (matched (s-match (caar compilation-error-regexp-alist) error-string))
6 | (file (cadr matched))
7 | (line (caddr matched))
8 | (column (cadddr matched)))
9 | (should (string-equal file "/home/lklich/Dokumenty/Projekty/LibertyGlobal/eclim-test-mvn-project/lib-a/src/main/java/com/acme/liba/LibraryA.java"))
10 | (should (string-equal line "22"))
11 | (should (string-equal column "12"))))
12 |
--------------------------------------------------------------------------------
/tests/parser-tests.el:
--------------------------------------------------------------------------------
1 |
2 | (ert-deftest java-parser-read-complex-signarure()
3 | (should (equal (eclim--java-parser-read "public Message> preSend(org.springframework.integration.Message>,org.springframework.integration.MessageChannel)")
4 | '(public Message ((\?)) preSend ((org\.springframework\.integration\.Message ((\?))) (org\.springframework\.integration\.MessageChannel))))))
5 |
6 | (ert-deftest parse-complex-signature ()
7 | (should (equal (eclim--java-parse-method-signature "public Message> preSend(org.springframework.integration.Message>,org.springframework.integration.MessageChannel)")
8 | '((:arglist ((:type org\.springframework\.integration\.Message ((\?)))) ((:type org\.springframework\.integration\.MessageChannel))) (:name . preSend) (:return public Message ((\?)))))))
9 |
10 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: java
2 | cache: apt
3 | env:
4 | - CI=true
5 |
6 | notifications:
7 | webhooks:
8 | urls:
9 | - https://webhooks.gitter.im/e/bde5c3d7214f513e47cb
10 | on_success: change # options: [always|never|change] default: always
11 | on_failure: always # options: [always|never|change] default: always
12 | on_start: false # default: false
13 |
14 |
15 | install:
16 | - sudo add-apt-repository -y ppa:cassou/emacs && sudo apt-get update -qq && sudo apt-get -f install -qq && sudo apt-get install -qq emacs24 emacs24-el
17 | - cd /tmp
18 | # - mkdir /home/travis/opt && wget http://download.eclipse.org/technology/epp/downloads/release/luna/R/eclipse-java-luna-R-linux-gtk-x86_64.tar.gz -O eclipse.tar.gz && tar -xf eclipse.tar.gz -C /home/travis/opt
19 | # - wget http://sourceforge.net/projects/eclim/files/latest/download -O eclim.jar && java -Dvim.skip=true -Declipse.home=/home/travis/opt/eclipse -jar eclim.jar install
20 | - cd - && cd tests && git clone https://github.com/nschum/elisp-lint.git
21 | - cd -
22 | script:
23 | - emacs -Q --batch -l tests/emacs-eclim-linter-init.el -f elisp-lint-files-batch *.el
24 | - emacs -Q --batch -l ert -l tests/emacs-eclim-linter-init.el -f ert-run-tests-batch-and-exit
--------------------------------------------------------------------------------
/tests/command-tests.el:
--------------------------------------------------------------------------------
1 | (ert-deftest test-command-should-sync-for-normal-commands ()
2 | (should (eclim--command-should-sync-p "java_complete" '("-f" "~/src/test/Panda.java")))
3 | (should (not (eclim--command-should-sync-p "other_cmd" '("-x" "~/src/test/Panda.java")))))
4 |
5 | (ert-deftest test-command-should-sync-for-special-commands ()
6 | (should (not (eclim--command-should-sync-p "project_by_resource" '("-f" "~/src/test/Panda.java"))))
7 | (should (not (eclim--command-should-sync-p "project_link_resource" '("-f" "~/src/test/Panda.java")))))
8 |
9 | (ert-deftest test-make-command-should-create-an-executable-string ()
10 | (let ((eclim-executable "/usr/local/eclipse/eclim"))
11 | (should (equal (eclim--make-command '("cmd" "-a" "arg"))
12 | "/usr/local/eclipse/eclim -command cmd -a arg"))))
13 |
14 | (ert-deftest test-make-command-should-escape-arguments ()
15 | (let ((eclim-executable "/usr/local/eclipse/eclim"))
16 | (should (equal (eclim--make-command '("cmd" "-a" ""))
17 | "/usr/local/eclipse/eclim -command cmd -a ''"))
18 | (should (equal (eclim--make-command '("cmd" "-a" " a b c "))
19 | "/usr/local/eclipse/eclim -command cmd -a \\ a\\ b\\ c\\ "))))
20 |
21 | (ert-deftest test-make-command-should-allow-nils()
22 | (let ((eclim-executable "/usr/local/eclipse/eclim"))
23 | (should (equal (eclim--make-command '("cmd" "-a" "arg" "-b" nil))
24 | "/usr/local/eclipse/eclim -command cmd -a arg -b"))
25 | (should (equal (eclim--make-command '("cmd" "-a" nil "-b" "arg"))
26 | "/usr/local/eclipse/eclim -command cmd -a -b arg"))))
27 |
--------------------------------------------------------------------------------
/tests/run-tests.el:
--------------------------------------------------------------------------------
1 | (require 'eclim)
2 | (require 'eclim-java-run)
3 |
4 | (ert-deftest command-for-java-configuration ()
5 | (let* ((conf '((name . "Test run")
6 | (main-class . "com.acme.Project")
7 | (program-args . "arg1 arg2")
8 | (vm-args . "-Dvm-arg1=42")))
9 | (run-command (eclim-java-run--command conf
10 | (eclim-java-run--java-vm-args "/opt/lib.jar"))))
11 | (should (string-equal run-command
12 | "java -classpath /opt/lib.jar -Dvm-arg1=42 com.acme.Project arg1 arg2"))))
13 |
14 | (ert-deftest command-for-debug-configuration ()
15 | (let* ((conf '((name . "Debug test run")
16 | (main-class . "com.acme.Project")
17 | (program-args . "arg1 arg2")
18 | (vm-args . "-Dvm-arg1=42")
19 | (debug . t)))
20 | (run-command (eclim-java-run--command conf
21 | (eclim-java-run--java-vm-args "/opt/lib.jar"))))
22 | (should (string-equal run-command
23 | "jdb -classpath/opt/lib.jar -Dvm-arg1=42 com.acme.Project arg1 arg2"))))
24 |
25 | (ert-deftest choose-configuration ()
26 | (let* ((conf1 '((name . "Debug")))
27 | (conf2 '((name . "Run")))
28 | (choosen-conf (eclim-java-run--configuration "Run" (list conf1 conf2))))
29 | (should (equal choosen-conf conf2))))
30 |
31 | (ert-deftest run-java-opens-buffer-in-correct-dir-with-correct-name ()
32 | (let* ((conf '((name . "Run")
33 | (main-class . "com.acme.Project")
34 | (program-args . "arg1")
35 | (vm-args . "-Dvm-args1=42")))
36 | (buffer (eclim-java-run--run-java conf "/opt/lib.jar" "/tmp/")))
37 | (with-current-buffer buffer
38 | (should (string-equal default-directory "/tmp/"))
39 | (should (string-equal (buffer-name) "*Run*")))))
40 |
--------------------------------------------------------------------------------
/tests/company-emacs-eclim-tests.el:
--------------------------------------------------------------------------------
1 | (require 'company-emacs-eclim)
2 |
3 | (ert-deftest compute-full-prefix-when-complete-import ()
4 | (with-temp-buffer
5 | (insert "import java.uti")
6 | (goto-char (point-max))
7 | (let ((before-prefix (company-emacs-eclim--before-prefix-in-buffer "uti")))
8 | (should (string-equal before-prefix "java.")))))
9 |
10 | (ert-deftest when-completing-import-prefix-should-be-trimmed ()
11 | (let ((candidates (emacs-eclim--candidates-for-temp-buffer
12 | '((content . "import java.uti")
13 | (mocked-response . ("java.util" "java.util.stream"))
14 | (prefix . "uti")))))
15 | (should (equal candidates '("util" "util.stream")))))
16 |
17 | (ert-deftest when-completing-fields-candidates-shouldnt-change ()
18 | (let ((candidates (emacs-eclim--candidates-for-temp-buffer
19 | '((content . "this.liba.sec")
20 | (mocked-response . ("secondChild : String - LibraryA"))
21 | (prefix . "sec")))))
22 | (should (equal candidates '("secondChild : String - LibraryA")))))
23 |
24 | (ert-deftest when-completing-method-real-candidate-is-passed-as-test-property ()
25 | (let ((candidates (emacs-eclim--candidates-for-temp-buffer
26 | '((content . "this.liba.get")
27 | (mocked-response . ("getterWithParams(int x, int y, int z) : int - LibraryA"))
28 | (prefix . "get")))))
29 | (should (equal candidates '("getterWithParams")))
30 | (should (equal (get-text-property 0 'eclim-meta (first candidates)) "getterWithParams(int x, int y, int z) : int - LibraryA"))))
31 |
32 | (defun emacs-eclim--candidates-for-temp-buffer (arg)
33 | (with-temp-buffer
34 | (insert (cdr (assoc 'content arg)))
35 | (goto-char (point-max))
36 | (flet ((eclim--completion-candidates () (cdr (assoc 'mocked-response arg))))
37 | (company-emacs-eclim--candidates (cdr (assoc 'prefix arg))))))
38 |
--------------------------------------------------------------------------------
/History.txt:
--------------------------------------------------------------------------------
1 | == In Git
2 |
3 | == 0.4
4 |
5 | === New features
6 | * company mode completion can be case insensitive
7 |
8 | == 0.3
9 |
10 | === New Features
11 | * organize imports
12 | * browsing with the class hierarchy
13 | * override / implement methods
14 | * display problems
15 | * run Ant targets
16 | * run Maven phases and goals
17 | * automatic refresh of the problems buffer
18 | * file-filter for the problems buffer
19 | * interface improvements for the problems buffer
20 | * improved mechanisms for calling the eclim server
21 | * rename/refactor in java code works as expected
22 | * functions for find declarations and refrences of java methods and members
23 | * Error highlighting in code
24 | * Support for auto-complete-mode
25 | * eclimd module to start/stop eclimd from emacs
26 | * Generation of getter and setter methods.
27 | * User commands for adding, listing and removing project natures.
28 | * Running all JUnit tests project or in a class or just the test method at point
29 | * additional info in modeline about errors and warnings
30 | * added support for scala completion
31 |
32 | === Bugfixes
33 | * removed extra parentheses around function
34 | * fixed a problem on startup when the eclimd server was not started
35 | * bugfix in call to eclim-call-process
36 | * Added a new directory for eclipse (may only be Arch linux specific)
37 | * Changed eclim--project-name to get the project name out of eclipse
38 | than futz with matching dir names to workspace names.
39 | * Added eclim-eclipse-dirs which can be used if eclipse is not
40 | installed in the standard location
41 | * Changed eclim--project-name to get the project name out of
42 | project_by_resource command, and eclim--project-current-file to use
43 | project_link_resource command, so that external source files not in the
44 | eclipse working directory are correctly handled.
45 | * fixed a problem with highlighting when files are in directories that are symbolic links
46 | * Make the project major modes adhere to emacs major mode conventions
47 | * fixed a problem with company-sort-by-occurrence when using company-mode
48 |
--------------------------------------------------------------------------------
/tests/emacs-eclim-linter-init.el:
--------------------------------------------------------------------------------
1 | (add-to-list 'load-path ".")
2 | (add-to-list 'load-path "./tests")
3 | (add-to-list 'load-path "./tests/elisp-lint")
4 |
5 | (require 'package)
6 | (add-to-list 'package-archives
7 | '("melpa" . "http://melpa.org/packages/") t)
8 | (package-initialize)
9 | (package-refresh-contents)
10 | (defvar optional-dependencies '((company "0.8.12")))
11 | (package-install (caar optional-dependencies))
12 |
13 | (require 'cl)
14 |
15 | (defun pkg-installed-p (pkg)
16 | (package-installed-p (car pkg) (version-to-list (cadr pkg))))
17 |
18 | (condition-case err
19 | (let* ((pkg-info
20 | (with-temp-buffer
21 | (insert-file-contents "emacs-eclim-pkg.el")
22 | (goto-char (point-min))
23 | (read (current-buffer))))
24 | (name (cadr pkg-info))
25 | (needed-packages (cadr (nth 4 pkg-info))))
26 | (assert (equal name "emacs-eclim"))
27 | (message "Loaded emacs-eclim-pkg.el")
28 | (message "Installing dependencies: %S" needed-packages)
29 | (if (every #'pkg-installed-p needed-packages)
30 | (message "All dependencies present.")
31 | (package-refresh-contents)
32 | (dolist (p needed-packages)
33 | (unless (pkg-installed-p p)
34 | (package-install (car p))
35 | (when (not (pkg-installed-p p))
36 | (error (message "Failed to install %s at %s." p)))
37 | ))))
38 | (error (message "Error loading dependencies: %s" err)))
39 |
40 | (add-hook 'emacs-lisp-mode-hook
41 | (lambda ()
42 | (setq indent-tabs-mode nil)
43 | (setq fill-column 80)
44 | (setq elisp-lint-ignored-validators '("package-format"
45 | "fill-column"
46 | "byte-compile"
47 | "indent"))))
48 | (let ((tests (directory-files "./tests" t "tests.el")))
49 | (while tests
50 | (load-file (car tests))
51 | (setq tests (cdr tests))))
52 |
53 | (require 'elisp-lint)
54 | (require 'eclim)
55 |
--------------------------------------------------------------------------------
/eclim-maven.el:
--------------------------------------------------------------------------------
1 | ;; eclim-maven.el --- an interface to the Eclipse IDE.
2 | ;;
3 | ;; Copyright (C) 2009 Yves Senn
4 | ;;
5 | ;; This program is free software: you can redistribute it and/or modify
6 | ;; it under the terms of the GNU General Public License as published by
7 | ;; the Free Software Foundation, either version 3 of the License, or
8 | ;; (at your option) any later version.
9 | ;;
10 | ;; This program is distributed in the hope that it will be useful,
11 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | ;; GNU General Public License for more details.
14 | ;;
15 | ;; You should have received a copy of the GNU General Public License
16 | ;; along with this program. If not, see .
17 | ;;
18 | ;;; Contributors
19 | ;;
20 | ;;; Conventions
21 | ;;
22 | ;; Conventions used in this file: Name internal variables and functions
23 | ;; "eclim--", and name eclim command invocations
24 | ;; "eclim/command-name", like eclim/project-list.
25 |
26 | ;;* Eclim Maven
27 |
28 | (require 'compile)
29 |
30 | ;; Add regexp to make compilation-mode understand maven2 errors
31 | (setq compilation-error-regexp-alist
32 | (append (list
33 | '("\\[ERROR]\\ \\(\/.*\\):\\[\\([0-9]*\\),\\([0-9]*\\)]" 1 2 3))
34 | compilation-error-regexp-alist))
35 |
36 | (define-key eclim-mode-map (kbd "C-c C-e m p") 'eclim-maven-lifecycle-phase-run)
37 | (define-key eclim-mode-map (kbd "C-c C-e m r") 'eclim-maven-run)
38 |
39 | (defvar eclim-maven-lifecycle-phases
40 | '("validate" "compile" "test" "package" "integration" "verify" "install" "deploy"))
41 |
42 | (defun eclim--maven-lifecycle-phase-read ()
43 | (completing-read "Phase: " eclim-maven-lifecycle-phases))
44 |
45 | (defun eclim--maven-pom-path ()
46 | (concat (eclim--project-dir) "/pom.xml "))
47 |
48 | (defun eclim--maven-execute (command)
49 | (let ((default-directory (eclim--project-dir)))
50 | (compile (concat "mvn -f " (eclim--maven-pom-path) " " command))))
51 |
52 |
53 |
54 | (defun eclim-maven-run (goal)
55 | "Execute a specific Maven goal in the context of the current
56 | project. The build output is displayed in the *compilation* buffer."
57 | (interactive "MGoal: ")
58 | (eclim--maven-execute goal))
59 |
60 | (defun eclim-maven-lifecycle-phase-run (phase)
61 | "Run any given Maven life-cycle phase in the context of the current
62 | project. The build output is displayed in the *compilation* buffer."
63 | (interactive (list (eclim--maven-lifecycle-phase-read)))
64 | (eclim-maven-run phase))
65 |
66 | (provide 'eclim-maven)
67 |
--------------------------------------------------------------------------------
/ac-emacs-eclim-source.el:
--------------------------------------------------------------------------------
1 | ;; ac-emacs-eclim-source.el --- an interface to the Eclipse IDE.
2 | ;;
3 | ;; Copyright (C) 2009 Fredrik Appelberg
4 | ;;
5 | ;; This program is free software: you can redistribute it and/or modify
6 | ;; it under the terms of the GNU General Public License as published by
7 | ;; the Free Software Foundation, either version 3 of the License, or
8 | ;; (at your option) any later version.
9 | ;;
10 | ;; This program is distributed in the hope that it will be useful,
11 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | ;; GNU General Public License for more details.
14 | ;;
15 | ;; You should have received a copy of the GNU General Public License
16 | ;; along with this program. If not, see .
17 | ;;
18 | ;;; Contributors
19 | ;;
20 | ;;; Conventions
21 | ;;
22 | ;; Conventions used in this file: Name internal variables and functions
23 | ;; "eclim--", and name eclim command invocations
24 | ;; "eclim/command-name", like eclim/project-list.
25 | ;;; Description
26 | ;;
27 | ;; ac-emacs-eclim-source.el -- a emacs eclime source for auto-complete-mode
28 | ;;
29 |
30 | (require 'eclim)
31 | (require 'eclim-java)
32 | (require 'eclim-completion)
33 | (require 'auto-complete)
34 |
35 | (defface ac-emacs-eclim-candidate-face
36 | '((t (:background "gold1" :foreground "black")))
37 | "Face for emacs-eclim candidate."
38 | :group 'auto-complete)
39 |
40 | (defface ac-emacs-eclim-selection-face
41 | '((t (:background "gold4" :foreground "white")))
42 | "Face for the emacs-eclim selected candidate."
43 | :group 'auto-complete)
44 |
45 | (ac-define-source emacs-eclim
46 | '((candidates . eclim--completion-candidates)
47 | (action . ac-emacs-eclim-action)
48 | (prefix . eclim-completion-start)
49 | (document . eclim--completion-documentation)
50 | (cache)
51 | (selection-face . ac-emacs-eclim-selection-face)
52 | (candidate-face . ac-emacs-eclim-candidate-face)
53 | (symbol . "f")))
54 |
55 | (defun ac-emacs-eclim-action ()
56 | (eclim--completion-action eclim--completion-start (point)))
57 |
58 | (defun ac-emacs-eclim-java-setup ()
59 | (add-to-list 'ac-sources 'ac-source-emacs-eclim))
60 |
61 | (defun ac-emacs-eclim-xml-setup ()
62 | (add-to-list 'ac-sources 'ac-source-emacs-eclim))
63 |
64 | (defun ac-emacs-eclim-php-setup ()
65 | (add-to-list 'ac-sources 'ac-source-emacs-eclim))
66 |
67 | (defun ac-emacs-eclim-ruby-setup ()
68 | (add-to-list 'ac-sources 'ac-source-emacs-eclim))
69 |
70 | (defun ac-emacs-eclim-c-setup ()
71 | (add-to-list 'ac-sources 'ac-source-emacs-eclim))
72 |
73 | (defun ac-emacs-eclim-scala-setup ()
74 | (add-to-list 'ac-sources 'ac-source-emacs-eclim))
75 |
76 | (defun ac-emacs-eclim-config ()
77 | (add-hook 'java-mode-hook 'ac-emacs-eclim-java-setup)
78 | (add-hook 'groovy-mode-hook '(lambda() (interactive)
79 | (add-to-list 'ac-sources 'ac-source-emacs-eclim)))
80 | (add-hook 'xml-mode-hook 'ac-emacs-eclim-xml-setup)
81 | (add-hook 'nxml-mode-hook 'ac-emacs-eclim-xml-setup)
82 | (add-hook 'php-mode-hook 'ac-emacs-eclim-php-setup)
83 | (add-hook 'ruby-mode-hook 'ac-emacs-eclim-ruby-setup)
84 | (add-hook 'c-mode-hook 'ac-emacs-eclim-c-setup)
85 | (add-hook 'c++-mode-hook 'ac-emacs-eclim-c-setup)
86 | (add-hook 'scala-mode-hook 'ac-emacs-eclim-scala-setup))
87 |
88 | (provide 'ac-emacs-eclim-source)
89 |
--------------------------------------------------------------------------------
/eclim-ant.el:
--------------------------------------------------------------------------------
1 | ;; eclim-ant.el --- an interface to the Eclipse IDE.
2 | ;;
3 | ;; Copyright (C) 2009 Yves Senn
4 | ;;
5 | ;; This program is free software: you can redistribute it and/or modify
6 | ;; it under the terms of the GNU General Public License as published by
7 | ;; the Free Software Foundation, either version 3 of the License, or
8 | ;; (at your option) any later version.
9 | ;;
10 | ;; This program is distributed in the hope that it will be useful,
11 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | ;; GNU General Public License for more details.
14 | ;;
15 | ;; You should have received a copy of the GNU General Public License
16 | ;; along with this program. If not, see .
17 | ;;
18 | ;;; Contributors
19 | ;;
20 | ;;
21 | ;;; Conventions
22 | ;;
23 | ;; Conventions used in this file: Name internal variables and functions
24 | ;; "eclim--", and name eclim command invocations
25 | ;; "eclim/command-name", like eclim/project-list.
26 |
27 | ;;* Eclim Ant
28 |
29 | (define-key eclim-mode-map (kbd "C-c C-e a c") 'eclim-ant-clear-cache)
30 | (define-key eclim-mode-map (kbd "C-c C-e a r") 'eclim-ant-run)
31 | (define-key eclim-mode-map (kbd "C-c C-e a a") 'eclim-ant-run)
32 | (define-key eclim-mode-map (kbd "C-c C-e a v") 'eclim-ant-validate)
33 |
34 | (defgroup eclim-ant nil
35 | "Build java projects using Apache Ant"
36 | :group 'eclim)
37 |
38 | (defcustom eclim-ant-directory ""
39 | "This variable contains the location, where the main buildfile is
40 | stored. It is used globally for all eclim projects."
41 | :group 'eclim-ant
42 | :type 'directory)
43 |
44 | (defcustom eclim-ant-buildfile-name "build.xml"
45 | "The name of the main buildfile from your projects."
46 | :group 'eclim-ant
47 | :type 'string)
48 |
49 | (defvar eclim--ant-target-cache nil)
50 |
51 | (defun eclim--ant-buildfile-name ()
52 | (concat (file-name-as-directory eclim-ant-directory) eclim-ant-buildfile-name))
53 |
54 | (defun eclim--ant-buildfile-path ()
55 | (file-name-directory (concat (eclim--project-dir) "/" (eclim--ant-buildfile-name))))
56 |
57 | (defun eclim--ant-targets (project buildfile)
58 | (when (null eclim--ant-target-cache)
59 | (setq eclim--ant-target-cache (make-hash-table :test 'equal)))
60 | (or (gethash buildfile eclim--ant-target-cache)
61 | (puthash buildfile (eclim/ant-target-list project buildfile) eclim--ant-target-cache)))
62 |
63 | (defun eclim--ant-read-target (project buildfile)
64 | (eclim--completing-read "Target: " (append (eclim--ant-targets project buildfile) nil)))
65 |
66 | (defun eclim/ant-validate (project buildfile)
67 | (eclim--check-project project)
68 | (mapcar (lambda (line)
69 | (split-string line "|"))
70 | (eclim--call-process "ant_validate" "-p" project "-f" file)))
71 |
72 | (defun eclim/ant-target-list (project buildfile)
73 | (eclim--check-project project)
74 | (eclim--call-process "ant_targets" "-p" project "-f" buildfile))
75 |
76 | (defun eclim-ant-clear-cache ()
77 | "Clear the cached ant target list. This can be usefull when the
78 | buildfile for the current project has changed and needs to be updated"
79 | (interactive)
80 | (setq eclim--ant-target-cache nil))
81 |
82 | (defun eclim-ant-validate (project file)
83 | "Run ant-xml validation against the file opened in the current
84 | buffer. The results are displayed in a dedicated compilation buffer."
85 | (interactive (list (eclim-project-name) (buffer-file-name)))
86 | (pop-to-buffer (get-buffer-create "*eclim: build*"))
87 | (let ((buffer-read-only nil))
88 | (erase-buffer)
89 | (dolist (line (eclim/ant-validate project file))
90 | (insert (eclim--convert-find-result-to-string line))
91 | (newline)))
92 | (beginning-of-buffer)
93 | (compilation-mode))
94 |
95 | (defun eclim-ant-run (target)
96 | "run a specified ant target in the scope of the current project. If
97 | the function is called interactively the users is presented with a
98 | list of all available ant targets."
99 | (interactive (list (eclim--ant-read-target (eclim-project-name)
100 | (eclim--ant-buildfile-name))))
101 | (let ((default-directory (eclim--ant-buildfile-path)))
102 | ;; TODO: use the right version of java to execute ant
103 | (compile (concat "ant " target))))
104 |
105 | (provide 'eclim-ant)
106 |
--------------------------------------------------------------------------------
/company-emacs-eclim.el:
--------------------------------------------------------------------------------
1 | ;; company-emacs-eclim.el --- an interface to the Eclipse IDE.
2 | ;;
3 | ;; Copyright (C) 2009-2012 Fredrik Appelberg
4 | ;; Copyright (C) 2013-2014 Dmitry Gutov
5 | ;;
6 | ;; This program is free software: you can redistribute it and/or modify
7 | ;; it under the terms of the GNU General Public License as published by
8 | ;; the Free Software Foundation, either version 3 of the License, or
9 | ;; (at your option) any later version.
10 | ;;
11 | ;; This program is distributed in the hope that it will be useful,
12 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | ;; GNU General Public License for more details.
15 | ;;
16 | ;; You should have received a copy of the GNU General Public License
17 | ;; along with this program. If not, see .
18 | ;;
19 | ;;; Description
20 | ;;
21 | ;; company-emacs-eclim.el -- company-mode backend that replaces company-eclim
22 | ;;
23 | ;; To activate this backend, replace company-eclim and/or company-nxml
24 | ;; with company-emacs-eclim in the eclim-backends list, or call the
25 | ;; convenience function company-emacs-eclim-setup.
26 | ;;
27 | ;; Minimum company-mode version required: 0.7.
28 |
29 | ;;* Eclim Company
30 |
31 | (require 'eclim)
32 | (require 'eclim-completion)
33 | (require 'eclim-java)
34 | (require 'company)
35 |
36 | (defcustom company-emacs-eclim-ignore-case t
37 | "If t, case is ignored in completion matches."
38 | :group 'eclim-company
39 | :type '(choice (const :tag "Yes" t)
40 | (const :tag "No" nil)))
41 |
42 | (defun company-emacs-eclim-setup ()
43 | "Convenience function that adds company-emacs-eclim to the list
44 | of available company backends."
45 | (setq company-backends
46 | (cons 'company-emacs-eclim
47 | (remove-if (lambda (b) (find b '(company-nxml company-eclim)))
48 | company-backends))))
49 |
50 | (defun company-emacs-eclim--before-prefix-in-buffer (prefix)
51 | "Search for the text before prefix that may be included as part of completions"
52 | (ignore-errors
53 | (save-excursion
54 | (let ((end (progn
55 | (backward-char (length prefix))
56 | (point)))
57 | (start (progn
58 | (while (save-excursion
59 | (backward-char)
60 | (eq ?. (char-after)))
61 | (backward-char)
62 | (beginning-of-thing 'symbol))
63 | (point))))
64 | (buffer-substring-no-properties start end)))))
65 |
66 | (defun company-emacs-eclim--candidates (prefix)
67 | (let ((before-prefix-in-buffer (company-emacs-eclim--before-prefix-in-buffer prefix)))
68 | (cl-labels
69 | ((annotate (str)
70 | (if (string-match "(" str)
71 | (propertize
72 | (substring str 0 (match-beginning 0)) 'eclim-meta str)
73 | str))
74 | (without-redundant-prefix (str)
75 | (if (and before-prefix-in-buffer
76 | (> (length before-prefix-in-buffer) 0)
77 | (string-prefix-p before-prefix-in-buffer str))
78 | (substring str (length before-prefix-in-buffer))
79 | str)))
80 | (mapcar
81 | (lambda (candidate)
82 | (annotate (without-redundant-prefix candidate)))
83 | (eclim--completion-candidates)))))
84 |
85 | (defun company-emacs-eclim--annotation (candidate)
86 | (let ((str (get-text-property 0 'eclim-meta candidate)))
87 | (when (and str (string-match "(" str))
88 | (substring str (match-beginning 0)))))
89 |
90 | (defun company-emacs-eclim (command &optional arg &rest ignored)
91 | "`company-mode' back-end for Eclim completion"
92 | (interactive (list 'interactive))
93 | (case command
94 | (interactive (company-begin-backend 'company-emacs-eclim))
95 | (prefix (let ((start (and eclim-mode
96 | (eclim--accepted-p (buffer-file-name))
97 | (eclim-completion-start))))
98 | (when start (buffer-substring-no-properties start (point)))))
99 | (candidates (company-emacs-eclim--candidates arg))
100 | (annotation (company-emacs-eclim--annotation arg))
101 | (meta (eclim--completion-documentation
102 | (concat arg (company-emacs-eclim--annotation arg))))
103 | (no-cache (equal arg ""))
104 | (ignore-case company-emacs-eclim-ignore-case)
105 | (sorted t)
106 | (post-completion (let ((ann (company-emacs-eclim--annotation arg)))
107 | (when ann
108 | (insert ann))
109 | (company-emacs-eclim-action arg ann)))))
110 |
111 | (defun company-emacs-eclim-action (completion annotation)
112 | (let* ((end (point))
113 | (len (+ (length completion) (length annotation)))
114 | (beg (- end len)))
115 | (eclim--completion-action beg end)))
116 |
117 | (provide 'company-emacs-eclim)
118 |
--------------------------------------------------------------------------------
/eclim-debug.el:
--------------------------------------------------------------------------------
1 | ;; eclim-debug.el --- an interface to the Eclipse IDE. -*- lexical-binding: t; -*-
2 | ;;
3 | ;; Copyright (C) 2015 Łukasz Klich
4 | ;;
5 | ;; Author: Lukasz Klich
6 | ;;
7 | ;; This program is free software: you can redistribute it and/or modify
8 | ;; it under the terms of the GNU General Public License as published by
9 | ;; the Free Software Foundation, either version 3 of the License, or
10 | ;; (at your option) any later version.
11 | ;;
12 | ;; This program is distributed in the hope that it will be useful,
13 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | ;; GNU General Public License for more details.
16 | ;;
17 | ;; You should have received a copy of the GNU General Public License
18 | ;; along with this program. If not, see .
19 | ;;
20 | ;;; Contributors
21 | ;;
22 | ;;; Conventions
23 | ;;
24 | ;; Conventions used in this file: Name internal variables and functions
25 | ;; "eclim--", and name external program invocations
26 | ;; "eclim/command-name", like eclim/project-list.
27 | ;;; Description
28 | ;;
29 | ;; eclim-debug.el -- emacs-eclim integration with gud and jdb to
30 | ;; support debugging
31 | ;;
32 |
33 | (require 'eclim-project)
34 | (require 'eclim-java)
35 | (require 'eclim-maven)
36 | (require 'eclim-ant)
37 | (require 'eclim-java-run)
38 | (require 'gud)
39 | (require 'dash)
40 | (require 's)
41 |
42 | (define-key eclim-mode-map (kbd "C-c C-e p t") 'eclim-debug-test)
43 | (define-key eclim-mode-map (kbd "C-c C-e p a") 'eclim-debug-attach)
44 |
45 | (defun eclim--debug-jdb-run-command (project main-class args)
46 | (let ((config `((name . ,(concat "*Debug - " main-class "*"))
47 | (debug . t)
48 | (main-class . ,main-class)
49 | (program-args . ,args)
50 | (vm-args . ,(concat "-sourcepath" (eclim-java-run-sourcepath project)))))
51 | (classpath (eclim/java-classpath project)))
52 | (eclim-java-run--command config (eclim-java-run--java-vm-args classpath))))
53 |
54 | (defun eclim--debug-jdb-attach-command (project port)
55 | (let ((sourcepath (eclim-java-run-sourcepath project)))
56 | (format "jdb -attach %s -sourcepath%s "
57 | port
58 | sourcepath)))
59 |
60 | (defun eclim--debug-attach-when-ready (txt project port)
61 | (when (s-contains? (concat "at address: " (number-to-string port)) txt)
62 | (remove-hook 'comint-output-filter-functions
63 | 'eclim--debug-attach-when-ready
64 | t)
65 | (eclim-debug-attach port project)))
66 |
67 | (defun eclim--debug-maven-run ()
68 | (concat "mvn -f " (eclim--maven-pom-path)
69 | "clean test -Dmaven.surefire.debug -Dtest=" (file-name-base)))
70 |
71 | (defun eclim--debug-project-maven? ()
72 | (eclim--debug-file-exists-in-project-root? "pom.xml"))
73 |
74 | (defun eclim--debug-ant-run (target)
75 | (let ((default-directory (eclim--ant-buildfile-path)))
76 | "ANT_OPTS=\"$ANT_OPTS -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005\" ant test"))
77 |
78 | (defun eclim--debug-project-ant? ()
79 | (eclim--debug-file-exists-in-project-root? "build.xml"))
80 |
81 | (defun eclim--debug-file-exists-in-project-root? (filename)
82 | (let* ((project-dir (eclim-java-run--project-dir (eclim-project-name)))
83 | (file (concat project-dir filename)))
84 | (file-exists-p file)))
85 |
86 | (defun eclim--debug-run-process-and-attach (command port)
87 | (let ((project (eclim-project-name)))
88 | (with-current-buffer (compile command t)
89 | (setq-local comint-prompt-read-only t)
90 | (make-local-variable 'comint-output-filter-functions)
91 | (add-hook 'comint-output-filter-functions
92 | (lambda (txt) (eclim--debug-attach-when-ready txt project port))))))
93 |
94 | (defun eclim-debug/jdb (command)
95 | (let ((buffer (current-buffer)))
96 | (toggle-maximize-buffer)
97 | (switch-to-buffer-other-window buffer t)
98 | (jdb command)
99 | (switch-to-buffer-other-window buffer t)))
100 |
101 | (defun eclim-debug-junit ()
102 | (interactive)
103 | (let ((project (eclim-project-name))
104 | (classes (eclim-package-and-class)))
105 | (eclim-debug/jdb
106 | (eclim--debug-jdb-run-command project "org.junit.runner.JUnitCore" classes))))
107 |
108 | (defun eclim-debug-maven-test ()
109 | (interactive)
110 | (eclim--debug-run-process-and-attach (eclim--debug-maven-run) 5005))
111 |
112 | (defun eclim-debug-ant-test ()
113 | (interactive)
114 | (eclim--debug-run-process-and-attach (eclim--debug-ant-run) 5005))
115 |
116 | (defun eclim-debug-attach (port project)
117 | (interactive (list (read-number "Port: " 5005) (eclim-project-name)))
118 | (eclim-debug/jdb (eclim--debug-jdb-attach-command project port)))
119 |
120 | (defun eclim-debug-test ()
121 | (interactive)
122 | (cond ((eclim-java-junit-buffer?) (eclim-debug-junit))
123 | ((eclim--debug-project-maven?) (eclim-debug-maven-test))
124 | ((eclim--debug-projecta-ant?) (eclim-debug-ant-test))
125 | (t (message "I can't debug this. I wasn't program smart enough. Please help me"))))
126 |
127 | (provide 'eclim-debug)
128 |
--------------------------------------------------------------------------------
/eclimd.el:
--------------------------------------------------------------------------------
1 | ;;; eclimd.el --- Start and stop eclimd from within emacs
2 |
3 | ;; Copyright (C) 2012 Vedat Hallac
4 |
5 | ;; Authors: Vedat Hallac
6 | ;; Version: 1.0
7 | ;; Created: 2012/05/11
8 | ;; Keywords: java, emacs-eclim
9 |
10 | ;; This file is NOT part of Emacs.
11 | ;;
12 | ;; This program is free software; you can redistribute it and/or
13 | ;; modify it under the terms of the GNU General Public License
14 | ;; as published by the Free Software Foundation; either version 2
15 | ;; of the License, or (at your option) any later version.
16 | ;;
17 | ;; This program is distributed in the hope that it will be useful,
18 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
19 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 | ;; GNU General Public License for more details.
21 | ;;
22 | ;; You should have received a copy of the GNU General Public License
23 | ;; along with this program; if not, write to the Free Software
24 | ;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25 | ;; MA 02110-1301, USA.
26 |
27 | (require 'eclim)
28 |
29 | (defgroup eclimd nil
30 | "eclimd customizations"
31 | :prefix "eclimd-"
32 | :group 'eclim)
33 |
34 | (defcustom eclimd-executable
35 | nil
36 | "The eclimd executable to use.
37 | Set to nil to auto-discover from `eclim-executable' value (the default value).
38 | Set to \"eclimd\" if eclim and eclimd are in `exec-path'. Otherwise, set to
39 | the full path to eclimd executable."
40 | :type '(choice (const :tag "Same directory as eclim-executable variable" nil)
41 | (string :tag "Custom value" "eclimd"))
42 | :group 'eclimd)
43 |
44 | (defcustom eclimd-default-workspace
45 | "~/workspace"
46 | "The workspace to use with eclimd"
47 | :type 'directory
48 | :group 'eclimd)
49 |
50 | (defcustom eclimd-wait-for-process
51 | t
52 | "Set to t if you want `start-eclimd' to wait until the eclimd process is ready.
53 | When this variable is nil, `start-eclimd' returns immediately after
54 | eclimd process is started. Since the eclimd process startup takes a few seconds,
55 | running eclim commands immediately after the function returns may cause failures.
56 | You can freeze emacs until eclimd is ready to accept commands with this variable."
57 | :tag "Wait until eclimd is ready"
58 | :type 'boolean
59 | :group 'eclimd)
60 |
61 | (defvar eclimd-process-buffer nil
62 | "Buffer used for communication with eclimd process")
63 |
64 | (defvar eclimd-process nil
65 | "The active eclimd process")
66 |
67 | (defconst eclimd-process-buffer-name "eclimd")
68 |
69 | (defun eclimd--executable-path ()
70 | (if eclimd-executable
71 | (executable-find eclimd-executable)
72 | (let ((eclim-prog (executable-find eclim-executable)))
73 | (expand-file-name "eclimd" (file-name-directory eclim-prog)))))
74 |
75 | (defun eclimd--running-p ()
76 | (not (null (get-buffer-process eclimd-process-buffer))))
77 |
78 | (defun eclimd--match-process-output (regexp proc)
79 | "Wait for the given process to output a string that matches the specified regexp.
80 | Return the string used for `match-string' if a match is found, and nil if the process is killed.
81 |
82 | The caller must use `save-match-data' to preserve the match data if necessary."
83 | (let ((old-filter (process-filter proc))
84 | (old-sentinel (process-sentinel proc))
85 | (output "")
86 | (terminated-p))
87 | (set-process-filter proc (lambda (proc string)
88 | (setf output (concat output string))
89 | ;; Chain to the old filter
90 | (if old-filter
91 | (funcall old-filter proc string))))
92 | (set-process-sentinel proc (lambda (proc state)
93 | (unless (eq 'run
94 | (process-status proc))
95 | (setf terminated-p t))))
96 | (while (and (not terminated-p)
97 | (not (string-match regexp output)))
98 | (accept-process-output proc))
99 | (set-process-sentinel proc old-sentinel)
100 | (set-process-filter proc old-filter)
101 | (and (not terminated-p) output)))
102 |
103 | (defun wait-eclimd-start ()
104 | "Wait for the eclimd server to become active.
105 | This function also waits for the eclimd server to report that it is started.
106 | It returns the port it is listening on"
107 | (let ((eclimd-start-regexp "Eclim Server Started on\\(?: port\\|:\\) \\(?:\\(?:[0-9]+\\.\\)\\{3\\}[0-9]+:\\)?\\([0-9]+\\)"))
108 | (save-match-data
109 | (let ((output (eclimd--match-process-output eclimd-start-regexp eclimd-process)))
110 | (when output
111 | (setq eclimd-port (match-string 1 output))
112 | (message (concat "eclimd serving at port " eclimd-port)))))
113 | eclimd-port))
114 |
115 | (defun start-eclimd (workspace-dir)
116 | (interactive (list (read-directory-name "Workspace directory: "
117 | eclimd-default-workspace nil t)))
118 | (let ((eclimd-prog (eclimd--executable-path)))
119 | (if (not eclimd-prog)
120 | (message "Cannot start eclimd: check eclimd-executable variable.")
121 | (if (eclimd--running-p)
122 | (message "Cannot start eclimd: eclimd is already running.")
123 | (message (concat "Starting eclimd for workspace: " workspace-dir "..."))
124 | (setq eclimd-process-buffer
125 | (make-comint eclimd-process-buffer-name
126 | eclimd-prog
127 | nil
128 | (concat "-Dosgi.instance.area.default="
129 | (replace-regexp-in-string "~" "@user.home" workspace-dir))))
130 | (setq eclimd-process (get-buffer-process eclimd-process-buffer))
131 | (when eclimd-wait-for-process
132 | (wait-eclimd-start))))))
133 |
134 | (defun stop-eclimd ()
135 | (interactive)
136 | (when eclimd-process
137 | (eclim/execute-command "shutdown")
138 | (eclimd--match-process-output "Process eclimd finished" eclimd-process)
139 | (delete-process eclimd-process)
140 | (setq eclimd-process nil))
141 | (when eclimd-process-buffer
142 | (kill-buffer eclimd-process-buffer)
143 | (setq eclimd-process-buffer nil)))
144 |
145 | (provide 'eclimd)
146 |
--------------------------------------------------------------------------------
/eclim-java-run.el:
--------------------------------------------------------------------------------
1 | ;; eclim-java-run.el --- an interface to the Eclipse IDE. -*- lexical-binding: t; -*-
2 | ;;
3 | ;; Copyright (C) 2015 Łukasz Klich
4 | ;;
5 | ;; Author: Lukasz Klich
6 | ;;
7 | ;; This program is free software: you can redistribute it and/or modify
8 | ;; it under the terms of the GNU General Public License as published by
9 | ;; the Free Software Foundation, either version 3 of the License, or
10 | ;; (at your option) any later version.
11 | ;;
12 | ;; This program is distributed in the hope that it will be useful,
13 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | ;; GNU General Public License for more details.
16 | ;;
17 | ;; You should have received a copy of the GNU General Public License
18 | ;; along with this program. If not, see .
19 | ;;
20 | ;;; Contributors
21 | ;;
22 | ;;; Conventions
23 | ;;
24 | ;; Conventions used in this file: Name internal variables and functions
25 | ;; "eclim--", and name external program invocations
26 | ;; "eclim/command-name", like eclim/project-list.
27 | ;;; Description
28 | ;;
29 | ;; eclim-java-run.el -- java run configurations for eclim
30 | ;;
31 |
32 | (require' eclim-project)
33 | (require 'eclim-java)
34 | (require 's)
35 | (require 'dash)
36 |
37 | (define-key eclim-mode-map (kbd "C-c C-e u r") 'eclim-java-run-run)
38 |
39 | (defun eclim-java-run-sourcepath (project)
40 | (let ((projects (-snoc (eclim-project-dependencies project) project)))
41 | (s-join ":" (-mapcat 'eclim-java-run--project-sourcepath projects))))
42 |
43 | (defun eclim-java-run--project-dir (project)
44 | (file-name-as-directory (cdr (assoc 'path (eclim/project-info project)))))
45 |
46 | (defun eclim-java-run--project-sourcepath (project)
47 | (eclim-java-run--read-sourcepath
48 | (concat (eclim-java-run--project-dir project)
49 | ".classpath")))
50 |
51 | (defun eclim-java-run--read-sourcepath (classpath-file)
52 | (let* ((root (car (xml-parse-file classpath-file)))
53 | (classpathentries (xml-get-children root 'classpathentry))
54 | (srcs (-filter 'eclim-java-run--src?? classpathentries))
55 | (paths-relative (-map 'eclim-java-run--get-path srcs))
56 | (paths-absolute (--map (concat (file-name-directory classpath-file) it) paths-relative)))
57 | paths-absolute))
58 |
59 | (defun eclim-java-run--src?? (classpathentry)
60 | (let* ((attrs (xml-node-attributes classpathentry))
61 | (kind (cdr (assq 'kind attrs))))
62 | (string-equal kind "src")))
63 |
64 | (defun eclim-java-run--get-path (classpathentry)
65 | (let* ((attrs (xml-node-attributes classpathentry))
66 | (path (cdr (assq 'path attrs))))
67 | path))
68 |
69 | (defun eclim-java-run--get-string-from-file (file-path)
70 | (with-temp-buffer
71 | (insert-file-contents file-path)
72 | (buffer-string)))
73 |
74 | (defun eclim-java-run--load-configurations (project)
75 | (let* ((configurations-path (concat (eclim-java-run--project-dir project) ".eclim"))
76 | (configurations (read (eclim-java-run--get-string-from-file configurations-path))))
77 | configurations))
78 |
79 | (defun eclim-java-run--get-value (key alist)
80 | (cdr (assoc key alist)))
81 |
82 | (defun eclim-java-run--jdb? (config)
83 | (and (eclim-java-run--get-value 'debug config)
84 | (not (eclim-java-run--get-value 'debug-port config))))
85 |
86 | (defun eclim-java-run--java-vm-args (classpath)
87 | (lambda (config)
88 | (concat "-classpath" (when (not (eclim-java-run--jdb? config)) " ")
89 | classpath " "
90 | (eclim-java-run--get-value 'vm-args config))))
91 |
92 | (defun eclim-java-run--debug-vm-args (classpath sourcepath)
93 | (lambda (config)
94 | (concat "-sourcepath"
95 | sourcepath " "
96 | (funcall (eclim-java-run--java-vm-args classpath) config))))
97 |
98 | (defun eclim-java-run--command (config vm-args-fn)
99 | (s-join " " (-flatten
100 | (list
101 | (if (eclim-java-run--jdb? config) "jdb" "java")
102 | (funcall vm-args-fn config)
103 | (eclim-java-run--get-value 'main-class config)
104 | (eclim-java-run--get-value 'program-args config)))))
105 |
106 | (defun eclim-java-run--run-jdb (config classpath sourcepath project-dir)
107 | (let* ((command (eclim-java-run--command config
108 | (eclim-java-run--debug-vm-args classpath sourcepath))))
109 | (with-temp-buffer
110 | (setq default-directory project-dir)
111 | (eclim-debug/jdb command))))
112 |
113 | (defun eclim-java-run--run-java (config classpath project-dir)
114 | (let* ((name (eclim-java-run--get-value 'name config))
115 | (command (eclim-java-run--command config (eclim-java-run--java-vm-args classpath)))
116 | (new-buffer-name (concat "*" name "*")))
117 | (when (buffer-live-p (get-buffer new-buffer-name)) (kill-buffer new-buffer-name))
118 | (with-temp-buffer
119 | (setq default-directory project-dir)
120 | (switch-to-buffer (process-buffer
121 | (start-process-shell-command name new-buffer-name command))))))
122 |
123 | (defun eclim-java-run--configuration (name confs)
124 | (car
125 | (--filter (string-equal (cdr (assoc 'name it)) name) confs)))
126 |
127 | (defun eclim-java-run--ask-which-configuration ()
128 | (completing-read "Which configuration do you want to run?"
129 | (--map (cdr (assoc 'name it))
130 | (eclim-java-run--load-configurations (eclim-project-name)))
131 | nil t))
132 |
133 | (defun eclim-java-run-run (configuration-name)
134 | (interactive (list (eclim-java-run--ask-which-configuration)))
135 | (let* ((configurations (eclim-java-run--load-configurations (eclim-project-name)))
136 | (configuration (eclim-java-run--configuration configuration-name configurations))
137 | (project-dir (eclim-java-run--project-dir (eclim-project-name)))
138 | (classpath (eclim/java-classpath (eclim-project-name)))
139 | (debug? (eclim-java-run--jdb? configuration)))
140 | (if debug?
141 | (eclim-java-run--run-jdb configuration
142 | classpath
143 | (eclim-java-run-sourcepath (eclim-project-name))
144 | project-dir)
145 | (eclim-java-run--run-java configuration
146 | classpath
147 | project-dir))))
148 |
149 | (provide 'eclim-java-run)
150 | ;;; eclim-java-run.el ends here
151 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [![License GPL 3][badge-license]](http://www.gnu.org/licenses/gpl-3.0.txt)
2 | [](https://travis-ci.org/senny/emacs-eclim)
3 | [](http://melpa.org/#/emacs-eclim)
4 | [](http://stable.melpa.org/#/emacs-eclim)
5 |
6 | ## Development has moved to https://github.com/emacs-eclim/emacs-eclim !!!
7 |
8 | ## Overview
9 |
10 | [](https://gitter.im/senny/emacs-eclim?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
11 |
12 | **Please Note: Development has moved to https://github.com/emacs-eclim/emacs-eclim**
13 |
14 | [Eclim](http://eclim.org) is an Eclipse plugin which exposes Eclipse
15 | features through a server interface. When this server is started, the
16 | command line utility eclim can be used to issue requests to that
17 | server.
18 |
19 | Emacs-eclim uses the eclim server to integrate eclipse with
20 | emacs. This project wants to bring some of the invaluable features
21 | from eclipse to emacs. Please note, emacs-eclim **is limited to mostly java support at this time.**
22 |
23 | It is also possible to start and stop the eclim daemon from emacs using the
24 | `eclimd` package.
25 |
26 | You can ask questions or discuss new features at our [Google Group](https://groups.google.com/forum/#!forum/emacs-eclim)
27 |
28 | ## A note about Eclim versions
29 |
30 | Prior to version 1.7.3, eclim used a proprietary protocol for
31 | communication with the eclim server. If you are running one of these
32 | older versions, you need version 0.1 of emacs-eclim.
33 |
34 | Eclim versions 1.7.3 and later however, serves responses using a
35 | standard JSON format. These are supported by emacs-eclim versions 0.2
36 | and later.
37 |
38 | Emacs-eclim versions are tagged with the appropriate version
39 | number. You can see and download previous releases
40 | [here](https://github.com/senny/emacs-eclim/tags).
41 |
42 | ## Installation
43 |
44 | 1. [Download and install](http://eclim.org/install.html) eclim.
45 | 1. Install emacs-eclim. You have two options:
46 | * Installation from the [MELPA][melpa] package archive. Just add
47 | the archive to `package-archives` if you haven't already, and then
48 | install emacs-eclim with the `package-install` command.
49 | * Manual installation from GitHub.
50 | 1. (`git clone git://github.com/senny/emacs-eclim.git`)
51 | 1. Add `(add-to-list 'load-path "/path/to/emacs-eclim/")` to your startup script.
52 | 1. Add the following code to your emacs startup script
53 |
54 | ```lisp
55 | (require 'eclim)
56 | (global-eclim-mode)
57 | ```
58 |
59 | If you want to control eclimd from emacs, also add:
60 |
61 | ```lisp
62 | (require 'eclimd)
63 | ```
64 |
65 |
66 | ## Configuration
67 |
68 | ### Eclipse installation
69 |
70 | Emacs-eclim tries its best to locate your Eclipse installation. If
71 | you have Eclipse installed in a non-standard location
72 | (i.e. ~/nonStandard/eclipse or /opt/eclipse) you may specify the paths manually by adding this to your startup script:
73 |
74 | ```lisp
75 | (custom-set-variables
76 | '(eclim-eclipse-dirs '("~/nonStandard/eclipse"))
77 | '(eclim-executable "~/nonStandard/eclipse/eclim"))
78 | ```
79 |
80 | ### Displaying compilation error messages in the echo area
81 |
82 | When the cursor is positioned on an error marker in a code buffer,
83 | emacs-eclim uses the local help feature in emacs to display the
84 | corresponding error message in the echo area. You can either invoke
85 | `(display-local-help)` manually or activate automatic display of local
86 | help by adding the following to .emacs:
87 |
88 | ```lisp
89 | (setq help-at-pt-display-when-idle t)
90 | (setq help-at-pt-timer-delay 0.1)
91 | (help-at-pt-set-timer)
92 | ```
93 |
94 | ### Configuring auto-complete-mode
95 |
96 | If you wish to use [auto-complete-mode] with emacs-eclim, add the
97 | following to your .emacs:
98 |
99 | ```lisp
100 | ;; regular auto-complete initialization
101 | (require 'auto-complete-config)
102 | (ac-config-default)
103 |
104 | ;; add the emacs-eclim source
105 | (require 'ac-emacs-eclim-source)
106 | (ac-emacs-eclim-config)
107 | ```
108 |
109 | ### Configuring company-mode
110 |
111 | Emacs-eclim can integrate with [company-mode] to provide pop-up
112 | dialogs for auto-completion. To activate this, you need to add the
113 | following to your .emacs:
114 |
115 | ```lisp
116 | (require 'company)
117 | (require 'company-emacs-eclim)
118 | (company-emacs-eclim-setup)
119 | (global-company-mode t)
120 | ```
121 |
122 | Emacs-eclim completions in company are case sensitive by default. To make completions
123 | case insensitive set `company-emacs-eclim-ignore-case` to `t`.
124 |
125 | ### Configuring eclimd module
126 |
127 | When `emacs-eclim` is configured correctly, you don't need to modify the
128 | configuration for the `eclimd` package. Still, there are some configurable
129 | variables you can tweak:
130 |
131 | 1. `eclimd-executable`: This variable is used for locating the `eclimd`
132 | executable file. You can set it to `nil` ("Same directory as eclim-executable
133 | variable" choice in customization screen) to indicate that the executable is in
134 | the same directory as the `eclim` program. Alternatively, you can give it a
135 | string value ("Custom value" choice in customization screen) to specify the
136 | location of the executable.
137 |
138 | 1. `eclimd-default-workspace`: When `start-eclimd` is executed, it will ask for
139 | the workspace directory to use. The default value for this question is
140 | controlled by this variable.
141 |
142 | 1. `eclimd-wait-for-process`: Normally, when `start-eclimd` starts the eclimd
143 | process, it pauses emacs until `eclimd` is ready to accept commands. If you
144 | change the value of this variable to `nil`, `start-eclimd` will return as
145 | soon as `eclimd` is started. Eclimd startup takes several seconds, so if you
146 | change the default value of this variable, `emacs-eclim` commands will fail
147 | until `eclimd` is ready.
148 |
149 | ## Dependencies
150 | * [dash.el](https://github.com/magnars/dash.el) for list manipulation functions
151 | * [s.el](https://github.com/magnars/s.el) for string manipulation functions
152 | * json.el (part of emacs as of version 23)
153 | * [popup.el](https://github.com/auto-complete/popup-el) for inplace popup menus
154 |
155 | ## Optional dependencies
156 | * A recent version (0.6.0 or later) of [yasnippet]
157 | * A recent version (tested with 0.5) of [company-mode]
158 | * A recent version (tested with 1.4) version of [auto-complete-mode]
159 | * ido-mode (part of emacs as of version 22)
160 |
161 | ## Usage
162 | To get started just launch the eclim executable that the placed in
163 | your Eclipse installation directory.
164 |
165 | * [Projects](http://wiki.github.com/senny/emacs-eclim/projects)
166 | * [Code Completion](http://wiki.github.com/senny/emacs-eclim/code-completion)
167 | * [Java](http://wiki.github.com/senny/emacs-eclim/java)
168 | * [Ant](http://wiki.github.com/senny/emacs-eclim/ant)
169 | * [Maven](http://wiki.github.com/senny/emacs-eclim/maven)
170 | * [Problems and Errors](http://wiki.github.com/senny/emacs-eclim/problems-and-errors)
171 |
172 | ### Controlling eclimd
173 |
174 | When you import the `eclimd` package, you will have access to two commands:
175 | `start-eclimd`, and `stop-eclimd`.
176 |
177 | `start-eclimd` will ask for a workspace directory, and it will attempt to start
178 | `eclimd` program with the entered workspace directory. The configurable variable
179 | `eclimd-default-workspace` controls the default value of this directory. After
180 | `start-eclimd` runs the daemon, it will monitor its log output, and wait for the
181 | message that indicates that it is ready to accept commands. This is done to
182 | prevent failures with `emacs-eclim` commands while `eclimd` is starting up.
183 | While `start-eclimd` is waiting for the daemon to be ready, emacs will not
184 | accept any user input. To prevent this pause, you can modify the configurable
185 | variable `eclimd-wait-for-process`.
186 |
187 | Normally, simply killing the buffer `*eclimd*` will allow you to stop the eclimd
188 | daemon. However, there is a command to perform a graceful shutdown:
189 | `stop-eclimd`. You should use this command when you wish to stop the `eclimd`
190 | program.
191 |
192 | ## Press
193 |
194 | Read more about emacs-eclim:
195 |
196 | * [Enterprise Java Development in Emacs](http://www.skybert.net/emacs/java/), \[Torstein Krause Johansen\]
197 | * [The Ballad of Emacs-Eclim](http://mulli.nu/2012/02/02/the-ballad-of-emacs-eclim.html), \[Fredrik Appelberg\]
198 | * [Emacs and Java](http://blog.senny.ch/blog/2012/10/13/emacs-and-java-journey-of-a-hard-friendship/), \[Yves Senn\]
199 | * [Java Autocompletion for Emacs](http://root42.blogspot.ch/2012/08/java-autocompletion-for-emacs.html), \[root42\]
200 | * [Eclim: Eclipse Meets Vim And Emacs](http://faruk.akgul.org/blog/eclim-eclipse-meets-vim-emacs/), \[Faruk Akgul\]
201 |
202 | ## Contributing
203 |
204 | Have a quick look at our [Contribution Guidelines](CONTRIBUTING.md)
205 | and hack away.
206 |
207 | [yasnippet]:https://github.com/capitaomorte/yasnippet
208 | [company-mode]:https://github.com/company-mode/company-mode
209 | [auto-complete-mode]:https://github.com/auto-complete/auto-complete
210 | [melpa]:http://melpa.milkbox.net/
211 | [repo]:https://github.com/senny/emacs-eclim
212 |
213 | [badge-license]: https://img.shields.io/badge/license-GPL_3-green.svg
214 |
--------------------------------------------------------------------------------
/eclim-completion.el:
--------------------------------------------------------------------------------
1 | ;; company-emacs-eclim.el --- an interface to the Eclipse IDE.
2 | ;;
3 | ;; Copyright (C) 2012 Fredrik Appelberg
4 | ;;
5 | ;; This program is free software: you can redistribute it and/or modify
6 | ;; it under the terms of the GNU General Public License as published by
7 | ;; the Free Software Foundation, either version 3 of the License, or
8 | ;; (at your option) any later version.
9 | ;;
10 | ;; This program is distributed in the hope that it will be useful,
11 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | ;; GNU General Public License for more details.
14 | ;;
15 | ;; You should have received a copy of the GNU General Public License
16 | ;; along with this program. If not, see .
17 | ;;
18 | ;;; Contributors
19 | ;;
20 | ;;; Conventions
21 | ;;
22 | ;; Conventions used in this file: Name internal variables and functions
23 | ;; "eclim--", and name eclim command invocations
24 | ;; "eclim/command-name", like eclim/project-list.
25 | ;;; Description
26 | ;;
27 | ;; company-emacs-eclim.el -- completion functions used by the company-mode
28 | ;; and auto-complete-mode backends.
29 | ;;
30 |
31 | (require 'thingatpt)
32 |
33 | (defun eclim--completion-candidate-type (candidate)
34 | "Returns the type of a candidate."
35 | (assoc-default 'type candidate))
36 |
37 | (defun eclim--completion-candidate-class (candidate)
38 | "Returns the class name of a candidate."
39 | (assoc-default 'info candidate))
40 |
41 | (defun eclim--completion-candidate-doc (candidate)
42 | "Returns the documentation for a candidate."
43 | (assoc-default 'menu candidate))
44 |
45 | (defun eclim--completion-candidate-package (candidate)
46 | "Returns the package name of a candidate."
47 | (let ((doc (eclim--completion-candidate-doc candidate)))
48 | (when (string-match "\\(.*\\)\s-\s\\(.*\\)" doc)
49 | (match-string 2 doc))))
50 |
51 | (defvar eclim--completion-candidates nil)
52 |
53 | (defun eclim--complete ()
54 | (setq eclim--is-completing t)
55 | (unwind-protect
56 | (setq eclim--completion-candidates
57 | (case major-mode
58 | (java-mode
59 | (assoc-default 'completions
60 | (eclim/execute-command "java_complete" "-p" "-f" "-e" ("-l" "standard") "-o")))
61 | ((xml-mode nxml-mode)
62 | (eclim/execute-command "xml_complete" "-p" "-f" "-e" "-o"))
63 | (groovy-mode
64 | (eclim/execute-command "groovy_complete" "-p" "-f" "-e" ("-l" "standard") "-o"))
65 | (ruby-mode
66 | (eclim/execute-command "ruby_complete" "-p" "-f" "-e" "-o"))
67 | (php-mode
68 | (eclim/execute-command "php_complete" "-p" "-f" "-e" "-o"))
69 | ((javascript-mode js-mode)
70 | (eclim/execute-command "javascript_complete" "-p" "-f" "-e" "-o"))
71 | (scala-mode
72 | (eclim/execute-command "scala_complete" "-p" "-f" "-e" ("-l" "standard") "-o"))
73 | ((c++-mode c-mode)
74 | (eclim/execute-command "c_complete" "-p" "-f" "-e" ("-l" "standard") "-o"))))
75 | (setq eclim--is-completing nil)))
76 |
77 | (defun eclim--completion-candidates-filter (c)
78 | (case major-mode
79 | ((xml-mode nxml-mode) (or (search "XML Schema" c)
80 | (search "Namespace" c)))
81 | (t nil)))
82 |
83 | (defun eclim--completion-candidate-menu-item (candidate)
84 | "Returns the part of the completion candidate to be displayed
85 | in a completion menu."
86 | (assoc-default (case major-mode
87 | (java-mode 'info)
88 | (t 'completion)) candidate))
89 |
90 | (defun eclim--completion-candidates ()
91 | (with-no-warnings
92 | (remove-if #'eclim--completion-candidates-filter
93 | (mapcar #'eclim--completion-candidate-menu-item
94 | (eclim--complete)))))
95 |
96 | (defun eclim--basic-complete-internal (completion-list)
97 | "Displays a buffer of basic completions."
98 | (let* ((window (get-buffer-window "*Completions*" 0))
99 | (c (eclim--java-identifier-at-point nil t))
100 | (beg (car c))
101 | (word (cdr c))
102 | (compl (try-completion word
103 | completion-list)))
104 | (if (and (eq last-command this-command)
105 | window (window-live-p window) (window-buffer window)
106 | (buffer-name (window-buffer window)))
107 | ;; If this command was repeated, and there's a fresh completion window
108 | ;; with a live buffer, and this command is repeated, scroll that
109 | ;; window.
110 | (with-current-buffer (window-buffer window)
111 | (if (pos-visible-in-window-p (point-max) window)
112 | (set-window-start window (point-min))
113 | (save-selected-window
114 | (select-window window)
115 | (scroll-up))))
116 | (cond
117 | ((null compl)
118 | (message "No completions."))
119 | ((stringp compl)
120 | (if (string= word compl)
121 | ;; Show completion buffer
122 | (let ((list (all-completions word completion-list)))
123 | (setq list (sort list 'string<))
124 | (with-output-to-temp-buffer "*Completions*"
125 | (display-completion-list list word)))
126 | ;; Complete
127 | (delete-region beg (point))
128 | (insert compl)
129 | ;; close completion buffer if there's one
130 | (let ((win (get-buffer-window "*Completions*" 0)))
131 | (if win (quit-window nil win)))))
132 | (t (message "That's the only possible completion."))))))
133 |
134 | (defun eclim-complete ()
135 | "Attempts a context sensitive completion for the current
136 | element, then displays the possible completions in a separate
137 | buffer."
138 | (interactive)
139 | (when eclim-auto-save (save-buffer))
140 | (eclim--basic-complete-internal
141 | (eclim--completion-candidates)))
142 |
143 | (defun eclim--completion-yasnippet-convert (completion)
144 | "Convert a completion string to a yasnippet template"
145 | (apply #' concat
146 | (loop for c across (replace-regexp-in-string ", " "," completion)
147 | collect (case c
148 | (40 "(${")
149 | (60 "<${")
150 | (44 "}, ${")
151 | (41 "})")
152 | (62 "}>")
153 | (t (char-to-string c))))))
154 |
155 | (defvar eclim--completion-start)
156 |
157 | (defun eclim-completion-start ()
158 | "Work out the point where completion starts."
159 | (setq eclim--completion-start
160 | (save-excursion
161 | (case major-mode
162 | ((java-mode javascript-mode js-mode ruby-mode groovy-mode php-mode c-mode c++-mode scala-mode)
163 | (progn
164 | (ignore-errors (beginning-of-thing 'symbol))
165 | ;; Completion candidates for annotations don't include '@'.
166 | (when (eq ?@ (char-after))
167 | (forward-char 1))
168 | (point)))
169 | ((xml-mode nxml-mode)
170 | (while (not (string-match "[<\n[:blank:]]" (char-to-string (char-before))))
171 | (backward-char))
172 | (point))))))
173 |
174 | (defun eclim--completion-action-java (beg end)
175 | (let ((completion (buffer-substring-no-properties beg end)))
176 | (cond ((string-match "\\(.*?\\) :.*- Override method" completion)
177 | (let ((sig (eclim--java-parse-method-signature (match-string 1 completion))))
178 | (delete-region beg end)
179 | (eclim-java-implement (symbol-name (assoc-default :name sig)))))
180 | ((string-match "\\([^-:]+\\) .*?\\(- *\\(.*\\)\\)?" completion)
181 | (let* ((insertion (match-string 1 completion))
182 | (rest (match-string 3 completion))
183 | (package (if (and rest (string-match "\\w+\\(\\.\\w+\\)*" rest)) rest nil))
184 | (template (eclim--completion-yasnippet-convert insertion)))
185 | (delete-region beg end)
186 | (if (and eclim-use-yasnippet template (featurep 'yasnippet) yas-minor-mode)
187 | (yas/expand-snippet template)
188 | (insert insertion))
189 | (when package
190 | (eclim-java-import
191 | (concat package "." (substring insertion 0 (or (string-match "[<(]" insertion)
192 | (length insertion)))))))))))
193 |
194 | (defun eclim--completion-action-xml (beg end)
195 | (when (string-match "[\n[:blank:]]" (char-to-string (char-before beg)))
196 | ;; we are completing an attribute; let's use yasnippet to get som nice completion going
197 | (let* ((c (buffer-substring-no-properties beg end))
198 | (completion (if (s-ends-with? "\"" c) c (concat c "=\"\""))))
199 | (when (string-match "\\(.*\\)=\"\\(.*\\)\"" completion)
200 | (delete-region beg end)
201 | (if (and eclim-use-yasnippet (featurep 'yasnippet) yas-minor-mode)
202 | (yas/expand-snippet (format "%s=\"${1:%s}\" $0" (match-string 1 completion) (match-string 2 completion)))
203 | (insert completion))))))
204 |
205 | (defun eclim--completion-action-default ()
206 | (when (and (= 40 (char-before)) (not (looking-at ")")))
207 | ;; we've inserted an open paren, so let's close it
208 | (if (and eclim-use-yasnippet (featurep 'yasnippet) yas-minor-mode)
209 | (yas/expand-snippet "$1)$0")
210 | (progn
211 | (insert ")")
212 | (backward-char)))))
213 |
214 | (defun eclim--completion-action (beg end)
215 | (case major-mode
216 | ('java-mode (eclim--completion-action-java beg end))
217 | ('groovy-mode (eclim--completion-action-java beg end))
218 | ((c-mode c++-mode) (eclim--completion-action-java beg end))
219 | ('nxml-mode (eclim--completion-action-xml beg end))
220 | (t (eclim--completion-action-default))))
221 |
222 | (defun eclim--render-doc (str)
223 | "Performs rudimentary rendering of HTML elements in
224 | documentation strings."
225 | (apply #'concat
226 | (loop for p = 0 then (match-end 0)
227 | while (string-match "[[:blank:]]*\\(.*?\\)\\(?.*?>\\)" str p) collect (match-string 1 str) into ret
228 | for tag = (downcase (match-string 2 str))
229 | when (or (string= tag "
") (string= tag "")) collect "\n" into ret
230 | when (string= tag "
") collect " " into ret
231 | when (string= tag "
") collect " * " into ret
232 | finally return (append ret (list (substring str p))))))
233 |
234 | (defun eclim--completion-documentation (symbol)
235 | "Looks up the documentation string for the given SYMBOL in the
236 | completion candidates list."
237 | (let ((doc (assoc-default 'info (find symbol eclim--completion-candidates :test #'string= :key #'eclim--completion-candidate-menu-item))))
238 | (when doc
239 | (eclim--render-doc doc))))
240 |
241 | (provide 'eclim-completion)
242 |
--------------------------------------------------------------------------------
/eclim-project.el:
--------------------------------------------------------------------------------
1 | ;; eclim-project.el --- an interface to the Eclipse IDE.
2 | ;;
3 | ;; Copyright (C) 2009 Yves Senn
4 | ;;
5 | ;; This program is free software: you can redistribute it and/or modify
6 | ;; it under the terms of the GNU General Public License as published by
7 | ;; the Free Software Foundation, either version 3 of the License, or
8 | ;; (at your option) any later version.
9 | ;;
10 | ;; This program is distributed in the hope that it will be useful,
11 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | ;; GNU General Public License for more details.
14 | ;;
15 | ;; You should have received a copy of the GNU General Public License
16 | ;; along with this program. If not, see .
17 | ;;
18 | ;;; Contributors
19 | ;;
20 | ;;; Conventions
21 | ;;
22 | ;; Conventions used in this file: Name internal variables and functions
23 | ;; "eclim--", and name eclim command invocations
24 | ;; "eclim/command-name", like eclim/project-list.
25 |
26 | ;;* Eclim Project
27 |
28 | (defvar eclim-project-mode-hook nil)
29 | (defvar eclim-project-info-mode-hook nil)
30 |
31 | (defvar eclim--project-scopes '("project"
32 | "workspace"))
33 |
34 | (defvar eclim-project-mode-map
35 | (let ((map (make-keymap)))
36 | (suppress-keymap map t)
37 | (define-key map (kbd "N") 'eclim-project-create)
38 | (define-key map (kbd "m") 'eclim-project-mark-current)
39 | (define-key map (kbd "M") 'eclim-project-mark-all)
40 | (define-key map (kbd "u") 'eclim-project-unmark-current)
41 | (define-key map (kbd "U") 'eclim-project-unmark-all)
42 | (define-key map (kbd "o") 'eclim-project-open)
43 | (define-key map (kbd "c") 'eclim-project-close)
44 | (define-key map (kbd "i") 'eclim-project-info-mode)
45 | (define-key map (kbd "I") 'eclim-project-import)
46 | (define-key map (kbd "RET") 'eclim-project-goto)
47 | (define-key map (kbd "D") 'eclim-project-delete)
48 | (define-key map (kbd "p") 'eclim-project-update)
49 | (define-key map (kbd "g") 'eclim-project-mode-refresh)
50 | (define-key map (kbd "R") 'eclim-project-rename)
51 | (define-key map (kbd "q") 'eclim-quit-window)
52 | (define-key map (kbd "r") 'eclim-project-refresh)
53 | map))
54 |
55 | (defvar eclim-project-info-mode-map
56 | (let ((map (make-sparse-keymap)))
57 | (set-keymap-parent map special-mode-map)
58 | map))
59 |
60 |
61 | (define-key eclim-mode-map (kbd "C-c C-e g") 'eclim-project-goto)
62 | (define-key eclim-mode-map (kbd "C-c C-e p p") 'eclim-project-mode)
63 | (define-key eclim-mode-map (kbd "C-c C-e p m") 'eclim-project-mode)
64 | (define-key eclim-mode-map (kbd "C-c C-e p i") 'eclim-project-import)
65 | (define-key eclim-mode-map (kbd "C-c C-e p c") 'eclim-project-create)
66 | (define-key eclim-mode-map (kbd "C-c C-e p g") 'eclim-project-goto)
67 |
68 | (defun eclim--project-clear-cache ()
69 | (setq eclim--project-natures-cache nil)
70 | (setq eclim--projects-cache nil))
71 |
72 | (defun eclim--check-nature (nature)
73 | (let ((natures (or eclim--project-natures-cache
74 | (setq eclim--project-natures-cache (eclim/project-nature-aliases)))))
75 | (when (not (member nature (append natures nil)))
76 | (error (concat "invalid project nature: " nature)))))
77 |
78 | (defun eclim--check-project (project)
79 | (let ((projects (or eclim--projects-cache
80 | (setq eclim--projects-cache (mapcar (lambda (p) (assoc-default 'name p)) (eclim/project-list))))))
81 | (when (not (assoc-string project projects))
82 | (error (concat "invalid project: " project))))) ;
83 |
84 | (defun eclim--project-read (&optional single)
85 | (interactive)
86 | (if (eq major-mode 'eclim-project-mode)
87 | (progn
88 | (or (if single nil (eclim--project-get-marked))
89 | (eclim--project-current-line)))
90 | (eclim--completing-read "Project: "
91 | (mapcar (lambda (p) (assoc-default 'name p)) (eclim/project-list)))))
92 |
93 | (defun eclim--project-buffer-refresh ()
94 | (eclim--project-clear-cache)
95 | (when (eq major-mode 'eclim-project-mode)
96 | (let ((inhibit-read-only t)
97 | (line-number (line-number-at-pos)))
98 | (erase-buffer)
99 | (loop for project across (eclim/project-list)
100 | do (eclim--insert-project project))
101 | (goto-line line-number))))
102 |
103 | (defun eclim--insert-project (project)
104 | (insert (format " | %-6s | %-30s | %s\n"
105 | (if (eq :json-false (assoc-default 'open project)) "closed" "open")
106 | (truncate-string-to-width (assoc-default 'name project) 30 0 nil t)
107 | (assoc-default 'path project))))
108 |
109 | (defun eclim--project-insert-mark-current (face)
110 | (let ((inhibit-read-only t))
111 | (save-excursion
112 | (beginning-of-line)
113 | (delete-char 1)
114 | (insert "*")
115 | (put-text-property (- (point) 1) (point) 'face face))))
116 |
117 | (defun eclim--project-remove-mark-current ()
118 | (let ((inhibit-read-only t))
119 | (save-excursion
120 | (beginning-of-line)
121 | (delete-char 1)
122 | (insert " "))))
123 |
124 | (defun eclim--project-get-marked ()
125 | (interactive)
126 | (let ((marked-projects '()))
127 | (save-excursion
128 | (beginning-of-buffer)
129 | (while (re-search-forward "*" nil t)
130 | (push (eclim--project-current-line) marked-projects)))
131 | marked-projects))
132 |
133 | (defun eclim--project-column-start (column)
134 | (save-excursion
135 | (+ (re-search-forward "|" nil t (- column 1)) 1)))
136 |
137 | (defun eclim--project-column-end (column)
138 | (save-excursion
139 | (- (re-search-forward "|" nil t column) 1)))
140 |
141 | (defun eclim--project-current-line ()
142 | (save-excursion
143 | (beginning-of-line)
144 | (eclim--string-strip (buffer-substring-no-properties
145 | (eclim--project-column-start 3)
146 | (eclim--project-column-end 3)))))
147 |
148 | (defun eclim/project-list ()
149 | (eclim/execute-command "project_list"))
150 |
151 | (defun eclim/project-import (folder)
152 | (eclim--project-clear-cache)
153 | (eclim--call-process "project_import" "-f" (expand-file-name folder)))
154 |
155 | (defun eclim/project-create (folder natures name &optional target package application depends)
156 | ;; TODO: allow multiple natures
157 | ;; add the vars target,package,application for android project
158 | (eclim--project-clear-cache)
159 | (eclim--call-process "project_create" "-f" folder "-n" natures "-p" name "-a" "--target" target "--package" package "--application" application))
160 |
161 | (defun eclim/project-delete (project)
162 | (eclim--check-project project)
163 | (eclim--project-clear-cache)
164 | (eclim--call-process "project_delete" "-p" project))
165 |
166 | (defun eclim/project-open (project)
167 | (eclim--check-project project)
168 | (eclim--call-process "project_open" "-p" project))
169 |
170 | (defun eclim/project-close (project)
171 | (eclim--check-project project)
172 | (eclim--call-process "project_close" "-p" project))
173 |
174 | (defun eclim/project-info (project)
175 | (eclim--check-project project)
176 | (eclim--call-process "project_info" "-p" project))
177 |
178 | (defun eclim/project-settings (project)
179 | (eclim--check-project project)
180 | ;; TODO: make the output useable
181 | (eclim--call-process "project_settings" "-p" project))
182 |
183 | (defun eclim/project-setting (project setting)
184 | (eclim--check-project project)
185 | ;; TODO: make the output useable
186 | (eclim--call-process "project_setting" "-p" project "-s" setting))
187 |
188 | (defun eclim/project-nature-add (project nature)
189 | (eclim--check-project project)
190 | (eclim--check-nature nature)
191 | (eclim--call-process "project_nature_add" "-p" project "-n" nature))
192 |
193 | (defun eclim/project-nature-remove (project nature)
194 | (eclim--check-project project)
195 | (eclim--check-nature nature)
196 | (eclim--call-process "project_nature_remove" "-p" project "-n" nature))
197 |
198 | (defun eclim/project-natures (project)
199 | (eclim--check-project project)
200 | (eclim--call-process "project_natures" "-p" project))
201 |
202 | (defun eclim/project-refresh (project)
203 | (eclim--check-project project)
204 | (eclim--call-process "project_refresh" "-p" project))
205 |
206 | (defun eclim/project-refresh-file (project file)
207 | (eclim--check-project project)
208 | (eclim--call-process "project_refresh_file" "-p" project "-f" file))
209 |
210 | (defun eclim/project-nature-aliases ()
211 | (eclim--call-process "project_nature_aliases"))
212 |
213 | (defun eclim/project-links (project)
214 | (eclim--check-project project)
215 | (eclim--call-process "project_links" "-p" project))
216 |
217 | (defun eclim/project-rename (project new-name)
218 | (eclim--check-project project)
219 | (eclim--call-process "project_rename" "-p" project "-n" new-name))
220 |
221 | (defun eclim/project-classpath (&optional delimiter)
222 | "return project classpath for the current buffer."
223 | (eclim/execute-command "java_classpath" "-p" ("-d" delimiter)))
224 |
225 | (defun eclim-project-rename (project name)
226 | (interactive (let ((project-name (eclim--project-read t)))
227 | (list project-name (read-string (concat "Rename <" project-name "> To: ")))))
228 | (message (eclim/project-rename project name))
229 | (eclim--project-buffer-refresh))
230 |
231 | (defun eclim-project-delete (projects)
232 | (interactive (list (eclim--project-read)))
233 | (when (not (listp projects)) (set 'projects (list projects)))
234 | (dolist (project projects)
235 | (when (yes-or-no-p (concat "Delete Project: <" project"> "))
236 | (message (eclim/project-delete project))))
237 | (eclim--project-buffer-refresh))
238 |
239 | (defun eclim-project-create (name path nature)
240 | (interactive (list (read-string "Name: ")
241 | (expand-file-name (read-directory-name "Project Directory: "))
242 | (eclim--project-nature-read)))
243 | ;;android project is need the vars target,package,application
244 | (if (string-equal nature "android")
245 | (progn (setq target (read-string "Target: "))
246 | (setq package (read-string "Package: "))
247 | (setq application (read-string "Application: "))
248 | (message (eclim/project-create path nature name target package application)))
249 | (message (eclim/project-create path nature name))
250 | (eclim--project-buffer-refresh)))
251 |
252 | (defun eclim-project-import (folder)
253 | (interactive "DProject Directory: ")
254 | (message (eclim/project-import folder))
255 | (eclim--project-buffer-refresh))
256 |
257 | (defun eclim--project-nature-read ()
258 | (completing-read "Nature: " (append (eclim/project-nature-aliases) nil)))
259 |
260 | (defun eclim-project-nature-add (nature)
261 | (interactive (list (eclim--project-nature-read)))
262 | (message (eclim/project-nature-add (eclim--project-current-line) nature)))
263 |
264 | (defun eclim-project-nature-remove (nature)
265 | (interactive (list (completing-read "Remove nature: "
266 | (append
267 | (cdadr (aref (eclim/project-natures (eclim--project-current-line)) 0))
268 | nil))))
269 | (message (eclim/project-nature-remove (eclim--project-current-line) nature)))
270 |
271 | (defun eclim-project-natures ()
272 | (interactive)
273 | (message (with-output-to-string
274 | (princ (cdadr (aref (eclim/project-natures (eclim--project-current-line)) 0))))))
275 |
276 | (defun eclim-project-dependencies (project)
277 | (cdr (assoc 'depends (eclim/project-info project))))
278 |
279 | (defun eclim-project-mode-refresh ()
280 | (interactive)
281 | (eclim--project-buffer-refresh)
282 | (message "projects refreshed..."))
283 |
284 | (defun eclim-project-update (projects)
285 | (interactive (list (eclim--project-read)))
286 | (when (not (listp projects)) (set 'projects (list projects)))
287 | (dolist (project projects)
288 | (eclim/execute-command "project_update" ("-p" project)))
289 | (eclim--project-buffer-refresh))
290 |
291 | (defun eclim-project-open (projects)
292 | (interactive (list (eclim--project-read)))
293 | (when (not (listp projects)) (set 'projects (list projects)))
294 | (dolist (project projects)
295 | (eclim/project-open project))
296 | (eclim--project-buffer-refresh))
297 |
298 | (defun eclim-project-close (projects)
299 | (interactive (list (eclim--project-read)))
300 | (when (not (listp projects)) (set 'projects (list projects)))
301 | (dolist (project projects)
302 | (eclim/project-close project))
303 | (eclim--project-buffer-refresh))
304 |
305 | (defun eclim-project-refresh (projects)
306 | (interactive (list (eclim--project-read)))
307 | (when (not (listp projects)) (set 'projects (list projects)))
308 | (dolist (project projects)
309 | (eclim/project-refresh project))
310 | (eclim--project-buffer-refresh))
311 |
312 | (defun eclim-project-mark-current ()
313 | (interactive)
314 | (eclim--project-insert-mark-current 'dired-mark)
315 | (forward-line 1))
316 |
317 | (defun eclim-project-mark-all ()
318 | (interactive)
319 | (save-excursion
320 | (beginning-of-buffer)
321 | (loop do (eclim--project-insert-mark-current 'dired-mark)
322 | until (not (forward-line 1)))))
323 |
324 | (defun eclim-project-unmark-current ()
325 | (interactive)
326 | (eclim--project-remove-mark-current)
327 | (forward-line 1))
328 |
329 | (defun eclim-project-unmark-all ()
330 | (interactive)
331 | (save-excursion
332 | (beginning-of-buffer)
333 | (loop do (eclim--project-remove-mark-current)
334 | until (not (forward-line 1)))))
335 |
336 | (defun eclim-project-goto (project)
337 | (interactive (list (eclim--project-read t)))
338 | (ido-find-file-in-dir
339 | (assoc-default 'path
340 | (find project (eclim/project-list)
341 | :key (lambda (e) (assoc-default 'name e))
342 | :test #'string=))))
343 |
344 | (defun eclim-project-info-mode (project)
345 | "Display detailed information about an eclim-project.
346 |
347 | \\{eclim-project-info-mode-map}"
348 | (interactive (list (eclim--project-read t)))
349 | (with-help-window "*eclim: info*"
350 | (with-current-buffer "*eclim: info*"
351 | (kill-all-local-variables)
352 | (save-excursion
353 | (loop for attr in (eclim/project-info project)
354 | do (princ (format "%s: %s\n" (car attr) (cdr attr))))
355 | (princ "\n\nSETTINGS:\n")
356 | (loop for attr across (eclim/project-settings project)
357 | do (princ (format "%s: %s\n" (assoc-default 'name attr) (assoc-default 'value attr))))
358 | (use-local-map eclim-project-info-mode-map)
359 | (setq major-mode 'eclim-project-info-mode
360 | mode-name "eclim/project-info")
361 | (put 'eclim-project-info-mode 'mode-class 'special)
362 | (run-mode-hooks eclim-project-info-mode-hook)))))
363 |
364 | (defun eclim-project-build ()
365 | "Triggers a build of the current project"
366 | (interactive)
367 | (eclim/execute-command-async
368 | (lambda (res) (message res))
369 | "project_build" "-p"))
370 |
371 | ;;;###autoload
372 | (defun eclim-project-mode ()
373 | "Manage all your eclim projects in one buffer.
374 |
375 | \\{eclim-project-mode-map}"
376 | (interactive)
377 | (switch-to-buffer (get-buffer-create "*eclim: projects*"))
378 | (eclim--project-clear-cache)
379 | (kill-all-local-variables)
380 | (buffer-disable-undo)
381 | (setq major-mode 'eclim-project-mode
382 | mode-name "eclim/project"
383 | mode-line-process ""
384 | truncate-lines t
385 | buffer-read-only t
386 | default-directory (eclim/workspace-dir))
387 | (setq-local line-move-visual nil)
388 | (put 'eclim-project-mode 'mode-class 'special)
389 | (hl-line-mode t)
390 | (use-local-map eclim-project-mode-map)
391 | (cd "~") ;; setting a defualt directoy avoids some problems with tramp
392 | (eclim--project-buffer-refresh)
393 | (beginning-of-buffer)
394 | (run-mode-hooks 'eclim-project-mode-hook))
395 |
396 | (defalias 'eclim-manage-projects 'eclim-project-mode)
397 |
398 | (provide 'eclim-project)
399 |
--------------------------------------------------------------------------------
/eclim-problems.el:
--------------------------------------------------------------------------------
1 | (require 'popup)
2 |
3 | (defgroup eclim-problems nil
4 | "Problems: settings for displaying the problems buffer and highlighting errors in code."
5 | :group 'eclim)
6 |
7 | (defcustom eclim-problems-refresh-delay 0.5
8 | "The delay (in seconds) to wait before we refresh the problem list buffer after a file is saved."
9 | :group 'eclim-problems
10 | :type 'number)
11 |
12 | (defcustom eclim-problems-resize-file-column t
13 | "Resizes file column in emacs-eclim problems mode"
14 | :group 'eclim-problems
15 | :type '(choice (const :tag "Off" nil)
16 | (const :tag "On" t)))
17 |
18 | (defcustom eclim-problems-show-pos nil
19 | "Shows problem line/column in emacs-eclim problems mode"
20 | :group 'eclim-problems
21 | :type '(choice (const :tag "Off" nil)
22 | (const :tag "On" t)))
23 |
24 | (defcustom eclim-problems-show-file-extension nil
25 | "Shows file extensions in emacs-eclim problems mode"
26 | :group 'eclim-problems
27 | :type '(choice (const :tag "Off" nil)
28 | (const :tag "On" t)))
29 |
30 | (defcustom eclim-problems-hl-errors t
31 | "Highlights errors in the problem list buffer"
32 | :group 'eclim-problems
33 | :type '(choice (const :tag "Off" nil)
34 | (const :tag "On" t)))
35 |
36 | (defface eclim-problems-highlight-error-face
37 | '((t (:underline "red")))
38 | "Face used for highlighting errors in code"
39 | :group 'eclim-problems)
40 |
41 | (defface eclim-problems-highlight-warning-face
42 | '((t (:underline "orange")))
43 | "Face used for highlighting errors in code"
44 | :group 'eclim-problems)
45 |
46 | (defvar eclim-autoupdate-problems t)
47 |
48 | (defvar eclim-problems-mode-hook nil)
49 |
50 | (defvar eclim--problems-filter-description "")
51 | (defvar eclim--problems-project nil) ;; problems are relative to this project
52 | (defvar eclim--problems-file nil) ;; problems are relative to this file (when eclim--problems-filefilter is non-nil)
53 |
54 | (setq eclim-problems-mode-map
55 | (let ((map (make-keymap)))
56 | (suppress-keymap map t)
57 | (define-key map (kbd "a") 'eclim-problems-show-all)
58 | (define-key map (kbd "e") 'eclim-problems-show-errors)
59 | (define-key map (kbd "g") 'eclim-problems-buffer-refresh)
60 | (define-key map (kbd "q") 'eclim-quit-window)
61 | (define-key map (kbd "w") 'eclim-problems-show-warnings)
62 | (define-key map (kbd "f") 'eclim-problems-toggle-filefilter)
63 | (define-key map (kbd "c") 'eclim-problems-correct)
64 | (define-key map (kbd "RET") 'eclim-problems-open-current)
65 | map))
66 |
67 | (define-key eclim-mode-map (kbd "C-c C-e b") 'eclim-problems)
68 | (define-key eclim-mode-map (kbd "C-c C-e o") 'eclim-problems-open)
69 |
70 | (defvar eclim--problems-list nil)
71 |
72 | (defvar eclim--problems-filter nil) ;; nil -> all problems, w -> warnings, e -> errors
73 | (defvar eclim--problems-filefilter nil) ;; should filter by file name
74 |
75 | (defconst eclim--problems-buffer-name "*eclim: problems*")
76 | (defconst eclim--problems-compilation-buffer-name "*compilation: eclim*")
77 |
78 | (defun eclim--problems-mode ()
79 | (kill-all-local-variables)
80 | (buffer-disable-undo)
81 | (setq major-mode 'eclim-problems-mode
82 | mode-name "eclim/problems"
83 | mode-line-process ""
84 | truncate-lines t
85 | buffer-read-only t
86 | default-directory (eclim/workspace-dir))
87 | (setq-local line-move-visual nil)
88 | (setq mode-line-format
89 | (list "-"
90 | 'mode-line-mule-info
91 | 'mode-line-modified
92 | 'mode-line-frame-identification
93 | 'mode-line-buffer-identification
94 |
95 | " "
96 | 'mode-line-position
97 |
98 | " "
99 | 'eclim--problems-filter-description
100 |
101 | " "
102 | 'mode-line-modes
103 | '(which-func-mode ("" which-func-format "--"))
104 |
105 | 'global-mode-string
106 | "-%-"))
107 | (hl-line-mode t)
108 | (use-local-map eclim-problems-mode-map)
109 | (run-mode-hooks 'eclim-problems-mode-hook))
110 |
111 | (defun eclim--problem-goto-pos (p)
112 | (save-restriction
113 | (widen)
114 | (goto-char (point-min))
115 | (forward-line (1- (assoc-default 'line p)))
116 | (dotimes (i (1- (assoc-default 'column p)))
117 | (forward-char))))
118 |
119 | (defun eclim--problems-apply-filter (f)
120 | (setq eclim--problems-filter f)
121 | (eclim-problems-buffer-refresh))
122 |
123 | (defun eclim-problems-show-errors ()
124 | (interactive)
125 | (eclim--problems-apply-filter "e"))
126 |
127 | (defun eclim-problems-toggle-filefilter ()
128 | (interactive)
129 | (setq eclim--problems-filefilter (not eclim--problems-filefilter))
130 | (eclim--problems-buffer-redisplay))
131 |
132 | (defun eclim-problems-show-warnings ()
133 | (interactive)
134 | (eclim--problems-apply-filter "w"))
135 |
136 | (defun eclim-problems-show-all ()
137 | (interactive)
138 | (eclim--problems-apply-filter nil))
139 |
140 | (defun eclim--problems-insert-highlight (problem)
141 | (save-excursion
142 | (eclim--problem-goto-pos problem)
143 | (let* ((id (eclim--java-identifier-at-point t t))
144 | (start (car id))
145 | (end (+ (car id) (length (cdr id)))))
146 | (let ((highlight (make-overlay start end (current-buffer) t t)))
147 | (overlay-put highlight 'face
148 | (if (eq t (assoc-default 'warning problem))
149 | 'eclim-problems-highlight-warning-face
150 | 'eclim-problems-highlight-error-face))
151 | (overlay-put highlight 'category 'eclim-problem)
152 | (overlay-put highlight 'kbd-help (assoc-default 'message problem))))))
153 |
154 | (defun eclim--problems-clear-highlights ()
155 | (remove-overlays nil nil 'category 'eclim-problem))
156 |
157 | (defun eclim-problems-highlight ()
158 | (interactive)
159 | (when (eclim--accepted-p (buffer-file-name))
160 | (save-restriction
161 | (widen)
162 | (eclim--problems-clear-highlights)
163 | (loop for problem across (remove-if-not (lambda (p) (string= (assoc-default 'filename p) (buffer-file-name))) eclim--problems-list)
164 | do (eclim--problems-insert-highlight problem)))))
165 |
166 | (defadvice find-file (after eclim-problems-highlight-on-find-file activate)
167 | (eclim-problems-highlight))
168 | (defadvice find-file-other-window (after eclim-problems-highlight-on-find-file-other-window activate)
169 | (eclim-problems-highlight))
170 | (defadvice other-window (after eclim-problems-highlight-on-other-window activate)
171 | (eclim-problems-highlight))
172 | (defadvice switch-to-buffer (after eclim-problems-highlight-switch-to-buffer activate)
173 | (eclim-problems-highlight))
174 |
175 | (defun eclim--problems-get-current-problem ()
176 | (let ((buf (get-buffer "*eclim: problems*")))
177 | (if (eq buf (current-buffer))
178 | ;; we are in the problems buffer
179 | (let ((problems (eclim--problems-filtered))
180 | (index (1- (line-number-at-pos))))
181 | (if (>= index (length problems))
182 | (error "No problem on this line.")
183 | (aref problems index)))
184 | ;; we need to figure out which problem corresponds to this pos
185 | (save-restriction
186 | (widen)
187 | (let ((line (line-number-at-pos))
188 | (col (current-column)))
189 | (or (find-if (lambda (p) (and (string= (assoc-default 'filename p) (file-truename buffer-file-name))
190 | (= (assoc-default 'line p) line)))
191 | eclim--problems-list)
192 | (error "No problem on this line")))))))
193 |
194 | (defun eclim-problems-open-current (&optional same-window)
195 | (interactive)
196 | (let* ((p (eclim--problems-get-current-problem)))
197 | (funcall (if same-window
198 | 'find-file
199 | 'find-file-other-window)
200 | (assoc-default 'filename p))
201 | (eclim--problem-goto-pos p)))
202 |
203 | (defun eclim-problems-correct ()
204 | (interactive)
205 | (let ((p (eclim--problems-get-current-problem)))
206 | (if (not (string-match "\\.\\(groovy\\|java\\)$" (cdr (assoc 'filename p))))
207 | (error "Not a Java or Groovy file. Corrections are currently supported only for Java or Groovy")
208 | (eclim-java-correct (cdr (assoc 'line p)) (eclim--byte-offset)))))
209 |
210 | (defmacro eclim--with-problems-list (problems &rest body)
211 | (declare (indent defun))
212 | "Utility macro to refresh the problem list and do operations on
213 | it asynchronously."
214 | (let ((res (gensym)))
215 | `(when eclim--problems-project
216 | (when (not (minibuffer-window-active-p (minibuffer-window)))
217 | (message "refreshing... %s " (current-buffer)))
218 | (eclim/with-results-async ,res ("problems" ("-p" eclim--problems-project) (when (string= "e" eclim--problems-filter) '("-e" "true")))
219 | (loop for problem across ,res
220 | do (let ((filecell (assq 'filename problem)))
221 | (when filecell (setcdr filecell (file-truename (cdr filecell))))))
222 | (setq eclim--problems-list ,res)
223 | (let ((,problems ,res))
224 | ,@body)))))
225 |
226 | (defun eclim-problems-buffer-refresh ()
227 | "Refresh the problem list and draw it on screen."
228 | (interactive)
229 | (eclim--with-problems-list problems
230 | (eclim--problems-buffer-redisplay)
231 | (if (not (minibuffer-window-active-p (minibuffer-window)))
232 | (if (string= "e" eclim--problems-filter)
233 | (message "Eclim reports %d errors." (length problems))
234 | (message "Eclim reports %d errors, %d warnings."
235 | (length (remove-if-not (lambda (p) (not (eq t (assoc-default 'warning p)))) problems))
236 | (length (remove-if-not (lambda (p) (eq t (assoc-default 'warning p))) problems)))))))
237 |
238 | (defun eclim--problems-cleanup-filename (filename)
239 | (let ((x (file-name-nondirectory (assoc-default 'filename problem))))
240 | (if eclim-problems-show-file-extension x (file-name-sans-extension x))))
241 |
242 | (defun eclim--problems-filecol-size ()
243 | (if eclim-problems-resize-file-column
244 | (min 40
245 | (apply #'max 0
246 | (mapcar (lambda (problem)
247 | (length (eclim--problems-cleanup-filename (assoc-default 'filename problem))))
248 | (eclim--problems-filtered))))
249 | 40))
250 |
251 | (defun eclim--problems-update-filter-description ()
252 | (if eclim--problems-filefilter
253 | (if eclim--problems-filter
254 | (setq eclim--problems-filter-description (concat "(file-" eclim--problems-filter ")"))
255 | (setq eclim--problems-filter-description "(file)"))
256 | (if eclim--problems-filter
257 | (setq eclim--problems-filter-description (concat eclim--problems-project "(" eclim--problems-filter ")"))
258 | (setq eclim--problems-filter-description eclim--problems-project))))
259 |
260 | (defun eclim--problems-buffer-redisplay ()
261 | "Draw the problem list on screen."
262 | (let ((buf (get-buffer "*eclim: problems*")))
263 | (when buf
264 | (save-excursion
265 | (set-buffer buf)
266 | (eclim--problems-update-filter-description)
267 | (save-excursion
268 | (dolist (b (mapcar #'window-buffer (window-list)))
269 | (set-buffer b)
270 | (eclim-problems-highlight)))
271 | (let ((inhibit-read-only t)
272 | (line-number (line-number-at-pos))
273 | (filecol-size (eclim--problems-filecol-size)))
274 | (erase-buffer)
275 | (loop for problem across (eclim--problems-filtered)
276 | do (eclim--insert-problem problem filecol-size))
277 | (goto-char (point-min))
278 | (forward-line (1- line-number)))))))
279 |
280 | (defun eclim--problems-filtered ()
281 | "Filter reported problems by eclim.
282 |
283 | It filters out problems using the ECLIM--PROBLEMS-FILEFILTER
284 | criteria. If IGNORE-TYPE-FILTER is nil (default), then problems
285 | are also filtered according to ECLIM--PROBLEMS-FILTER, i.e.,
286 | error type. Otherwise, error type is ignored. This is useful when
287 | other mechanisms, like compilation's mode
288 | COMPILATION-SKIP-THRESHOLD, implement this feature."
289 | (eclim--filter-problems eclim--problems-filter eclim--problems-filefilter eclim--problems-file eclim--problems-list))
290 |
291 | (defun eclim--warning-filterp (x)
292 | (eq t (assoc-default 'warning x)))
293 |
294 | (defun eclim--error-filterp (x)
295 | (not (eclim--warning-filterp x)))
296 |
297 | (defun eclim--choose-type-filter (type-filter)
298 | (cond
299 | ((not type-filter) '(lambda (_) t))
300 | ((string= "e" type-filter) 'eclim--error-filterp)
301 | (t 'eclim--warning-filterp)))
302 |
303 | (defun eclim--choose-file-filter (file-filter file)
304 | (if (not file-filter)
305 | '(lambda (_) t)
306 | '(lambda (x) (string= (assoc-default 'filename x) file))))
307 |
308 | (defun eclim--filter-problems (type-filter file-filter file problems)
309 | (let ((type-filterp (eclim--choose-type-filter type-filter))
310 | (file-filterp (eclim--choose-file-filter file-filter file)))
311 | (remove-if-not (lambda (x) (and (funcall type-filterp x) (funcall file-filterp x))) problems)))
312 |
313 | (defun eclim--insert-problem (problem filecol-size)
314 | (let* ((filecol-format-string (concat "%-" (number-to-string filecol-size) "s"))
315 | (problem-new-line-pos (position ?\n (assoc-default 'message problem)))
316 | (problem-message
317 | (if problem-new-line-pos
318 | (concat (substring (assoc-default 'message problem)
319 | 0 problem-new-line-pos)
320 | "...")
321 | (assoc-default 'message problem)))
322 | (filename (truncate-string-to-width
323 | (eclim--problems-cleanup-filename (assoc-default 'filename problem))
324 | 40 0 nil t))
325 | (text (if eclim-problems-show-pos
326 | (format (concat filecol-format-string
327 | " | line %-12s"
328 | " | %s")
329 | filename
330 | (assoc-default 'line problem)
331 | problem-message)
332 | ;; else
333 | (format (concat filecol-format-string
334 | " | %s")
335 | filename
336 | problem-message))))
337 | (when (and eclim-problems-hl-errors (eq :json-false (assoc-default 'warning problem)))
338 | (put-text-property 0 (length text) 'face 'bold text))
339 | (insert text)
340 | (insert "\n")))
341 |
342 | (defun eclim--get-problems-buffer ()
343 | "Return the eclim problems buffer, if it exists. Otherwise,
344 | create and initialize a new buffer."
345 | (or (get-buffer "*eclim: problems*")
346 | (let ((buf (get-buffer-create "*eclim: problems*")))
347 | (save-excursion
348 | ;; (setq eclim--problems-project (eclim-project-name))
349 | (setq eclim--problems-file buffer-file-name)
350 | (set-buffer buf)
351 | (eclim--problems-mode)
352 | ;;(eclim-problems-buffer-refresh)
353 | (goto-char (point-min))))))
354 |
355 | (defun eclim--problems-mode-init (&optional quiet)
356 | "Create and initialize the eclim problems buffer. If the
357 | argument QUIET is non-nil, open the buffer in the background
358 | without switching to it."
359 | (let ((buf (get-buffer-create "*eclim: problems*")))
360 | (save-excursion
361 | (setq eclim--problems-project (eclim-project-name))
362 | (setq eclim--problems-file buffer-file-name)
363 | (set-buffer buf)
364 | (eclim--problems-mode)
365 | (eclim-problems-buffer-refresh)
366 | (goto-char (point-min)))
367 | (if (not quiet)
368 | (switch-to-buffer buf))))
369 |
370 | (defun eclim-problems ()
371 | "Show current compilation problems in a separate window."
372 | (interactive)
373 | (if (eclim-project-name)
374 | (eclim--problems-mode-init)
375 | (error "Could not figure out the current project. Is this an eclim managed buffer?")))
376 |
377 | (defun eclim-problems-open ()
378 | "Opens a new (emacs) window inside the current frame showing the current project compilation problems"
379 | (interactive)
380 | (let ((w (selected-window)))
381 | (select-window (split-window nil (round (* (window-height w) 0.75)) nil))
382 | (eclim-problems)
383 | (select-window w)))
384 |
385 | (add-hook 'find-file-hook
386 | (lambda () (when (and (eclim--accepted-p (buffer-file-name))
387 | (not (get-buffer eclim--problems-buffer-name)))
388 | (eclim--problems-mode-init t))))
389 |
390 | (defun eclim-problems-refocus ()
391 | (interactive)
392 | (when (eclim--project-dir)
393 | (setq eclim--problems-project (eclim-project-name))
394 | (setq eclim--problems-file buffer-file-name)
395 | (with-current-buffer eclim--problems-buffer-name
396 | (eclim-problems-buffer-refresh))))
397 |
398 | (defun eclim-problems-next (&optional same-window)
399 | (interactive)
400 | (let ((prob-buf (get-buffer eclim--problems-buffer-name)))
401 | (when prob-buf
402 | (set-buffer prob-buf)
403 | (if (boundp 'eclim--problems-list-at-first)
404 | (setq eclim--problems-list-at-first nil)
405 | (forward-line 1))
406 | (hl-line-move hl-line-overlay)
407 | (eclim-problems-open-current same-window))))
408 |
409 | (defun eclim-problems-previous (&optional same-window)
410 | (interactive)
411 | (let ((prob-buf (get-buffer eclim--problems-buffer-name)))
412 | (when prob-buf
413 | (set-buffer prob-buf)
414 | (forward-line -1)
415 | (hl-line-move hl-line-overlay)
416 | (eclim-problems-open-current same-window))))
417 |
418 | (defun eclim-problems-next-same-window ()
419 | (interactive)
420 | (eclim-problems-next t))
421 |
422 | (defun eclim-problems-previous-same-window ()
423 | (interactive)
424 | (eclim-problems-previous t))
425 |
426 | (defun eclim--problems-update-maybe ()
427 | "If autoupdate is enabled, this function triggers a delayed
428 | refresh of the problems buffer."
429 | (when (and (not eclim--is-completing)
430 | (eclim--project-dir)
431 | eclim-autoupdate-problems)
432 | (setq eclim--problems-project (eclim-project-name))
433 | (setq eclim--problems-file buffer-file-name)
434 | (run-with-idle-timer eclim-problems-refresh-delay nil (lambda () (eclim-problems-buffer-refresh)))))
435 |
436 | (defun eclim-problems-compilation-buffer ()
437 | "Creates a compilation buffer from eclim error messages. This
438 | is convenient as it lets the user navigate between errors using
439 | `next-error' (\\[next-error])."
440 | (interactive)
441 | (lexical-let ((filecol-size (eclim--problems-filecol-size))
442 | (project-directory (concat (eclim--project-dir buffer-file-name) "/"))
443 | (compil-buffer (get-buffer-create eclim--problems-compilation-buffer-name)))
444 | (eclim--with-problems-list problems
445 | (with-current-buffer compil-buffer
446 | (setq default-directory project-directory)
447 | (setq buffer-read-only nil)
448 | (erase-buffer)
449 | (insert (concat "-*- mode: compilation; default-directory: "
450 | project-directory
451 | " -*-\n\n"))
452 | (let ((errors 0) (warnings 0))
453 | (loop for problem across (eclim--problems-filtered)
454 | do (eclim--insert-problem-compilation problem filecol-size project-directory)
455 | (cond ((assoc-default 'warning problem)
456 | (setq warnings (1+ warnings)))
457 | (t
458 | (setq errors (1+ errors)))))
459 | (insert (format "\nCompilation results: %d errors and %d warnings."
460 | errors warnings)))
461 | (compilation-mode))
462 | (display-buffer compil-buffer 'other-window))))
463 |
464 | (defun eclim--insert-problem-compilation (problem filecol-size project-directory)
465 | (let ((filename (first (split-string (assoc-default 'filename problem) project-directory t)))
466 | (description (assoc-default 'message problem))
467 | (type (if (eq t (assoc-default 'warning problem)) "W" "E")))
468 | (let ((line (assoc-default 'line problem))
469 | (col (assoc-default 'column problem)))
470 | (insert (format "%s:%s:%s: %s: %s\n" filename line col (upcase type) description)))))
471 |
472 | (defun eclim--count-current-errors ()
473 | (length
474 | (eclim--filter-problems "e" t (buffer-file-name (current-buffer)) eclim--problems-list)))
475 |
476 | (defun eclim--count-current-warnings ()
477 | (length
478 | (eclim--filter-problems "w" t (buffer-file-name (current-buffer)) eclim--problems-list)))
479 |
480 | (defun eclim-problems-modeline-string ()
481 | "Returns modeline string with additional info about
482 | problems for current file"
483 | (concat (format " : %s/%s"
484 | (eclim--count-current-errors)
485 | (eclim--count-current-warnings))))
486 |
487 | (provide 'eclim-problems)
488 |
--------------------------------------------------------------------------------
/eclim.el:
--------------------------------------------------------------------------------
1 | ;; eclim.el --- an interface to the Eclipse IDE.
2 | ;;
3 | ;; Copyright (C) 2009, 2012 Tassilo Horn
4 | ;;
5 | ;; This program is free software: you can redistribute it and/or modify
6 | ;; it under the terms of the GNU General Public License as published by
7 | ;; the Free Software Foundation, either version 3 of the License, or
8 | ;; (at your option) any later version.
9 | ;;
10 | ;; This program is distributed in the hope that it will be useful,
11 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | ;; GNU General Public License for more details.
14 | ;;
15 | ;; You should have received a copy of the GNU General Public License
16 | ;; along with this program. If not, see .
17 | ;;
18 | ;;; Contributors
19 | ;;
20 | ;; - Nikolaj Schumacher
21 | ;; - Yves Senn
22 | ;; - Fredrik Appelberg
23 | ;; - Alessandro Arzilli
24 | ;;
25 | ;;; Conventions
26 | ;;
27 | ;; Conventions used in this file: Name internal variables and functions
28 | ;; "eclim--", and name eclim command invocations
29 | ;; "eclim/command-name", like eclim/project-list.
30 |
31 | ;;* Eclim
32 |
33 | (eval-when-compile (require 'cl))
34 | (require 'etags)
35 | (require 's)
36 |
37 | ;;** Basics
38 |
39 | (defgroup eclim nil
40 | "Interface to the Eclipse IDE."
41 | :group 'tools)
42 |
43 | (defcustom eclim-eclipse-dirs '("/Applications/eclipse" "/usr/lib/eclipse"
44 | "/usr/local/lib/eclipse" "/usr/share/eclipse")
45 | "Path to the eclipse directory"
46 | :type '(sexp)
47 | :group 'eclim)
48 |
49 | (defun eclim-executable-find ()
50 | (let (file)
51 | (dolist (eclipse-root eclim-eclipse-dirs)
52 | (and (file-exists-p
53 | (setq file (expand-file-name "plugins" eclipse-root)))
54 | (setq file (car (last (directory-files file t "^org.eclim_"))))
55 | (file-exists-p (setq file (expand-file-name "bin/eclim" file)))
56 | (return file)))))
57 |
58 | (defun eclim-homedir-executable-find ()
59 | (let ((file "~/.eclipse"))
60 | (and (file-exists-p
61 | (setq file (expand-file-name file)))
62 | (setq file (car (last (directory-files file t "^org.eclipse.platform_"))))
63 | (file-exists-p
64 | (setq file (expand-file-name "plugins" file)))
65 | (setq file (car (last (directory-files file t "^org.eclim_"))))
66 | (file-exists-p (setq file (expand-file-name "bin/eclim" file)))
67 | file)))
68 |
69 | (defcustom eclim-interactive-completion-function (if ido-mode 'ido-completing-read 'completing-read)
70 | "Defines a function which is used by eclim to complete a list of
71 | choices interactively."
72 | :group 'eclim
73 | :type 'function)
74 |
75 | (defcustom eclim-executable
76 | (or (executable-find "eclim") (eclim-homedir-executable-find) (eclim-executable-find))
77 | "Location of eclim executable."
78 | :group 'eclim
79 | :type 'file)
80 |
81 | (defcustom eclim-auto-save t
82 | "Determines whether to save the buffer when retrieving completions.
83 | eclim can only complete correctly when the buffer has been
84 | saved."
85 | :group 'eclim
86 | :type '(choice (const :tag "Off" nil)
87 | (const :tag "On" t)))
88 |
89 | (defcustom eclim-use-yasnippet t
90 | "Determines whether the eclim snippets get turned on or off"
91 | :group 'eclim
92 | :type '(choice (const :tag "Off" nil)
93 | (const :tag "On" t)))
94 |
95 | (defcustom eclim-print-debug-messages nil
96 | "Determines whether debug messages should be printed."
97 | :group 'eclim
98 | :type '(choice (const :tag "Off" nil)
99 | (const :tag "On" t)))
100 |
101 | (defcustom eclim-limit-search-results t
102 | "Determines if code search results should be limited to files
103 | in the current workspace."
104 | :group 'eclim
105 | :type '(choice (const :tag "Off" nil)
106 | (const :tag "On" t)))
107 |
108 | (defvar-local eclim--project-name nil)
109 |
110 | (defvar-local eclim--project-current-file nil)
111 |
112 | (defvar eclim--project-natures-cache nil)
113 | (defvar eclim--projects-cache nil)
114 |
115 | (defvar eclim--file-coding-system-mapping
116 | '(("undecided-dos" . "iso-8859-1")
117 | ("dos" . "iso-8859-1")
118 | ("undecided-unix" . "iso-8859-1")
119 | ("utf-8-dos" . "utf-8")
120 | ("utf-8-unix" . "utf-8")
121 | ("utf-8-emacs-unix" . "utf-8")))
122 |
123 | (defvar eclim--compressed-urls-regexp "^\\(\\(?:jar\\|file\\|zip\\)://\\)")
124 | (defvar eclim--compressed-file-path-replacement-regexp "\\\\")
125 | (defvar eclim--compressed-file-path-removal-regexp "^/")
126 |
127 | (defun eclim-toggle-print-debug-messages ()
128 | (interactive)
129 | (message "Debug messages %s."
130 | (if (setq eclim-print-debug-messages (not eclim-print-debug-messages))
131 | "on" "off")))
132 |
133 | (defun eclim-quit-window (&optional kill-buffer)
134 | "Bury the buffer and delete its window. With a prefix argument, kill the
135 | buffer instead."
136 | (interactive "P")
137 | (quit-window kill-buffer (selected-window)))
138 |
139 | (defun eclim--make-command (args)
140 | "Creates a command string that can be executed from the
141 | shell. The first element in ARGS is the name of the eclim
142 | operation, and the rest are flags/values to be passed on to
143 | eclimd."
144 | (when (not eclim-executable)
145 | (error "Eclim installation not found. Please set eclim-executable."))
146 | (reduce (lambda (a b) (format "%s %s" a b))
147 | (append (list eclim-executable "-command" (first args))
148 | (loop for a = (rest args) then (rest (rest a))
149 | for arg = (first a)
150 | for val = (second a)
151 | while arg append (if val (list arg (shell-quote-argument val)) (list arg))))))
152 |
153 | (defun eclim--parse-result (result)
154 | "Parses the result of an eclim operation, raising an error if
155 | the result is not valid JSON."
156 | (if (string-match (rx string-start (zero-or-more (any " " "\n" "\t")) string-end) result)
157 | nil
158 | (condition-case nil
159 | (json-read-from-string result)
160 | ('json-readtable-error
161 | (cond ((string-match "java.io.UnsupportedEncodingException: \\(.*\\)" result)
162 | (let ((e (match-string 1 result)))
163 | (error "Eclim doesn't know how to handle the encoding %s. You can avoid this by
164 | evaluating (add-to-list 'eclim--file-coding-system-mapping '(\"%s\" . \"\"))
165 | where is the corresponding java name for this encoding." e e)))
166 | ((string-match "No command '\\(.*\\)' found" result)
167 | (let ((c (assoc-default (match-string 1 result)
168 | '(("xml_complete" "XML" "Eclipse Web Developer Tools")
169 | ("groovy_complete" "Groovy" "Eclipse Groovy Development Tools")
170 | ("ruby_complete" "Ruby" "Eclipse Ruby Development Tools")
171 | ("c_complete" "C/C++" "Eclipse C/C++ Development Tools")
172 | ("php_complete" "PHP" "Eclipse PHP Development Tools")
173 | ("scala_complete" "Scala" "Eclipse Scala Development Tools")))))
174 | (if c (error "Eclim was not installed with %s support. Please make sure that %s are installed, then reinstall eclim." (first c) (second c))
175 | (error result))))
176 | ((string-match ".*Exception: \\(.*\\)" result)
177 | (error (match-string 1 result)))
178 | (t (error result)))))))
179 |
180 | (defun eclim--call-process (&rest args)
181 | "Calls eclim with the supplied arguments. Consider using
182 | `eclim/execute-command' instead, as it has argument expansion,
183 | error checking, and some other niceties.."
184 | (let ((cmd (eclim--make-command args)))
185 | (when eclim-print-debug-messages (message "Executing: %s" cmd))
186 | (eclim--parse-result (shell-command-to-string cmd))))
187 |
188 | (defvar eclim--currently-running-async-calls nil)
189 |
190 | (defun eclim--call-process-async (callback &rest args)
191 | "Like `eclim--call-process', but the call is executed
192 | asynchronously. CALLBACK is a function that accepts a list of
193 | strings and will be called on completion."
194 | (lexical-let ((handler callback)
195 | (cmd (eclim--make-command args)))
196 | (when (not (find cmd eclim--currently-running-async-calls :test #'string=))
197 | (lexical-let ((buf (get-buffer-create (generate-new-buffer-name "*eclim-async*"))))
198 | (when eclim-print-debug-messages
199 | (message "Executing: %s" cmd)
200 | (message "Using async buffer %s" buf))
201 | (push cmd eclim--currently-running-async-calls)
202 | (let ((proc (start-process-shell-command "eclim" buf (eclim--make-command args))))
203 | (let ((sentinel (lambda (process signal)
204 | (unwind-protect
205 | (save-excursion
206 | (setq eclim--currently-running-async-calls (remove-if (lambda (x) (string= cmd x)) eclim--currently-running-async-calls))
207 | (set-buffer (process-buffer process))
208 | (funcall handler (eclim--parse-result (buffer-substring 1 (point-max)))))
209 | (kill-buffer buf)))))
210 | (set-process-sentinel proc sentinel)))))))
211 |
212 | (setq eclim--default-args
213 | '(("-n" . (eclim-project-name))
214 | ("-p" . (or (eclim-project-name) (error "Could not find eclipse project for %s" (buffer-name (current-buffer)))))
215 | ("-e" . (eclim--current-encoding))
216 | ("-f" . (eclim--project-current-file))
217 | ("-o" . (eclim--byte-offset))
218 | ("-s" . "project")))
219 |
220 | (defun eclim--args-contains (args flags)
221 | "Check if an (unexpanded) ARGS list contains any of the
222 | specified FLAGS."
223 | (loop for f in flags
224 | return (find f args :test #'string= :key (lambda (a) (if (listp a) (car a) a)))))
225 |
226 | (defun eclim--expand-args (args)
227 | "Takes a list of command-line arguments with which to call the
228 | eclim server. Each element should be either a string or a
229 | list. If it is a string, its default value is looked up in
230 | `eclim--default-args' and used to construct a list. The argument
231 | lists are then appended together."
232 | (mapcar (lambda (a) (if (numberp a) (number-to-string a) a))
233 | (loop for a in args
234 | append (if (listp a)
235 | (if (stringp (car a))
236 | (list (car a) (eval (cadr a)))
237 | (or (eval a) (list nil nil)))
238 | (list a (eval (cdr (or (assoc a eclim--default-args)
239 | (error "sorry, no default value for: %s" a)))))))))
240 |
241 | (defun eclim--command-should-sync-p (cmd args)
242 | (and (eclim--args-contains args '("-f" "-o"))
243 | (not (or (string= cmd "project_by_resource")
244 | (string= cmd "project_link_resource")))))
245 |
246 | (defun eclim--execute-command-internal (executor cmd args)
247 | (lexical-let* ((expargs (eclim--expand-args args))
248 | (sync (eclim--command-should-sync-p cmd args))
249 | (check (eclim--args-contains args '("-p"))))
250 | (when sync (eclim/java-src-update))
251 | (when check
252 | (ignore-errors
253 | (eclim--check-project (if (listp check) (eval (second check)) (eclim-project-name)))))
254 | (let ((attrs-before (if sync (file-attributes (buffer-file-name)) nil)))
255 | (funcall executor (cons cmd expargs)
256 | (lambda ()
257 | (when sync
258 | (let ((attrs-curr (file-attributes (buffer-file-name))))
259 | (when (and (file-exists-p (buffer-file-name))
260 | attrs-before
261 | (or
262 | (not (= (second (sixth attrs-before)) (second (sixth attrs-curr)))) ;; mod time
263 | (not (= (eighth attrs-before) (eighth attrs-curr))))) ;; size time
264 | (revert-buffer t t t)))))))))
265 |
266 | (defmacro eclim/execute-command (cmd &rest args)
267 | "Calls `eclim--expand-args' on ARGS, then calls eclim with the
268 | results. Automatically saves the current buffer (and optionally
269 | other java buffers as well), performs an eclim source update
270 | operation, and refreshes the current buffer if necessary. Raises
271 | an error if the connection is refused. Automatically calls
272 | `eclim--check-project' if neccessary."
273 | `(eclim--execute-command-internal
274 | (lambda (command-line on-complete-fn)
275 | (let ((res (apply 'eclim--call-process command-line)))
276 | (funcall on-complete-fn)
277 | res))
278 | ,cmd ',args))
279 |
280 | (defmacro eclim/execute-command-async (callback cmd &rest args)
281 | "Calls `eclim--expand-args' on ARGS, then calls eclim with the
282 | results. Automatically saves the current buffer (and optionally
283 | other java buffers as well), performs an eclim source update
284 | operation, and refreshes the current buffer if necessary. Raises
285 | an error if the connection is refused. Automatically calls
286 | `eclim--check-project' if neccessary. CALLBACK is a lambda
287 | expression which is called with the results of the operation."
288 | `(eclim--execute-command-internal
289 | (lambda (command-line on-complete-fn)
290 | (lexical-let ((on-complete-fn on-complete-fn))
291 | (apply 'eclim--call-process-async
292 | (lambda (res)
293 | (funcall on-complete-fn)
294 | (when ,callback
295 | (funcall ,callback res)))
296 | command-line)))
297 | ,cmd ',args))
298 |
299 | (defmacro eclim/with-results (result params &rest body)
300 | "Convenience macro. PARAMS is a list where the first element is
301 | CMD to execute and the rest an ARGS list. Calls eclim with CMD
302 | and the expanded ARGS list and binds RESULT to the results. If
303 | RESULT is non-nil, BODY is executed."
304 | (declare (indent defun))
305 | (let ((sync (eclim--args-contains (rest params) (list "-f" "-o"))))
306 | `(let* ((,result (eclim/execute-command ,@params))
307 | (eclim-auto-save (and eclim-auto-save (not ,sync))))
308 | (when ,result
309 | ,@body))))
310 |
311 | (defmacro eclim/with-results-async (result params &rest body)
312 | "Convenience macro. PARAMS is a list where the first element is
313 | CMD to execute and the rest an ARGS list. Calls eclim with CMD
314 | and the expanded ARGS list and binds RESULT to the results. If
315 | RESULT is non-nil, BODY is executed."
316 | (declare (indent defun))
317 | (let ((sync (eclim--args-contains (rest params) (list "-f" "-o"))))
318 | `(eclim/execute-command-async
319 | (lambda (,result)
320 | (let ((eclim-auto-save (and eclim-auto-save (not ,sync))))
321 | (when ,result ,@body)))
322 | ,@params)))
323 |
324 | (defun eclim--completing-read (prompt choices)
325 | (funcall eclim-interactive-completion-function prompt choices))
326 |
327 | (defun eclim--file-managed-p (&optional filename)
328 | "Return t if and only if this file is part of a project managed
329 | by eclim. If the optional argument FILENAME is given, the return
330 | value is computed for that file's instead."
331 | (ignore-errors
332 | (let ((file (or filename buffer-file-name)))
333 | (and file
334 | (eclim-project-name file)))))
335 |
336 | (defun eclim--project-dir (&optional projectname)
337 | "Return this project's root directory. If the optional
338 | argument PROJECTNAME is given, return that project's root directory."
339 | (assoc-default 'path (eclim/project-info (or projectname (eclim-project-name)))))
340 |
341 | (defun eclim-project-name (&optional filename)
342 | "Returns this file's project name. If the optional argument
343 | FILENAME is given, return that file's project name instead."
344 | (labels ((get-project-name (file)
345 | (eclim/execute-command "project_by_resource" ("-f" file))))
346 | (if filename
347 | (get-project-name filename)
348 | (or eclim--project-name
349 | (and buffer-file-name (setq eclim--project-name (get-project-name buffer-file-name)))))))
350 |
351 | (defun eclim--find-file (path-to-file)
352 | (if (not (string-match-p "!" path-to-file))
353 | (unless (and (buffer-file-name) (file-equal-p path-to-file (buffer-file-name)))
354 | (find-file path-to-file))
355 | (let* ((parts (split-string path-to-file "!"))
356 | (archive-name (replace-regexp-in-string eclim--compressed-urls-regexp "" (first parts)))
357 | (file-name (second parts)))
358 | (find-file-other-window archive-name)
359 | (beginning-of-buffer)
360 | (re-search-forward (replace-regexp-in-string
361 | eclim--compressed-file-path-removal-regexp ""
362 | (regexp-quote (replace-regexp-in-string
363 | eclim--compressed-file-path-replacement-regexp
364 | "/" file-name))))
365 | (let ((old-buffer (current-buffer)))
366 | (archive-extract)
367 | (beginning-of-buffer)
368 | (kill-buffer old-buffer)))))
369 |
370 | (defun eclim--find-display-results (pattern results &optional open-single-file)
371 | (let ((results (remove-if (lambda (result) (string-match (rx bol (or "jar" "zip") ":") (assoc-default 'filename result))) results)))
372 | (if (and (= 1 (length results)) open-single-file) (eclim--visit-declaration (elt results 0))
373 | (pop-to-buffer (get-buffer-create "*eclim: find"))
374 | (let ((buffer-read-only nil))
375 | (erase-buffer)
376 | (insert (concat "-*- mode: eclim-find; default-directory: " default-directory " -*-"))
377 | (newline 2)
378 | (insert (concat "eclim java_search -p " pattern))
379 | (newline)
380 | (loop for result across results
381 | do (insert (eclim--format-find-result result default-directory)))
382 | (goto-char 0)
383 | (grep-mode)))))
384 |
385 | (defun eclim--format-find-result (line &optional directory)
386 | (let ((converted-directory (replace-regexp-in-string "\\\\" "/" (assoc-default 'filename line))))
387 | (format "%s:%d:%d:%s\n"
388 | (if converted-directory
389 | (replace-regexp-in-string (concat (regexp-quote directory) "/?") "" converted-directory)
390 | converted-directory)
391 | (assoc-default 'line line)
392 | (assoc-default 'column line)
393 | (assoc-default 'message line))))
394 |
395 | (defun eclim--visit-declaration (line)
396 | (ring-insert find-tag-marker-ring (point-marker))
397 | (eclim--find-file (assoc-default 'filename line))
398 | (goto-line (assoc-default 'line line))
399 | (move-to-column (1- (assoc-default 'column line))))
400 |
401 | (defun eclim--string-strip (content)
402 | (replace-regexp-in-string "\s*$" "" content))
403 |
404 | (defun eclim--project-current-file ()
405 | (or eclim--project-current-file
406 | (setq eclim--project-current-file
407 | (eclim/execute-command "project_link_resource" ("-f" buffer-file-name)))))
408 |
409 | (defun eclim--byte-offset (&optional text)
410 | ;; TODO: restricted the ugly newline counting to dos buffers => remove it all the way later
411 | (let ((current-offset (1-(position-bytes (point)))))
412 | (when (not current-offset) (setq current-offset 0))
413 | (if (string-match "dos" (symbol-name buffer-file-coding-system))
414 | (+ current-offset (how-many "\n" (point-min) (point)))
415 | current-offset)))
416 |
417 | (defun eclim--current-encoding ()
418 | (let* ((coding-system (symbol-name buffer-file-coding-system))
419 | (mapped-coding-system (cdr (assoc
420 | coding-system
421 | eclim--file-coding-system-mapping))))
422 | (if mapped-coding-system mapped-coding-system coding-system)))
423 |
424 | ;; Commands
425 |
426 | (defun eclim-file-locate (pattern &optional case-insensitive)
427 | (interactive (list (read-string "Pattern: ") "P"))
428 | (eclim/with-results hits ("locate_file" ("-p" (concat "^.*" pattern ".*$")) ("-s" "workspace") (if case-insensitive '("-i" "")))
429 | (eclim--find-display-results pattern
430 | (apply #'vector
431 | (mapcar (lambda (hit) (list (cons 'filename (assoc-default 'path hit))
432 | (cons 'line 1)
433 | (cons 'column 1)
434 | (cons 'message "")))
435 | hits))
436 | t)))
437 |
438 | ;;;###autoload
439 | (defun eclim/workspace-dir ()
440 | (eclim--call-process "workspace_dir"))
441 |
442 | (defun eclim/jobs (&optional family)
443 | (eclim/execute-command "jobs" ("-f" family)))
444 |
445 | ;;** The minor mode and its keymap
446 |
447 | ;;;###autoload
448 | (defvar eclim-mode-map
449 | (let ((map (make-sparse-keymap)))
450 | (define-key map (kbd "M-TAB") 'eclim-complete)
451 | map)
452 | "The keymap used in `eclim-mode'.")
453 |
454 | (defvar eclim-mode-hook nil)
455 |
456 | ;;;###autoload
457 | (define-minor-mode eclim-mode
458 | "An interface to the Eclipse IDE."
459 | nil
460 | (:eval (eclim-modeline-string))
461 | eclim-mode-map
462 | (if eclim-mode
463 | (progn
464 | (kill-local-variable 'eclim--project-dir)
465 | (kill-local-variable 'eclim-project-name)
466 | (kill-local-variable 'eclim--project-current-file)
467 | (add-hook 'after-save-hook 'eclim--problems-update-maybe nil 't)
468 | (add-hook 'after-save-hook 'eclim--after-save-hook nil 't))
469 | (remove-hook 'after-save-hook 'eclim--problems-update-maybe 't)
470 | (remove-hook 'after-save-hook 'eclim--after-save-hook 't)))
471 |
472 | (defcustom eclim-accepted-file-regexps
473 | '("\\.java" "\\.js" "\\.xml" "\\.rb" "\\.groovy" "\\.php" "\\.c" "\\.cc" "\\.h" "\\.scala")
474 | "List of regular expressions that are matched against filenames
475 | to decide if eclim should be automatically started on a
476 | particular file. By default all files part of a project managed
477 | by eclim can be accepted (see `eclim--accepted-filename' for more
478 | information). It is nevertheless possible to restrict eclim to
479 | some files by changing this variable. For example, a value
480 | of (\"\\\\.java\\\\'\" \"build\\\\.xml\\\\'\") can be used to restrict
481 | the use of eclim to java and ant files."
482 | :group 'eclim
483 | :type '(repeat regexp))
484 |
485 | (defun eclim--accepted-filename-p (filename)
486 | "Return t if and only one of the regular expressions in
487 | `eclim-accepted-file-regexps' matches FILENAME."
488 | (if (member-if
489 | (lambda (regexp) (string-match regexp filename))
490 | eclim-accepted-file-regexps)
491 | t))
492 |
493 | (defun eclim--accepted-p (filename)
494 | "Return t if and only if eclim should be automatically started on filename."
495 | (and
496 | filename
497 | (eclim--accepted-filename-p filename)
498 | (eclim--file-managed-p filename)))
499 |
500 | ;; Request an eclipse source update when files are saved
501 | (defun eclim--after-save-hook ()
502 | (when (eclim--accepted-p (buffer-file-name))
503 | (ignore-errors
504 | (apply 'eclim--call-process
505 | (case major-mode
506 | (java-mode "java_src_update")
507 | (groovy-mode "groovy_src_update")
508 | (ruby-mode "ruby_src_update")
509 | (php-mode "php_src_update")
510 | (scala-mode "scala_src_update")
511 | ((c-mode c++-mode) "c_src_update")
512 | ((javascript-mode js-mode) "javascript_src_update"))
513 | (eclim--expand-args (list "-p" "-f")))))
514 | t)
515 |
516 | (defun revert-buffer-keep-history (&optional IGNORE-AUTO NOCONFIRM PRESERVE-MODES)
517 | (interactive)
518 | (save-excursion
519 | ;; tell Emacs the modtime is fine, so we can edit the buffer
520 | (clear-visited-file-modtime)
521 | ;; insert the current contents of the file on disk
522 | (widen)
523 | (delete-region (point-min) (point-max))
524 | (insert-file-contents (buffer-file-name))
525 | ;; mark the buffer as not modified
526 | (not-modified)
527 | (set-visited-file-modtime)))
528 |
529 | ;;;###autoload
530 | (define-globalized-minor-mode global-eclim-mode eclim-mode
531 | (lambda ()
532 | (if (and buffer-file-name
533 | (eclim--accepted-p buffer-file-name)
534 | (eclim--project-dir))
535 | (eclim-mode 1))))
536 |
537 | (require 'eclim-project)
538 | (require 'eclim-java)
539 | (require 'eclim-ant)
540 | (require 'eclim-maven)
541 | (require 'eclim-problems)
542 | (require 'eclim-debug)
543 | (require 'eclim-java-run)
544 |
545 | (defun eclim-modeline-string ()
546 | (when eclim-mode
547 | (concat " Eclim " (eclim-problems-modeline-string))))
548 |
549 | (provide 'eclim)
550 |
--------------------------------------------------------------------------------
/eclim-java.el:
--------------------------------------------------------------------------------
1 | ;; eclim-java.el --- an interface to the Eclipse IDE.
2 | ;;
3 | ;; Copyright (C) 2009 Yves Senn
4 | ;;
5 | ;; This program is free software: you can redistribute it and/or modify
6 | ;; it under the terms of the GNU General Public License as published by
7 | ;; the Free Software Foundation, either version 3 of the License, or
8 | ;; (at your option) any later version.
9 | ;;
10 | ;; This program is distributed in the hope that it will be useful,
11 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | ;; GNU General Public License for more details.
14 | ;;
15 | ;; You should have received a copy of the GNU General Public License
16 | ;; along with this program. If not, see .
17 | ;;
18 | ;;; Contributors
19 | ;;
20 | ;; - Tassilo Horn
21 | ;;
22 | ;;; Conventions
23 | ;;
24 | ;; Conventions used in this file: Name internal variables and functions
25 | ;; "eclim--", and name eclim command invocations
26 | ;; "eclim/command-name", like eclim/project-list.
27 |
28 | ;;* Eclim Java
29 |
30 | (require 'json)
31 | (require 'dash)
32 |
33 | (define-key eclim-mode-map (kbd "C-c C-e s") 'eclim-java-method-signature-at-point)
34 | (define-key eclim-mode-map (kbd "C-c C-e f d") 'eclim-java-find-declaration)
35 | (define-key eclim-mode-map (kbd "C-c C-e f r") 'eclim-java-find-references)
36 | (define-key eclim-mode-map (kbd "C-c C-e f t") 'eclim-java-find-type)
37 | (define-key eclim-mode-map (kbd "C-c C-e f f") 'eclim-java-find-generic)
38 | (define-key eclim-mode-map (kbd "C-c C-e r") 'eclim-java-refactor-rename-symbol-at-point)
39 | (define-key eclim-mode-map (kbd "C-c C-e i") 'eclim-java-import-organize)
40 | (define-key eclim-mode-map (kbd "C-c C-e h") 'eclim-java-hierarchy)
41 | (define-key eclim-mode-map (kbd "C-c C-e z") 'eclim-java-implement)
42 | (define-key eclim-mode-map (kbd "C-c C-e d") 'eclim-java-doc-comment)
43 | (define-key eclim-mode-map (kbd "C-c C-e f s") 'eclim-java-format)
44 | (define-key eclim-mode-map (kbd "C-c C-e g") 'eclim-java-generate-getter-and-setter)
45 | (define-key eclim-mode-map (kbd "C-c C-e t") 'eclim-run-junit)
46 |
47 | (defvar eclim-java-show-documentation-map
48 | (let ((map (make-keymap)))
49 | (suppress-keymap map)
50 | (define-key map (kbd "") 'forward-button)
51 | (define-key map (kbd "S-") 'backward-button)
52 | (define-key map (kbd "q") 'eclim-quit-window)
53 | map))
54 |
55 |
56 | (defgroup eclim-java nil
57 | "Java: editing, browsing, refactoring"
58 | :group 'eclim)
59 |
60 | (defcustom eclim-java-major-modes '(java-mode jde-mode)
61 | "This variable contains a list of major modes to edit java
62 | files. There are certain operations, that eclim will only perform when
63 | the current buffer is contained within this list"
64 | :group 'eclim-java
65 | :type 'list)
66 |
67 | ;; Could this value be taken from Eclipse somehow?"
68 | (defcustom eclim-java-documentation-root nil
69 | "Root directory of Java HTML documentation.
70 |
71 | If Android is used then Eclipse may refer standard Java elements from the copy of
72 | Java documentation under Android docs, so don't forget to set
73 | `eclim-java-android-documentation-root' too in that case."
74 | :group 'eclim-java
75 | :type 'directory)
76 |
77 | ;; Could this value be taken from Eclipse somehow?"
78 | (defcustom eclim-java-android-documentation-root nil
79 | "Root directory of Android HTML documentation."
80 | :group 'eclim-java
81 | :type 'directory)
82 |
83 |
84 | (defvar eclim--java-search-types '("all"
85 | "annotation"
86 | "class"
87 | "classOrEnum"
88 | "classOrInterface"
89 | "constructor"
90 | "enum"
91 | "field"
92 | "interface"
93 | "method"
94 | "package"
95 | "type"))
96 |
97 | (defvar eclim--java-search-scopes '("all"
98 | "project"
99 | "type"))
100 |
101 | (defvar eclim--java-search-contexts '("all"
102 | "declarations"
103 | "implementors"
104 | "references"))
105 |
106 | (defvar eclim--is-completing nil)
107 |
108 | (defun eclim/groovy-src-update (&optional save-others)
109 | "If `eclim-auto-save' is non-nil, save the current java
110 | buffer. In addition, if `save-others' is non-nil, also save any
111 | other unsaved buffer. Finally, tell eclim to update its java
112 | sources."
113 | (when eclim-auto-save
114 | (when (buffer-modified-p) (save-buffer)) ;; auto-save current buffer, prompt on saving others
115 | (when save-others (save-some-buffers nil (lambda () (string-match "\\.groovy$" (buffer-file-name)))))))
116 |
117 | (defun eclim/java-src-update (&optional save-others)
118 | "If `eclim-auto-save' is non-nil, save the current java
119 | buffer. In addition, if `save-others' is non-nil, also save any
120 | other unsaved buffer. Finally, tell eclim to update its java
121 | sources."
122 | (when eclim-auto-save
123 | (when (buffer-modified-p) (save-buffer)) ;; auto-save current buffer, prompt on saving others
124 | (when save-others (save-some-buffers nil (lambda () (string-match "\\.java$" (buffer-file-name)))))))
125 |
126 | (defadvice delete-file (around eclim--delete-file activate)
127 | "Advice the `delete-file' function to trigger a source update
128 | in eclim when appropriate."
129 | (let ((pr nil)
130 | (fn nil))
131 | (ignore-errors
132 | (and (setq pr (eclim-project-name filename))
133 | (setq fn (file-relative-name filename (eclim--project-dir pr)))))
134 | ad-do-it
135 | (when (and pr fn)
136 | (ignore-errors (apply 'eclim--call-process (list "java_src_update" "-p" pr "-f" fn))))))
137 |
138 | (defun eclim--java-parser-read (str)
139 | (first
140 | (read-from-string
141 | (format "(%s)"
142 | (replace-regexp-in-string
143 | "[<>(),?]"
144 | (lambda (m) (assoc-default m '(("<" . "((") (">" . "))")
145 | ("(" . "((") (")" ."))")
146 | ("," . ")(")
147 | ("?" . "\\\\?"))))
148 | str)))))
149 |
150 | (defun eclim--java-parse-method-signature (signature)
151 | (cl-flet ((parser3/parse-arg (arg)
152 | (let ((arg-rev (reverse arg)))
153 | (cond ((null arg) nil)
154 | ((= (length arg) 1) (list (list :type (first arg))))
155 | ((listp (first arg-rev)) (list (cons :type arg)))
156 | (t (list (cons :name (first arg-rev)) (cons :type (reverse (rest arg-rev)))))))))
157 | (let ((ast (reverse (eclim--java-parser-read signature))))
158 | (list (cons :arglist (mapcar #'parser3/parse-arg (first ast)))
159 | (cons :name (second ast))
160 | (cons :return (reverse (rest (rest ast))))))))
161 |
162 | (defun eclim--java-current-type-name (&optional type)
163 | "Searches backward in the current buffer until a type
164 | declaration has been found. TYPE may be either 'class',
165 | 'interface', 'enum' or nil, meaning 'match all of the above'."
166 | (save-excursion
167 | (if (re-search-backward
168 | (concat (or type "\\(class\\|interface\\|enum\\)") "\\s-+\\([^<{\s-]+\\)") nil t)
169 | (match-string-no-properties 2)
170 | "")))
171 |
172 | (defun eclim--java-current-class-name ()
173 | "Searches backward in the current buffer until a class declaration
174 | has been found."
175 | (eclim--java-current-type-name "\\(class\\)"))
176 |
177 | (defun eclim/java-classpath (project)
178 | (eclim--check-project project)
179 | (eclim--call-process "java_classpath" "-p" project))
180 |
181 | (defun eclim/java-classpath-variables ()
182 | ;; TODO: fix trailing whitespaces
183 | (mapcar (lambda (line)
184 | (split-string line "-")) (eclim--call-process "java_classpath_variables")))
185 |
186 | (defun eclim/java-classpath-variable-create (name path)
187 | (eclim--call-process "java_classpath_variable_create" "-n" name "-p" path))
188 |
189 | (defun eclim/java-classpath-variable-delete (name)
190 | (eclim--call-process "java_classpath_variable_create" "-n" name))
191 |
192 | (defun eclim-java-doc-comment ()
193 | "Inserts or updates a javadoc comment for the element at point."
194 | (interactive)
195 | (eclim/execute-command "javadoc_comment" "-p" "-f" "-o"))
196 |
197 | (defun eclim-run-java-doc ()
198 | "Run Javadoc on current or all projects."
199 | (interactive)
200 | (let ((project-list (mapcar 'third (eclim/project-list))))
201 | (if (y-or-n-p "Run Javadoc for all projects?")
202 | (dolist (project project-list)
203 | (eclim/execute-command "javadoc" ("-p" project)))
204 | (eclim/execute-command "javadoc" "-p"))
205 | (message "Javadoc creation finished.")))
206 |
207 | (defun eclim-java-format ()
208 | "Format the source code of the current java source file."
209 | (interactive)
210 | (eclim/execute-command "java_format" "-p" "-f" ("-h" 0) ("-t" (1- (point-max))) "-e"))
211 |
212 | (defun eclim-java-generate-getter-and-setter (project file offset encoding)
213 | "Generates getter and setter methods for the symbol at point."
214 | (interactive (list (eclim-project-name)
215 | (eclim--project-current-file)
216 | (eclim--byte-offset)
217 | (eclim--current-encoding)))
218 |
219 | (eclim--call-process "java_bean_properties"
220 | "-p" project
221 | "-f" file
222 | "-o" (number-to-string offset)
223 | "-e" encoding
224 | "-r" (cdr (eclim--java-identifier-at-point t))
225 | "-t" "gettersetter")
226 | (revert-buffer t t t))
227 |
228 | (defun eclim-java-constructor ()
229 | (interactive)
230 | (eclim/execute-command "java_constructor" "-p" "-f" "-o"))
231 |
232 | (defun eclim/java-call-hierarchy (project file offset length encoding)
233 | (eclim--call-process "java_callhierarchy"
234 | "-p" project
235 | "-f" file
236 | "-o" (number-to-string offset)
237 | "-l" (number-to-string length)
238 | "-e" encoding))
239 |
240 | (defun eclim/java-hierarchy (project file offset encoding)
241 | (eclim--call-process "java_hierarchy"
242 | "-p" project
243 | "-f" file
244 | "-o" (number-to-string offset)
245 | "-e" encoding))
246 |
247 | (defun eclim-java-refactor-rename-symbol-at-point ()
248 | "Rename the java symbol at point."
249 | (interactive)
250 | (let* ((i (eclim--java-identifier-at-point t))
251 | (n (read-string (concat "Rename " (cdr i) " to: ") (cdr i))))
252 | (eclim/with-results res ("java_refactor_rename" "-p" "-e" "-f" ("-n" n)
253 | ("-o" (car i)) ("-l" (length (cdr i))))
254 | (if (stringp res) (error res))
255 | (loop for (from to) in (mapcar (lambda (x) (list (assoc-default 'from x) (assoc-default 'to x))) res)
256 | do (when (and from to)
257 | (kill-buffer (find-buffer-visiting from))
258 | (find-file to)))
259 | (save-excursion
260 | (loop for file in (mapcar (lambda (x) (assoc-default 'file x)) res)
261 | do (when file
262 | (let ((buf (get-file-buffer (file-name-nondirectory file))))
263 | (when buf
264 | (switch-to-buffer buf)
265 | (revert-buffer t t t))))))
266 | (message "Done"))))
267 |
268 | (defun eclim-java-call-hierarchy (project file encoding)
269 | (interactive (list (eclim-project-name)
270 | (eclim--project-current-file)
271 | (eclim--current-encoding)))
272 | (let ((boundary "\\([<>()\\[\\.\s\t\n!=,;]\\|]\\)"))
273 | (save-excursion
274 | (if (re-search-backward boundary nil t)
275 | (forward-char))
276 | (let ((top-node (eclim/java-call-hierarchy project file (eclim--byte-offset)
277 | (length (cdr (eclim--java-identifier-at-point t))) encoding)))
278 | (pop-to-buffer "*eclim: call hierarchy*" t)
279 | (special-mode)
280 | (let ((buffer-read-only nil))
281 | (erase-buffer)
282 | (eclim--java-insert-call-hierarchy-node
283 | project
284 | top-node
285 | 0))))))
286 | (defun eclim--java-insert-call-hierarchy-node (project node level)
287 | (let ((declaration (cdr (assoc 'name node))))
288 | (insert (format (concat "%-"(number-to-string (* level 2)) "s=> ") ""))
289 | (lexical-let ((position (cdr (assoc 'position node))))
290 | (if position
291 | (insert-text-button declaration
292 | 'follow-link t
293 | 'help-echo declaration
294 | 'action #'(lambda (&rest ignore)
295 | (eclim--visit-declaration position)))
296 | (insert declaration)))
297 | (newline)
298 | (loop for caller across (cdr (assoc 'callers node))
299 | do (eclim--java-insert-call-hierarchy-node project caller (1+ level)))))
300 |
301 | (defun eclim-java-hierarchy (project file offset encoding)
302 | (interactive (list (eclim-project-name)
303 | (eclim--project-current-file)
304 | (eclim--byte-offset)
305 | (eclim--current-encoding)))
306 | (let ((top-node (eclim/java-hierarchy project file offset encoding)))
307 | (pop-to-buffer "*eclim: hierarchy*" t)
308 | (special-mode)
309 | (let ((buffer-read-only nil))
310 | (erase-buffer)
311 | (eclim--java-insert-hierarchy-node
312 | project
313 | top-node
314 | 0))))
315 |
316 | (defun eclim--java-insert-file-path-for-hierarchy-node (node)
317 | (eclim/with-results hits ("java_search" ("-p" (cdr (assoc 'qualified node))) ("-t" "type") ("-x" "declarations") ("-s" "workspace"))
318 | (assoc-default 'filename (elt hits 0))))
319 |
320 | (defun eclim--java-insert-hierarchy-node (project node level)
321 | (let ((declaration (cdr (assoc 'name node)))
322 | (qualified-name (cdr (assoc 'qualified node))))
323 | (insert (format (concat "%-"(number-to-string (* level 2)) "s=> ") ""))
324 | (lexical-let ((file-path (eclim--java-insert-file-path-for-hierarchy-node node)))
325 | (if file-path
326 | (insert-text-button declaration
327 | 'follow-link t
328 | 'help-echo qualified-name
329 | 'action (lambda (&rest ignore)
330 | (eclim--find-file file-path)))
331 | (insert declaration))))
332 | (newline)
333 | (let ((children (cdr (assoc 'children node))))
334 | (loop for child across children do
335 | (eclim--java-insert-hierarchy-node project child (+ level 1)))))
336 |
337 | (defun eclim-java-find-declaration ()
338 | "Find and display the declaration of the java identifier at point."
339 | (interactive)
340 | (let ((i (eclim--java-identifier-at-point t)))
341 | (eclim/with-results hits ("java_search" "-n" "-f" ("-o" (car i)) ("-l" (length (cdr i))) ("-x" "declaration"))
342 | (eclim--find-display-results (cdr i) hits t))))
343 |
344 | (defun eclim-c-find-declaration ()
345 | "Find and display the declaration of the c identifier at point."
346 | (interactive)
347 | (let ((i (eclim--java-identifier-at-point t)))
348 | (eclim/with-results hits ("c_search" "-n" "-f" ("-o" (car i)) ("-l" (length (cdr i))))
349 | (eclim--find-display-results (cdr i) hits t))))
350 |
351 | (defun eclim-java-find-references ()
352 | "Find and display references for the java identifier at point."
353 | (interactive)
354 | (let ((i (eclim--java-identifier-at-point t)))
355 | (eclim/with-results hits ("java_search" "-n" "-f" ("-o" (car i)) ("-l" (length (cdr i))) ("-x" "references"))
356 | (eclim--find-display-results (cdr i) hits))))
357 |
358 | (defun eclim-java-find-type (type-name &optional case-insensitive)
359 | "Searches the project for a given class. The TYPE-NAME is the
360 | pattern, which will be used for the search. If invoked with the
361 | universal argument the search will be made CASE-INSENSITIVE."
362 | (interactive (list (read-string "Name: " (let ((case-fold-search nil)
363 | (current-symbol (symbol-name (symbol-at-point))))
364 | (if (string-match-p "^[A-Z]" current-symbol)
365 | current-symbol
366 | (eclim--java-current-type-name))))
367 | "P"))
368 | (eclim-java-find-generic "workspace" "declarations" "type" type-name case-insensitive t))
369 |
370 | (defun eclim-java-find-generic (scope context type pattern &optional case-insensitive open-single-file)
371 | "Searches within SCOPE (all/project/type) for a
372 | TYPE (all/annotation/class/classOrEnum/classOrInterface/constructor/enum/field/interface/method/package/type)
373 | matching the given
374 | CONTEXT (all/declarations/implementors/references) and
375 | PATTERN. If invoked with the universal argument the search will
376 | be made CASE-INSENSITIVE."
377 | (interactive (list (eclim--completing-read "Scope: " eclim--java-search-scopes)
378 | (eclim--completing-read "Context: " eclim--java-search-contexts)
379 | (eclim--completing-read "Type: " eclim--java-search-types)
380 | (read-string "Pattern: ")
381 | "P"))
382 | (eclim/with-results hits ("java_search" ("-p" pattern) ("-t" type) ("-x" context) ("-s" scope) (if case-insensitive '("-i" "")))
383 | (eclim--find-display-results pattern hits open-single-file)))
384 |
385 | (defun eclim--java-identifier-at-point (&optional full position)
386 | "Returns a cons cell (BEG . IDENTIFIER) where BEG is the start
387 | buffer byte offset of the token/identifier at point, and
388 | IDENTIFIER is the string from BEG to (point). If argument FULL is
389 | non-nill, IDENTIFIER will contain the whole identifier, not just
390 | the start. If argument POSITION is non-nil, BEG will contain the
391 | position of the identifier instead of the byte offset (which only
392 | matters for buffers containing non-ASCII characters)."
393 | (let ((boundary "\\([<>()\\[\\.\s\t\n!=,;]\\|]\\)"))
394 | ;; TODO: make this work for dos buffers
395 | (save-excursion
396 | (if (and full (re-search-forward boundary nil t))
397 | (backward-char))
398 | (let ((end (point))
399 | (start (progn
400 | (if (re-search-backward boundary nil t) (forward-char))
401 | (point))))
402 | (cons (if position (point) (eclim--byte-offset))
403 | (buffer-substring-no-properties start end))))))
404 |
405 | (defun eclim--java-package-components (package)
406 | "Returns the components of a Java package statement."
407 | (split-string package "\\."))
408 |
409 | (defun eclim--java-current-package ()
410 | "Returns the package for the class in the current buffer."
411 | (save-excursion
412 | (goto-char 0)
413 | (if (re-search-forward "package \\(.*?\\);" (point-max) t)
414 | (match-string-no-properties 1))))
415 |
416 | (defun eclim-soft-revert-imports (ignore-auto noconfirm)
417 | "Can be used as a REVERT-BUFFER-FUNCTION to only replace the
418 | imports section of a java source file. This will preserve the
419 | undo history."
420 | (interactive)
421 | (cl-flet ((cut-imports ()
422 | (beginning-of-buffer)
423 | (if (re-search-forward "^import" nil t)
424 | (progn
425 | (beginning-of-line)
426 | (let ((beg (point)))
427 | (end-of-buffer)
428 | (re-search-backward "^import")
429 | (end-of-line)
430 | (let ((imports (buffer-substring-no-properties beg (point))))
431 | (delete-region beg (point))
432 | imports)))
433 | (progn
434 | (forward-line 1)
435 | (delete-blank-lines)
436 | (insert "\n\n\n")
437 | (forward-line -2)))))
438 | (save-excursion
439 | (clear-visited-file-modtime)
440 | (cut-imports)
441 | (widen)
442 | (insert
443 | (let ((fname (buffer-file-name)))
444 | (with-temp-buffer
445 | (insert-file-contents fname)
446 | (cut-imports))))
447 | (not-modified)
448 | (set-visited-file-modtime))))
449 |
450 | (defun eclim-java-import (type)
451 | "Adds an import statement for the given type, if one does not
452 | exist already."
453 | (save-excursion
454 | (beginning-of-buffer)
455 | (let ((revert-buffer-function 'eclim-soft-revert-imports))
456 | (when (not (re-search-forward (format "^import %s;" type) nil t))
457 | (eclim/execute-command "java_import" "-p" "-f" "-o" "-e" ("-t" type))
458 | (eclim--problems-update-maybe)))))
459 |
460 | (defun eclim-java-import-organize (&optional types)
461 | "Checks the current file for missing imports, removes unused imports and
462 | sorts import statements. "
463 | (interactive)
464 | (let ((revert-buffer-function 'eclim-soft-revert-imports))
465 | (eclim/with-results res ("java_import_organize" "-p" "-f" "-o" "-e"
466 | (when types (list "-t" (reduce (lambda (a b) (concat a "," b)) types))))
467 | (eclim--problems-update-maybe)
468 | (when (vectorp res)
469 | (save-excursion
470 | (eclim-java-import-organize
471 | (mapcar (lambda (imports) (eclim--completing-read "Import: " (append imports '()))) res)))))))
472 |
473 | (defun format-type (type)
474 | (cond ((null type) nil)
475 | ((listp (first type))
476 | (append (list "<") (rest (mapcan (lambda (type) (append (list ", ") (format-type type))) (first type))) (list ">")
477 | (format-type (rest type))))
478 | (t (cons (let ((type-name (symbol-name (first type))))
479 | (when (string-match "\\(.*\\.\\)?\\(.*\\)" type-name)
480 | (match-string 2 type-name)))
481 | (format-type (rest type))))))
482 |
483 | (defun eclim-java-implement (&optional name)
484 | "Lets the user select from a list of methods to
485 | implemnt/override, then inserts a skeleton for the chosen
486 | method."
487 | (interactive)
488 | (eclim/with-results response ("java_impl" "-p" "-f" "-o")
489 | (cl-flet ((join (glue items)
490 | (cond ((null items) "")
491 | ((= 1 (length items)) (format "%s" (first items)))
492 | (t (reduce (lambda (a b) (format "%s%s%s" a glue b)) items))))
493 | (format-type (type)
494 | (cond ((null type) nil)
495 | ((listp (first type))
496 | (append (list "<") (rest (mapcan (lambda (type) (append (list ", ") (format-type type))) (first type))) (list ">")
497 | (format-type (rest type))))
498 | (t (cons (let ((type-name (symbol-name (first type))))
499 | (when (string-match "\\(.*\\.\\)?\\(.*\\)" type-name)
500 | (let ((package (match-string 1 type-name))
501 | (class (match-string 2 type-name)))
502 | (eclim-java-import (concat package class))
503 | class)))
504 | (format-type (rest type)))))))
505 | (let* ((methods (remove-if-not (lambda (m) (or (null name)
506 | (string-match name m)))
507 | (mapcar (lambda (x) (replace-regexp-in-string "[ \n\t]+" " " x))
508 | (apply 'append
509 | (mapcar (lambda (x) (append (assoc-default 'methods x) nil))
510 | (assoc-default 'superTypes response))))))
511 | (method (if (= 1 (length methods)) (first methods)
512 | (eclim--completing-read "Signature: " methods)))
513 | (sig (eclim--java-parse-method-signature method))
514 | (ret (assoc-default :return sig)))
515 | (yas/expand-snippet (format "@Override\n%s %s(%s) {$0}"
516 | (apply #'concat
517 | (join " " (remove-if-not (lambda (m) (find m '(public protected private void))) (subseq ret 0 (1- (length ret)))))
518 | " "
519 | (format-type (remove-if (lambda (m) (find m '(abstract public protected private ))) ret)))
520 | (assoc-default :name sig)
521 | (join ", " (loop for arg in (remove-if #'null (assoc-default :arglist sig))
522 | for i from 0
523 | collect (format "%s ${arg%s}" (apply #'concat (format-type (assoc-default :type arg))) i)))))))))
524 |
525 | (defun eclim-package-and-class ()
526 | (let ((package-name (eclim--java-current-package))
527 | (class-name (eclim--java-current-class-name)))
528 | (if package-name (concat package-name "." class-name)
529 | class-name)))
530 |
531 | (defun eclim-run-class ()
532 | "Run the current class."
533 | (interactive)
534 | (if (not (string= major-mode "java-mode"))
535 | (message "Sorry cannot run current buffer.")
536 | (compile (concat eclim-executable " -command java -p " (eclim-project-name)
537 | " -c " (eclim-package-and-class)))))
538 |
539 | (defun eclim--java-junit-file (project file offset encoding)
540 | (concat eclim-executable
541 | " -command java_junit -p " project
542 | " -f " file
543 | " -o " (number-to-string offset)
544 | " -e " encoding))
545 |
546 | (defun eclim--java-junit-project (project encoding)
547 | (concat eclim-executable
548 | " -command java_junit -p " project
549 | " -e " encoding))
550 |
551 | (defun eclim--buffer-contains-substring (string)
552 | (save-excursion
553 | (save-match-data
554 | (goto-char (point-min))
555 | (search-forward string nil t))))
556 |
557 | (defun eclim--java-make-popup-item (correction)
558 | (popup-make-item
559 | (cdr (assoc 'description correction))
560 | :value (cdr (assoc 'index correction))
561 | :document (cdr (assoc 'preview correction))))
562 |
563 | (defun eclim-java-junit-buffer? ()
564 | (eclim--buffer-contains-substring "org.junit.Test"))
565 |
566 | (defun eclim-java-testng-buffer? ()
567 | (eclim--buffer-contains-substring "org.testng.annotations.Test"))
568 |
569 | (defun eclim-run-junit (project file offset encoding)
570 | "Run the current JUnit tests for current project or
571 | current class or current method.
572 |
573 | This method hooks onto the running Eclipse process and is thus
574 | much faster than running mvn test -Dtest=TestClass#method."
575 | (interactive (list (eclim-project-name)
576 | (eclim--project-current-file)
577 | (eclim--byte-offset)
578 | (eclim--current-encoding)))
579 | (if (not (string= major-mode "java-mode"))
580 | (message "Running JUnit tests only makes sense for Java buffers.")
581 | (compile (if (eclim-java-junit-buffer?)
582 | (eclim--java-junit-file project file offset encoding)
583 | (eclim--java-junit-project project encoding)))))
584 |
585 | (defun eclim-java-correct (line offset)
586 | (eclim/with-results correction-info ("java_correct" "-p" "-f" ("-l" line) ("-o" offset))
587 | (if (stringp correction-info)
588 | (message correction-info)
589 | (-if-let* ((corrections (cdr (assoc 'corrections correction-info)))
590 | (cmenu (mapcar 'eclim--java-make-popup-item corrections))
591 | (choice (popup-menu* cmenu)))
592 | (eclim/with-results correction-info
593 | ("java_correct"
594 | ("-p" (eclim-project-name))
595 | "-f"
596 | ("-l" line)
597 | ("-o" offset)
598 | ("-a" choice)))
599 | (message "No automatic corrections found. Sorry")))))
600 |
601 | (defun eclim-java-show-documentation-for-current-element ()
602 | "Displays the doc comments for the element at the pointers position."
603 | (interactive)
604 | (let ((symbol (symbol-at-point)))
605 | (if symbol
606 | (let ((bounds (bounds-of-thing-at-point 'symbol))
607 | (window-config (current-window-configuration)))
608 | (eclim/with-results doc ("java_element_doc"
609 | ("-p" (eclim-project-name))
610 | "-f"
611 | ("-l" (- (cdr bounds) (car bounds)))
612 | ("-o" (save-excursion
613 | (goto-char (car bounds))
614 | (eclim--byte-offset))))
615 |
616 | (pop-to-buffer "*java doc*")
617 | (use-local-map eclim-java-show-documentation-map)
618 |
619 | (eclim--java-show-documentation-and-format doc)
620 |
621 | (message (substitute-command-keys
622 | (concat
623 | "\\[forward-button] - move to next link, "
624 | "\\[backward-button] - move to previous link, "
625 | "\\[eclim-quit-window] - quit")))))
626 |
627 | (message "No element found at point."))))
628 |
629 |
630 | (defun eclim--java-show-documentation-and-format (doc &optional add-to-history)
631 | (make-local-variable 'eclim-java-show-documentation-history)
632 | (setq eclim-java-show-documentation-history
633 | (if add-to-history
634 | (push (buffer-substring (point-min) (point-max))
635 | eclim-java-show-documentation-history)))
636 |
637 | (erase-buffer)
638 | (insert (cdr (assoc 'text doc)))
639 |
640 | (let ((links (cdr (assoc 'links doc)))
641 | link placeholder text href)
642 | (dotimes (i (length links))
643 | (setq link (aref links i))
644 | (setq text (cdr (assoc 'text link)))
645 | (setq href (cdr (assoc 'href link)))
646 | (setq placeholder (format "|%s[%s]|" text i))
647 | (goto-char (point-min))
648 | (while (search-forward placeholder nil t)
649 | (replace-match text)
650 | (make-text-button (match-beginning 0)
651 | (+ (match-beginning 0) (length text))
652 | 'action 'eclim-java-show-documentation-follow-link
653 | 'url href))))
654 |
655 | (when add-to-history
656 | (goto-char (point-max))
657 | (insert "\n\n")
658 | (insert-text-button "back" 'action 'eclim--java-show-documentation-go-back))
659 |
660 | (goto-char (point-min)))
661 |
662 |
663 | (defun eclim-java-show-documentation-follow-link (link)
664 | (interactive)
665 | (let ((url (button-get link 'url)))
666 | (if (string-match "^eclipse-javadoc" url)
667 | (eclim/with-results doc ("java_element_doc"
668 | ("-u" url))
669 | (eclim--java-show-documentation-and-format doc t))
670 |
671 | (if (string-match "^\.\." url)
672 | (let* ((doc-root-vars '(eclim-java-documentation-root
673 | eclim-java-android-documentation-root))
674 | (path (replace-regexp-in-string "^[./]+" "" url))
675 | (fullpath (some (lambda (var)
676 | (let ((fullpath (concat (symbol-value var)
677 | "/"
678 | path)))
679 | (if (file-exists-p (replace-regexp-in-string
680 | "#.+"
681 | ""
682 | fullpath))
683 | fullpath)))
684 | doc-root-vars)))
685 | (if fullpath
686 | (browse-url (concat "file://" fullpath))
687 |
688 | (message (concat "Can't find the root directory for this file: %s. "
689 | "Are the applicable variables set properly? (%s)")
690 | path
691 | (mapconcat (lambda (var)
692 | (symbol-name var))
693 | doc-root-vars ", "))))
694 |
695 | (message "There is no handler for this kind of url yet. Implement it! : %s"
696 | url)))))
697 |
698 |
699 | (defun eclim--java-show-documentation-go-back (link)
700 | (erase-buffer)
701 | (insert (pop eclim-java-show-documentation-history))
702 | (goto-char (point-min)))
703 |
704 | (provide 'eclim-java)
705 |
--------------------------------------------------------------------------------