开发工具 Emacs 中的补全方式

zhouchongzxc · 2015年03月23日 · 最后由 zhouchongzxc 回复于 2015年04月02日 · 3479 次阅读

使用 emacs 的时间不算很长 了解点 elisp 的皮毛

写了几行 elisp 的代码 代码的作用就是从一个文本文件中提取用于补全的代码

下面附有代码(抱歉 没有测试 没有版本号 没有依赖 没有配置 是 compamy 而不是 auto-complete 在别人的机器上应该运行不了)

;;; company-chong.el --- company-mode completion back-end for aid_files

;; Author: Zhou Chong

;; This file is Not part of GNU Emacs.

;; GNU Emacs 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.

;; GNU Emacs 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 GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.


;;; Commentary:
;;

;;; Code:

(eval-when-compile (require 'cl))
(require 'company)

(defgroup company-chong nil
  "Completion back-end for chong."
  :group 'company)

(defcustom company-chong-ignore-case nil
  "Non-nil to ignore case in completion candidates."
  :type 'boolean)


(defun company-chong-read-data ()
  "从文件中提取一段代码 并将其分割成多个补全方式"
  (save-excursion
    (let ((start (line-end-position))
          end parts)
      (if (search-forward-regexp "\n<\\([^>]+\\)>" nil t)
          (setq end (line-beginning-position))
        (setq end (point-max)))
      (setq parts (buffer-substring start end))
      (setq parts (split-string parts "\n\s*<<" t))
      parts)))

;;(company-chong-read "/Users/mac/Downloads/emacs-24.1/lisp/company-mode-0.7.3/chong/sqlite.txt")

(defun company-chong-read (file)
  "用于从文件中得到数据"
  (let (title  data
        (file-hash (make-hash-table :test 'equal)))
    (unless (file-exists-p file)
      (error "`company-chong-read' file not exist :%s" file))
    (with-temp-buffer
      (insert-file-contents file)
      (goto-char 1)
      (while (search-forward-regexp "\n<\\([^>]+\\)>" nil t)
        (setq title (match-string 1))
        (message "%s" title)
        ;; 接下来就是从中提取数据了 数据要能够得到一段的描述
        (setq data (company-chong-read-data))
        (puthash title data file-hash)))
    file-hash))


(defun company-chong-add-hash (file title data)
  "用于将数据加入到与文件对应的hash中" ;; data中的数据是包含多个 sub title>>  的
  )


(defvar company-chong-hash (make-hash-table :test 'equal)
  "用于存储从文件中得到的数据")
(make-local-variable 'company-chong-hash)


(defvar company-chong-hash-all-mode (make-hash-table :test 'equal)
  "所有mode中的hash都在这里")


(defun company-chong-add-file (file cur-mode)
  "添加新的文件到数据库中 文件的路径一定要是绝对的路径
并且会覆盖原来系统文件的数据"
  (chong-debug nil
   (message "`company-chong-add-file' file:%s  mode:%s" file cur-mode))
  (let ((cur-mode-hash (gethash cur-mode company-chong-hash-all-mode))
        (old-data )
        (new-data (company-chong-read file)))
    (unless cur-mode-hash
      (setq cur-mode-hash (make-hash-table :test 'equal))
      (puthash cur-mode cur-mode-hash company-chong-hash-all-mode))
    (setq  company-chong-hash cur-mode-hash)
    (setq old-data (gethash file company-chong-hash))
    (when old-data (message "old-data be overwrited :%s" file))
    (puthash file new-data company-chong-hash)
    ;;(print company-chong-hash)
    ;;(print cur-mode-hash)
    (message "new-data OK :%s" file)))


;;(print (company-chong--candidates "lis"))


(defun company-chong--candidates (prefix)
  "用于得到文件中的补全
补全不仅要得到一行 还要能得到一段"
  (let (ret candi-item)
    (chong-debug
     (message "`company-chong--candidates' the buffer is %s"
              (buffer-name (current-buffer))))
    (maphash
     (lambda (file file-hash)
       (maphash
        (lambda (title data)
          (when (string-match prefix title)
            (setq candi-item
                  (propertize title 'data data 'file file))
            (setq ret (cons candi-item ret))
            ))
        file-hash))
     company-chong-hash)
    ;;(print ret)
    ret))


(defun company-chong--annotation (candidate) ; 我可以让他得到更多的meta 进而得到更多的补全
  "其实就是得到原来存储的meta"               ; 或是用另外的一种方法
  (let ((meta (company-chong--meta candidate))
        (metas))
    (message "Meta->>>>")
    (print meta)
    (message "company-chong--annotation")
    (print (cond
     ((null meta)
      (message "Error meta is nil")
      nil)
     (t
      (setq metas (company-chong-subtitles meta))
      (message "Metas >>>>>")(print metas)
      (company-abort)
      (setq meta (popup-menu* metas))
      )))))


(defun company-chong-subtitles (subtitles)
  "参数是一个list 得到list中的第一个含有>>的行"
  (let (sub-title title data ret)
        (mapc
         (lambda (subtiltle)
           (setq sub-title (split-string subtiltle "\n"))
           (while (and sub-title (not (string-match ">>" (car sub-title))))
             (setq sub-title (cdr sub-title)))
           (setq title (car sub-title))
           (setq sub-title (cdr sub-title))
           (setq data (mapconcat (lambda (it) it) sub-title "\n"))
           (when title (setq ret (cons (propertize title 'data data) ret)))
           )
         subtitles)
        ret))

(defun company-chong--meta (candidate)
  (let (my-candi
        (cands company-candidates))
    (mapc
     (lambda (cand)
       (when  (equal cand candidate)
         (setq my-candi cand)))
     cands)
    (setq ret (get-text-property 0 'data my-candi))
    (unless ret (error "`company-chong--meta' error ,no data"))
    ;;(print "company-chong--meta")  (print ret)
    ret))


(defun company-chong--annotation-1 (candidate) ; 我可以让他得到更多的meta 进而得到更多的补全
  "其实就是得到原来存储的meta"               ; 或是用另外的一种方法
  (let ((meta (company-chong--meta candidate)))
    (cond
     ((null meta) nil)
     ((stringp  meta)
      (substring meta 0 100))
     ((listp meta)
      (let (sub-title)
        (concat
         "  "
         (mapconcat
          (lambda (subtiltle)
            (setq sub-title (split-string subtiltle "\n"))
            (while (and sub-title (not (string-match ">>" (car sub-title))))
              (setq sub-title (cdr sub-title)))
            (car sub-title))
          meta " ")))))))


(defun company-chong-init ()
  "初始化每个buffer"
  (interactive)
  (company-chong-load-file))



;;;###autoload
(defun company-chong (command &optional arg &rest ignored)
  "`company-mode' completion back-end for chong aid files."
  (interactive (list 'interactive))
  (case command
    (interactive (company-begin-backend 'company-chong))
    (init (company-chong-load-file)
          (message "company-chong load files OK")
          t)
    (prefix (and 
                 (not (company-in-string-or-comment))
                 ;;(or (> (hash-table-count company-chong-hash) 0)
                     ;;(message "`company-chong-hash' count ==0 ,use (company-chong-add-file file) to add file"))
                 (or (company-grab-symbol) 'stop)))
    (candidates (company-chong--candidates arg))
    (meta       (company-chong--annotation-1 arg))
    (annotation (company-chong--annotation-1 arg))
    (post-completion (let* ((ttt (print arg))
                           (anno (company-chong--annotation arg))
                           (ttx (print anno))
                           data)
                       (print anno)
                       (when anno  ;;(insert anno)
                         (message "anno %s" anno)
                         (setq data (get-text-property 0 'data anno))
                         (message "应该注释掉当前的行")
                         (insert  "\n" data)
                         )))
    (location (let ((prefix company-prefix)
                    (file (get-text-property 1 'file arg)))
                    (let ((buffer (find-file file)))
                      (cons buffer
                            (with-current-buffer buffer
                              (goto-char 1)
                              (search-forward-regexp (concat "\n<" arg) nil t)
                              (point))))))))




(defvar company-chong-load-file
  "根据不同的mode 装载不同的txt文件")
(setq company-chong-load-file 
      (list
       '(ruby-mode   ("/Users/mac/ruby/ruby.txt"))
       '(emacs-lisp-mode ("/Users/mac/Elisp/elisp.txt"))
       ))

(defun company-chong-load-file ()
  "会根据不同的mode 装载不同的txt文件"
  (let* ((cur-mode major-mode)
         (files (cadr (assq cur-mode company-chong-load-file))))
    (if (not files)
        (message "`company-chong-load-file' not found files for this mode")
      (mapc
       (lambda (file)
         (company-chong-add-file file cur-mode))
       files))))

(defun chong-move-next ()
  "用于移动到下一个节点  会根据当前的节点移动
比如说 当前处于 <class>  则会移动到下一个 <>
       当前处于 <<attribute >>  则会移动到下一个 <<>>"
  (interactive)
  (let ((line (current-line)))
    (cond
     ((string-match "^<[^>]+>" line)
      (end-of-line)
      (search-forward-regexp "\n<" nil t))
     ((string-match "^\s*<<[^>]+>>" line)
      (end-of-line)
      (search-forward-regexp "\n\s*<<" nil t))
     (t
      (search-forward-regexp "\n<" nil t)
      ))))


(provide 'company-chong)
;;; company-chong.el ends here

文本文件的格式 如下:


<>
www.ruby-lang.org

<>
www.ruby-doc.org

<>

def self.meth() # 用 self 开始的就是 classmethod
...
end

<>
attr 可控读写
attr_reader 只读
attr_writer 可写
attr_accessor 可读写

attr_accessor :attr1 #将这句代码直接的放进 class define 中 就能得到属性了

# 还可以用过方法来定义属性 那样你会有更多的控制权(好像 OBJC 也行的)
def attr2
@attr2
end

def attr2= (val)
@attr2 = val
end

匿名 #3 2015年03月24日

:plus1: 有些问题想问你,在 QQ 上 😄

你只知道我 QQ 号码吗?

为什么不能在论坛上呢?

封装:

类 是对数据与算法的封装。其实他们可以单独存在

原来 文档也是可以封装的。

应该让他读取 org-mode 的文件

应该很容易实现

;;; company-chong.el --- company-mode completion back-end for org-files

;; Copyright (C) 2015 Free Software Foundation, Inc.

;; Author: Zhou Chong

;; This file is Not part of GNU Emacs.

;; GNU Emacs 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.

;; GNU Emacs 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 GNU Emacs. If not, see http://www.gnu.org/licenses/.

;;; Commentary: ;;

;;; Code:

(eval-when-compile (require 'cl)) (require 'company)

(defgroup company-chong nil "Completion back-end for chong." :group 'company)

(defcustom company-chong-ignore-case nil "Non-nil to ignore case in completion candidates." :type 'boolean)

(defun company-chong-read-data () "从文件中提取一段代码 并将其分割成多个补全方式" (save-excursion (let ((start (line-end-position)) end parts) (if (search-forward-regexp "\n\\s\([^\n]+\)" nil t) (setq end (line-beginning-position)) (setq end (point-max))) (setq parts (buffer-substring start end)) (setq parts (cdr (split-string parts "\n\s\*\*\s" t))) parts)))

;;(company-chong-read "/Users/mac/Downloads/emacs-24.1/lisp/company-mode-0.7.3/chong/sqlite.txt")

(defun company-chong-read (file) "用于从文件中得到数据" (let (title data (file-hash (make-hash-table :test 'equal))) (unless (file-exists-p file) (error "`company-chong-read' file not exist :%s" file)) (with-temp-buffer (insert-file-contents file) (goto-char 1) (while (search-forward-regexp "\n\*\s\([^\n]+\)" nil t) (setq title (match-string 1)) (message "%s" title) ;; 接下来就是从中提取数据了 数据要能够得到一段的描述 (setq data (company-chong-read-data)) (puthash title data file-hash))) file-hash))

(defun company-chong-add-hash (file title data) "用于将数据加入到与文件对应的 hash 中" ;; data 中的数据是包含多个 sub title>> 的 )

(defvar company-chong-hash (make-hash-table :test 'equal) "用于存储从文件中得到的数据") (make-local-variable 'company-chong-hash)

(defvar company-chong-hash-all-mode (make-hash-table :test 'equal) "所有 mode 中的 hash 都在这里")

(defun company-chong-add-file (file cur-mode) "添加新的文件到数据库中 文件的路径一定要是绝对的路径 并且会覆盖原来系统文件的数据" (chong-debug nil (message "`company-chong-add-file' file:%s mode:%s" file cur-mode)) (let ((cur-mode-hash (gethash cur-mode company-chong-hash-all-mode)) (old-data ) (new-data (company-chong-read file))) (unless cur-mode-hash (setq cur-mode-hash (make-hash-table :test 'equal)) (puthash cur-mode cur-mode-hash company-chong-hash-all-mode)) (setq company-chong-hash cur-mode-hash) (setq old-data (gethash file company-chong-hash)) (when old-data (message "old-data be overwrited :%s" file)) (puthash file new-data company-chong-hash) ;;(print company-chong-hash) ;;(print cur-mode-hash) (message "new-data OK :%s" file)))

;;(print (company-chong--candidates ""))

(defun company-chong--candidates (prefix) "用于得到文件中的补全 补全不仅要得到一行 还要能得到一段" (let (ret candi-item) (chong-debug (message "`company-chong--candidates' the buffer is %s" (buffer-name (current-buffer)))) (maphash (lambda (file file-hash) (maphash (lambda (title data) (when (string-match prefix title) (setq candi-item (propertize title 'data data 'file file)) (setq ret (cons candi-item ret)) )) file-hash)) company-chong-hash) ;;(print ret) ret))

(defun company-chong--annotation (candidate) ; 我可以让他得到更多的 meta 进而得到更多的补全 "其实就是得到原来存储的 meta" ; 或是用另外的一种方法 (let ((meta (company-chong--meta candidate)) (metas)) (message "Meta->>>>") (print meta) (message "company-chong--annotation") (print (cond ((null meta) (message "Error meta is nil") nil) (t (setq metas (company-chong-subtitles meta)) (message "Metas >>>>>")(print metas) (company-abort) (setq meta (popup-menu* metas)) )))))

(defun company-chong-subtitles (subtitles) "参数是一个 list 得到 list 中的第一个含有>>的行" (let (sub-title title data ret) (mapc (lambda (subtiltle) (setq sub-title (split-string subtiltle "\n")) (setq title (car sub-title)) (message "title %s" title) (setq sub-title (cdr sub-title)) (setq data (mapconcat (lambda (it) it) sub-title "\n")) (when title (setq ret (cons (propertize title 'data data) ret))) ) subtitles) ret))

(defun company-chong--meta (candidate) (let (my-candi (cands company-candidates)) (mapc (lambda (cand) (when (equal cand candidate) (setq my-candi cand))) cands) (setq ret (get-text-property 0 'data my-candi)) (unless ret (error "`company-chong--meta' error ,no data")) ;;(print "company-chong--meta") (print ret) ret))

(defun company-chong--annotation-1 (candidate) ; 我可以让他得到更多的 meta 进而得到更多的补全 "其实就是得到原来存储的 meta" ; 或是用另外的一种方法 (let ((meta (company-chong--meta candidate))) (cond ((null meta) nil) ((stringp meta) ;;(message "yyyyyyyy") (substring meta 0 100)) ((listp meta) (let (sub-title) (concat " " (mapconcat (lambda (subtiltle) (setq sub-title (split-string subtiltle "\n")) (car sub-title)) meta " ")))))))

(defun company-chong-init () "初始化每个 buffer" (interactive) (company-chong-load-file))

;;;###autoload (defun company-chong (command &optional arg &rest ignored) "company-mode' completion back-end for chong aid files." (interactive (list 'interactive)) (case command (interactive (company-begin-backend 'company-chong)) (init (company-chong-load-file) (message "company-chong load files OK") t) (prefix (and (not (company-in-string-or-comment)) ;;(or (> (hash-table-count company-chong-hash) 0) ;;(message "company-chong-hash' count ==0 ,use (company-chong-add-file file) to add file")) (or (company-grab-symbol) 'stop))) (candidates (company-chong--candidates arg)) (meta (company-chong--annotation-1 arg)) (annotation (company-chong--annotation-1 arg)) (post-completion (let* ((ttt (print arg)) (anno (company-chong--annotation arg)) (ttx (print anno)) data) (print anno) (when anno ;;(insert anno) (message "anno %s" anno) (setq data (get-text-property 0 'data anno)) (message "应该注释掉当前的行") (insert "\n" data) ))) (location (let ((prefix company-prefix) (file (get-text-property 1 'file arg))) (let ((buffer (find-file file))) (cons buffer (with-current-buffer buffer (goto-char 1) (search-forward-regexp (concat "\n* " arg) nil t) (point))))))))

(defvar company-chong-load-file "根据不同的 mode 装载不同的 txt 文件") (setq company-chong-load-file (list '(ruby-mode ("/Users/mac/ruby/ruby.txt")) '(emacs-lisp-mode ("/Users/mac/Elisp/elisp.txt")) ))

(defun company-chong-load-file () "会根据不同的 mode 装载不同的 txt 文件" (let* ((cur-mode major-mode) (files (cadr (assq cur-mode company-chong-load-file)))) (if (not files) (message "`company-chong-load-file' not found files for this mode") (mapc (lambda (file) (company-chong-add-file file cur-mode)) files))))

;;; -----------------下面的函数是用来辅助编辑的

(defun ruby-run-current-region () "通过搜索两个注释行 确定要执行的代码" (interactive) (let (start end str temp-buf) (save-excursion (if (not (search-forward-regexp "\n#" nil t)) (setq end (point-max)) (line-move -1) (setq end (line-end-position)))) (save-excursion (if (not (search-backward-regexp "\n#" nil t)) (setq start 1) (line-move 2) (setq start (line-beginning-position)))) (setq str (buffer-substring-no-properties start end)) (setq temp-buf (find-buffer-visiting "~/zx.rb")) (unless temp-buf (setq temp-buf (find-file "~/zx.rb"))) (with-current-buffer temp-buf (erase-buffer) (insert str)(my-ruby-run))))

;;--- 给 ruby 添加测试功能

(defun ruby-test-region () "得到所有的 class 区间 主要是针对 class 来写的 对于 modeule 也是一样的" )

(defun ruby-test-region-str () "通过上面的 region 得到中所有的 ##T str")

(defun ruby-test-region-gene () "根据 ##T str 生成一个测试函数")

(defun ruby-test-gene2class () "根据所有的测试函数生成测试类")

;; # require "test/unit" ;; # class TestSongList < Test::Unit::TestCase ;; # def test_delete ;; # nil ;; # end ;; # end

;;--- 我追加的代码

(defvar my-ruby-output nil "记录输出的数据用的")

(defun my-ruby-filter (proc data) "只是添加数据而已" (setq my-ruby-output (concat my-ruby-output data)) )

(defun my-ruby-sent (proc data) "只是添加数据而已" ;; (message "OK") (if (null my-ruby-output) (popup-tip "请用 puts 或 print 输出") (popup-tip my-ruby-output)))

(defun run-ruby-chong () "定义我自己的方法 用于显示 ruby 的结果" (interactive) (let (proc (file (buffer-file-name (current-buffer)))) (save-buffer) (setq proc (start-process "ruby" "Ruby" "ruby" file)) (setq my-ruby-output nil) ;清空缓存 (set-process-filter proc 'my-ruby-filter) (set-process-sentinel proc 'my-ruby-sent) ))

;; 你可以修改的部分 (defvar my-edit-replace-word '("1" "2" "3") "用于搜索并替换的hook-word 比如 \11 等")

(defun my-edit-of-select () "得到用户的选定行 如果有区间就用区间 否则当前的行" (let (ret (region (region-active-p))) (if region (buffer-substring (region-beginning)(region-end)) (buffer-substring (line-beginning-position)(line-end-position)))))

(defun my-edit-of-tokens (edit-line) "用逗号将句子分开 可以用 => 表示关联" (when (string-match "def\s+[a-zA-Z0-9_!?=]+(\(.\))" edit-line) (setq edit-line (match-string 1 edit-line))) (let ((tokens (split-string edit-line "\s,\s*"))) (print tokens) (mapcar (lambda (str) (with-temp-buffer (insert str) (while (looking-back "\s") (delete-char -1 ) ) (goto-char 1) (while (looking-at "\s") (delete-char 1 ) ) (buffer-string))) tokens) )) ;; (my-edit-of-tokens " zhou ,chong ") ;; (define-key popup-isearch-keymap [return] 'popup-isearch-done)

(defun* edit-arg-chong (tokens &key (cursor-color popup-isearch-cursor-color) (keymap popup-isearch-keymap) callback help-delay) "编辑各个单词 并用 pattern 替换" (interactive "P") (let ((list (or tokens (my-edit-of-tokens (my-edit-of-select)))) (pattern "") (main-win (selected-window)) prompt key binding) (unwind-protect (block nil (while t (setq prompt (concat "Use:" pattern)) (setq key (popup-menu-read-key-sequence keymap prompt help-delay)) (if (null key) (unless (funcall popup-menu-show-quick-help-function popup nil :prompt prompt) (clear-this-command-keys) (push (read-event prompt) unread-command-events)) (setq binding (lookup-key keymap key)) (cond ((and (stringp key) (popup-isearch-char-p (aref key 0))) (setq pattern (concat pattern key))) ((eq binding 'popup-isearch-done) (my-edit-done pattern list) (return nil)) ((eq binding 'popup-isearch-cancel) (return t)) ((eq binding 'popup-isearch-delete) (if (> (length pattern) 0) (setq pattern (substring pattern 0 (1- (length pattern)))))) (t (setq unread-command-events (append (listify-key-sequence key) unread-command-events)) (return nil))) (my-main-buf-popup main-win pattern list)))))))

(defun ruby-new-line () "缩进并回车" (interactive) (ruby-indent-line) (newline) (ruby-indent-line) )

(defun my-edit-done (pattern tokens) "在你补全之后应该 insert 了" (let ((ret (my-edit-result pattern tokens)) pos end) (end-of-line) (insert "\n")(setq pos (point)) (insert ret "\nend") (setq end (point-marker)) (goto-char pos) (while (< (point) end) (ruby-indent-line) (line-move 1 t) ) (beginning-of-line) (left-char 1) (ruby-new-line)))

(defun my-main-buf-popup (main-win pattern tokens) "显示你所修改的" (let ((cur-win (selected-window))) (select-window main-win) ;;(message "pattern:%s " pattern )(print tokens) (popup-tip (my-edit-result pattern tokens))))

(defun my-edit-result (pattern tokens) "用于得到结果的 对 tokens 做处理" (mapconcat (lambda (token) (let (words) (setq words (split-string token "\s*=>\s*")) (with-temp-buffer (insert pattern) (dotimes (i (length words)) ;; 从 0 开始 不包含 COUNT (goto-char 1) (while (search-forward (nth i my-edit-replace-word) nil t) (replace-match (nth i words)))) (buffer-string)))) tokens "\n") )

(provide 'company-chong) ;;; company-chong.el ends here

可以读 org 文件了

使用方法:C-s interactive

需要 登录 后方可回复, 如果你还没有账号请 注册新账号