Skip to content

Commit

Permalink
Merge pull request #19 from tonini/complete-functionality
Browse files Browse the repository at this point in the history
Complete functionality
  • Loading branch information
tonini committed Nov 22, 2014
2 parents 75a9b4c + 2cdc09d commit eff2407
Show file tree
Hide file tree
Showing 9 changed files with 318 additions and 99 deletions.
8 changes: 7 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
# Changelog

## v0.7.0-dev
## v0.7.0

### Enhancements

* Implement alchemist-company backend.
* Separate alchemist-complete functionality.
* Optimize the building of the complete list.
* Replace `shell-command-to-string` with background processes to improve.
performance and remove emacs workflow distruption.
* Add project directory check to save-hook (thx @pragdave)
* Remove dispensable informations in compilation buffers

## v0.6.0
Expand Down
3 changes: 2 additions & 1 deletion Cask
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@

(development
(depends-on "f")
(depends-on "pkg-info"))
(depends-on "pkg-info")
(depends-on "company"))
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
- [Execute](#execute-functions)
- [Inline Docs](#inline-documentation)
- [Keymap](#alchemist-help-minor-mode-keymap)
- [Auto-completion](#auto-completion)
- [Hooks](#hooks)
- [Modeline](#modeline)
- [Keymap](#keymap)
Expand Down Expand Up @@ -210,6 +211,13 @@ What does that mean? It means no matter which Elixir version is currently
installed on the system, the documentation you get by `alchemist` is the same
`IEx` would deliver.

By default the ANSI color formated documentation is disabled. You can enable it
by setting the following variable to `t`.

```el
(setq alchemist-help-ansi-color t)
```

<table>
<tr>
<th>Command (For the <code>M-x</code> prompt.)</th>
Expand Down Expand Up @@ -278,6 +286,17 @@ You're always be able to continue to search inside the `*elixir help*` buffer.
</tr>
</table>

## Auto-completion

Alchemist users are advised to use
[company-mode](http://company-mode.github.io/) to enable auto-completion inside
of Elixir source code.

Alchemist enables a company-mode elixir backend by default if company-mode is
installed.

![Alchemist Company](logo/alchemist-company.gif)

## Hooks

There is a `after-save-hook` called `alchemist-hooks--test-on-save` which runs
Expand Down
45 changes: 45 additions & 0 deletions alchemist-company.el
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
;;; alchemist-company.el --- company-mode completion back-end for Elixir -*- lexical-binding: t -*-

;; Copyright © 2014 Samuel Tonini

;; Author: Samuel Tonini <[email protected]

;; This file is not part of GNU Emacs.

;; This program is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.

;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.

;;; Commentary:

;; company-mode completion back-end for Elixir

;;; Code:

(require 'company)

(defun alchemist-company (command &optional arg &rest ignored)
"`company-mode' completion back-end for Elixir."
(interactive (list 'interactive))
(case command
(interactive (company-begin-backend 'alchemist-company))
(init (when (eq major-mode 'elixir-mode)))
(prefix (and (eq major-mode 'elixir-mode)
(alchemist-help--exp-at-point)))
(candidates (cons :async
(lambda (cb) (alchemist-complete-candidates arg cb))))))

(add-to-list 'company-backends 'alchemist-company)

(provide 'alchemist-company)

;;; alchemist-company.el ends here
129 changes: 129 additions & 0 deletions alchemist-complete.el
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
;;; alchemist-complete.el --- -*- lexical-binding: t -*-

;; Copyright © 2014 Samuel Tonini

;; Author: Samuel Tonini <[email protected]

;; This file is not part of GNU Emacs.

;; This program is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.

;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.

;;; Commentary:

;;

;;; Code:

(defun alchemist-complete--clean-functions (candidates)
(mapcar (lambda (c) (replace-regexp-in-string "/[0-9]$" "" c)) candidates))

(defun alchemist-complete--concat-prefix-with-functions (prefix functions &optional add-prefix)
(let* ((prefix (mapconcat 'concat (butlast (split-string prefix "\\.") 1) "."))
(candidates (mapcar (lambda (c) (concat prefix "." c)) (cdr functions))))
(if add-prefix
(push prefix candidates)
candidates)))

(defun alchemist-complete--build-candidates (a-list)
(let* ((search-term (car a-list))
(candidates (alchemist-complete--clean-functions a-list))
(candidates (cond ((string-match-p "\\." search-term)
(alchemist-complete--concat-prefix-with-functions search-term candidates))
((and (string-match-p "^:" search-term)
(not (string-match-p "\\.$" search-term)))
(mapcar (lambda (c) (concat ":" c)) (cdr candidates)))
(t (cdr candidates))))
(candidates (delete-dups candidates)))
candidates))

(defun alchemist-complete--build-help-candidates (a-list)
(let* ((search-term (car a-list))
(candidates (cond ((string-match-p "\\.$" search-term)
(alchemist-complete--concat-prefix-with-functions search-term a-list t))
((string-match-p "\\..+" search-term)
(alchemist-complete--concat-prefix-with-functions search-term a-list))
(t (cdr a-list)))))
(delete-dups candidates)))

(defun alchemist-complete--elixir-output-to-list (output)
(let* ((output (replace-regexp-in-string "\"\\|\\[\\|\\]\\|'\\|\n\\|\s" "" output))
(a-list (split-string output ",")))
a-list))

(defun alchemist-complete--command (exp)
(let* ((elixir-code (format "
defmodule Alchemist do
def expand(exp) do
{status, result, list } = IEx.Autocomplete.expand(Enum.reverse(exp))
case { status, result, list } do
{ :yes, [], _ } -> List.insert_at(list, 0, exp)
{ :yes, _, _ } -> expand(exp ++ result)
_t -> exp
end
end
end
IO.inspect Alchemist.expand('%s')
" exp))
(command (if (alchemist-project-p)
(format "%s --no-compile -e \"%s\"" alchemist-help-mix-run-command elixir-code)
(format "%s -e \"%s\"" alchemist-execute-command elixir-code))))
(when (alchemist-project-p)
(alchemist-project--establish-root-directory))
command))

(defun alchemist-complete (exp callback)
(let* ((buffer (get-buffer-create "alchemist-complete-buffer"))
(command (alchemist-complete--command exp))
(proc (start-process-shell-command "alchemist-complete-proc" buffer command)))
(set-process-sentinel proc (lambda (process signal)
(when (equal signal "finished\n")
(let ((output (alchemist-complete--elixir-output-to-list (with-current-buffer (process-buffer process)
(set-buffer-modified-p nil)
(buffer-substring (point-min) (point-max))))))
(funcall callback output)
(with-current-buffer (process-buffer process)
(erase-buffer))))))))

(defun alchemist-complete-candidates (exp callback)
(let* ((buffer (get-buffer-create "alchemist-complete-buffer"))
(command (alchemist-complete--command exp))
(proc (start-process-shell-command "alchemist-complete-proc" buffer command)))
(set-process-sentinel proc (lambda (process signal)
(when (equal signal "finished\n")
(let* ((output (alchemist-complete--elixir-output-to-list (with-current-buffer (process-buffer process)
(set-buffer-modified-p nil)
(buffer-substring (point-min) (point-max)))))
(candidates (alchemist-complete--build-candidates output)))
(funcall callback candidates)
(with-current-buffer (process-buffer process)
(erase-buffer))))))))

(defun alchemist-complete--completing-prompt (initial completing-collection)
(let* ((completing-collection (alchemist-complete--build-help-candidates completing-collection)))
(cond ((equal (length completing-collection) 1)
(car completing-collection))
(completing-collection
(completing-read
"Elixir help: "
completing-collection
nil
nil
initial))
(t initial))))

(provide 'alchemist-complete)

;;; alchemist-complete.el ends here
Loading

0 comments on commit eff2407

Please sign in to comment.