File Coverage

blib/lib/Emacs/Run/ExtractDocs.pm
Criterion Covered Total %
statement 33 94 35.1
branch 0 22 0.0
condition 0 2 0.0
subroutine 11 16 68.7
pod 2 2 100.0
total 46 136 33.8


line stmt bran cond sub pod time code
1             package Emacs::Run::ExtractDocs;
2 1     1   24819 use base qw( Class::Base );
  1         3  
  1         929  
3              
4             =head1 NAME
5              
6             Emacs::Run::ExtractDocs - extract elisp docstrings to html form
7              
8             =head1 SYNOPSIS
9              
10             use Emacs::Run::ExtractDocs;
11             my $reed = Emacs::Run::ExtractDocs->new({
12             html_output_location => "/tmp/html",
13             });
14              
15             # needed only if extract-docstrings.el isn't in load-path
16             $reed->set_main_library("/tmp/new/elisp/extract-doctrings.el");
17              
18             $reed->elisp_docstrings_to_html("my-elisp-with-a-lot-of-docstrings.el");
19              
20             =head1 DESCRIPTION
21              
22             Emacs::Run::ExtractDocs is a module that provides ways of working
23             with the "docstrings" from emacs lisp packages and transforming
24             them to other formats (at present, just html).
25              
26             The central feature is the elisp_docstrings_to_html method,
27             which can create a web page displaying the docstrings of any
28             given elisp package.
29              
30             Note that this is a very unusual "perl" package, in that it
31             depends on having emacs installed (most likely, GNU/Emacs).
32             Also, the extract-docstrings.el file that is shipped with this
33             perl package must be installed somewhere in the emacs load-path.
34              
35             =head2 METHODS
36              
37             =over
38              
39             =cut
40              
41 1     1   1387 use 5.8.0;
  1         4  
  1         42  
42 1     1   6 use strict;
  1         6  
  1         28  
43 1     1   5 use warnings;
  1         2  
  1         75  
44 1     1   6 use Carp;
  1         2  
  1         81  
45 1     1   1232 use Data::Dumper;
  1         11688  
  1         84  
46 1     1   904 use Hash::Util qw( lock_keys unlock_keys );
  1         2687  
  1         7  
47 1     1   108 use File::Basename qw( fileparse basename dirname );
  1         3  
  1         77  
48 1     1   1657 use Env qw( $HOME );
  1         3494  
  1         9  
49              
50 1     1   1281 use Emacs::Run;
  1         37687  
  1         771  
51              
52             our $VERSION = '0.03';
53             my $DEBUG = 0; # TODO change to 0 before shipping
54              
55             # needed for accessor generation
56             our $AUTOLOAD;
57             my %ATTRIBUTES = ();
58              
59             =item new
60              
61             Creates a new Emacs::Run::ExtractDocs object.
62              
63             Takes a hashref as an argument, with named fields identical
64             to the names of the object attributes. These attributes are:
65              
66             =over
67              
68             =item html_output_location
69              
70             Directory to put generated html.
71              
72             =item main_library
73              
74             Defaults to the elisp library name "extract-doctrings",
75             so the system can find "extract-doctrings.el" once it's
76             installed in the emacs load_path.
77              
78             This can be set to a different library name, or more likely
79             to a full path to the extract-docstrings.el in an unusual
80             location. (This is very useful for testing, so that the
81             code can run before it's installed.)
82              
83             =item emacs_runner
84              
85             An Emacs::Run object, used internally to call utility
86             routines to probe the emacs installation, and run pieces
87             of emacs lisp code. This will normally be created automatically,
88             but if some unusual options are needed, one can be created
89             externally and passed in as an attribute.
90              
91             =back
92              
93             Takes an optional hashref as an argument, with named fields
94             identical to the names of the object attributes.
95              
96             =cut
97              
98             # Note: "new" is inherited from Class::Base and
99             # calls the following "init" routine automatically.
100              
101             =item init
102              
103             Method that initializes object attributes and then locks them
104             down to prevent accidental creation of new ones.
105              
106             Any class that inherits from this one should have an B of
107             it's own that calls this B. Otherwise, it's an internally
108             used routine that is not of much interest to client coders.
109              
110             =cut
111              
112             sub init {
113 0     0 1   my $self = shift;
114 0           my $args = shift;
115 0           unlock_keys( %{ $self } );
  0            
116              
117 0 0         if ($DEBUG) {
118 0           $self->debugging(1);
119             }
120              
121 0           my @attributes = qw(
122             html_output_location
123             main_library
124             emacs_runner
125             );
126              
127 0           foreach my $field (@attributes) {
128 0           $ATTRIBUTES{ $field } = 1;
129 0           $self->{ $field } = $args->{ $field };
130             }
131              
132             # default to the lib name (works once extract-docstrings.el
133             # is installed)
134 0   0       $self->{ main_library } ||= 'extract-docstrings';
135 0           my $main_library = $self->{ main_library };
136              
137 0 0         unless ( $self->emacs_runner ) {
138 0           my $er = Emacs::Run->new({
139             emacs_libs => [ $main_library ],
140             });
141 0           $self->set_emacs_runner( $er );
142             }
143              
144 0           lock_keys( %{ $self } );
  0            
145 0           return $self;
146             }
147              
148             =back
149              
150             =head2 main methods
151              
152             =over
153              
154             =item elisp_docstrings_to_html
155              
156             Given the name of an emacs lisp library (sans path or extension),
157             or the file name of the library (with extension *.el), generates
158             an html file in the standard location defined in the object:
159             html_output_location
160              
161             The output file naming convention is: _el.html
162              
163             =cut
164              
165             sub elisp_docstrings_to_html {
166 0     0 1   my $self = shift;
167 0           my $thingie = shift; # either file or library
168 0           my $progname = ( caller(0) )[3];
169 0           my $er = $self->emacs_runner;
170              
171             # Add the given library to the ones loaded by the Emacs::Run object
172 0           $er->push_emacs_libs( $thingie );
173              
174 0           my $loader_elisp = $er->generate_elisp_to_load_library( $thingie );
175 0           $loader_elisp = $er->quote_elisp( $loader_elisp );
176              
177 0           my ($elisp_file, $elisp_lib);
178 0 0         if ( $thingie =~ m/\.el$/ ) {
179 0           $elisp_file = $thingie;
180 0           ($elisp_lib, undef, undef) = fileparse( $elisp_file, qr{ \.el$ }x );
181             } else {
182 0           $elisp_lib = $thingie;
183 0           $elisp_file = $self->emacs_runner->elisp_file_from_library_name_if_in_loadpath( $elisp_lib );
184             }
185              
186 0           my $output_loc = $self->html_output_location;
187 0           my $output_file = "$output_loc/$elisp_lib" . '_el.html';
188              
189 0 0         unlink $output_file if -e $output_file; # redundant with elisp feature
190              
191 0           my $extractor_elisp = qq{
192             (extract-doctrings-generate-html-for-elisp-file
193             "$elisp_file"
194             "$output_file"
195             "Documentation for $elisp_lib.el (extracted docstrings)")
196             };
197              
198 0           $self->emacs_runner->eval_elisp( $extractor_elisp ); # Note: eval_elisp does a quote_elisp internally.
199              
200 0           my $output_created_flag = -e $output_file;
201              
202             # check that there's a closing at the bottom
203 0 0         if ($output_created_flag) {
204 0 0         open my $fh, '<', $output_file or die "Could not open $output_file for read: $!";
205 0           local $/;
206 0           my $content = <$fh>;
207 0           close( $fh );
208 0 0         unless( $content =~ m{ \s* \z }xmsi ) {
209 0           $output_created_flag = 0;
210             }
211             }
212              
213 0           return $output_created_flag;
214             }
215              
216             =back
217              
218             =head2 setters and getters
219              
220             The naming convention in use here is that setters begin with
221             "set_", but getters have *no* prefix: the most commonly used case
222             deserves the simplest syntax (and mutators are deprecated).
223              
224             These accessors exist for all of the object attributes (documented
225             above) irrespective of whether they're expected to be externally useful.
226              
227             =head2 automatic generation of accessors
228              
229             =over
230              
231             =item AUTOLOAD
232              
233             =cut
234              
235             sub AUTOLOAD {
236 0 0   0     return if $AUTOLOAD =~ /DESTROY$/; # skip calls to DESTROY ()
237              
238 0           my ($name) = $AUTOLOAD =~ /([^:]+)$/; # extract method name
239 0           (my $field = $name) =~ s/^set_//;
240              
241             # check that this is a valid accessor call
242 0 0         croak("Unknown method '$AUTOLOAD' called")
243             unless defined( $ATTRIBUTES{ $field } );
244              
245 1     1   14 { no strict 'refs';
  1         3  
  1         232  
  0            
246              
247             # create the setter and getter and install them in the symbol table
248              
249 0 0         if ( $name =~ /^set_/ ) {
    0          
250              
251             *$name = sub {
252 0     0     my $self = shift;
253 0           $self->{ $field } = shift;
254 0           return $self->{ $field };
255 0           };
256              
257 0           goto &$name; # jump to the new method.
258             } elsif ( $name =~ /^get_/ ) {
259 0           carp("Apparent attempt at using a getter with unneeded 'get_' prefix.");
260             }
261              
262             *$name = sub {
263 0     0     my $self = shift;
264 0           return $self->{ $field };
265 0           };
266              
267 0           goto &$name; # jump to the new method.
268             }
269             }
270              
271              
272             1;
273              
274             =back
275              
276             =head1 MOTIVATION
277              
278             Publishing code to a web site is essentially a systems
279             administration task that is a very good fit for perl, but when
280             the code you're publishing is emacs lisp, then emacs lisp is
281             convenient for some of the tasks: hence this franken-project,
282             gluing an emacs lisp package (extract-docstrings.el) into a perl
283             module framework.
284              
285             Emacs lisp has a feature where a "docstring" can be defined for
286             each function or variable. This was primarily intended for the
287             use of the emacs on-line help system, as opposed to the texinfo
288             format used by the Gnu project for it's more formal documentation.
289              
290             A practice started by Ilya Zakharovich when he wrote
291             cperl-mode was to abuse this system of docstrings, in
292             order to lower the bar to writing documentation: essentially
293             it's a way of faking "pod" in elisp.
294              
295             If your documentation is embedded in the emacs help system in
296             the form of these docstrings, then when creating web pages about
297             the code, it's useful to be able to extract the docstrings and
298             format them as an html page.
299              
300             And that's the small need this lash-up of a module fills.
301              
302              
303             =head1 TODO
304              
305             o With this version, I use the (rather cheesy, in my opinion)
306             cop-out of instructing the user to manually install the elisp
307             somewhere in the load-path. Question: can this be automated?
308              
309             o Currently, this is file-oriented: one *.el in, one *.html out.
310             Would like to work on a set of elisp files, and handle
311             internal links inside the set appropriately.
312              
313             o Look into skeleton or tempo to do html headers and footers.
314             At present, these are hardcoded strings (to dodge the old
315             dependency on the non-standard template.el).
316              
317             =head1 SEE ALSO
318              
319             L
320              
321             =head1 AUTHOR
322              
323             Joseph Brenner, Edoom@kzsu.stanford.eduE,
324             02 Mar 2008
325              
326             =head1 COPYRIGHT AND LICENSE
327              
328             Copyright (C) 2008 by Joseph Brenner
329              
330             This library is free software; you can redistribute it and/or modify
331             it under the same terms as Perl itself, either Perl version 5.8.2 or,
332             at your option, any later version of Perl 5 you may have available.
333              
334             =head1 BUGS
335              
336             None reported... yet.
337              
338             =cut