├── .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 | [![Build Status](https://travis-ci.org/senny/emacs-eclim.svg?branch=master)](https://travis-ci.org/senny/emacs-eclim) 3 | [![MELPA](http://melpa.org/packages/emacs-eclim-badge.svg)](http://melpa.org/#/emacs-eclim) 4 | [![MELPA Stable](http://stable.melpa.org/packages/emacs-eclim-badge.svg)](http://stable.melpa.org/#/emacs-eclim) 5 | 6 | ## Development has moved to https://github.com/emacs-eclim/emacs-eclim !!! 7 | 8 | ## Overview 9 | 10 | [![Join the chat at https://gitter.im/senny/emacs-eclim](https://badges.gitter.im/Join%20Chat.svg)](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 | --------------------------------------------------------------------------------