Mercurial > hg > xemacs-beta
comparison lisp/oobr/br-python-ft.el @ 0:376386a54a3c r19-14
Import from CVS: tag r19-14
author | cvs |
---|---|
date | Mon, 13 Aug 2007 08:45:50 +0200 |
parents | |
children | 4103f0995bd7 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:376386a54a3c |
---|---|
1 ;;!emacs | |
2 ;; | |
3 ;; FILE: br-python-ft.el | |
4 ;; SUMMARY: Python OO-Browser class and member functions. | |
5 ;; USAGE: GNU Emacs Lisp Library | |
6 ;; KEYWORDS: python, oop, tools | |
7 ;; | |
8 ;; AUTHOR: Harri Pasanen, based on the C++ feature browser | |
9 ;; by Bob Weiner | |
10 ;; ORG: Tekla Oy | |
11 ;; | |
12 ;; ORIG-DATE: 5-Apr-96 | |
13 ;; LAST-MOD: 1-May-96 | |
14 ;; | |
15 ;; Copyright (C) 1990-1995 Free Software Foundation, Inc. | |
16 ;; See the file BR-COPY for license information. | |
17 ;; | |
18 ;; This file is part of the OO-Browser. | |
19 ;; | |
20 ;; DESCRIPTION: | |
21 ;; There may still be traces of C++ origin in this file. | |
22 ;; DESCRIP-END. | |
23 | |
24 ;;; ************************************************************************ | |
25 ;;; Other required Elisp libraries | |
26 ;;; ************************************************************************ | |
27 | |
28 (require 'br-python) | |
29 | |
30 ;;; ************************************************************************ | |
31 ;;; Public variables | |
32 ;;; ************************************************************************ | |
33 | |
34 (defvar python-import-dirs '("/usr/local/lib/python/") | |
35 "Ordered list of module directories by default searched by python | |
36 interpreter. Each directory must end with a directory separator.") | |
37 | |
38 (defconst python-type-tag-separator "@" | |
39 "String that separates a tag's type from its normalized definition form. | |
40 This should be a single character which is unchanged when quoted for use as a | |
41 literal in a regular expression.") | |
42 | |
43 (defconst python-tag-fields-regexp | |
44 ;; The \\\\? below is necessary because we sometimes use this expression to | |
45 ;; test against a string that has ben regexp-quoted and some of the | |
46 ;; characters in br-feature-type-regexp will then be preceded by \\. | |
47 (format "\\`\\([^%s \n]+\\)%s\\\\?\\(%s \\)\\([^%s\n]+\\)%s" | |
48 python-type-tag-separator python-type-tag-separator br-feature-type-regexp | |
49 python-type-tag-separator python-type-tag-separator) | |
50 "Regexp matching the fields of a Python feature tag line. | |
51 Group 1 is the class of the feature. Group 2 is the prefix preceding the | |
52 feature when displayed within a listing buffer. Group 3 is the feature name. | |
53 The feature definition signature begins at the end of the regexp match, | |
54 i.e. (match-end 0), and goes to the end of the string or line.") | |
55 | |
56 ;;; ************************************************************************ | |
57 ;;; Public functions | |
58 ;;; ************************************************************************ | |
59 | |
60 (defun python-add-default-classes () | |
61 ;; Add to 'system' class table. | |
62 ;; Add this default class for global functions | |
63 (br-add-class "[functions]" br-null-path nil)) | |
64 | |
65 (defun python-feature-implementors (name) | |
66 "Return unsorted list of Python feature tags which implement feature NAME. | |
67 This includes classes which define the interface for NAME as a pure virtual | |
68 function." | |
69 (python-feature-matches (concat "^" (regexp-quote name) "$"))) | |
70 | |
71 | |
72 (defun python-feature-signature-to-name (signature &optional with-class for-display) | |
73 "Extracts the feature name from SIGNATURE. | |
74 The feature's class name is dropped from signature unless optional WITH-CLASS | |
75 is non-nil. If optional FOR-DISPLAY is non-nil, a feature type character is | |
76 prepended to the name for display in a browser listing." | |
77 (let ((name)) | |
78 (cond | |
79 ;; member | |
80 ((string-match python-tag-fields-regexp signature) | |
81 (setq name (substring signature (match-beginning (if for-display 2 3)) | |
82 (match-end 3))) | |
83 (if with-class | |
84 (setq name (concat | |
85 (substring signature (match-beginning 1) (match-end 1)) | |
86 "." name))) | |
87 ;; Remove any trailing whitespace. | |
88 (br-delete-space name)) | |
89 ;; | |
90 ;; unknown | |
91 (t ;; Remove any trailing whitespace and add display prefix. | |
92 (setq name (br-delete-space signature)) | |
93 (if for-display (python-feature-add-prefix name "" signature) name))))) | |
94 | |
95 (defun python-feature-tree-command-p (class-or-signature) | |
96 "Display definition of CLASS-OR-SIGNATURE if a signature and return t, else return nil." | |
97 (if (python-routine-p class-or-signature) | |
98 (progn | |
99 (if (br-in-browser) (br-to-view-window)) | |
100 (br-feature-found-p (br-feature-file class-or-signature) | |
101 class-or-signature)))) | |
102 | |
103 (defun python-list-features (class &optional indent) | |
104 "Return sorted list of Python feature tags lexically defined in CLASS." | |
105 (let ((obuf (current-buffer)) | |
106 (features) | |
107 (class-tag (concat "\n" class python-type-tag-separator)) | |
108 feature) | |
109 (set-buffer (funcall br-find-file-noselect-function br-feature-tags-file)) | |
110 (goto-char 1) | |
111 (if (or (null indent) (<= indent 2)) | |
112 ;; Include all features. | |
113 (while (search-forward class-tag nil t) | |
114 (setq features (cons (br-feature-current) features))) | |
115 ;; Omit friend features which are not inherited since indent > 2. | |
116 (let ((friend-regexp (format "%s%% " python-type-tag-separator))) | |
117 (while (search-forward class-tag nil t) | |
118 (setq feature (br-feature-current)) | |
119 (or (string-match friend-regexp feature) | |
120 (setq features (cons feature features)))))) | |
121 (set-buffer obuf) | |
122 (python-sort-features (nreverse features)))) | |
123 | |
124 (defun python-routine-p (str) | |
125 (string-match "([^\)]*)" str)) | |
126 | |
127 (defun python-scan-features () | |
128 "Return reverse ordered list of Python function definitions in current | |
129 buffer. Assume point is at beginning of widened buffer. | |
130 '[functions]@- foo@foo(arguments)'" | |
131 (save-excursion | |
132 (let ((routines) class name rout) | |
133 (while (re-search-forward python-routine-def nil t) | |
134 (setq class "[functions]" | |
135 name (buffer-substring (match-beginning python-feature-name-grpn) | |
136 (match-end python-feature-name-grpn)) | |
137 rout (python-feature-normalize | |
138 (concat "def " name (python-scan-arguments)) class name) | |
139 routines (cons rout routines))) | |
140 routines))) | |
141 | |
142 (defun python-scan-arguments() | |
143 "Return the functions arguments, point is assumed to be at the start of them" | |
144 (let ((opoint (point))) | |
145 (progn | |
146 (search-forward ":" nil t) | |
147 (buffer-substring opoint (point))))) | |
148 | |
149 (defun python-sort-features (routine-list) | |
150 (sort routine-list 'python-feature-lessp)) | |
151 | |
152 (defun python-to-definition (&optional other-win) | |
153 "If point is on an import statement, look for the module file. | |
154 With OTHER-WIN non-nil, show it in another window." | |
155 (interactive) | |
156 (let ((opoint (point))) | |
157 (cond | |
158 ((python-import-file other-win)) | |
159 (t (beep) | |
160 (message | |
161 "(OO-Browser): Select a module from import statement display its source.") | |
162 nil)))) | |
163 | |
164 (defun python-store-class-info (class) | |
165 "Lookup Python doc-string for class or method/function" | |
166 (setq python-docstring (python-lookup-docstring class))) | |
167 | |
168 | |
169 (defun python-insert-class-info () | |
170 "Use the info facility to display Python doc-strings" | |
171 (interactive) | |
172 (insert python-docstring)) | |
173 | |
174 ;;; ************************************************************************ | |
175 ;;; Private functions | |
176 ;;; ************************************************************************ | |
177 | |
178 (defun python-lookup-docstring (class) | |
179 "Looks up a docstring for any browser listing entry." | |
180 (let ((entry class) | |
181 (filename nil) | |
182 (feature-sig nil) | |
183 (docstring nil)) | |
184 (cond ((br-find-feature-entry) | |
185 (progn | |
186 (setq feature-sig (br-feature-get-signature)) | |
187 (setq filename (br-feature-file feature-sig)))) | |
188 ((and (setq entry (br-find-class-name)) | |
189 (br-class-in-table-p entry)) | |
190 (setq filename (br-class-path entry))) | |
191 (t (error "(OO-Browser): Entry may be referenced but not defined in the Environment."))) | |
192 (if filename | |
193 (setq docstring | |
194 (python-get-docstring-from-source entry feature-sig filename))) | |
195 (if docstring | |
196 docstring | |
197 (concat class " does not have a documentation string.")))) | |
198 | |
199 (defun python-get-file-buffer (filename) | |
200 "Insert FILENAME contents into a temporary buffer and select buffer. | |
201 Does not run any find-file hooks. Marks buffer read-only to prevent | |
202 any accidental editing." | |
203 (let ((buf (get-buffer-create *br-tmp-buffer*))) | |
204 (set-buffer buf) | |
205 (buffer-disable-undo buf) | |
206 (setq buffer-read-only nil) | |
207 (erase-buffer) | |
208 (insert-file-contents filename t))) | |
209 | |
210 (defun python-get-docstring-from-source (entry feature-sig filename) | |
211 "Scan source for docstring for entry. If feature-sig non nil, locate | |
212 feature, otherwise entry is the class" | |
213 (let ((no-kill (get-file-buffer filename)) | |
214 (docstring nil)) | |
215 (if no-kill | |
216 (set-buffer no-kill) | |
217 (python-get-file-buffer filename)) | |
218 (save-restriction | |
219 (save-excursion | |
220 (widen) | |
221 (goto-char (point-min)) | |
222 (if feature-sig | |
223 (if (python-feature-locate-p feature-sig) | |
224 (setq docstring (python-extract-docstring)) | |
225 nil) | |
226 (if (re-search-forward (python-class-definition-regexp entry) nil t) | |
227 (setq docstring (python-extract-docstring)) | |
228 nil)))) | |
229 (if (not no-kill) | |
230 (kill-buffer *br-tmp-buffer*)) | |
231 docstring)) | |
232 | |
233 (defun python-extract-docstring () | |
234 "Return the documentation string for the class or method at point, or | |
235 nil if it does not exist" | |
236 (search-forward ":" nil t) | |
237 (if (looking-at | |
238 (concat python-empty-line "+" | |
239 whitespace python-string-start)) | |
240 (progn | |
241 (let ((start (match-end 0)) | |
242 (end-quote (buffer-substring (match-beginning 4) (match-end 4)))) | |
243 (goto-char start) | |
244 (search-forward end-quote nil t) | |
245 (buffer-substring start (match-beginning 0)))) | |
246 nil)) | |
247 | |
248 (defconst python-string-start | |
249 (concat | |
250 "\\(" | |
251 "'''" ; triple single-quoted | |
252 "\\|" ; or | |
253 "\"\"\"" ; triple double-quoted | |
254 "\\|" ; or | |
255 "'" ; single-quoted, not empty | |
256 "\\|" ; or | |
257 "\"" ; double-quoted, not empty | |
258 "\\)") | |
259 "regexp matching python string literal starting quotes") | |
260 | |
261 (defconst python-empty-line | |
262 (concat | |
263 "\\(" | |
264 "\\(" whitespace "\n\\)" | |
265 "\\|" | |
266 "\\(" whitespace "#.*$\\)" | |
267 "\\)") | |
268 "regexp matching an empty python line, which can be a comment line") | |
269 | |
270 (defun python-feature-decl () | |
271 (if (looking-at python-class-decl) | |
272 nil | |
273 (looking-at python-feature-decl))) | |
274 | |
275 (defun py-count-triple-quotes-forward () | |
276 "Count the number of trible quotes from the point to eof" | |
277 (let ((count 0)) | |
278 (while (re-search-forward "'''\\|\"\"\"" nil t) | |
279 (setq count (1+ count))) | |
280 count)) | |
281 | |
282 (defun python-within-string-p () | |
283 "Return non-nil if point is within a multi-line python string." | |
284 (save-excursion | |
285 (if (= (% (py-count-triple-quotes-forward) 2) 1) | |
286 t | |
287 nil))) | |
288 | |
289 (defun python-feature-lessp (routine1 routine2) | |
290 (string-lessp (python-feature-signature-to-name routine1) | |
291 (python-feature-signature-to-name routine2))) | |
292 | |
293 (defun python-feature-matches (regexp) | |
294 "Return an unsorted list of feature tags whose names match in part or whole to REGEXP." | |
295 ;; Ensure match to feature names only; also handle "^" and "$" meta-chars | |
296 (setq regexp | |
297 (concat (format "^[^%s \n]+%s%s " | |
298 python-type-tag-separator python-type-tag-separator | |
299 br-feature-type-regexp) | |
300 (if (equal (substring regexp 0 1) "^") | |
301 (progn (setq regexp (substring regexp 1)) nil) | |
302 python-identifier-chars) | |
303 (if (equal (substring regexp -1) "$") | |
304 (substring regexp 0 -1) | |
305 (concat regexp python-identifier-chars)) | |
306 python-type-tag-separator)) | |
307 (save-excursion | |
308 (set-buffer (funcall br-find-file-noselect-function br-feature-tags-file)) | |
309 (goto-char 1) | |
310 (let ((features)) | |
311 (while (re-search-forward regexp nil t) | |
312 (setq features (cons (br-feature-current) features))) | |
313 features))) | |
314 | |
315 (defun python-feature-normalize (routine class name) | |
316 (setq class (br-delete-space class) | |
317 name (concat "- " name) | |
318 routine (concat class python-type-tag-separator | |
319 name python-type-tag-separator | |
320 (br-delete-space routine))) | |
321 routine) | |
322 | |
323 (defun python-feature-tag-class (signature) | |
324 "Extract the class name from SIGNATURE." | |
325 (cond ((string-match python-type-tag-separator signature) | |
326 (substring signature 0 (match-beginning 0))) | |
327 (t ""))) | |
328 | |
329 (defun python-feature-tags-lookup (class-list ftr-pat &optional other-win) | |
330 "Display routine definition derived from CLASS-LIST, matching FTR-PAT. | |
331 Use routine tags table to locate a match. Caller must use 'set-buffer' | |
332 to restore prior buffer when a match is not found." | |
333 (set-buffer (funcall br-find-file-noselect-function br-feature-tags-file)) | |
334 (let ((classes class-list) | |
335 (found-ftr) | |
336 (ftr-regexp) | |
337 (class) | |
338 (ftr-path)) | |
339 (if (or (null class-list) (equal class-list '(nil))) | |
340 nil | |
341 (while (and (not found-ftr) classes) | |
342 (setq class (car classes) | |
343 ftr-regexp (funcall ftr-pat class) | |
344 ftr-path (br-feature-def-file ftr-regexp) | |
345 found-ftr (if ftr-path | |
346 (br-edit-feature (br-feature-current) | |
347 ftr-path other-win)) | |
348 classes (if found-ftr nil (cdr classes)))) | |
349 (if found-ftr | |
350 (or class t) | |
351 (python-feature-tags-lookup | |
352 (apply 'append (mapcar (function (lambda (cl) (br-get-parents cl))) | |
353 class-list)) | |
354 ftr-pat))))) | |
355 | |
356 (defun python-files-with-source (class) | |
357 "Use CLASS to compute set of files that match to a Python source file regexp. | |
358 Return as a list." | |
359 (let ((file (if class (br-class-path class) buffer-file-name))) | |
360 (and file | |
361 (let* ((src-file-regexp (concat "^" (br-filename-head file) | |
362 python-code-file-regexp)) | |
363 (dir (file-name-directory file)) | |
364 (files (directory-files dir nil src-file-regexp))) | |
365 (mapcar (function (lambda (f) (concat dir f))) | |
366 files))))) | |
367 | |
368 (defun python-find-ancestors-feature (class-list ftr-pat &optional other-win) | |
369 "Scan ancestors of CLASS-LIST and show routine definition matching FTR-PAT." | |
370 ;; If no class, search for non-member function. | |
371 (or class-list (setq class-list '(nil))) | |
372 (let ((obuf (current-buffer))) | |
373 (prog1 | |
374 (if (and br-feature-tags-file | |
375 (file-exists-p br-feature-tags-file) | |
376 (file-readable-p br-feature-tags-file)) | |
377 (python-feature-tags-lookup class-list ftr-pat other-win) | |
378 ;; Only works if features are in same directory as class def. | |
379 (python-scan-ancestors-feature class-list ftr-pat other-win)) | |
380 (set-buffer obuf)))) | |
381 | |
382 (defun python-find-class-name () | |
383 "Return current word as a potential class name." | |
384 (save-excursion | |
385 (let* ((start) | |
386 (ignore "\]\[ \t\n;,.\(\){}*&-") | |
387 (pat (concat "^" ignore))) | |
388 (forward-char 1) | |
389 (skip-chars-backward ignore) | |
390 (skip-chars-backward pat) | |
391 (setq start (point)) | |
392 (skip-chars-forward (concat pat ":")) | |
393 (buffer-substring start (point))))) | |
394 | |
395 | |
396 (defun python-get-class-name-from-source () | |
397 "Return class name from closest class definition preceding point or nil." | |
398 (let ((opoint (point)) | |
399 (class)) | |
400 (save-excursion | |
401 (if (re-search-backward python-class-def-regexp nil t) | |
402 (progn (goto-char (match-beginning python-class-def-derived-grpn)) | |
403 (setq class (python-normalize-class-match nil)) | |
404 ;; Ensure that declaration occurs within class definition. | |
405 (forward-list) | |
406 (and (> (point) opoint) | |
407 class)))))) | |
408 | |
409 (defun python-get-feature-tags (routine-file &optional routine-list) | |
410 "Scan Python ROUTINE-FILE and hold routine tags in 'br-feature-tags-file'. | |
411 Assume ROUTINE-FILE has already been read into a buffer and that | |
412 'br-feature-tags-init' has been called. Optional ROUTINE-LIST can be | |
413 provided so that a non-standard scan function can be used before calling | |
414 this function." | |
415 (interactive) | |
416 (let ((obuf (current-buffer))) | |
417 (or routine-list | |
418 (setq routine-list (python-sort-features (nreverse | |
419 (python-scan-features))))) | |
420 (set-buffer (funcall br-find-file-noselect-function br-feature-tags-file)) | |
421 (goto-char 1) | |
422 ;; Delete any prior routine tags associated with routine-file | |
423 (if (search-forward routine-file nil 'end) | |
424 (progn (forward-line -1) | |
425 (let ((start (point))) | |
426 (search-forward "\^L" nil 'end 2) | |
427 (backward-char 1) | |
428 (delete-region start (point)) | |
429 ))) | |
430 (if routine-list | |
431 (progn (insert "\^L\n" routine-file "\n") | |
432 (mapcar (function (lambda (tag) (insert tag "\n"))) | |
433 routine-list) | |
434 )) | |
435 (set-buffer obuf))) | |
436 | |
437 (defun python-find-module-name () | |
438 "Return current word as a potential module name." | |
439 (save-excursion | |
440 (let ((start)) | |
441 (forward-char 1) | |
442 (skip-chars-backward python-identifier-chars) | |
443 (setq start (point)) | |
444 (skip-chars-forward python-identifier-chars) | |
445 (buffer-substring start (point))))) | |
446 | |
447 (defun python-import-file (&optional other-win) | |
448 "If point is on an import module line, try to display module. | |
449 Return non-nil iff an import file line, even if file is not found. | |
450 Look for include file in directory list 'python-import-dirs'" | |
451 (let ((opoint (point))) | |
452 (beginning-of-line) | |
453 (if (and (looking-at python-import-regexp) | |
454 (goto-char opoint)) | |
455 (let ((file (concat (python-find-module-name) ".py")) | |
456 (path) | |
457 (dir-list (append python-lib-search-dirs python-sys-search-dirs | |
458 python-import-dirs)) | |
459 (found)) | |
460 (setq dir-list (cons (file-name-directory buffer-file-name) | |
461 dir-list)) | |
462 (while dir-list | |
463 (setq path (concat (car dir-list) file) | |
464 dir-list (if (setq found (file-exists-p path)) | |
465 nil | |
466 (cdr dir-list)))) | |
467 ;; | |
468 ;; If not found in normal include dirs, check all Env paths also. | |
469 ;; | |
470 (if (not found) | |
471 (let ((paths (delq nil (hash-map 'cdr br-paths-htable)))) | |
472 (while paths | |
473 (setq path (car paths)) | |
474 (if (string-equal (file-name-nondirectory path) file) | |
475 (setq found t paths nil) | |
476 (setq paths (cdr paths)))))) | |
477 ;; | |
478 ;; If found, display file | |
479 ;; | |
480 (if found | |
481 (if (file-readable-p path) | |
482 (progn | |
483 (funcall br-edit-file-function path other-win) | |
484 (if (not (fboundp 'br-lang-mode)) | |
485 (python-mode-setup)) | |
486 (br-major-mode)) | |
487 (beep) | |
488 (message "(OO-Browser): Module file '%s' unreadable." path)) | |
489 (beep) | |
490 (message "(OO-Browser): Module file '%s' not found." file)) | |
491 path) | |
492 (goto-char opoint) | |
493 nil))) | |
494 | |
495 (defun python-locate-feature (ftr class ftr-pat &optional other-win) | |
496 ;; 'class' may = nil, implying non-member function | |
497 (or class (setq class "[functions]")) | |
498 (let ((def-class)) | |
499 (if (and ftr-pat | |
500 (setq def-class | |
501 (python-find-ancestors-feature (list class) | |
502 ftr-pat other-win))) | |
503 (progn (if (and class (not (equal class def-class))) | |
504 (message | |
505 "Member `%s` of class '%s' inherited from class '%s'." | |
506 ftr class def-class)) | |
507 t)))) | |
508 | |
509 (defun python-scan-ancestors-feature (class-list ftr-pat &optional other-win) | |
510 "Display routine definition derived from CLASS-LIST, matching FTR-PAT. | |
511 Scan files with same base name as class file." | |
512 (let ((classes class-list) | |
513 (found-ftr) | |
514 (code-def-files) | |
515 (file) | |
516 (ftr-regexp) | |
517 (class)) | |
518 (if (null class-list) | |
519 nil | |
520 (while (and (not found-ftr) classes) | |
521 (setq class (car classes) | |
522 code-def-files (python-files-with-source class) | |
523 ftr-regexp (funcall ftr-pat class)) | |
524 (while (and (setq file (car code-def-files)) | |
525 (not (setq found-ftr | |
526 (br-feature-found-p file ftr-regexp | |
527 nil other-win t)))) | |
528 (setq code-def-files (cdr code-def-files))) | |
529 (setq classes (if found-ftr nil (cdr classes)))) | |
530 (if found-ftr | |
531 (or class t) | |
532 (python-scan-ancestors-feature | |
533 (apply 'append (mapcar (function (lambda (cl) (br-get-parents cl))) | |
534 class-list)) | |
535 ftr-pat))))) | |
536 | |
537 (defun python-scan-features-in-class (class start end) | |
538 "Return reverse ordered list of Python routine definitions within CLASS def. | |
539 START and END give buffer region to search." | |
540 (setq class (br-delete-space class)) | |
541 (save-excursion | |
542 (save-restriction | |
543 (narrow-to-region start end) | |
544 (goto-char start) | |
545 (let ((routines) rout name type) | |
546 ;; | |
547 ;; Get member definitions | |
548 ;; | |
549 (while (re-search-forward python-routine-def-in-class nil t) | |
550 (setq start (match-beginning 0) | |
551 name (buffer-substring | |
552 (match-beginning python-feature-name-grpn) | |
553 (match-end python-feature-name-grpn)) | |
554 rout (python-feature-normalize | |
555 (concat "def " name (python-scan-arguments)) class name) | |
556 routines (cons rout routines))) | |
557 routines)))) | |
558 | |
559 (defun python-skip-past-comments () | |
560 "Skip over comments immediately following point." | |
561 (skip-chars-forward " \t\n") | |
562 (while | |
563 (cond ((looking-at "#") | |
564 (equal (forward-line 1) 0)) | |
565 (t nil)))) | |
566 | |
567 (defun python-skip-to-statement () | |
568 (if (re-search-backward "^[ \t]*" nil t) | |
569 (progn (goto-char (match-end 0)) | |
570 (skip-chars-forward " \t") | |
571 t))) | |
572 | |
573 (defun python-feature-locate-p (feature-tag &optional regexp-flag) | |
574 "Leaves point at the start of FEATURE-TAG's definition in the current buffer. | |
575 Assumes caller has moved point to the beginning of the buffer or to the point | |
576 of desired search start. | |
577 Optional REGEXP-FLAG means FEATURE-TAG is a regular expression." | |
578 ;; | |
579 ;; first move to the proper class implementation if feature-tag does not | |
580 ;; include a <class>:: part and this is not a [default-class], so that if | |
581 ;; two classes in the same file have the same feature signature, we still | |
582 ;; end up at the right one. | |
583 (if (string-match python-tag-fields-regexp feature-tag) | |
584 (let ((class (substring feature-tag (match-beginning 1) (match-end 1)))) | |
585 (setq feature-tag (substring feature-tag (match-end 0))) | |
586 (if regexp-flag | |
587 (if (not (string-match "\\`\\\\\\[\\|::" feature-tag)) | |
588 (re-search-forward (python-class-definition-regexp class t) | |
589 nil t)) | |
590 (if (not (string-match "\\`\\[\\|::" feature-tag)) | |
591 (re-search-forward (python-class-definition-regexp class) | |
592 nil t))))) | |
593 (let ((found) (start)) | |
594 ;; Now look for feature expression. | |
595 (or regexp-flag (setq feature-tag | |
596 (python-feature-signature-to-regexp feature-tag))) | |
597 (while (and (re-search-forward feature-tag nil t) | |
598 (setq start (match-beginning 0)) | |
599 (not (setq found (not | |
600 (if (python-within-string-p) | |
601 (progn (search-forward "*/" nil t) | |
602 t))))))) | |
603 (if found | |
604 (progn (goto-char start) | |
605 (skip-chars-forward " \t\n") | |
606 (python-to-comments-begin) | |
607 (recenter 0) | |
608 (goto-char start) | |
609 t)))) | |
610 | |
611 (defun python-feature-name-to-regexp (name) | |
612 "Converts routine NAME into a regular expression matching the routine's name tag." | |
613 (setq name (python-feature-signature-to-regexp name)) | |
614 (aset name (1- (length name)) ?\() ;; Match only to functions | |
615 name) | |
616 | |
617 | |
618 (defun python-feature-signature-to-regexp (signature) | |
619 "Given a Python SIGNATURE, return regexp used to match to its definition." | |
620 (setq signature (regexp-quote signature)) | |
621 (let ((prefix-info | |
622 (if (string-match python-tag-fields-regexp signature) | |
623 (prog1 (substring signature (match-beginning 0) (match-end 0)) | |
624 (setq signature (substring signature (match-end 0))))))) | |
625 (let ((pat) (i 0) (c) (len (length signature))) | |
626 (while (< i len) | |
627 (setq c (aref signature i) | |
628 pat (cond ((= c ? ) | |
629 ;; Allow for possible single line comment | |
630 ;; following any whitespace, e.g. following | |
631 ;; each routine argument. | |
632 (concat pat "[ \t\n\^M]*\\(//.*\\)?")) | |
633 (t | |
634 (concat pat (char-to-string c)))) | |
635 i (1+ i))) | |
636 (setq pat (concat prefix-info pat))))) | |
637 | |
638 | |
639 | |
640 | |
641 ;;; ************************************************************************ | |
642 ;;; Private variables | |
643 ;;; ************************************************************************ | |
644 | |
645 (defvar python-docstring "" | |
646 "Documentation string for python class, method or function.") | |
647 | |
648 (defconst python-code-file-regexp "\\.py\\" | |
649 "Regular expression matching a unique part of Python source (non-header) file name and no others.") | |
650 | |
651 (defconst python-import-regexp | |
652 (concat "\\([ \t]*import[ \t]+\\)\\|" | |
653 "\\([ \t]*from[ \t]+" | |
654 python-identifier | |
655 "[ \t]+import[ \t]+\\)") | |
656 "Regexp to match to Python import statement | |
657 of include, user-specified via double quote, or system-related starting with | |
658 '<' is given by grouping 1.") | |
659 | |
660 (defconst python-feature-name-grpn 1) | |
661 | |
662 (defconst python-routine-def | |
663 (concat "^def[ \t]+" python-identifier whitespace) | |
664 "Matches global python function definition. group 1 gives the function name. | |
665 On return the point is at the starting '(' for parameters") | |
666 | |
667 (defconst python-routine-def-in-class | |
668 (concat "^[ \t]+def[ \t]+" python-identifier) | |
669 "Matches python class method. group 1 gives the function name. | |
670 On return the point is at the starting '(' for parameters") | |
671 | |
672 (defconst python-decl-template-grpn 3) | |
673 (defconst python-class-name-grpn 5) | |
674 | |
675 (defconst python-stringlit | |
676 (concat | |
677 "'\\([^'\n\\]\\|\\\\.\\)*'" ; single-quoted | |
678 "\\|" ; or | |
679 "\"\\([^\"\n\\]\\|\\\\.\\)*\"") ; double-quoted | |
680 "regexp matching a Python string literal") | |
681 | |
682 | |
683 (provide 'br-python-ft) |