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 |