]> code.delx.au - gnu-emacs/blob - lisp/cedet/ede/detect.el
Update copyright year to 2015
[gnu-emacs] / lisp / cedet / ede / detect.el
1 ;;; ede/detect.el --- EDE project detection and file associations
2
3 ;; Copyright (C) 2014-2015 Free Software Foundation, Inc.
4
5 ;; Author: Eric M. Ludlam <eric@siege-engine.com>
6
7 ;; This file is part of GNU Emacs.
8
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.
13
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.
18
19 ;; You should have received a copy of the GNU General Public License
20 ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
21
22 ;;; Commentary:
23 ;;
24 ;; Project detection for EDE;
25 ;;
26 ;; Detection comes in multiple forms:
27 ;;
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.
33
34 ;;; Code:
35
36 (require 'ede/auto) ;; Autoload settings.
37
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))))
46
47
48 ;;; BASIC PROJECT SCAN
49 ;;
50 (defun ede--detect-stop-scan-p (dir)
51 "Return non-nil if we need to stop scanning upward in DIR."
52 ;;(let ((stop
53 (file-exists-p (expand-file-name ".ede_stop_scan" dir)))
54 ;;)
55 ;;(when stop
56 ;;(message "Stop Scan at %s" dir))
57 ;;stop))
58
59 (defvar ede--detect-found-project nil
60 "When searching for a project, temporarily save that file.")
61
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)
65 (throw 'stopscan nil)
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)
70 (progn
71 ;; We found one!
72 (setq ede--detect-found-project (car types)))
73 (setq types (cdr types)))
74 )
75 ede--detect-found-project)))
76
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
80 use any file caches.
81 Return a cons cell:
82 ( ROOTDIR . PROJECT-AUTOLOAD)"
83 (let* ((ede--detect-found-project nil)
84 (root
85 (catch 'stopscan
86 (locate-dominating-file directory
87 'ede--detect-ldf-predicate))))
88 (when root
89 (cons root ede--detect-found-project))))
90
91 ;;; Root Only project detect
92 ;;
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)
99 (throw 'stopscan nil)
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))
103 (if (and
104 (oref (car types) root-only)
105 (ede-auto-detect-in-dir (car types) dir))
106 (progn
107 ;; We found one!
108 (setq ede--detect-found-project (car types)))
109 (setq types (cdr types)))
110 )
111 ede--detect-found-project)))
112
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
116 use any file caches.
117 Return a cons cell:
118 ( ROOTDIR . PROJECT-AUTOLOAD)"
119 (let* ((ede--detect-found-project nil)
120 (root
121 (catch 'stopscan
122 (locate-dominating-file directory
123 'ede--detect-ldf-rootonly-predicate))))
124 (when root
125 (cons root ede--detect-found-project))))
126
127
128 ;;; NESTED PROJECT SCAN
129 ;;
130 ;; For projects that can have their dominating file exist in all their
131 ;; sub-directories as well.
132
133 (defvar ede--detect-nomatch-auto nil
134 "An ede autoload that needs to be un-matched.")
135
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.
144 t
145 ;; If it is different, check updir for the file.
146 (not (ede-auto-detect-in-dir ede--detect-nomatch-auto updir))))))
147
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)))
156 root))
157
158 ;;; TOP LEVEL SCAN
159 ;;
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.
164 Return a cons cell:
165 ( ROOTDIR . PROJECT-AUTOLOAD)"
166 (let* ((scan (ede--detect-scan-directory-for-project directory))
167 (root (car scan))
168 (auto (cdr scan)))
169 (when scan
170 ;; If what we found is already a root-only project, return it.
171 (if (oref auto root-only)
172 scan
173
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.
179 (if moreroot
180 moreroot
181
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)))
185
186 ;; Non-generic non-root projects also need to rescan upward.
187 (cons (ede--detect-scan-directory-for-project-root root auto) auto)))
188
189 )))
190
191 ;;; TEST
192 ;;
193 ;; A quick interactive testing fcn.
194 (defun ede-detect-qtest ()
195 "Run a quick test for autodetecting on BUFFER."
196 (interactive)
197 (let ((start (current-time))
198 (ans (ede-detect-directory-for-project default-directory))
199 (end (current-time)))
200 (if ans
201 (message "Project found in %d sec @ %s of type %s"
202 (float-time (time-subtract end start))
203 (car ans)
204 (eieio-object-name-string (cdr ans)))
205 (message "No Project found.") )))
206
207
208 (provide 'ede/detect)
209
210 ;;; ede/detect.el ends here