summaryrefslogtreecommitdiff
path: root/lisp/cedet/ede/detect.el
diff options
context:
space:
mode:
Diffstat (limited to 'lisp/cedet/ede/detect.el')
-rw-r--r--lisp/cedet/ede/detect.el210
1 files changed, 210 insertions, 0 deletions
diff --git a/lisp/cedet/ede/detect.el b/lisp/cedet/ede/detect.el
new file mode 100644
index 00000000000..2b329185c84
--- /dev/null
+++ b/lisp/cedet/ede/detect.el
@@ -0,0 +1,210 @@
+;;; ede/detect.el --- EDE project detection and file associations
+
+;; Copyright (C) 2014 Free Software Foundation, Inc.
+
+;; Author: Eric M. Ludlam <eric@siege-engine.com>
+
+;; This file is 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:
+;;
+;; Project detection for EDE;
+;;
+;; Detection comes in multiple forms:
+;;
+;; `ede-detect-scan-directory-for-project' -
+;; Scan for a project via the file system.
+;; `ede-detect-directory-for-project' -
+;; Check our file cache for a project. If that failes, use
+;; the scan fcn above.
+
+;;; Code:
+
+(require 'ede/auto) ;; Autoload settings.
+
+(when (or (<= emacs-major-version 23)
+ ;; predicate as name added in Emacs 24.2
+ (and (= emacs-major-version 24)
+ (< emacs-minor-version 2)))
+ (message "Loading CEDET fallback autoload library.")
+ (require 'cedet/dominate
+ (expand-file-name "../../../etc/fallback-libraries/dominate.el"
+ (file-name-directory load-file-name))))
+
+
+;;; BASIC PROJECT SCAN
+;;
+(defun ede--detect-stop-scan-p (dir)
+ "Return non-nil if we need to stop scanning upward in DIR."
+ ;;(let ((stop
+ (file-exists-p (expand-file-name ".ede_stop_scan" dir)))
+;;)
+;;(when stop
+;;(message "Stop Scan at %s" dir))
+;;stop))
+
+(defvar ede--detect-found-project nil
+ "When searching for a project, temporarilly save that file.")
+
+(defun ede--detect-ldf-predicate (dir)
+ "Non-nil if DIR contain any known EDE project types."
+ (if (ede--detect-stop-scan-p dir)
+ (throw 'stopscan nil)
+ (let ((types ede-project-class-files))
+ ;; Loop over all types, loading in the first type that we find.
+ (while (and types (not ede--detect-found-project))
+ (if (ede-auto-detect-in-dir (car types) dir)
+ (progn
+ ;; We found one!
+ (setq ede--detect-found-project (car types)))
+ (setq types (cdr types)))
+ )
+ ede--detect-found-project)))
+
+(defun ede--detect-scan-directory-for-project (directory)
+ "Detect an EDE project for the current DIRECTORY by scanning.
+This function ALWAYS scans files and directories and DOES NOT
+use any file caches.
+Return a cons cell:
+ ( ROOTDIR . PROJECT-AUTOLOAD)"
+ (let* ((ede--detect-found-project nil)
+ (root
+ (catch 'stopscan
+ (locate-dominating-file directory
+ 'ede--detect-ldf-predicate))))
+ (when root
+ (cons root ede--detect-found-project))))
+
+;;; Root Only project detect
+;;
+;; For projects that only have a detectible ROOT file, but may in fact
+;; contain a generic file such as a Makefile, we need to do a second scan
+;; to make sure we don't miss-match.
+(defun ede--detect-ldf-rootonly-predicate (dir)
+ "Non-nil if DIR contain any known EDE project types."
+ (if (ede--detect-stop-scan-p dir)
+ (throw 'stopscan nil)
+ (let ((types ede-project-class-files))
+ ;; Loop over all types, loading in the first type that we find.
+ (while (and types (not ede--detect-found-project))
+ (if (and
+ (oref (car types) root-only)
+ (ede-auto-detect-in-dir (car types) dir))
+ (progn
+ ;; We found one!
+ (setq ede--detect-found-project (car types)))
+ (setq types (cdr types)))
+ )
+ ede--detect-found-project)))
+
+(defun ede--detect-scan-directory-for-rootonly-project (directory)
+ "Detect an EDE project for the current DIRECTORY by scanning.
+This function ALWAYS scans files and directories and DOES NOT
+use any file caches.
+Return a cons cell:
+ ( ROOTDIR . PROJECT-AUTOLOAD)"
+ (let* ((ede--detect-found-project nil)
+ (root
+ (catch 'stopscan
+ (locate-dominating-file directory
+ 'ede--detect-ldf-rootonly-predicate))))
+ (when root
+ (cons root ede--detect-found-project))))
+
+
+;;; NESTED PROJECT SCAN
+;;
+;; For projects that can have their dominating file exist in all their
+;; sub-directories as well.
+
+(defvar ede--detect-nomatch-auto nil
+ "An ede autoload that needs to be un-matched.")
+
+(defun ede--detect-ldf-root-predicate (dir)
+ "Non-nil if DIR no longer match `ede--detect-nomatch-auto'."
+ (or (ede--detect-stop-scan-p dir)
+ ;; To know if DIR is at the top, we need to look just above
+ ;; to see if there is a match.
+ (let ((updir (file-name-directory (directory-file-name dir))))
+ (if (equal updir dir)
+ ;; If it didn't change, then obviously this must be the top.
+ t
+ ;; If it is different, check updir for the file.
+ (not (ede-auto-detect-in-dir ede--detect-nomatch-auto updir))))))
+
+(defun ede--detect-scan-directory-for-project-root (directory auto)
+ "If DIRECTORY has already been detected with AUTO, find the root.
+Some projects have their dominating file in all their directories, such
+as Project.ede. In that case we will detect quickly, but then need
+to scan upward to find the topmost occurance of that file."
+ (let* ((ede--detect-nomatch-auto auto)
+ (root (locate-dominating-file directory
+ 'ede--detect-ldf-root-predicate)))
+ root))
+
+;;; TOP LEVEL SCAN
+;;
+;; This function for combining the above scans.
+(defun ede-detect-directory-for-project (directory)
+ "Detect an EDE project for the current DIRECTORY.
+Scan the filesystem for a project.
+Return a cons cell:
+ ( ROOTDIR . PROJECT-AUTOLOAD)"
+ (let* ((scan (ede--detect-scan-directory-for-project directory))
+ (root (car scan))
+ (auto (cdr scan)))
+ (when scan
+ ;; If what we found is already a root-only project, return it.
+ (if (oref auto root-only)
+ scan
+
+ ;; If what we found is a generic project, check to make sure we aren't
+ ;; in some other kind of root project.
+ (if (oref auto generic-p)
+ (let ((moreroot (ede--detect-scan-directory-for-rootonly-project root)))
+ ;; If we found a rootier project, return that.
+ (if moreroot
+ moreroot
+
+ ;; If we didn't find a root from the generic project, then
+ ;; we need to rescan upward.
+ (cons (ede--detect-scan-directory-for-project-root root auto) auto)))
+
+ ;; Non-generic non-root projects also need to rescan upward.
+ (cons (ede--detect-scan-directory-for-project-root root auto) auto)))
+
+ )))
+
+;;; TEST
+;;
+;; A quick interactive testing fcn.
+(defun ede-detect-qtest ()
+ "Run a quick test for autodetecting on BUFFER."
+ (interactive)
+ (let ((start (current-time))
+ (ans (ede-detect-directory-for-project default-directory))
+ (end (current-time)))
+ (if ans
+ (message "Project found in %d sec @ %s of type %s"
+ (float-time (time-subtract end start))
+ (car ans)
+ (eieio-object-name-string (cdr ans)))
+ (message "No Project found.") )))
+
+
+(provide 'ede/detect)
+
+;;; ede/detect.el ends here