From 33c0ef4d5272f15ed0524cbd950bac1bac773051 Mon Sep 17 00:00:00 2001 From: Ovidiu Gheorghioiu Date: Sun, 2 Aug 2015 11:17:00 -0700 Subject: [PATCH 01/11] Make `eclim-problems' correct work properly from the problems buffer. It was not selecting the source code buffer. --- eclim-problems.el | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/eclim-problems.el b/eclim-problems.el index b25124a..d614996 100644 --- a/eclim-problems.el +++ b/eclim-problems.el @@ -201,10 +201,19 @@ (eclim--problem-goto-pos p))) (defun eclim-problems-correct () + "Pops up a suggestion for the current correction. This can be +invoked in either the problems buffer or a source code buffer." (interactive) (let ((p (eclim--problems-get-current-problem))) - (if (not (string-match "\\.\\(groovy\\|java\\)$" (cdr (assoc 'filename p)))) - (error "Not a Java or Groovy file. Corrections are currently supported only for Java or Groovy") + (unless (string-match "\\.\\(groovy\\|java\\)$" (cdr (assoc 'filename p))) + (error "Not a Java or Groovy file. Corrections are currently supported only for Java or Groovy")) + (if (eq major-mode 'eclim-problems-mode) + (let ((p-buffer (find-file-other-window (assoc-default 'filename p)))) + (with-selected-window (get-buffer-window p-buffer t) + ;; Intentionally DON'T save excursion. Often times we need edits. + (eclim--problem-goto-pos p) + (eclim-java-correct (cdr (assoc 'line p)) (eclim--byte-offset)))) + ;; source code buffer (eclim-java-correct (cdr (assoc 'line p)) (eclim--byte-offset))))) (defmacro eclim--with-problems-list (problems &rest body) From e945a76168a71d41270c7b429ff82f0d3cfc153c Mon Sep 17 00:00:00 2001 From: Ovidiu Gheorghioiu Date: Sun, 2 Aug 2015 11:29:24 -0700 Subject: [PATCH 02/11] Replace distracting "refreshing File" message with small modeline indicator. When we save, a lot of different messages zip by in the minibuffer and create a distraction. Instead, a '*' in the modeline right by the error counts that are about to be updated is discreet but noticeable. Removed an extraneous space. --- eclim-problems.el | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/eclim-problems.el b/eclim-problems.el index d614996..d6de590 100644 --- a/eclim-problems.el +++ b/eclim-problems.el @@ -68,6 +68,7 @@ (define-key eclim-mode-map (kbd "C-c C-e o") 'eclim-problems-open) (defvar eclim--problems-list nil) +(defvar eclim--problems-refreshing nil) ;; Set to true while refreshing probs. (defvar eclim--problems-filter nil) ;; nil -> all problems, w -> warnings, e -> errors (defvar eclim--problems-filefilter nil) ;; should filter by file name @@ -222,14 +223,14 @@ invoked in either the problems buffer or a source code buffer." it asynchronously." (let ((res (gensym))) `(when eclim--problems-project - (when (not (minibuffer-window-active-p (minibuffer-window))) - (message "refreshing... %s " (current-buffer))) + (setq eclim--problems-refreshing t) (eclim/with-results-async ,res ("problems" ("-p" eclim--problems-project) (when (string= "e" eclim--problems-filter) '("-e" "true"))) (loop for problem across ,res do (let ((filecell (assq 'filename problem))) (when filecell (setcdr filecell (file-truename (cdr filecell)))))) (setq eclim--problems-list ,res) (let ((,problems ,res)) + (setq eclim--problems-refreshing nil) ,@body))))) (defun eclim-problems-buffer-refresh () @@ -489,8 +490,9 @@ is convenient as it lets the user navigate between errors using (defun eclim-problems-modeline-string () "Returns modeline string with additional info about problems for current file" - (concat (format " : %s/%s" + (concat (format ": %s/%s" (eclim--count-current-errors) - (eclim--count-current-warnings)))) + (eclim--count-current-warnings)) + (when eclim--problems-refreshing "*"))) (provide 'eclim-problems) From 7c66c2a44c6d73bd932217b386a9341ec4d9df1b Mon Sep 17 00:00:00 2001 From: Ovidiu Gheorghioiu Date: Sun, 2 Aug 2015 11:58:30 -0700 Subject: [PATCH 03/11] Some love for the compilation-problems buffer. This version fits my typical compilation-based workflow better than the problems buffer. * Fix an errant call to eclim--project-dir. * Fix the counting of errors/warnings. Errors apparently get 'warning = ":json-false", not nil. * Store the project-name in the buffer. This allows it to be called from within itself ("refreshed") easily. * Bind the standard "g" key to do just that. Otherwise compilation-mode will happily start a new shell make that confuses eclipse (in my config). * Use the standard "process status" part of the modeline to display "refreshing" / results, the latter in red if there are errors. * The local variables header text is unnecessary if we set it programatically. * Instead, put the result text at beginning as well and tag with time. * Try to not reposition the buffer upon refresh if possible. Note that I don't think we should make compilation-buffer refresh automatically when we get an updated list, even though it's quite easy. Other compilations stay unchanged until manually refreshed and uniformity is nice. --- eclim-problems.el | 72 +++++++++++++++++++++++++++++++++-------------- 1 file changed, 51 insertions(+), 21 deletions(-) diff --git a/eclim-problems.el b/eclim-problems.el index d6de590..34177b4 100644 --- a/eclim-problems.el +++ b/eclim-problems.el @@ -449,28 +449,58 @@ is convenient as it lets the user navigate between errors using `next-error' (\\[next-error])." (interactive) (lexical-let ((filecol-size (eclim--problems-filecol-size)) - (project-directory (concat (eclim--project-dir buffer-file-name) "/")) - (compil-buffer (get-buffer-create eclim--problems-compilation-buffer-name))) + (project-directory (concat (eclim--project-dir) "/")) + (compil-buffer (get-buffer-create eclim--problems-compilation-buffer-name)) + (project-name (eclim-project-name))) ; To store it in buffer. + + (with-current-buffer compil-buffer + (setq default-directory project-directory) + (setq mode-line-process + (concat ": " (propertize "refreshing" + 'face 'compilation-mode-line-run)))) + ;; Remember that the part below is asynchronous. This can be tricky. (eclim--with-problems-list problems - (with-current-buffer compil-buffer - (setq default-directory project-directory) - (setq buffer-read-only nil) - (erase-buffer) - (insert (concat "-*- mode: compilation; default-directory: " - project-directory - " -*-\n\n")) - (let ((errors 0) (warnings 0)) - (loop for problem across (eclim--problems-filtered) - do (eclim--insert-problem-compilation problem filecol-size project-directory) - (cond ((assoc-default 'warning problem) - (setq warnings (1+ warnings))) - (t - (setq errors (1+ errors))))) - (insert (format "\nCompilation results: %d errors and %d warnings." - errors warnings))) - (compilation-mode)) - (display-buffer compil-buffer 'other-window)))) - + (let (saved-user-pos) + (with-current-buffer compil-buffer + (buffer-disable-undo) + (setq buffer-read-only nil) + (setq saved-user-pos (point)) + (erase-buffer) + (let ((errors 0) (warnings 0)) + (loop for problem across (eclim--problems-filtered) do + (eclim--insert-problem-compilation + problem filecol-size project-directory) + (if (eq t (assoc-default 'warning problem)) ; :json-false, WTH + (setq warnings (1+ warnings)) + (setq errors (1+ errors)))) + (let ((msg (format + "Compilation results: %d errors, %d warnings [%s].\n" + errors warnings (current-time-string)))) + (insert "\n" msg) + (goto-char (point-min)) + (insert msg "\n")) + (compilation-mode) + ;; The above killed local variables, so recover our lexical-lets + (setq default-directory project-directory) + (setq eclim-project-name project-name) + ;; Remap the very dangerous "g" command :) A make -k in some of + ;; my projects would throw Eclipse off-balance by cleaning .classes. + ;; May look funky, but it's safe. + (local-set-key "g" 'eclim-problems-compilation-buffer) + + (setq mode-line-process + (concat ": " + (propertize (format "%d/%d" errors warnings) + 'face (when (> errors 0) + 'compilation-mode-line-fail)))))) + ;; Sometimes, buffer was already current. Note outside with-current-buf. + (unless (eq compil-buffer (current-buffer)) + (display-buffer compil-buffer 'other-window)) + (with-selected-window (get-buffer-window compil-buffer t) + (when (< saved-user-pos (point-max)) + (goto-char saved-user-pos))))))) + + (defun eclim--insert-problem-compilation (problem filecol-size project-directory) (let ((filename (first (split-string (assoc-default 'filename problem) project-directory t))) (description (assoc-default 'message problem)) From c5465f1797e0778a1609c122bad64418681bb6de Mon Sep 17 00:00:00 2001 From: Ovidiu Gheorghioiu Date: Sun, 2 Aug 2015 12:13:21 -0700 Subject: [PATCH 04/11] Add simple commands to move back and forth between problems in current file. These are entirely cursor based rather than dependent on position in the problems-buffer. They are intended to be navigational aids in the absence of Eclipse's scroll bar indicators, which we can't do in emacs. Otherwise, errors in long files can be hard to find. --- eclim-problems.el | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/eclim-problems.el b/eclim-problems.el index 34177b4..507afc1 100644 --- a/eclim-problems.el +++ b/eclim-problems.el @@ -517,6 +517,40 @@ is convenient as it lets the user navigate between errors using (length (eclim--filter-problems "w" t (buffer-file-name (current-buffer)) eclim--problems-list))) +(defun eclim-problems-next-same-file (&optional up) + "Moves to the next problem in the current file, with wraparound. If UP +or prefix arg, moves to previous instead; see `eclim-problems-prev-same-file'." + (interactive "P") + ;; This seems pretty inefficient, but it's fast enough. Would be even + ;; more inefficient if we didn't assume problems were sorted. + (let ((problems-file + (eclim--filter-problems nil t (buffer-file-name (current-buffer)) + eclim--problems-list)) + (pass-line (line-number-at-pos)) + (pass-col (+ (current-column) (if up 0 1))) + (first-passed nil) (last-not-passed nil)) + (when (= 0 (length problems-file)) (error "No problems in this file")) + (loop for p across problems-file until first-passed do + (let ((line (assoc-default 'line p)) + (col (assoc-default 'column p))) + (if (or (> line pass-line) + (and (= line pass-line) (> col pass-col))) + (setq first-passed p) + (setq last-not-passed p)))) + (eclim--problem-goto-pos + (or + (if up last-not-passed first-passed) + (when up (message "Moved past first error, continuing to last") + (elt problems-file (- (length problems-file) 1))) ; Ugh, vector + (progn (message "Moved past last error, continuing to first") + (elt problems-file 0)))))) + +(defun eclim-problems-prev-same-file () + "Moves to the previous problem in the same file, with wraparound." + (interactive) + (eclim-problems-next-same-file t)) + + (defun eclim-problems-modeline-string () "Returns modeline string with additional info about problems for current file" From 14094e0a8ad6b4c0ee4eae50273c9878dd0a3a96 Mon Sep 17 00:00:00 2001 From: Ovidiu Gheorghioiu Date: Sun, 2 Aug 2015 14:32:52 -0700 Subject: [PATCH 05/11] Minor: add follow-link=t to javadoc text buttons, allowing left click. This helps on Macbook. It's consistent with help-mode, for example. --- eclim-java.el | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/eclim-java.el b/eclim-java.el index f9b9e16..5894bfd 100644 --- a/eclim-java.el +++ b/eclim-java.el @@ -657,13 +657,14 @@ much faster than running mvn test -Dtest=TestClass#method." (replace-match text) (make-text-button (match-beginning 0) (+ (match-beginning 0) (length text)) + 'follow-link t 'action 'eclim-java-show-documentation-follow-link 'url href)))) (when add-to-history (goto-char (point-max)) (insert "\n\n") - (insert-text-button "back" 'action 'eclim--java-show-documentation-go-back)) + (insert-text-button "back" 'follow-link t 'action 'eclim--java-show-documentation-go-back)) (goto-char (point-min))) From 6c577593eeb396479ff7db9662cca8efdff00d82 Mon Sep 17 00:00:00 2001 From: Ovidiu Gheorghioiu Date: Fri, 7 Aug 2015 10:11:37 -0700 Subject: [PATCH 06/11] company-eclim: make ignore-case=nil filter candidates to match prefix. Eclipse changed the relevant option to apply to camelCase only, and when completing a variable we get a *huge* selection of all classes that begin with that prefix. Try completing a variable that starts with "re" for example. Eclimd's sorting is not particularly good, either -- in Eclipse at least we get the lowercase matches first. Company-mode's view is that the backend is responsible for ensuring the prefix matches properly. --- company-emacs-eclim.el | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/company-emacs-eclim.el b/company-emacs-eclim.el index 27c9d9b..0cf2fb2 100644 --- a/company-emacs-eclim.el +++ b/company-emacs-eclim.el @@ -80,7 +80,11 @@ (mapcar (lambda (candidate) (annotate (without-redundant-prefix candidate))) - (eclim--completion-candidates))))) + ;; Company says backend is responsible for filtering prefix case. + (if company-emacs-eclim-ignore-case + (eclim--completion-candidates) + (remove-if-not #'(lambda(str) (string-prefix-p prefix str)) + (eclim--completion-candidates))))))) (defun company-emacs-eclim--annotation (candidate) (let ((str (get-text-property 0 'eclim-meta candidate))) From a562831ee70ceda6baf0b59e415642c25b87dc6c Mon Sep 17 00:00:00 2001 From: Ovidiu Gheorghioiu Date: Sun, 9 Aug 2015 13:39:14 -0700 Subject: [PATCH 07/11] Allow completion after an open bracket or template bracket. This matches the way it works in Eclipse -- complete the open call / type rather than going for a new identifier. beginning-of-thing does not by itself cross the open bracket. --- eclim-completion.el | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/eclim-completion.el b/eclim-completion.el index 7e7f65e..060caf1 100644 --- a/eclim-completion.el +++ b/eclim-completion.el @@ -161,6 +161,15 @@ buffer." (case major-mode ((java-mode javascript-mode js-mode ruby-mode groovy-mode php-mode c-mode c++-mode scala-mode) (progn + ;; Allow completion after open bracket. Eclipse/eclim do. + (when (or (eq ?\( (char-before)) + ;; Template? Technically it could be a less-than sign + ;; but it's unlikely the user completes there and + ;; no particular harm done. + (and (eq ?\< (char-before)) + (memq major-mode + '(java-mode c++-mode goovy-mode)))) + (backward-char 1)) (ignore-errors (beginning-of-thing 'symbol)) ;; Completion candidates for annotations don't include '@'. (when (eq ?@ (char-after)) From 0b39946228d487e9ba1da6ed1d3a2b573ce0522b Mon Sep 17 00:00:00 2001 From: Ovidiu Gheorghioiu Date: Fri, 14 Aug 2015 17:18:33 -0700 Subject: [PATCH 08/11] Make problems refresh after an automated correction. Otherwise it's strange to accept a correction, see the buffer save & refresh, but the error highlight persist. I don't like automatic problem updates always (tried it!), but in this case I'm actively expecting Eclipse to fix one. Minor: remove an extraneous space from Eclim modeline. "Eclim: 0/13" looks nicer to me than "Eclim : 0/13", and modeline space is precious. --- eclim-java.el | 5 ++++- eclim.el | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/eclim-java.el b/eclim-java.el index 5894bfd..1f16d23 100644 --- a/eclim-java.el +++ b/eclim-java.el @@ -603,7 +603,10 @@ much faster than running mvn test -Dtest=TestClass#method." "-f" ("-l" line) ("-o" offset) - ("-a" choice))) + ("-a" choice)) + ;; Problem updates can be distracting, but here the user was + ;; actively trying to fix one. + (eclim--problems-update-maybe)) (message "No automatic corrections found. Sorry"))))) (defun eclim-java-show-documentation-for-current-element () diff --git a/eclim.el b/eclim.el index 8a52de5..0f4c7cd 100644 --- a/eclim.el +++ b/eclim.el @@ -544,6 +544,6 @@ the use of eclim to java and ant files." (defun eclim-modeline-string () (when eclim-mode - (concat " Eclim " (eclim-problems-modeline-string)))) + (concat " Eclim" (eclim-problems-modeline-string)))) (provide 'eclim) From f684c86046e5576f8172ceff8737cc190517e47a Mon Sep 17 00:00:00 2001 From: Ovidiu Gheorghioiu Date: Mon, 17 Aug 2015 21:56:07 -0700 Subject: [PATCH 09/11] Fix eclim-java-implement; it now uses Eclipse rather than emacs insertion. Eclipse is much better at it than we can hope to be. For example: * It imports the return type correctly, which eclim doesn't give us fully * It imports in the right place syntactically, even when called from within a method * It looks up argument names in javadoc if available [note] * Is much less fragile [note] which means that many times the reason we used yasnippet at all becomes unnecessary, since the javadoc names are something the user typically wants to keep in overrides. This whole thing started as a quest to expunge yasnippet which was being used unconditionally. --- eclim-java.el | 180 +++++++++++++++++++++++++++++++++++--------------- 1 file changed, 127 insertions(+), 53 deletions(-) diff --git a/eclim-java.el b/eclim-java.el index a50ce63..bff8101 100644 --- a/eclim-java.el +++ b/eclim-java.el @@ -251,7 +251,7 @@ has been found." (n (read-string (concat "Rename " (cdr i) " to: ") (cdr i)))) (eclim/with-results res ("java_refactor_rename" "-p" "-e" "-f" ("-n" n) ("-o" (car i)) ("-l" (length (cdr i)))) - (if (stringp res) (error res)) + (if (stringp res) (error "%s" res)) (loop for (from to) in (mapcar (lambda (x) (list (assoc-default 'from x) (assoc-default 'to x))) res) do (when (and from to) (kill-buffer (find-buffer-visiting from)) @@ -402,10 +402,6 @@ matters for buffers containing non-ASCII characters)." (cons (if position (point) (eclim--byte-offset)) (buffer-substring-no-properties start end)))))) -(defun eclim--java-package-components (package) - "Returns the components of a Java package statement." - (split-string package "\\.")) - (defun eclim--java-current-package () "Returns the package for the class in the current buffer." (save-excursion @@ -470,57 +466,135 @@ sorts import statements. " (eclim-java-import-organize (mapcar (lambda (imports) (eclim--completing-read "Import: " (append imports '()))) res))))))) -(defun format-type (type) - (cond ((null type) nil) - ((listp (first type)) - (append (list "<") (rest (mapcan (lambda (type) (append (list ", ") (format-type type))) (first type))) (list ">") - (format-type (rest type)))) - (t (cons (let ((type-name (symbol-name (first type)))) - (when (string-match "\\(.*\\.\\)?\\(.*\\)" type-name) - (match-string 2 type-name))) - (format-type (rest type)))))) + +(defun eclim--signature-has-keyword (sig java-keyword) + "Returns true if a method signature SIG has the keyword JAVA-KEYWORD." + ;; \_< is beginning of identifier E.g. don't match do_abstract". + (string-match-p (format "\\_<%s\\_>" java-keyword) sig)) + + +(defun eclim--colorize-signature (sig) + "Minimal colorization for a method signature that we offer for completion, +so the essential bits stand out from the block of text that ido presents. +Keep this minimal: more highlighting could easily make things worse not better." + (save-match-data + (mapc #'(lambda(re-g-f) ;; expecting single match per RE + (when (string-match (elt re-g-f 0) sig) + (setq sig (replace-match + (propertize (match-string (elt re-g-f 1) sig) + 'face (elt re-g-f 2)) + nil nil sig (elt re-g-f 1))))) + '(("\\_<\\(class\\|interface\\)\\s +\\([[:alnum:]_]+\\_>\\)" + 2 font-lock-type-face) + ("\\_<\\([[:alnum:]_]+\\)(" 1 font-lock-function-name-face) + ("all [[:digit:]]+ \\w+ methods" 0 font-lock-function-name-face)))) + sig) + (defun eclim-java-implement (&optional name) - "Lets the user select from a list of methods to -implemnt/override, then inserts a skeleton for the chosen -method." + "Implement or override methods from parents of the class, prompting the +user to select with a completing read (even if one, as confirmation). If +NAME was specified programmatically, filters for that name (strict, +although only on method name not arguments) and if only one choice +implement it without prompting. The actual change is done by Eclipse +and will be close to point although not necessarily at it (e.g. if in a +sub block)." (interactive) - (eclim/with-results response ("java_impl" "-p" "-f" "-o") - (cl-flet ((join (glue items) - (cond ((null items) "") - ((= 1 (length items)) (format "%s" (first items))) - (t (reduce (lambda (a b) (format "%s%s%s" a glue b)) items)))) - (format-type (type) - (cond ((null type) nil) - ((listp (first type)) - (append (list "<") (rest (mapcan (lambda (type) (append (list ", ") (format-type type))) (first type))) (list ">") - (format-type (rest type)))) - (t (cons (let ((type-name (symbol-name (first type)))) - (when (string-match "\\(.*\\.\\)?\\(.*\\)" type-name) - (let ((package (match-string 1 type-name)) - (class (match-string 2 type-name))) - (eclim-java-import (concat package class)) - class))) - (format-type (rest type))))))) - (let* ((methods (remove-if-not (lambda (m) (or (null name) - (string-match name m))) - (mapcar (lambda (x) (replace-regexp-in-string "[ \n\t]+" " " x)) - (apply 'append - (mapcar (lambda (x) (append (assoc-default 'methods x) nil)) - (assoc-default 'superTypes response)))))) - (method (if (= 1 (length methods)) (first methods) - (eclim--completing-read "Signature: " methods))) - (sig (eclim--java-parse-method-signature method)) - (ret (assoc-default :return sig))) - (yas/expand-snippet (format "@Override\n%s %s(%s) {$0}" - (apply #'concat - (join " " (remove-if-not (lambda (m) (find m '(public protected private void))) (subseq ret 0 (1- (length ret))))) - " " - (format-type (remove-if (lambda (m) (find m '(abstract public protected private ))) ret))) - (assoc-default :name sig) - (join ", " (loop for arg in (remove-if #'null (assoc-default :arglist sig)) - for i from 0 - collect (format "%s ${arg%s}" (apply #'concat (format-type (assoc-default :type arg))) i))))))))) + (eclim/with-results list-response ("java_impl" "-p" "-f" "-o") + (let* ((supertypes (assoc-default 'superTypes list-response)) + ;; "Choices" are lists of user-friendly method names. We want to + ;; present interfaces/abstract first, otherwise Object can barge in. + (choices nil) (choices-opt nil) (choices-last nil) + ;; Maps a choice to a (supertype method1 method2...), needed + ;; when we request eclim to implement that method. + (choice-data (make-hash-table :test 'equal))) + (loop + for super-entry across supertypes do + (let* ((package (assoc-default 'packageName super-entry)) + (super-sig (assoc-default 'signature super-entry)) + ;; Erase type arguments. This looks like "class List". + (friendly-super (replace-regexp-in-string "<[^<]*>" "" super-sig)) + (full-super (concat package "." + (replace-regexp-in-string "^\\w+ " "" + friendly-super))) + (is-interface (eclim--signature-has-keyword + super-sig "interface")) + (methods (assoc-default 'methods super-entry)) + (required-methods nil)) ;; Eclim names here + (loop + for method across methods + ;; Skip if specified name doesn't match. + if (or (null name) + (string-match-p (format "\\_<%s(" (regexp-quote name)) method)) + do + ;; This regexp stuff is how vim (and thus eclim) does it. Nothing + ;; fancy. If it breaks, Google eclim/java/impl.vim for changes. + (let ((name-for-eclim + ;; Remove keywords and return type. \_< begins identifier. + (replace-regexp-in-string "^\\s *[^(]*\\(\\_<[[:alnum:]_]+(\\)" + "\\1" + ;; Remove any and all type parameters. + (replace-regexp-in-string "<[^<]*>" "" method))) + ;; For the user, we have very different requirements. I like + ;; knowing public and abstract, and the return type. I hate + ;; packages -- I'm already implementing this class so I know. + (friendly-name + ;; Packages are non-trivial to find (think Map.Entry) but + ;; if we stop at the first capitalized portion we're okay. + (replace-regexp-in-string "\\_<[[:lower:]][[:alnum:]_]+\\." + "" method)) + (is-required (or is-interface (eclim--signature-has-keyword + method "abstract")))) + (let ((choice (format "%s [%s]" friendly-name friendly-super)) + (data (list full-super name-for-eclim))) + ;; This is probably overkill but what if our package erasing + ;; resulted in duplicates? Use full name then. As in, really full. + (when (gethash choice choice-data) + (setq choice (format "%s [%s]" name-for-eclim full-super))) + (cond (is-required (push choice choices)) + ((member full-super '("java.lang.Object")) ; others like it? + (push choice choices-last)) + (t (push choice choices-opt))) + (puthash choice (list full-super name-for-eclim) choice-data) + (when is-required (push name-for-eclim required-methods))))) + ;; Since we don't allow multiple selection like Eclipse / vim, let's + ;; provide for the cases that matter. Note that full non-abstract + ;; overrides are typically a use case for *delegates*. + (when (> (length required-methods) 1) ;; 1 method already there + (let ((choice + (format "" + (length required-methods) + (cond (is-interface "missing") (name) (t "abstract")) + friendly-super)) + (data (cons full-super (reverse required-methods)))) + (push choice choices) ;; I'll not worry about conflict here. + (puthash choice data choice-data))))) + ;; Keep inital order, except for our tweaks. + (setq choices (append (nreverse choices) (reverse choices-opt) + (reverse choices-last))) + (unless choices + (if name (error "No such unimplemented method: %s" name) ;most likely + (error "No candidates to implement"))) ;; Rare, given Object ancestor. + + ;; Ask user even if only one choice, for confirmation. Otherwise it's + ;; possible to not even notice the change from a bad key combo. Unless + ;; we were called programmatically a for specific method. + (let ((choice + (if (and name (eq 1 (length choices))) + (first choices) + (funcall eclim-interactive-completion-function + "Implement: " + (mapcar #'eclim--colorize-signature choices) + nil t)))) ; require match + (setq choice (substring-no-properties choice)) ; uncolorize + (let* ((eclim-data (gethash choice choice-data)) + (super (car eclim-data)) (methods (cdr eclim-data)) + (methods-str (json-encode methods))) + (eclim/with-results impl-result ("java_impl" "-p" "-f" "-o" + ("-s" super) ("-m" methods-str)) + ;; eclim should give us a smaller list if it did something. But + ;; it's probably not worth an error in case this changes. + (revert-buffer t t t))))))) (defun eclim-package-and-class () (let ((package-name (eclim--java-current-package)) From 308587d1cfdb7af4ee433a8359d6cb23cf9e411d Mon Sep 17 00:00:00 2001 From: Ovidiu Gheorghioiu Date: Mon, 24 Aug 2015 23:05:43 -0700 Subject: [PATCH 10/11] Problem highlights: add variable that causes highlights to be suppressed. This allows the user to easily set up commands that disable highlights (e.g. temporarily, while writing a bunch of new code), globally or buffer-local (more common in my usage). Unlike a problem filter, the problem counts remain and the problems are navigable. Rename eclim--problems-clear-highlights to indicate it's not private. --- eclim-problems.el | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/eclim-problems.el b/eclim-problems.el index 507afc1..c31e63c 100644 --- a/eclim-problems.el +++ b/eclim-problems.el @@ -33,6 +33,17 @@ :type '(choice (const :tag "Off" nil) (const :tag "On" t))) +(defcustom eclim-problems-suppress-highlights nil + "When set, error and warning highlights are disabled in source files, +although counts are printed and they remain navigable. This is +designed to be made buffer-local (by user, not eclim) most of the +time, but it also works globally." + :group 'eclim-problems + :type '(choice (const :tag "Allow" nil) + (const :tag "Suppress" t) + (sexp :tag "Suppress when" + :value (lambda() 'for-example buffer-read-only)))) + (defface eclim-problems-highlight-error-face '((t (:underline "red"))) "Face used for highlighting errors in code" @@ -152,17 +163,27 @@ (overlay-put highlight 'category 'eclim-problem) (overlay-put highlight 'kbd-help (assoc-default 'message problem)))))) -(defun eclim--problems-clear-highlights () + +(defun eclim-problems-clear-highlights () + "Clears all eclim problem highlights in the current buffer. This is temporary +until the next refresh." + (interactive) (remove-overlays nil nil 'category 'eclim-problem)) + (defun eclim-problems-highlight () + "Inserts the currently active problem highlights in the current buffer, +if `eclim-problems-suppress-highlights' allows it." (interactive) (when (eclim--accepted-p (buffer-file-name)) (save-restriction (widen) - (eclim--problems-clear-highlights) - (loop for problem across (remove-if-not (lambda (p) (string= (assoc-default 'filename p) (buffer-file-name))) eclim--problems-list) - do (eclim--problems-insert-highlight problem))))) + (eclim-problems-clear-highlights) + (unless (if (functionp eclim-problems-suppress-highlights) + (funcall eclim-problems-suppress-highlights) + eclim-problems-suppress-highlights) + (loop for problem across (remove-if-not (lambda (p) (string= (assoc-default 'filename p) (buffer-file-name))) eclim--problems-list) + do (eclim--problems-insert-highlight problem)))))) (defadvice find-file (after eclim-problems-highlight-on-find-file activate) (eclim-problems-highlight)) From ac9bfff180bec5214ee0f7b42af24af042fd1001 Mon Sep 17 00:00:00 2001 From: Ovidiu Gheorghioiu Date: Mon, 24 Aug 2015 23:16:14 -0700 Subject: [PATCH 11/11] Add a function to find files by name. I'm using this to help Emacs find the file from stack traces, which are basename only, via compilation-find-file advice. --- eclim.el | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/eclim.el b/eclim.el index 4029d01..20075c3 100644 --- a/eclim.el +++ b/eclim.el @@ -435,6 +435,26 @@ FILENAME is given, return that file's project name instead." hits)) t))) +(defun eclim-find-file-path-strict (filename &optional project directory) + "Locates a file (basename) in Eclipse. If PROJECT is a string, +searches only that project; if nil, the project of the current +file. If t, searches all Eclipse projects. If DIRECTORY is +specified, returns only files that are under that +directory. Returns a list of matching absolute paths; possibly +empty. This can be used to help resolve exception stack traces, +for example." + (let* ((results (apply #'eclim--call-process "locate_file" + "-p" (regexp-quote filename) + (if (eq project t) + (list "-s" "workspace") + (list "-s" "project" "-n" + (or project (eclim-project-name)))))) + (paths (mapcar #'(lambda(hit) (assoc-default 'path hit)) results))) + (if directory + (remove-if-not #'(lambda (f) (file-in-directory-p f directory)) paths) + paths))) + + ;;;###autoload (defun eclim/workspace-dir () (eclim--call-process "workspace_dir")) @@ -474,7 +494,7 @@ FILENAME is given, return that file's project name instead." "List of regular expressions that are matched against filenames to decide if eclim should be automatically started on a particular file. By default all files part of a project managed -by eclim can be accepted (see `eclim--accepted-filename' for more +by eclim can be accepted (see `eclim--accepted-filename-p' for more information). It is nevertheless possible to restrict eclim to some files by changing this variable. For example, a value of (\"\\\\.java\\\\'\" \"build\\\\.xml\\\\'\") can be used to restrict