docstring.el 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623
  1. ;;; docstring.el --- utilities for Guile docstring maintenance
  2. ;;;
  3. ;;; Copyright (C) 2001, 2004 Neil Jerram
  4. ;;;
  5. ;;; This file is not part of GNU Emacs, but the same permissions apply.
  6. ;;;
  7. ;;; GNU Emacs is free software; you can redistribute it and/or modify
  8. ;;; it under the terms of the GNU General Public License as published by
  9. ;;; the Free Software Foundation; either version 2, or (at your option)
  10. ;;; any later version.
  11. ;;;
  12. ;;; GNU Emacs is distributed in the hope that it will be useful,
  13. ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. ;;; GNU General Public License for more details.
  16. ;;;
  17. ;;; You should have received a copy of the GNU General Public License
  18. ;;; along with GNU Emacs; see the file COPYING. If not, write to the
  19. ;;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
  20. ;;; Boston, MA 02110-1301, USA.
  21. ;;; Commentary:
  22. ;; The basic premise of these utilities is that - at least in the
  23. ;; short term - we can get a lot of reference manual mileage by
  24. ;; co-opting the docstrings that are snarfed automatically from
  25. ;; Guile's C and Scheme source code. But this leads to problems of
  26. ;; synchronization... How do you track when a docstring has been
  27. ;; updated in the source and so needs updating in the reference
  28. ;; manual. What if a procedure is removed from the Guile source? And
  29. ;; so on. To complicate matters, the exact snarfed docstring text
  30. ;; will probably need to be modified so that it fits into the flow of
  31. ;; the manual section in which it appears. Can we design solutions to
  32. ;; synchronization problems that continue to work even when the manual
  33. ;; text has been enhanced in this way?
  34. ;;
  35. ;; This file implements an approach to this problem that I have found
  36. ;; useful. It involves keeping track of three copies of each
  37. ;; docstring:
  38. ;;
  39. ;; "MANUAL" = the docstring as it appears in the reference manual.
  40. ;;
  41. ;; "SNARFED" = the docstring as snarfed from the current C or Scheme
  42. ;; source.
  43. ;;
  44. ;; "TRACKING" = the docstring as it appears in a tracking file whose
  45. ;; purpose is to record the most recent snarfed docstrings
  46. ;; that are known to be in sync with the reference manual.
  47. ;;
  48. ;; The approaches are as follows.
  49. ;;
  50. ;; 1. Comparison of MANUAL-DOC, SOURCE-DOC and TRACK-DOC, to produce a
  51. ;; summary output buffer in which keystrokes are defined to bring up
  52. ;; detailed comparisons.
  53. ;;
  54. ;; 2. Comparison of MANUAL-DOC, SOURCE-DOC and TRACK-DOC using Ediff.
  55. ;;
  56. ;; Here is a brief list of commands available (via "M-x COMMAND"):
  57. ;;
  58. ;; docstring-process-current-buffer
  59. ;; docstring-process-current-region BEG END
  60. ;; docstring-process-module MODULE
  61. ;; docstring-ediff-this-line
  62. ;; docstring-show-source
  63. (defvar guile-core-dir (or (getenv "GUILE_MAINTAINER_GUILE_CORE_DIR")
  64. (error "GUILE_MAINTAINER_GUILE_CORE_DIR not set"))
  65. "*Full path of guile-core source directory.")
  66. (defvar guile-build-dir (or (getenv "GUILE_MAINTAINER_BUILD_CORE_DIR")
  67. guile-core-dir)
  68. "*Full path of guile-core build directory. Defaults to guile-core-dir.")
  69. (defvar docstring-manual-directory (expand-file-name "doc/ref" guile-core-dir)
  70. "*The directory containing the Texinfo source for the Guile reference manual.")
  71. (defvar docstring-tracking-root (expand-file-name "doc/maint" guile-core-dir)
  72. "*Root directory for docstring tracking files. The tracking file
  73. for module (a b c) is expected to be in the file
  74. <docstring-tracking-root>/a/b/c.texi.")
  75. (defvar docstring-snarfed-roots (mapcar
  76. #'(lambda (frag)
  77. (expand-file-name frag guile-build-dir))
  78. '("libguile" "ice-9" "oop"))
  79. "*List of possible root directories for snarfed docstring files.
  80. For each entry in this list, the snarfed docstring file for module (a
  81. b c) is looked for in the file <entry>/a/b/c.texi.")
  82. (defvar docstring-manual-files
  83. (directory-files docstring-manual-directory nil "\\.texi$" t)
  84. "List of Texinfo source files that comprise the Guile reference manual.")
  85. (defvar docstring-new-docstrings-file "new-docstrings.texi"
  86. "The name of a file in the Guile reference manual source directory
  87. to which new docstrings should be added.")
  88. ;; Apply FN in turn to each element in the list CANDIDATES until the
  89. ;; first application that returns non-nil.
  90. (defun or-map (fn candidates args)
  91. (let ((result nil))
  92. (while candidates
  93. (setq result (apply fn (car candidates) args))
  94. (if result
  95. (setq result (cons (car candidates) result)
  96. candidates nil)
  97. (setq candidates (cdr candidates))))
  98. result))
  99. ;; Return t if the current buffer position is in the scope of the
  100. ;; specified MODULE, as determined by "@c module-for-docstring ..." comments in the
  101. ;; buffer. DEFAULT-OK specifies the return value in the case that
  102. ;; there are no preceding module comments at all.
  103. (defun docstring-in-module (module default-ok)
  104. (save-excursion
  105. (if (re-search-backward "^@c module-for-docstring " nil t)
  106. (progn
  107. (search-forward "@c module-for-docstring ")
  108. (equal module (read (current-buffer))))
  109. default-ok)))
  110. ;; Find a docstring in the specified FILE-NAME for the item in module
  111. ;; MODULE and with description DESCRIPTION. MODULE should be a list
  112. ;; of symbols, Guile-style, for example: '(ice-9 session).
  113. ;; DESCRIPTION should be the string that is expected after the @deffn,
  114. ;; for example "primitive acons" or "syntax let*".
  115. (defun find-docstring (file-name module description)
  116. (and (file-exists-p file-name)
  117. (let ((buf (find-file-noselect file-name))
  118. (deffn-regexp (concat "^@deffnx? "
  119. (regexp-quote description)
  120. "[ \n\t]"))
  121. found
  122. result)
  123. (save-excursion
  124. (set-buffer buf)
  125. (goto-char (point-min))
  126. (while (and (not found)
  127. (re-search-forward deffn-regexp nil t))
  128. (save-excursion
  129. (goto-char (match-beginning 0))
  130. (beginning-of-line)
  131. (if (docstring-in-module module t)
  132. (setq found t))))
  133. (if found
  134. (setq result
  135. (list (current-buffer)
  136. (progn
  137. (re-search-backward "^@deffn ")
  138. (beginning-of-line)
  139. (point))
  140. (progn
  141. (re-search-forward "^@end deffn")
  142. (forward-line 1)
  143. (point))))))
  144. result)))
  145. ;; Find the reference manual version of the specified docstring.
  146. ;; MODULE and DESCRIPTION specify the docstring as per
  147. ;; `find-docstring'. The set of files that `find-manual-docstring'
  148. ;; searches is determined by the value of the `docstring-manual-files'
  149. ;; variable.
  150. (defun find-manual-docstring (module description)
  151. (let* ((result
  152. (or-map 'find-docstring
  153. (mapcar (function (lambda (file-name)
  154. (concat docstring-manual-directory
  155. "/"
  156. file-name)))
  157. (cons docstring-new-docstrings-file
  158. docstring-manual-files))
  159. (list module
  160. description)))
  161. (matched-file-name (and (cdr result)
  162. (file-name-nondirectory (car result)))))
  163. (if matched-file-name
  164. (setq docstring-manual-files
  165. (cons matched-file-name
  166. (delete matched-file-name docstring-manual-files))))
  167. (cdr result)))
  168. ;; Convert MODULE to a directory subpath.
  169. (defun module-to-path (module)
  170. (mapconcat (function (lambda (component)
  171. (symbol-name component)))
  172. module
  173. "/"))
  174. ;; Find the current snarfed version of the specified docstring.
  175. ;; MODULE and DESCRIPTION specify the docstring as per
  176. ;; `find-docstring'. The file that `find-snarfed-docstring' looks in
  177. ;; is automatically generated from MODULE.
  178. (defun find-snarfed-docstring (module description)
  179. (let ((modpath (module-to-path module)))
  180. (cdr (or-map (function (lambda (root)
  181. (find-docstring (concat root
  182. "/"
  183. modpath
  184. ".texi")
  185. module
  186. description)))
  187. docstring-snarfed-roots
  188. nil))))
  189. ;; Find the tracking version of the specified docstring. MODULE and
  190. ;; DESCRIPTION specify the docstring as per `find-docstring'. The
  191. ;; file that `find-tracking-docstring' looks in is automatically
  192. ;; generated from MODULE.
  193. (defun find-tracking-docstring (module description)
  194. (find-docstring (concat docstring-tracking-root
  195. "/"
  196. (module-to-path module)
  197. ".texi")
  198. module
  199. description))
  200. ;; Extract an alist of modules and descriptions from the current
  201. ;; buffer.
  202. (defun make-module-description-list ()
  203. (let ((alist nil)
  204. (module '(guile)))
  205. (save-excursion
  206. (goto-char (point-min))
  207. (while (re-search-forward "^\\(@c module-for-docstring \\|@deffnx? \\({[^}]+}\\|[^ ]+\\) \\([^ \n]+\\)\\)"
  208. nil
  209. t)
  210. (let ((matched (buffer-substring (match-beginning 1)
  211. (match-end 1))))
  212. (if (string-equal matched "@c module-for-docstring ")
  213. (setq module (read (current-buffer)))
  214. (let ((type (buffer-substring (match-beginning 2)
  215. (match-end 2))))
  216. (if (string-equal type "{C Function}")
  217. nil
  218. (setq matched
  219. (concat type
  220. " "
  221. (buffer-substring (match-beginning 3)
  222. (match-end 3))))
  223. (message "Found docstring: %S: %s" module matched)
  224. (let ((descriptions (assoc module alist)))
  225. (setq alist
  226. (cons (cons module (cons matched (cdr-safe descriptions)))
  227. (if descriptions
  228. (delete descriptions alist)
  229. alist))))))))))
  230. alist))
  231. ;; missing in some environments?
  232. (defun caddr (list)
  233. (nth 2 list))
  234. ;; Return the docstring from the specified LOCATION. LOCATION is a
  235. ;; list of three elements: buffer, start position and end position.
  236. (defun location-to-docstring (location)
  237. (and location
  238. (save-excursion
  239. (set-buffer (car location))
  240. (buffer-substring (cadr location) (caddr location)))))
  241. ;; Perform a comparison of the specified docstring. MODULE and
  242. ;; DESCRIPTION are as per usual.
  243. (defun docstring-compare (module description)
  244. (let* ((manual-location (find-manual-docstring module description))
  245. (snarf-location (find-snarfed-docstring module description))
  246. (track-location (find-tracking-docstring module description))
  247. (manual-docstring (location-to-docstring manual-location))
  248. (snarf-docstring (location-to-docstring snarf-location))
  249. (track-docstring (location-to-docstring track-location))
  250. action
  251. issue)
  252. ;; Decide what to do.
  253. (cond ((null snarf-location)
  254. (setq action nil
  255. issue (if manual-location
  256. 'consider-removal
  257. nil)))
  258. ((null manual-location)
  259. (setq action 'add-to-manual issue nil))
  260. ((null track-location)
  261. (setq action nil
  262. issue (if (string-equal manual-docstring snarf-docstring)
  263. nil
  264. 'check-needed)))
  265. ((string-equal track-docstring snarf-docstring)
  266. (setq action nil issue nil))
  267. ((string-equal track-docstring manual-docstring)
  268. (setq action 'auto-update-manual issue nil))
  269. (t
  270. (setq action nil issue 'update-needed)))
  271. ;; Return a pair indicating any automatic action that can be
  272. ;; taken, and any issue for resolution.
  273. (cons action issue)))
  274. ;; Add the specified docstring to the manual.
  275. (defun docstring-add-to-manual (module description)
  276. (let ((buf (find-file-noselect (concat docstring-manual-directory
  277. "/"
  278. docstring-new-docstrings-file))))
  279. (save-excursion
  280. (set-buffer buf)
  281. (goto-char (point-max))
  282. (or (docstring-in-module module nil)
  283. (insert "\n@c module-for-docstring " (prin1-to-string module) "\n"))
  284. (insert "\n" (location-to-docstring (find-snarfed-docstring module
  285. description))))))
  286. ;; Auto-update the specified docstring in the manual.
  287. (defun docstring-auto-update-manual (module description)
  288. (let ((manual-location (find-manual-docstring module description))
  289. (track-location (find-tracking-docstring module description)))
  290. (save-excursion
  291. (set-buffer (car manual-location))
  292. (goto-char (cadr manual-location))
  293. (delete-region (cadr manual-location) (caddr manual-location))
  294. (insert (location-to-docstring (find-snarfed-docstring module
  295. description))))))
  296. ;; Process an alist of modules and descriptions, and produce a summary
  297. ;; buffer describing actions taken and issues to be resolved.
  298. (defun docstring-process-alist (alist)
  299. (let (check-needed-list
  300. update-needed-list
  301. consider-removal-list
  302. added-to-manual-list
  303. auto-updated-manual-list)
  304. (mapcar
  305. (function (lambda (module-list)
  306. (let ((module (car module-list)))
  307. (message "Module: %S" module)
  308. (mapcar
  309. (function (lambda (description)
  310. (message "Comparing docstring: %S: %s" module description)
  311. (let* ((ai (docstring-compare module description))
  312. (action (car ai))
  313. (issue (cdr ai)))
  314. (cond ((eq action 'add-to-manual)
  315. (docstring-add-to-manual module description)
  316. (setq added-to-manual-list
  317. (cons (cons module description)
  318. added-to-manual-list)))
  319. ((eq action 'auto-update-manual)
  320. (docstring-auto-update-manual module description)
  321. (setq auto-updated-manual-list
  322. (cons (cons module description)
  323. auto-updated-manual-list))))
  324. (cond ((eq issue 'check-needed)
  325. (setq check-needed-list
  326. (cons (cons module description)
  327. check-needed-list)))
  328. ((eq issue 'update-needed)
  329. (setq update-needed-list
  330. (cons (cons module description)
  331. update-needed-list)))
  332. ((eq issue 'consider-removal)
  333. (setq consider-removal-list
  334. (cons (cons module description)
  335. consider-removal-list)))))))
  336. (reverse (cdr module-list))))))
  337. alist)
  338. ;; Prepare a buffer describing the results.
  339. (set-buffer (get-buffer-create "*Docstring Results*"))
  340. (erase-buffer)
  341. (insert "
  342. The following items have been automatically added to the manual in
  343. file `" docstring-manual-directory "/" docstring-new-docstrings-file "'.\n\n")
  344. (if added-to-manual-list
  345. (mapcar (function (lambda (moddesc)
  346. (insert (prin1-to-string (car moddesc))
  347. ": "
  348. (cdr moddesc)
  349. "\n")))
  350. added-to-manual-list)
  351. (insert "(none)\n"))
  352. (insert "
  353. The following items have been automatically updated in the manual.\n\n")
  354. (if auto-updated-manual-list
  355. (mapcar (function (lambda (moddesc)
  356. (insert (prin1-to-string (car moddesc))
  357. ": "
  358. (cdr moddesc)
  359. "\n")))
  360. auto-updated-manual-list)
  361. (insert "(none)\n"))
  362. (insert "
  363. The following items are already documented in the manual but are not
  364. mentioned in the reference copy of the snarfed docstrings file.
  365. You should check that the manual documentation matches the docstring
  366. in the current snarfed docstrings file.\n\n")
  367. (if check-needed-list
  368. (mapcar (function (lambda (moddesc)
  369. (insert (prin1-to-string (car moddesc))
  370. ": "
  371. (cdr moddesc)
  372. "\n")))
  373. check-needed-list)
  374. (insert "(none)\n"))
  375. (insert "
  376. The following items have manual documentation that is different from
  377. the docstring in the reference copy of the snarfed docstrings file,
  378. and the snarfed docstring has changed. You need to update the manual
  379. documentation by hand with reference to the snarfed docstring changes.\n\n")
  380. (if update-needed-list
  381. (mapcar (function (lambda (moddesc)
  382. (insert (prin1-to-string (car moddesc))
  383. ": "
  384. (cdr moddesc)
  385. "\n")))
  386. update-needed-list)
  387. (insert "(none)\n"))
  388. (insert "
  389. The following items are documented in the manual but are no longer
  390. present in the snarfed docstrings file. You should consider whether
  391. the existing manual documentation is still pertinent. If it is, its
  392. docstring module comment may need updating, to connect it with a
  393. new snarfed docstring file.\n\n")
  394. (if consider-removal-list
  395. (mapcar (function (lambda (moddesc)
  396. (insert (prin1-to-string (car moddesc))
  397. ": "
  398. (cdr moddesc)
  399. "\n")))
  400. consider-removal-list)
  401. (insert "(none)\n"))
  402. (insert "\n")
  403. (goto-char (point-min))
  404. (local-set-key "d" 'docstring-ediff-this-line)
  405. ;; Popup the issues buffer.
  406. (let ((pop-up-frames t))
  407. (set-window-point (display-buffer (current-buffer))
  408. (point-min)))))
  409. (defun docstring-process-current-buffer ()
  410. (interactive)
  411. (docstring-process-alist (make-module-description-list)))
  412. (defun docstring-process-current-region (beg end)
  413. (interactive "r")
  414. (narrow-to-region beg end)
  415. (unwind-protect
  416. (save-excursion
  417. (docstring-process-alist (make-module-description-list)))
  418. (widen)))
  419. (defun docstring-process-module (module)
  420. (interactive "xModule: ")
  421. (let ((modpath (module-to-path module))
  422. (mdlist nil))
  423. (mapcar (function (lambda (root)
  424. (let ((fn (concat root
  425. "/"
  426. modpath
  427. ".texi")))
  428. (if (file-exists-p fn)
  429. (save-excursion
  430. (find-file fn)
  431. (message "Getting docstring list from %s" fn)
  432. (setq mdlist
  433. (append mdlist
  434. (make-module-description-list))))))))
  435. docstring-snarfed-roots)
  436. (docstring-process-alist mdlist)))
  437. (defun docstring-ediff-this-line ()
  438. (interactive)
  439. (let (module
  440. description)
  441. (save-excursion
  442. (beginning-of-line)
  443. (setq module (read (current-buffer)))
  444. (forward-char 2)
  445. (setq description (buffer-substring (point)
  446. (progn
  447. (end-of-line)
  448. (point)))))
  449. (message "Ediff docstring: %S: %s" module description)
  450. (let ((track-location (or (find-tracking-docstring module description)
  451. (docstring-temp-location "No docstring in tracking file")))
  452. (snarf-location (or (find-snarfed-docstring module description)
  453. (docstring-temp-location "No docstring in snarfed file")))
  454. (manual-location (or (find-manual-docstring module description)
  455. (docstring-temp-location "No docstring in manual"))))
  456. (setq docstring-ediff-buffers
  457. (list (car track-location)
  458. (car snarf-location)
  459. (car manual-location)))
  460. (docstring-narrow-to-location track-location)
  461. (docstring-narrow-to-location snarf-location)
  462. (docstring-narrow-to-location manual-location)
  463. (add-hook 'ediff-quit-hook 'docstring-widen-ediff-buffers)
  464. (ediff-buffers3 (nth 0 docstring-ediff-buffers)
  465. (nth 1 docstring-ediff-buffers)
  466. (nth 2 docstring-ediff-buffers)))))
  467. (defun docstring-narrow-to-location (location)
  468. (save-excursion
  469. (set-buffer (car location))
  470. (narrow-to-region (cadr location) (caddr location))))
  471. (defun docstring-temp-location (str)
  472. (let ((buf (generate-new-buffer "*Docstring Temp*")))
  473. (save-excursion
  474. (set-buffer buf)
  475. (erase-buffer)
  476. (insert str "\n")
  477. (list buf (point-min) (point-max)))))
  478. (require 'ediff)
  479. (defvar docstring-ediff-buffers '())
  480. (defun docstring-widen-ediff-buffers ()
  481. (remove-hook 'ediff-quit-hook 'docstring-widen-ediff-buffers)
  482. (save-excursion
  483. (mapcar (function (lambda (buffer)
  484. (set-buffer buffer)
  485. (widen)))
  486. docstring-ediff-buffers)))
  487. ;;; Tests:
  488. ;(find-docstring "/home/neil/Guile/cvs/guile-core/doc/maint/guile.texi" nil "primitive sloppy-assq")
  489. ;(find-manual-docstring '(guile) "primitive sloppy-assq")
  490. ;(find-tracking-docstring '(guile) "primitive sloppy-assq")
  491. ;(find-snarfed-docstring '(guile) "primitive sloppy-assq")
  492. (defvar docstring-libguile-directory (expand-file-name "libguile"
  493. guile-core-dir)
  494. "*The directory containing the C source for libguile.")
  495. (defvar docstring-libguile-build-directory (expand-file-name "libguile"
  496. guile-build-dir)
  497. "*The directory containing the libguile build directory.")
  498. (defun docstring-display-location (file line)
  499. (let ((buffer (find-file-noselect
  500. (expand-file-name file docstring-libguile-directory))))
  501. (if buffer
  502. (let* ((window (or (get-buffer-window buffer)
  503. (display-buffer buffer)))
  504. (pos (save-excursion
  505. (set-buffer buffer)
  506. (goto-line line)
  507. (point))))
  508. (set-window-point window pos)))))
  509. (defun docstring-show-source ()
  510. "Given that point is sitting in a docstring in one of the Texinfo
  511. source files for the Guile manual, and that that docstring may be
  512. snarfed automatically from a libguile C file, determine whether the
  513. docstring is from libguile and, if it is, display the relevant C file
  514. at the line from which the docstring was snarfed.
  515. Why? When updating snarfed docstrings, you should usually edit the C
  516. source rather than the Texinfo source, so that your updates benefit
  517. Guile's online help as well. This function locates the C source for a
  518. docstring so that it is easy for you to do this."
  519. (interactive)
  520. (let* ((deffn-line
  521. (save-excursion
  522. (end-of-line)
  523. (or (re-search-backward "^@deffn " nil t)
  524. (error "No docstring here!"))
  525. (buffer-substring (point)
  526. (progn
  527. (end-of-line)
  528. (point)))))
  529. (guile-texi-file
  530. (expand-file-name "guile.texi" docstring-libguile-build-directory))
  531. (source-location
  532. (save-excursion
  533. (set-buffer (find-file-noselect guile-texi-file))
  534. (save-excursion
  535. (goto-char (point-min))
  536. (or (re-search-forward (concat "^"
  537. (regexp-quote deffn-line)
  538. "$")
  539. nil t)
  540. (error "Docstring not from libguile"))
  541. (forward-line -1)
  542. (if (looking-at "^@c snarfed from \\([^:]+\\):\\([0-9]+\\)$")
  543. (cons (match-string 1)
  544. (string-to-int (match-string 2)))
  545. (error "Corrupt docstring entry in guile.texi"))))))
  546. (docstring-display-location (car source-location)
  547. (cdr source-location))))
  548. (provide 'docstring)
  549. ;;; docstring.el ends here