From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.org!.POSTED!not-for-mail From: Chen Bin Newsgroups: gmane.emacs.devel Subject: [ELPA] New package: mctags Date: Thu, 12 Oct 2017 22:05:16 +1100 Message-ID: <87h8v439ub.fsf@gmail.com> NNTP-Posting-Host: blaine.gmane.org Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" X-Trace: blaine.gmane.org 1507806354 17911 195.159.176.226 (12 Oct 2017 11:05:54 GMT) X-Complaints-To: usenet@blaine.gmane.org NNTP-Posting-Date: Thu, 12 Oct 2017 11:05:54 +0000 (UTC) To: emacs-devel@gnu.org Original-X-From: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Thu Oct 12 13:05:48 2017 Return-path: Envelope-to: ged-emacs-devel@m.gmane.org Original-Received: from lists.gnu.org ([208.118.235.17]) by blaine.gmane.org with esmtp (Exim 4.84_2) (envelope-from ) id 1e2bJA-0003GC-O2 for ged-emacs-devel@m.gmane.org; Thu, 12 Oct 2017 13:05:45 +0200 Original-Received: from localhost ([::1]:44783 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1e2bJG-0003aZ-8C for ged-emacs-devel@m.gmane.org; Thu, 12 Oct 2017 07:05:50 -0400 Original-Received: from eggs.gnu.org ([2001:4830:134:3::10]:37236) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1e2bIx-0003a8-Is for emacs-devel@gnu.org; Thu, 12 Oct 2017 07:05:37 -0400 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1e2bIr-0003DC-Mk for emacs-devel@gnu.org; Thu, 12 Oct 2017 07:05:31 -0400 Original-Received: from mail-pf0-x22a.google.com ([2607:f8b0:400e:c00::22a]:49093) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1e2bIr-0003CT-2E for emacs-devel@gnu.org; Thu, 12 Oct 2017 07:05:25 -0400 Original-Received: by mail-pf0-x22a.google.com with SMTP id b79so4300184pfk.5 for ; Thu, 12 Oct 2017 04:05:23 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:subject:date:message-id:mime-version; bh=ekX8MnBZ2SUZA5qhwssmq0QlT0Rx67MDpav+/6sKfXA=; b=CBrkK9CgRzvcy/czz5EcEngcUuBzeFRMg9fUT3ptCJc5bsm2LIWle+N8Seob2g37pc iV3FElyZezFDBiHeXxygSQTlbiwR52zuJo2SSJkExU3PXAQhuIYKqOlZELjba8VBWogY Yhr1vTrEq2GUOD9T8BsbIoeKTecS5GCtTYitOchnneZdB9qvV/K7jYkIk3NO+42tEMyQ RwWoqEQ1GG0EQW+fwzzZqJ+S+A4PkRHOp2nqVt62WhJaow3FSdZ5bGYl5iKSquNHZ0Cl ZCR58lyM+JsCIBKXLNapydmDyQIqtFHy42Pxc3mO/aLZT1/MfqKWhkXk9O7bE3H0XSsq 4low== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:subject:date:message-id:mime-version; bh=ekX8MnBZ2SUZA5qhwssmq0QlT0Rx67MDpav+/6sKfXA=; b=aEydqnFWTMIFw9MgispNqJFjrejpWU+sRwGDukK1JNTG/NP0x7cTAnPMZvgKHkigE2 iZc4xBtUGlmmq0/3wXES5WUUOcHoxVQ3cGRJGC4fV4/z4ZuHcLSpbC2u6+MhenbDJU7n q/vIdCIByhTX0/zPMtcSdI/tUIi6tdjcCQr1PNtLAhUgJb6WlKZYRSRxROuNVqogcIox 2bxCkCPgKbLcAS3c27SX0y50RultHVWmP6M+WazonTeCWezmaVD9oaVr+cCLIXLpyRwc Sl9M7PG6YGDMJ2ytRRVpE0oEuK+BeoxERlgd8B5/CKg3z43UiyHJVpQU5RTHoqRQthtD uFXA== X-Gm-Message-State: AMCzsaVUBN3DrKxD1xLp+/u8EMNdJdpxC7LHHJIp+7IOZbArypNZmaW3 FkWvsIYwStlFMRLLC6Je1d1pqQ== X-Google-Smtp-Source: AOwi7QBsXejB0cF1GY7GXIfu42jPllC27ISmcCCv3uYxsEEQFlkN2lvDlyLLVGmsecSd1nAsYOYS6w== X-Received: by 10.98.93.75 with SMTP id r72mr1816544pfb.331.1507806322447; Thu, 12 Oct 2017 04:05:22 -0700 (PDT) Original-Received: from sydneypc.homepc ([115.187.209.174]) by smtp.gmail.com with ESMTPSA id p70sm30439357pfk.130.2017.10.12.04.05.19 for (version=TLS1_2 cipher=ECDHE-RSA-CHACHA20-POLY1305 bits=256/256); Thu, 12 Oct 2017 04:05:21 -0700 (PDT) X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 2607:f8b0:400e:c00::22a X-BeenThere: emacs-devel@gnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: "Emacs development discussions." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Original-Sender: "Emacs-devel" Xref: news.gmane.org gmane.emacs.devel:219406 Archived-At: --=-=-= Content-Type: text/plain Hi, Sorry to re-send mail. I forget to attach the source code in previous mail. I developed a package using Ctags. https://github.com/redguardtoo/mctags/ I would like this package be accepted by ELPA. I'm the only developer of this package which is only dependent on package counsel. Counsel is already accepted by ELPA. I've Emacs signed Copyright Papers when contributing company-mode, https://github.com/company-mode/company-mode/pull/13 My situation is not changed. Here is summary of mctags: Fast and complete Ctags solution. Usage: "M-x mctags-find-tag-at-point" to navigate. This command will also run `mctags-scan-code' automatically if tags file is not built yet. "M-x mctags-scan-code" to create tags file "M-x mctags-grep" to grep --=-=-= Content-Type: application/emacs-lisp Content-Disposition: attachment; filename=mctags.el Content-Transfer-Encoding: quoted-printable ;;; mctags.el --- Fast and complete Ctags solution ;; Copyright (C) 2017 Free Software Foundation, Inc. ;; Author: Chen Bin ;; Maintainer: Chen Bin ;; URL: http://github.com/redguardtoo/mctags ;; Package-Requires: ((emacs "24.3") (counsel "0.9.1")) ;; Keywords: tools, convenience ;; Version: 1.1.7 ;; 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 . ;; This file is not part of GNU Emacs. ;;; Commentary: ;; Usage: ;; "M-x mctags-find-tag-at-point" to navigate. This command will also ;; run `mctags-scan-code' automatically if tags file is not built yet. ;; ;; "M-x mctags-scan-code" to create tags file ;; "M-x mctags-grep" to grep ;; ;; That's all! ;; ;; Tips: ;; You can use ivy's negative pattern to filter candidates. ;; For example, input "keyword1 !keyword2 keyword3" means: ;; "(keyword1 and (not (keyword2 or keyword3))" ;; ;; See https://github.com/redguardtoo/mctags/ for more advanced tips. ;;; Code: (require 'xref nil t) (require 'cl-lib) (require 'counsel) ; counsel dependent on ivy (defgroup mctags nil "Complete solution to use ctags." :group 'tools) (defcustom mctags-ignore-directories '(;; VCS ".git" ".svn" ".cvs" ".bzr" ".hg" ;; project misc "bin" "fonts" "images" ;; Mac ".DS_Store" ;; html/javascript/css ".npm" ".tmp" ; TypeScript ".sass-cache" ; SCSS/SASS ".idea*" "node_modules" "bower_components" ;; python ".tox" ;; emacs ".cask") "Ignore directories. Wildcast is supported." :group 'mctags :type '(repeat 'string)) (defcustom mctags-ignore-filenames '(;; VCS ;; simple text file "*.json" ;; project misc "*.log" ;; Ctags "tags" "TAGS" ;; compressed "*.gz" "*.zip" "*.tar" "*.rar" ;; Global/Cscope "GTAGS" "GPATH" "GRTAGS" "cscope.files" ;; html/javascript/css "*min.js" "*min.css" ;; Images "*.png" "*.jpg" "*.jpeg" "*.gif" "*.bmp" "*.tiff" "*.ico" ;; documents "*.doc" "*.docx" "*.xls" "*.ppt" "*.pdf" "*.odt" ;; C/C++ "*.obj" "*.o" "*.a" "*.dylib" "*.lib" "*.d" "*.dll" "*.exe" ;; Java ".metadata*" "*.class" "*.war" "*.jar" ;; Emacs/Vim "*flymake" "#*#" ".#*" "*.swp" "*~" "*.elc" ;; Python "*.pyc") "Ignore file names. Wildcast is supported." :group 'mctags :type '(repeat 'string)) (defcustom mctags-project-file '(".svn" ".hg" ".git") "The file/directory used to locate project root. May be set using .dir-locals.el. Checks each entry if set to a list." :group 'mctags :type '(repeat 'string)) (defcustom mctags-max-file-size 64 "Ignore files bigger than `mctags-max-file-size' kilobytes." :group 'mctags :type 'integer) (defcustom mctags-after-update-tags-file-hook nil "Hook after tags file is actually updated. The parameter of hook is full path of tags file." :group 'mctags :type 'hook) (defcustom mctags-update-interval 300 "The interval (seconds) to update TAGS. Used by `mctags-update-tags-file'. Default value is 300 seconds." :group 'mctags :type 'integer) (defcustom mctags-find-program nil "GNU find program. Automatically detected if it's nil." :group 'mctags :type 'string) (defcustom mctags-ctags-program nil "Ctags program. Automatically detected if it's nil." :group 'mctags :type 'string) (defcustom mctags-grep-program nil "Grep program. Automatically detected if it's nil." :group 'mctags :type 'string) ;; Timer to run auto-update TAGS. (defvar mctags-timer nil "Internal timer.") (defvar mctags-keyword nil "The keyword to grep.") (defvar mctags-opts-cache '() "Grep CLI options cache.") (defvar mctags-tagname-history nil "History of tagnames.") (defvar mctags-find-tag-candidates nil "Find tag candidate.") (defun mctags-guess-program (name) "Guess executable path from its NAME on Windows." (let* (rlt) (when (eq system-type 'windows-nt) (cond ((file-executable-p (setq rlt (concat "c:\\\\cygwin64\\\\bin\\\\" na= me ".exe")))) ((file-executable-p (setq rlt (concat "d:\\\\cygwin64\\\\bin\\\\" na= me ".exe")))) ((file-executable-p (setq rlt (concat "e:\\\\cygwin64\\\\bin\\\\" na= me ".exe")))) (t (setq rlt nil)))) (if rlt rlt name))) ;;;###autoload (defun mctags-get-hostname () "Reliable way to get current hostname. `(getenv \"HOSTNAME\")' won't work because $HOSTNAME is NOT an environment variable. `system-name' won't work because /etc/hosts could be modified" (with-temp-buffer (shell-command "hostname" t) (goto-char (point-max)) (delete-char -1) (buffer-string))) (defun mctags-locate-tags-file () "Find tags file: either from `tags-file-name' or parent directory." (cond ((and tags-file-name (file-exists-p tags-file-name)) tags-file-name) (t (let* ((dir (locate-dominating-file default-directory "TAGS"))) (if dir (concat dir "TAGS")))))) (defun mctags-project-root () "Return the root of the project." (let* ((project-root (if (listp mctags-project-file) (cl-some (apply-partially 'locate-dominating-file default-directory) mctags-project-file) (locate-dominating-file default-directory mctags-project-file)))) (or (file-name-as-directory project-root) (progn (message "No project was defined.") nil)))) (defun mctags-scan-dir (src-dir &optional force) "Create tags file from SRC-DIR. If FORCE is t, the commmand is executed without checking the timer." ;; TODO save the ctags-opts into hash (let* ((find-pg (or mctags-find-program (mctags-guess-program "find"))) (ctags-pg (or mctags-ctags-program (mctags-guess-program "ctags"))) (default-directory src-dir) ;; run find&ctags to create TAGS (cmd (format "%s . \\( %s \\) -prune -o -type f -not -size +%sk %s= | %s -e -L -" find-pg (mapconcat (lambda (p) (format "-iwholename \"*/%s*\"" (shell-quote-argument (file-name= -as-directory p)))) mctags-ignore-directories " -or ") mctags-max-file-size (mapconcat (lambda (n) (format "-not -name \"%s\"" (shell-quote= -argument n))) mctags-ignore-filenames " ") ctags-pg)) (tags-file (concat (file-name-as-directory src-dir) "TAGS")) (doit (or force (not (file-exists-p tags-file))))) ;; always update cli options (when doit (message "%s at %s" cmd default-directory) (shell-command cmd) (visit-tags-table tags-file t)))) ;;;###autoload (defun mctags-directory-p (regex) "Does directory of current file match REGEX?" (let* ((dir (or (when buffer-file-name (file-name-directory buffer-file-name)) ;; buffer is created in real time default-directory ""))) (string-match-p regex dir))) ;;;###autoload (defun mctags-filename-p (regex) "Does current file match REGEX?" (let* ((file (or buffer-file-name default-directory ""))) (string-match-p regex file))) ;;;###autoload (defun mctags-update-tags-file-force (&optional quiet) "Update tags file. Be quiet if QUIET is t." (interactive) (let* ((tags-file (mctags-locate-tags-file))) (when tags-file (mctags-scan-dir (file-name-directory tags-file) t) (run-hook-with-args 'mctags-after-update-tags-file-hook tags-file) (unless quiet (message "%s is updated!" tags-file))))) (defun mctags-read-file (file) "Return FILE's content." (with-temp-buffer (insert-file-contents file) (buffer-string))) (defun mctags-collect-cands (tagname) "Parse tags file to find occurrences of TAGNAME." (let* ((str (mctags-read-file (mctags-locate-tags-file))) (tag-regex (concat "^.*?\\(" "\^?\\(.+[:.']" tagname "\\)\^A" "\\|" "\^?" tagname "\^A" "\\|" "\\<" tagname "[ \f\t()=3D,;]*\^?[0-9,]" "\\)")) (tag-file-path (file-name-directory (mctags-locate-tags-file))) cands) (with-temp-buffer (insert str) (modify-syntax-entry ?_ "w") (goto-char (point-min)) (while (search-forward tagname nil t) (beginning-of-line) (when (re-search-forward tag-regex (point-at-eol) 'goto-eol) (beginning-of-line) (re-search-forward "\\s-*\\(.*?\\)\\s-*\^?\\(.*?\\)\\([0-9]+\\),[= 0-9]+$") (end-of-line) (let* ((tag-line (match-string-no-properties 1)) (linenum (string-to-number (match-string-no-properties 3))) (filename (save-excursion (re-search-backward "\f") (re-search-forward "^\\(.*?\\),") (match-string-no-properties 1)))) (add-to-list 'cands (cons (format "%s:%d:%s" filename linenum tag-line) (list filename linenum tagname)))))) (modify-syntax-entry ?_ "_")) cands)) (defun mctags-selected-str () "Get selected string." (when (region-active-p) (buffer-substring-no-properties (region-beginning) (region-end)))) (defun mctags-tagname-at-point () "Get tag name at point." (if (mctags-selected-str) (mctags-selected-str) (find-tag-default))) (defun mctags-forward-line (lnum) "Forward LNUM lines." (when (and lnum (> lnum 0)) (goto-char (point-min)) (forward-line (1- lnum)))) (defun mctags-open-file-api (file linenum dir &optional tagname) "Open FILE and goto LINENUM while `default-directory' is DIR. Focus on TAGNAME if it's not nil." (let* ((default-directory dir)) ;; open file (find-file file) ;; goto line (mctags-forward-line linenum) (when tagname ;; highlight the tag (beginning-of-line) (re-search-forward tagname) (goto-char (match-beginning 0))) ;; flash, Emacs v25 only API (when (fboundp 'xref-pulse-momentarily) (xref-pulse-momentarily)))) (defun mctags-open-file (item) "Find and open file of ITEM." (let* ((val (cdr item))) (mctags-open-file-api (nth 0 val) ; file (nth 1 val) ; linenum (file-name-directory (mctags-locate-tags-file)) (nth 2 val)))) ; tagname (defun mctags-open-cand (cands time) "Open CANDS. Start open tags file at TIME." ;; mark current point for `pop-tag-mark' (when (fboundp 'xref-push-marker-stack) (xref-push-marker-stack)) (cond ((=3D 1 (length cands)) ;; open the file directly (mctags-open-file (car cands))) (t (ivy-read (format "Find Tag (%.01f seconds): " (float-time (time-since time))) cands :action #'mctags-open-file :caller 'mctags-find-tag)))) (defun mctags-find-tag-occur () "Generate a custom occur buffer for `mctags-find-tag'." (unless (eq major-mode 'ivy-occur-grep-mode) (ivy-occur-grep-mode)) ;; we use regex in elisp, don't unquote regex (let* ((cands (ivy--filter ivy-text mctags-find-tag-candidates))) ;; Need precise number of header lines for `wgrep' to work. (insert (format "-*- mode:grep; default-directory: %S -*-\n\n\n" (file-name-directory (mctags-locate-tags-file)))) (insert (format "%d candidates:\n" (length cands))) (ivy--occur-insert-lines (mapcar (lambda (cand) (concat "./" cand)) cands)))) (ivy-set-occur 'mctags-find-tag 'mctags-find-tag-occur) (ivy-set-display-transformer 'mctags-find-tag 'counsel-git-grep-transformer) (defun mctags-tags-file-must-exist () "Make sure tags file does exist." (when (not (mctags-locate-tags-file)) (let* ((src-dir (read-directory-name "Ctags will scan code at:" (mctags-project-root)))) (if src-dir (mctags-scan-dir src-dir t) (error "Can't find TAGS. Please run `mctags-scan-code'!"))))) ;;;###autoload (defun mctags-scan-code (&optional dir) "Use Ctags to scan code at DIR." (interactive) (let* ((src-dir (or dir (read-directory-name "Ctags will scan code at:" (mctags-project-root))))) (when src-dir (mctags-scan-dir src-dir t)))) (defun mctags-find-tag-api (tagname) "Find tag with given TAGNAME." (let* ((time (current-time))) (setq mctags-find-tag-candidates (mctags-collect-cands tagname)) (add-to-list 'mctags-tagname-history tagname) (cond ((not mctags-find-tag-candidates) ;; OK let's try grep if no tag found (mctags-grep tagname "No tag found. ")) (t (mctags-open-cand mctags-find-tag-candidates time))))) ;;;###autoload (defun mctags-find-tag () "Input tagname to find tag." (interactive) (let* ((tagname (read-string "Please input tag name:"))) (when (and tagname (not (string=3D tagname ""))) (mctags-find-tag-api tagname)))) ;;;###autoload (defun mctags-find-tag-at-point () "Find tag using tagname at point, and display all matched tags." (interactive) (mctags-tags-file-must-exist) (let* ((tagname (mctags-tagname-at-point))) (cond (tagname (mctags-find-tag-api tagname)) (t (message "No tag at point"))))) ;;;###autoload (defun mctags-update-tags-file() "Scan the code and create tags file again." (interactive) (let* ((dir (and buffer-file-name (file-name-directory buffer-file-name))) (tags-file (mctags-locate-tags-file))) (when (and dir tags-file (string-match-p (file-name-directory tags-file) dir)) (cond ((not mctags-timer) ;; start timer if not started yet (setq mctags-timer (current-time))) ((< (- (float-time (current-time)) (float-time mctags-timer)) mctags-update-interval) ;; do nothing, can't run ctags too often ) (t (setq mctags-timer (current-time)) (mctags-update-tags-file-force t) (message "All tag files have been updated after %d seconds!" (- (float-time (current-time)) (float-time mctags-timer)))))))) (defun mctags-read-keyword (hint) "Read keyword with HINT." (cond ((region-active-p) (setq mctags-keyword (counsel-unquote-regex-parens (mctags-selected-str= ))) ;; de-select region (set-mark-command nil)) (t (setq mctags-keyword (read-string hint)))) mctags-keyword) (defun mctags-has-quick-grep () "Does ripgrep program exist?" (executable-find "rg")) (defun mctags-exclude-opts (use-cache) "Grep CLI options. IF USE-CACHE is t, the options is read from cache." (let* ((ignore-dirs (if use-cache (plist-get mctags-opts-cache :ignore-di= rs) mctags-ignore-directories)) (ignore-file-names (if use-cache (plist-get mctags-opts-cache :ign= ore-file-names) mctags-ignore-filenames))) (cond ((mctags-has-quick-grep) (concat (mapconcat (lambda (e) (format "-g=3D'!%s/*'" (shell-quote-argument e))) ignore-dirs " ") " " (mapconcat (lambda (e) (format "-g=3D'!%s'" (shell-quote-argument e))) ignore-file-names " "))) (t (concat (mapconcat (lambda (e) (format "--exclude-dir=3D'%s'" (shell-quote-argu= ment e))) ignore-dirs " ") " " (mapconcat (lambda (e) (format "--exclude=3D'%s'" (shell-quote-argument= e))) ignore-file-names " ")))))) (defun mctags-grep-cli (keyword use-cache) "Use KEYWORD and USE-CACHE to build CLI. Extended regex is used, like (pattern1|pattern2)." (cond ((mctags-has-quick-grep) (format "%s %s \"%s\" --" (concat (executable-find "rg") " -n -M 512 --no-heading --color never -s") (mctags-exclude-opts use-cache) keyword)) (t ;; use extended regex always (format "%s -rsnE %s \"%s\" *" (or mctags-grep-program (mctags-guess-program "grep")) (mctags-exclude-opts use-cache) keyword)))) ;;;###autoload (defun mctags-grep (&optional default-keyword hint) "Grep at project root directory or current directory. Try to find best grep program (ripgrep, grep...) automatically. Extended regex like (pattern1|pattern2) is used. If DEFAULT-KEYWORD is not nil, it's used as grep keyword. If HINT is not nil, it's used as grep hint." (interactive) (let* ((keyword (if default-keyword default-keyword (mctags-read-keyword "Enter grep pattern: "))) (default-directory (mctags-project-root)) (time (current-time)) (collection (split-string (shell-command-to-string (mctags-grep-cl= i keyword nil)) "[\r\n]+" t)) (dir-summary (file-name-as-directory (file-name-base (directory-fi= le-name (mctags-project-root)))))) (setq mctags-opts-cache (plist-put mctags-opts-cache :ignore-dirs mctag= s-ignore-directories)) (setq mctags-opts-cache (plist-put mctags-opts-cache :ignore-file-names= mctags-ignore-filenames)) (ivy-read (concat hint (format "Grep \"%s\" at %s (%.01f seconds): " keyword dir-summary (float-time (time-since time)))) collection :history 'counsel-git-grep-history ; share history with couns= el :action `(lambda (line) (let* ((lst (split-string line ":")) (file (car lst)) (linenum (string-to-number (cadr lst)))) (mctags-open-file-api file linenum (mctags-proje= ct-root)))) :caller 'mctags-grep))) (defun mctags-grep-occur () "Generate a custom occur buffer for `mctags-grep'." (unless (eq major-mode 'ivy-occur-grep-mode) (ivy-occur-grep-mode)) ;; useless to set `default-directory', it's already correct ;; we use regex in elisp, don't unquote regex (let* ((cands (ivy--filter ivy-text (split-string (shell-command-to-string (mctags= -grep-cli mctags-keyword t)) "[\r\n]+" t)))) ;; Need precise number of header lines for `wgrep' to work. (insert (format "-*- mode:grep; default-directory: %S -*-\n\n\n" default-directory)) (insert (format "%d candidates:\n" (length cands))) (ivy--occur-insert-lines (mapcar (lambda (cand) (concat "./" cand)) cands)))) (ivy-set-occur 'mctags-grep 'mctags-grep-occur) (ivy-set-display-transformer 'mctags-grep 'counsel-git-grep-transformer) (provide 'mctags) ;;; mctags.el ends here --=-=-= Content-Type: text/plain -- Best Regards, Chen Bin -- Help me, help you --=-=-=--