• Emacs 中的补全方式 at 2015年04月02日

    可以读 org 文件了

    使用方法:C-s interactive

  • Emacs 中的补全方式 at 2015年04月02日

    ;;; 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

  • Emacs 中的补全方式 at 2015年03月24日

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

    应该很容易实现

  • Emacs 中的补全方式 at 2015年03月24日

    封装 :

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

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

  • Emacs 中的补全方式 at 2015年03月24日

    你只知道我 QQ 号码吗?

    为什么不能在论坛上呢?

  • Emacs 中的补全方式 at 2015年03月23日

    文本文件的格式 如下:


    <>
    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

  • Emacs 中的补全方式 at 2015年03月23日
    ;;; 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
    
  • 有个性的程序员 at 2014年06月10日

    嗯 个性嘛 https://github.com/richardmstallman 算吗?

  • 转:Ruby 的白魔法书 at 2014年06月08日

    回复楼上 代码里的实现只有一种 书里面的实现可能、大概、或许、也许、说不定 就有两种

  • 怎么说呢?

    写 ruby 代码的人

    居然在用以 python 语言做插件的编辑器

    ruby 的悲哀?

    (希望有一天会有一个用 ruby 写插件的编辑器) (好像已经有好几个了)