;;; fcomp.el --- fcomp front-end -*- lexical-binding: t; -*- ;; Copyright (C) 2019 Anastasis Grammenos ;; Author: Anastasis Grammenos ;; Keywords: lisp ;; Version: 0.0.1 ;; 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 . ;;; Commentary: ;; Emacs front-end for fcomp. Get it from https://ubuntos.dynu.net/git/fcomp ;;; Install: ;; git clone https://ubuntos.dynu.net/git/fcomp && cd fcomp ;; make && make install # install copies the binary under /usr/local/bin ;; In the emacs init file: ;; (add-to-list 'load-path "/your/custom/path") ;; (require 'fcomp) ;; (setq fcomp-path "/your/custom/path/fcomp") ;; (global-set-key (kbd "C-") 'fcomp-autocomplete) ;; or with use-package ;; (use-package fcomp ;; :load-path "/your/custom/path" ;; :bind (("C-" . fcomp-autocomplete)) ;; :config ;; (setq fcomp-path "/your/custom/path/fcomp")) ;;; Code: (require 'ido) (require 'subr-x) ;; User variables (defvar fcomp-path "/usr/local/bin/fcomp" "Path to fcomp executable.") (defvar fcomp-min-word-length 3 "Minimum word length to be counted as token.") (defvar fcomp-extra-valid-chars "" "Extra valid chars for the tokenization.") (defvar fcomp-extra-invalid-chars "" "Extra invalid chars for the tokenization.") (defvar fcomp-completing-read-func 'completing-read "Function to call when showing results to user.") (defun empty-string-p (string) "Return true if the string is empty or nil. Expects string." (or (null string) (zerop (length (string-trim string))))) (defun fcomp--start (query) "Initiaize fcomp variables" (setq fcomp--query query) (setq fcomp--output "")) (defun fcomp--filter (proc str) "Fill the output buffer" (let* ((fcomp-pre (if (empty-string-p (process-get proc 'input-word)) nil (format "%s" (process-get proc 'input-word))))) (setq fcomp--output (concat fcomp--output str)))) (defun fcomp--handle-output () "Handle output buffer" (let ((initial-input fcomp--query) (out-string fcomp--output) (result nil)) (if (empty-string-p fcomp--output) nil (setq result (funcall fcomp-completing-read-func "Complete:" (remove "" (split-string fcomp--output "\n")) nil nil initial-input))) (if (empty-string-p result) (message "%s" "No completion candidates") (insert (string-remove-prefix (if (empty-string-p initial-input) (format "%s" "") (format "%s" initial-input)) result))) (setq fcomp--query "") (setq fcomp--output ""))) (defun fcomp--autocomplete (word point &optional x) "Start fcomp process and orchistrate it's output." (let ((term (if (empty-string-p word) (format "%s" "-au") (format "%s" word))) (input-len (buffer-size))) (fcomp--start word) (setq proc (start-process "fcomp" nil (format "%s" fcomp-path) "-w" (format "%s" fcomp-min-word-length) "-v" (format "\"%s\"" fcomp-extra-valid-chars) "-i" (format "\"%s\"" fcomp-extra-invalid-chars) term)) (set-process-filter proc #'fcomp--filter) (set-process-coding-system proc 'raw-text-unix 'raw-text-unix) (process-put proc 'input-word word) (if (< input-len 50000) (process-send-region proc (point-min) (point-max)) (process-send-region proc (if (< (- point 25000) 0) (point-min) (- point 25000)) (if (> (+ point 25000) input-len) (point-max) (+ point 25000)))) (process-send-string proc "\n") (process-send-eof proc) (if (accept-process-output proc nil nil t) (unless x (fcomp--handle-output)) (progn (unless x (message "No completion for %s" word)) (delete-process proc))))) (defun fcomp--make-syntax-table () "Modifies the current active sytanx table to include '_', '-' and `fcomp-extra-valid-chars' as part of a word." ;; Thanks Dan for the syntax-table trick ;; https://emacs.stackexchange.com/questions/9583/how-to-treat-underscore-as-part-of-the-word (let ((table (copy-syntax-table (syntax-table)))) (when (not (empty-string-p fcomp-extra-valid-chars)) (mapc (lambda (char) (modify-syntax-entry char "w" table)) fcomp-extra-valid-chars)) (modify-syntax-entry ?- "w" table) (modify-syntax-entry ?_ "w" table) (copy-syntax-table table))) (defun fcomp-get-canditates (word) "Return a list of candidates for WORD curated by fcomp." (progn (fcomp--autocomplete word (point) t)) (remove "" (split-string fcomp--output "\n"))) (defun fcomp-autocomplete () "Autocompete using fcomp.c When `thing-at-point' is a word, autocomplete it based on buffer contents. When invoked with no `thing-at-point' show a list of all possible candidates. The syntax-table is temporarily modified and includes '_', '-' and `fcomp-extra-valid-chars' as part of a word. Requires fcomp. Get it at URL `https://git.eyesin.space/git/fcomp'" (interactive) (with-syntax-table (fcomp--make-syntax-table) (fcomp--autocomplete (thing-at-point 'word 'no-properties) (point)))) (provide 'fcomp) ;;; fcomp.el ends here