1 ;;; ede/detect.el --- EDE project detection and file associations
3 ;; Copyright (C) 2014-2018 Free Software Foundation, Inc.
5 ;; Author: Eric M. Ludlam <eric@siege-engine.com>
7 ;; This file is part of GNU Emacs.
9 ;; GNU Emacs is free software: you can redistribute it and/or modify
10 ;; it under the terms of the GNU General Public License as published by
11 ;; the Free Software Foundation, either version 3 of the License, or
12 ;; (at your option) any later version.
14 ;; GNU Emacs is distributed in the hope that it will be useful,
15 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
16 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 ;; GNU General Public License for more details.
19 ;; You should have received a copy of the GNU General Public License
20 ;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
24 ;; Project detection for EDE;
26 ;; Detection comes in multiple forms:
28 ;; `ede-detect-scan-directory-for-project' -
29 ;; Scan for a project via the file system.
30 ;; `ede-detect-directory-for-project' -
31 ;; Check our file cache for a project. If that fails, use
32 ;; the scan fcn above.
36 (require 'ede
/auto
) ;; Autoload settings.
38 (when (or (<= emacs-major-version
23)
39 ;; predicate as name added in Emacs 24.2
40 (and (= emacs-major-version
24)
41 (< emacs-minor-version
2)))
42 (message "Loading CEDET fallback autoload library.")
43 (require 'cedet
/dominate
44 (expand-file-name "../../../etc/fallback-libraries/dominate.el"
45 (file-name-directory load-file-name
))))
48 ;;; BASIC PROJECT SCAN
50 (defun ede--detect-stop-scan-p (dir)
51 "Return non-nil if we need to stop scanning upward in DIR."
53 (file-exists-p (expand-file-name ".ede_stop_scan" dir
)))
56 ;;(message "Stop Scan at %s" dir))
59 (defvar ede--detect-found-project nil
60 "When searching for a project, temporarily save that file.")
62 (defun ede--detect-ldf-predicate (dir)
63 "Non-nil if DIR contain any known EDE project types."
64 (if (ede--detect-stop-scan-p dir
)
66 (let ((types ede-project-class-files
))
67 ;; Loop over all types, loading in the first type that we find.
68 (while (and types
(not ede--detect-found-project
))
69 (if (ede-auto-detect-in-dir (car types
) dir
)
72 (setq ede--detect-found-project
(car types
)))
73 (setq types
(cdr types
)))
75 ede--detect-found-project
)))
77 (defun ede--detect-scan-directory-for-project (directory)
78 "Detect an EDE project for the current DIRECTORY by scanning.
79 This function ALWAYS scans files and directories and DOES NOT
82 ( ROOTDIR . PROJECT-AUTOLOAD)"
83 (let* ((ede--detect-found-project nil
)
86 (locate-dominating-file directory
87 'ede--detect-ldf-predicate
))))
89 (cons root ede--detect-found-project
))))
91 ;;; Root Only project detect
93 ;; For projects that only have a detectable ROOT file, but may in fact
94 ;; contain a generic file such as a Makefile, we need to do a second scan
95 ;; to make sure we don't miss-match.
96 (defun ede--detect-ldf-rootonly-predicate (dir)
97 "Non-nil if DIR contain any known EDE project types."
98 (if (ede--detect-stop-scan-p dir
)
100 (let ((types ede-project-class-files
))
101 ;; Loop over all types, loading in the first type that we find.
102 (while (and types
(not ede--detect-found-project
))
104 (oref (car types
) root-only
)
105 (ede-auto-detect-in-dir (car types
) dir
))
108 (setq ede--detect-found-project
(car types
)))
109 (setq types
(cdr types
)))
111 ede--detect-found-project
)))
113 (defun ede--detect-scan-directory-for-rootonly-project (directory)
114 "Detect an EDE project for the current DIRECTORY by scanning.
115 This function ALWAYS scans files and directories and DOES NOT
118 ( ROOTDIR . PROJECT-AUTOLOAD)"
119 (let* ((ede--detect-found-project nil
)
122 (locate-dominating-file directory
123 'ede--detect-ldf-rootonly-predicate
))))
125 (cons root ede--detect-found-project
))))
128 ;;; NESTED PROJECT SCAN
130 ;; For projects that can have their dominating file exist in all their
131 ;; sub-directories as well.
133 (defvar ede--detect-nomatch-auto nil
134 "An ede autoload that needs to be un-matched.")
136 (defun ede--detect-ldf-root-predicate (dir)
137 "Non-nil if DIR no longer match `ede--detect-nomatch-auto'."
138 (or (ede--detect-stop-scan-p dir
)
139 ;; To know if DIR is at the top, we need to look just above
140 ;; to see if there is a match.
141 (let ((updir (file-name-directory (directory-file-name dir
))))
142 (if (equal updir dir
)
143 ;; If it didn't change, then obviously this must be the top.
145 ;; If it is different, check updir for the file.
146 (not (ede-auto-detect-in-dir ede--detect-nomatch-auto updir
))))))
148 (defun ede--detect-scan-directory-for-project-root (directory auto
)
149 "If DIRECTORY has already been detected with AUTO, find the root.
150 Some projects have their dominating file in all their directories, such
151 as Project.ede. In that case we will detect quickly, but then need
152 to scan upward to find the topmost occurrence of that file."
153 (let* ((ede--detect-nomatch-auto auto
)
154 (root (locate-dominating-file directory
155 'ede--detect-ldf-root-predicate
)))
160 ;; This function for combining the above scans.
161 (defun ede-detect-directory-for-project (directory)
162 "Detect an EDE project for the current DIRECTORY.
163 Scan the filesystem for a project.
165 ( ROOTDIR . PROJECT-AUTOLOAD)"
166 (let* ((scan (ede--detect-scan-directory-for-project directory
))
170 ;; If what we found is already a root-only project, return it.
171 (if (oref auto root-only
)
174 ;; If what we found is a generic project, check to make sure we aren't
175 ;; in some other kind of root project.
176 (if (oref auto generic-p
)
177 (let ((moreroot (ede--detect-scan-directory-for-rootonly-project root
)))
178 ;; If we found a rootier project, return that.
182 ;; If we didn't find a root from the generic project, then
183 ;; we need to rescan upward.
184 (cons (ede--detect-scan-directory-for-project-root root auto
) auto
)))
186 ;; Non-generic non-root projects also need to rescan upward.
187 (cons (ede--detect-scan-directory-for-project-root root auto
) auto
)))
193 ;; A quick interactive testing fcn.
194 (defun ede-detect-qtest ()
195 "Run a quick test for autodetecting on BUFFER."
197 (let ((start (current-time))
198 (ans (ede-detect-directory-for-project default-directory
))
199 (end (current-time)))
201 (message "Project found in %d sec @ %s of type %s"
202 (float-time (time-subtract end start
))
204 (eieio-object-name-string (cdr ans
)))
205 (message "No Project found.") )))
208 (provide 'ede
/detect
)
210 ;;; ede/detect.el ends here