X-Git-Url: https://code.delx.au/gnu-emacs-elpa/blobdiff_plain/b9840a3cd477063193ab89dd807acffb3b2df171..900ae0d7a2270ea1d2ea00567e80a619333fd4e5:/company-cmake.el diff --git a/company-cmake.el b/company-cmake.el index 0c96eb676..010df32e4 100644 --- a/company-cmake.el +++ b/company-cmake.el @@ -1,24 +1,22 @@ -;;; company-cmake.el --- company-mode completion back-end for CMake +;;; company-cmake.el --- company-mode completion backend for CMake -;; Copyright (C) 2013 Chen Bin +;; Copyright (C) 2013-2014 Free Software Foundation, Inc. ;; Author: Chen Bin -;; Version: 0.1 +;; Version: 0.2 -;; This file is NOT part of GNU Emacs. - -;; GNU Emacs is free software: you can redistribute it and/or modify +;; 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. -;; GNU Emacs is distributed in the hope that it will be useful, +;; 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 GNU Emacs. If not, see . +;; along with this program. If not, see . ;;; Commentary: ;; @@ -26,10 +24,12 @@ ;; commands used by CMake. And their descriptions. ;;; Code: + (require 'company) +(require 'cl-lib) (defgroup company-cmake nil - "Completion back-end for CMake." + "Completion backend for CMake." :group 'company) (defcustom company-cmake-executable @@ -45,48 +45,115 @@ They affect which types of symbols we get completion candidates for.") (defvar company-cmake--completion-pattern - "^\\(%s[a-zA-Z0-9_]%s\\)$" + "^\\(%s[a-zA-Z0-9_<>]%s\\)$" "Regexp to match the candidates.") (defvar company-cmake-modes '(cmake-mode) "Major modes in which cmake may complete.") +(defvar company-cmake--candidates-cache nil + "Cache for the raw candidates.") + (defvar company-cmake--meta-command-cache nil "Cache for command arguments to retrieve descriptions for the candidates.") -(defun company-cmake--parse-output (prefix cmd) - "Analyze the temp buffer and collect lines." - (goto-char (point-min)) - (let ((pattern (format company-cmake--completion-pattern +(defun company-cmake--replace-tags (rlt) + (setq rlt (replace-regexp-in-string + "\\(.*?\\(IS_GNU\\)?\\)\\(.*\\)" + (lambda (_match) + (mapconcat 'identity + (if (match-beginning 2) + '("\\1CXX\\3" "\\1C\\3" "\\1G77\\3") + '("\\1CXX\\3" "\\1C\\3" "\\1Fortran\\3")) + "\n")) + rlt t)) + (setq rlt (replace-regexp-in-string + "\\(.*\\)\\(.*\\)" + (mapconcat 'identity '("\\1DEBUG\\2" "\\1RELEASE\\2" + "\\1RELWITHDEBINFO\\2" "\\1MINSIZEREL\\2") + "\n") + rlt)) + rlt) + +(defun company-cmake--fill-candidates-cache (arg) + "Fill candidates cache if needed." + (let (rlt) + (unless company-cmake--candidates-cache + (setq company-cmake--candidates-cache (make-hash-table :test 'equal))) + + ;; If hash is empty, fill it. + (unless (gethash arg company-cmake--candidates-cache) + (with-temp-buffer + (let ((res (call-process company-cmake-executable nil t nil arg))) + (unless (zerop res) + (message "cmake executable exited with error=%d" res))) + (setq rlt (buffer-string))) + (setq rlt (company-cmake--replace-tags rlt)) + (puthash arg rlt company-cmake--candidates-cache)) + )) + +(defun company-cmake--parse (prefix content cmd) + (let ((start 0) + (pattern (format company-cmake--completion-pattern (regexp-quote prefix) (if (zerop (length prefix)) "+" "*"))) - (case-fold-search nil) - lines match) - (while (re-search-forward pattern nil t) - (setq match (match-string-no-properties 1)) - (puthash match cmd company-cmake--meta-command-cache) - (push match lines)) - lines)) + (lines (split-string content "\n")) + match + rlt) + (dolist (line lines) + (when (string-match pattern line) + (let ((match (match-string 1 line))) + (when match + (puthash match cmd company-cmake--meta-command-cache) + (push match rlt))))) + rlt)) (defun company-cmake--candidates (prefix) - (let ((res 0) - results - cmd) - (setq company-cmake--meta-command-cache (make-hash-table :test 'equal)) + (let (results + cmd-opts + str) + + (unless company-cmake--meta-command-cache + (setq company-cmake--meta-command-cache (make-hash-table :test 'equal))) + (dolist (arg company-cmake-executable-arguments) - (with-temp-buffer - (setq res (call-process company-cmake-executable nil t nil arg)) - (unless (eq 0 res) - (message "cmake executable exited with error=%d" res)) - (setq cmd (replace-regexp-in-string "-list$" "" arg) ) - (setq results (nconc results (company-cmake--parse-output prefix cmd))))) + (company-cmake--fill-candidates-cache arg) + (setq cmd-opts (replace-regexp-in-string "-list$" "" arg) ) + + (setq str (gethash arg company-cmake--candidates-cache)) + (when str + (setq results (nconc results + (company-cmake--parse prefix str cmd-opts))))) results)) -(defun company-cmake--meta (prefix) - (let ((cmd-opts (gethash prefix company-cmake--meta-command-cache)) +(defun company-cmake--unexpand-candidate (candidate) + (cond + ((string-match "^CMAKE_\\(C\\|CXX\\|Fortran\\)\\(_.*\\)$" candidate) + (setq candidate (concat "CMAKE_" (match-string 2 candidate)))) + + ;; C flags + ((string-match "^\\(.*_\\)IS_GNU\\(C\\|CXX\\|G77\\)$" candidate) + (setq candidate (concat (match-string 1 candidate) "IS_GNU"))) + + ;; C flags + ((string-match "^\\(.*_\\)OVERRIDE_\\(C\\|CXX\\|Fortran\\)$" candidate) + (setq candidate (concat (match-string 1 candidate) "OVERRIDE_"))) + + ((string-match "^\\(.*\\)\\(_DEBUG\\|_RELEASE\\|_RELWITHDEBINFO\\|_MINSIZEREL\\)\\(.*\\)$" candidate) + (setq candidate (concat (match-string 1 candidate) + "_" + (match-string 3 candidate))))) + candidate) + +(defun company-cmake--meta (candidate) + (let ((cmd-opts (gethash candidate company-cmake--meta-command-cache)) result) + (setq candidate (company-cmake--unexpand-candidate candidate)) + + ;; Don't cache the documentation of every candidate (command) + ;; Cache in this case will cost too much memory. (with-temp-buffer - (call-process company-cmake-executable nil t nil cmd-opts prefix) + (call-process company-cmake-executable nil t nil cmd-opts candidate) ;; Go to the third line, trim it and return the result. ;; Tested with cmake 2.8.9. (goto-char (point-min)) @@ -96,10 +163,12 @@ They affect which types of symbols we get completion candidates for.") (setq result (replace-regexp-in-string "^[ \t\n\r]+" "" result)) result))) -(defun company-cmake--doc-buffer (prefix) - (let ((cmd-opts (gethash prefix company-cmake--meta-command-cache))) +(defun company-cmake--doc-buffer (candidate) + (let ((cmd-opts (gethash candidate company-cmake--meta-command-cache))) + + (setq candidate (company-cmake--unexpand-candidate candidate)) (with-temp-buffer - (call-process company-cmake-executable nil t nil cmd-opts prefix) + (call-process company-cmake-executable nil t nil cmd-opts candidate) ;; Go to the third line, trim it and return the doc buffer. ;; Tested with cmake 2.8.9. (goto-char (point-min)) @@ -109,10 +178,10 @@ They affect which types of symbols we get completion candidates for.") (point-max)))))) (defun company-cmake (command &optional arg &rest ignored) - "`company-mode' completion back-end for CMake. + "`company-mode' completion backend for CMake. CMake is a cross-platform, open-source make system." (interactive (list 'interactive)) - (case command + (cl-case command (interactive (company-begin-backend 'company-cmake)) (init (when (memq major-mode company-cmake-modes) (unless company-cmake-executable