line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
|
2
|
|
|
|
|
|
|
=head1 NAME |
3
|
|
|
|
|
|
|
|
4
|
|
|
|
|
|
|
Devel::PerlySense - Perl IDE backend with Emacs frontend |
5
|
|
|
|
|
|
|
|
6
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
=head1 DESCRIPTION |
8
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
PerlySense is a Perl IDE backend that integrates with editor |
10
|
|
|
|
|
|
|
frontends, currently Emacs. |
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
(While no one has written a Vim frontend, PerlySense can emit Vim |
13
|
|
|
|
|
|
|
style data structures.) |
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
Conveniently navigate and browse the code and documentation of your |
16
|
|
|
|
|
|
|
project and Perl installation. Navigate between tests and source, and |
17
|
|
|
|
|
|
|
between related files. |
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
Search through the project for method declarations, invocants or free |
20
|
|
|
|
|
|
|
text using Ack. |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
Run tests and scripts with easy navigation to errors/warnings/failing |
23
|
|
|
|
|
|
|
tests. Tests can be run under Devel::Cover to collect (and display) |
24
|
|
|
|
|
|
|
test coverage information. |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
Automate common editing tasks related to source code, tests, regular |
27
|
|
|
|
|
|
|
expressions, etc. |
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
Highlight syntax errors, warnings, Perl::Critic complaints, and |
30
|
|
|
|
|
|
|
Devel::Cover test coverage in the source while editing. |
31
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
PerlySense has a plugin system for understanding custom syntax, |
33
|
|
|
|
|
|
|
e.g. Moose. |
34
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
=head1 SYNOPSIS |
38
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
=head2 From Emacs |
41
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
B<Overview> -- C<C-o C-o> -- Show information about the Class at point |
43
|
|
|
|
|
|
|
or the current Class. There are also shortcuts to show a single |
44
|
|
|
|
|
|
|
section: |
45
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
=over 4 |
47
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
=item * C-o o i -- Inheritance |
49
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
=item * C-o o a -- API |
51
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
=item * C-o o b -- Bookmarks |
53
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
=item * C-o o u -- Uses |
55
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
=item * C-o o h -- NeighbourHood |
57
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
=back |
59
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
B<Docs> -- C<C-o C-d> -- Show docs (POD/signature/etc) for the symbol |
61
|
|
|
|
|
|
|
(module/method/sub) at point. A doc hint is displayed in the echo area |
62
|
|
|
|
|
|
|
(for methods and subs), or a new POD buffer is created (for modules). |
63
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
B<Document Inheritance> -- C<C-o d i> -- Show the Inheritance hierarchy |
65
|
|
|
|
|
|
|
for the current Class in the echo area. |
66
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
C<C-o d u> -- Document 'use Module' statements in the echo area. |
68
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
B<Go To> -- C<C-o C-g> -- Open file at proper location for module, |
70
|
|
|
|
|
|
|
method/sub declaration for the symbol (module/method/sub) at point. If |
71
|
|
|
|
|
|
|
no sub declaration is available (like for generated getters/setters), |
72
|
|
|
|
|
|
|
any appropriate POD is used instead. |
73
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
B<Go To Use> -- C<C-o g u> -- Go to the 'use Module' section of the current buffer. |
75
|
|
|
|
|
|
|
|
76
|
|
|
|
|
|
|
B<Go To 'new'> -- C<C-o g n> -- Go to the 'new' method of the current |
77
|
|
|
|
|
|
|
class. |
78
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
B<Go To Base Class> -- C<C-o g b> -- Open the file of the base class |
80
|
|
|
|
|
|
|
of the current class. This will take you up one level in the |
81
|
|
|
|
|
|
|
inheritance hierarchy. |
82
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
B<Go To Module> -- C<C-o g m> -- Open the source file of the module at |
84
|
|
|
|
|
|
|
point. |
85
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
B<Go To Version Control> -- C<C-o g v> -- Go to the Project view of |
87
|
|
|
|
|
|
|
the current Version Control system. |
88
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
B<Go To Tests - Other Files> -- C<C-o g t o> -- Go to any related test |
90
|
|
|
|
|
|
|
or source files given a L<Devel::CoverX::Covered> covered db. |
91
|
|
|
|
|
|
|
|
92
|
|
|
|
|
|
|
B<Go To Project's Other Files> -- C<C-o g p o> -- Go to |
93
|
|
|
|
|
|
|
I<corresponding> files given a C<.corresponding_file> config file (see |
94
|
|
|
|
|
|
|
L<File::Corresponding>). |
95
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
B<Find with Ack> -- C<C-o f a> -- Search for the selected text, or |
97
|
|
|
|
|
|
|
word at point, or whatever, using Ack. |
98
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
B<Find sub declarations> -- C<C-o f s> -- Search for sub declarations |
100
|
|
|
|
|
|
|
of the method name, or word at point. |
101
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
B<Find method calls> -- C<C-o f c> -- Search for method calls of the |
103
|
|
|
|
|
|
|
method name, or word at point. |
104
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
B<Go To Find Buffer> -- C<C-o g f> to go to the B<*grep*> buffer. |
106
|
|
|
|
|
|
|
|
107
|
|
|
|
|
|
|
B<Run file> -- C<C-o C-r> -- Run the current file using the |
108
|
|
|
|
|
|
|
Compilation mode and the settings appropriate for the source type |
109
|
|
|
|
|
|
|
(Test, Module, etc.). Highlight errors and jump to source with C-c |
110
|
|
|
|
|
|
|
C-c. |
111
|
|
|
|
|
|
|
|
112
|
|
|
|
|
|
|
B<Run file under Devel::CoverX::Covered> -- C<C-o r c> -- Run the |
113
|
|
|
|
|
|
|
current file, collecting Devel::CoverX::Covered information. |
114
|
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
B<Edit - Copy Package Name> -- C<C-o e c p> -- Copy the current package name. |
116
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
B<Edit - Copy Package Name From File> -- C<C-o e c P> -- Copy the |
118
|
|
|
|
|
|
|
current package name from file name. |
119
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
B<Edit - Copy Sub Name> -- C<C-o e c s> -- Copy the current sub name. |
121
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
B<Edit - Copy File Name> -- C<C-o e c f> -- Copy the current file name. |
123
|
|
|
|
|
|
|
|
124
|
|
|
|
|
|
|
B<Edit - Add Use Statement> -- C<C-o e a u> -- Add a 'use Module' |
125
|
|
|
|
|
|
|
statement to the 'use Module' section at the top. Default Module name |
126
|
|
|
|
|
|
|
is module at point. |
127
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
B<Edit - Move Use Statement> -- C<C-o e m u> -- Move the 'use Module' |
129
|
|
|
|
|
|
|
statement at point to the 'use Module' section at the top. |
130
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
B<Extract Variable> - C<C-o e e v> -- Do the refactoring Extract |
132
|
|
|
|
|
|
|
Variable of the active region. |
133
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
B<Find Callers> - C>C-o e f c> -- Find callers of a method in the |
135
|
|
|
|
|
|
|
project and insert the call tree as a comment in the source. |
136
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
B<Visualize Callers> - C>C-o e v c> -- Visualize the callers comment |
138
|
|
|
|
|
|
|
created by "Find Callers" using GrapViz. |
139
|
|
|
|
|
|
|
|
140
|
|
|
|
|
|
|
B<Visualize Callers> - C>C-o e v c> -- Visualize callers in a call |
141
|
|
|
|
|
|
|
tree (found by Find Callers) by drawing the call tree using GraphViz. |
142
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
B<Edit Test Count> -- C<C-o e t c> -- Increase the test count |
144
|
|
|
|
|
|
|
(e.g. "tests => 43") |
145
|
|
|
|
|
|
|
|
146
|
|
|
|
|
|
|
B<Assist With Test Count> -- C<C-o a t> -- Synchronize invalid test |
147
|
|
|
|
|
|
|
count in .t file with the B<*compilation*> buffer. |
148
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
Flymake may be used to highlight syntax errors, warnings, and |
150
|
|
|
|
|
|
|
Perl::Critic violations in the source while editing (continously or at |
151
|
|
|
|
|
|
|
every save). |
152
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
=head2 From Vim |
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
There is no integraton with Vim available. Well, not properly |
158
|
|
|
|
|
|
|
anyway. If you pass the option |
159
|
|
|
|
|
|
|
|
160
|
|
|
|
|
|
|
--io_type=editor_vim |
161
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
to perly_sense, the output will be serialized to Vim L<Dictionary data |
163
|
|
|
|
|
|
|
structures|http://vimdoc.sourceforge.net/htmldoc/eval.html#Dictionaries>. |
164
|
|
|
|
|
|
|
|
165
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
|
167
|
|
|
|
|
|
|
=head2 From other editors |
168
|
|
|
|
|
|
|
|
169
|
|
|
|
|
|
|
Any editor that is programmable and that can call a shell script could |
170
|
|
|
|
|
|
|
take advantage of at least some parts of PerlySense to implement |
171
|
|
|
|
|
|
|
something similar to the Emacs functionality. And most editors are |
172
|
|
|
|
|
|
|
programmable by the authors, if not by the users. |
173
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
|
176
|
|
|
|
|
|
|
=head2 From the command line |
177
|
|
|
|
|
|
|
|
178
|
|
|
|
|
|
|
=over 4 |
179
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
=item * Create Project |
181
|
|
|
|
|
|
|
|
182
|
|
|
|
|
|
|
perly_sense create_project [--dir=DIR] |
183
|
|
|
|
|
|
|
|
184
|
|
|
|
|
|
|
Create a PerlySense project in DIR (default is current dir). |
185
|
|
|
|
|
|
|
|
186
|
|
|
|
|
|
|
If there is already a project.yml file, back it up with a datestamp |
187
|
|
|
|
|
|
|
first. |
188
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
(Note that you don't need to create a project before start using |
190
|
|
|
|
|
|
|
PerlySense. Read more below). |
191
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
|
193
|
|
|
|
|
|
|
=item * Process Project Source Files |
194
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
perly_sense process_project [--dir=.] |
196
|
|
|
|
|
|
|
|
197
|
|
|
|
|
|
|
Cache all modules in the project that --dir belongs to. |
198
|
|
|
|
|
|
|
|
199
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
=item * Process Source Files in @INC |
201
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
perly_sense process_inc |
203
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
Cache all the modules in @INC. |
205
|
|
|
|
|
|
|
|
206
|
|
|
|
|
|
|
This is a useful thing to do after installation (and after each |
207
|
|
|
|
|
|
|
upgrade), but it will take a while so put it in the background and let |
208
|
|
|
|
|
|
|
it churn away at those modules. |
209
|
|
|
|
|
|
|
|
210
|
|
|
|
|
|
|
=over 4 |
211
|
|
|
|
|
|
|
|
212
|
|
|
|
|
|
|
=item * Unix |
213
|
|
|
|
|
|
|
|
214
|
|
|
|
|
|
|
perly_sense process_inc & # (well, you knew that already) |
215
|
|
|
|
|
|
|
|
216
|
|
|
|
|
|
|
=item * Windows |
217
|
|
|
|
|
|
|
|
218
|
|
|
|
|
|
|
start /MIN perly_sense process_inc |
219
|
|
|
|
|
|
|
|
220
|
|
|
|
|
|
|
=back |
221
|
|
|
|
|
|
|
|
222
|
|
|
|
|
|
|
|
223
|
|
|
|
|
|
|
=item * Get Info |
224
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
perly_sense info |
226
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
Display useful information about what the current project directory, |
228
|
|
|
|
|
|
|
user home directory, etc. is. |
229
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
=back |
231
|
|
|
|
|
|
|
|
232
|
|
|
|
|
|
|
|
233
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
=head1 INSTALLATION |
235
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
=head2 Module Installation |
237
|
|
|
|
|
|
|
|
238
|
|
|
|
|
|
|
Install the Devel::PerlySense module and accompanying elisp by using a |
239
|
|
|
|
|
|
|
configured CPAN shell, like this: |
240
|
|
|
|
|
|
|
|
241
|
|
|
|
|
|
|
cpan Devel::PerlySense |
242
|
|
|
|
|
|
|
|
243
|
|
|
|
|
|
|
When everything is installed, verify by running |
244
|
|
|
|
|
|
|
|
245
|
|
|
|
|
|
|
perly_sense info |
246
|
|
|
|
|
|
|
|
247
|
|
|
|
|
|
|
The elisp is installed next to the Perl source (so it works to install |
248
|
|
|
|
|
|
|
as an unpriviliged user, and you don't I<have> to have Emacs |
249
|
|
|
|
|
|
|
installed, and the elisp and Perl source are always in sync). |
250
|
|
|
|
|
|
|
|
251
|
|
|
|
|
|
|
|
252
|
|
|
|
|
|
|
=head2 Supporting modules |
253
|
|
|
|
|
|
|
|
254
|
|
|
|
|
|
|
These aren't needed to begin with, but may be very useful. |
255
|
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
=over 4 |
257
|
|
|
|
|
|
|
|
258
|
|
|
|
|
|
|
=item * L<Devel::CoverX::Covered> |
259
|
|
|
|
|
|
|
|
260
|
|
|
|
|
|
|
If you have a lot of tests to navigate and run a nightly build with |
261
|
|
|
|
|
|
|
Devel::Cover to generate test coverage. You can also run individual |
262
|
|
|
|
|
|
|
files under Devel::CoverX::Covered with C<C-o r c>. |
263
|
|
|
|
|
|
|
|
264
|
|
|
|
|
|
|
=item * L<File::Corresponding> |
265
|
|
|
|
|
|
|
|
266
|
|
|
|
|
|
|
If you have an MVC style class structure with the same entity |
267
|
|
|
|
|
|
|
represented in different directories (e.g. Controller::Aeroplane, |
268
|
|
|
|
|
|
|
Model::Aeroplane, etc.). |
269
|
|
|
|
|
|
|
|
270
|
|
|
|
|
|
|
=back |
271
|
|
|
|
|
|
|
|
272
|
|
|
|
|
|
|
|
273
|
|
|
|
|
|
|
|
274
|
|
|
|
|
|
|
=head2 Emacs installation |
275
|
|
|
|
|
|
|
|
276
|
|
|
|
|
|
|
Make sure the Devel::PerlySense CPAN module is installed, it contains |
277
|
|
|
|
|
|
|
the required elisp files which will be loaded automatically with the |
278
|
|
|
|
|
|
|
following in your .emacs config file: |
279
|
|
|
|
|
|
|
|
280
|
|
|
|
|
|
|
|
281
|
|
|
|
|
|
|
;; *** PerlySense Config *** |
282
|
|
|
|
|
|
|
|
283
|
|
|
|
|
|
|
;; ** PerlySense ** |
284
|
|
|
|
|
|
|
;; The PerlySense prefix key (unset only if needed, like for \C-o) |
285
|
|
|
|
|
|
|
(global-unset-key "\C-o") |
286
|
|
|
|
|
|
|
(setq ps/key-prefix "\C-o") |
287
|
|
|
|
|
|
|
|
288
|
|
|
|
|
|
|
|
289
|
|
|
|
|
|
|
;; ** Flymake ** |
290
|
|
|
|
|
|
|
;; Load flymake if t |
291
|
|
|
|
|
|
|
;; Flymake must be installed. |
292
|
|
|
|
|
|
|
;; It is included in Emacs 22 |
293
|
|
|
|
|
|
|
;; (or http://flymake.sourceforge.net/, put flymake.el in your load-path) |
294
|
|
|
|
|
|
|
(setq ps/load-flymake t) |
295
|
|
|
|
|
|
|
;; Note: more flymake config below, after loading PerlySense |
296
|
|
|
|
|
|
|
|
297
|
|
|
|
|
|
|
|
298
|
|
|
|
|
|
|
;; *** PerlySense load (don't touch) *** |
299
|
|
|
|
|
|
|
(setq ps/external-dir (shell-command-to-string "perly_sense external_dir")) |
300
|
|
|
|
|
|
|
(if (string-match "Devel.PerlySense.external" ps/external-dir) |
301
|
|
|
|
|
|
|
(progn |
302
|
|
|
|
|
|
|
(message |
303
|
|
|
|
|
|
|
"PerlySense elisp files at (%s) according to perly_sense, loading..." |
304
|
|
|
|
|
|
|
ps/external-dir) |
305
|
|
|
|
|
|
|
(setq load-path (cons |
306
|
|
|
|
|
|
|
(expand-file-name |
307
|
|
|
|
|
|
|
(format "%s/%s" ps/external-dir "emacs") |
308
|
|
|
|
|
|
|
) load-path)) |
309
|
|
|
|
|
|
|
(load "perly-sense") |
310
|
|
|
|
|
|
|
) |
311
|
|
|
|
|
|
|
(message "Could not identify PerlySense install dir. |
312
|
|
|
|
|
|
|
Is Devel::PerlySense installed properly? |
313
|
|
|
|
|
|
|
Does 'perly_sense external_dir' give you a proper directory? (%s)" ps/external-dir) |
314
|
|
|
|
|
|
|
) |
315
|
|
|
|
|
|
|
|
316
|
|
|
|
|
|
|
|
317
|
|
|
|
|
|
|
;; ** Flymake Config ** |
318
|
|
|
|
|
|
|
;; If you only want syntax check whenever you save, not continously |
319
|
|
|
|
|
|
|
(setq flymake-no-changes-timeout 9999) |
320
|
|
|
|
|
|
|
(setq flymake-start-syntax-check-on-newline nil) |
321
|
|
|
|
|
|
|
|
322
|
|
|
|
|
|
|
;; ** Code Coverage Visualization ** |
323
|
|
|
|
|
|
|
;; If you have a Devel::CoverX::Covered database handy and want to |
324
|
|
|
|
|
|
|
;; display the sub coverage in the source, set this to t |
325
|
|
|
|
|
|
|
(setq ps/enable-test-coverage-visualization nil) |
326
|
|
|
|
|
|
|
|
327
|
|
|
|
|
|
|
;; ** Color Config ** |
328
|
|
|
|
|
|
|
;; Emacs named colors: http://www.geocities.com/kensanata/colors.html |
329
|
|
|
|
|
|
|
;; The following colors work fine with a white X11 |
330
|
|
|
|
|
|
|
;; background. They may not look that great on a console with the |
331
|
|
|
|
|
|
|
;; default color scheme. |
332
|
|
|
|
|
|
|
(set-face-background 'flymake-errline "antique white") |
333
|
|
|
|
|
|
|
(set-face-background 'flymake-warnline "lavender") |
334
|
|
|
|
|
|
|
(set-face-background 'dropdown-list-face "lightgrey") |
335
|
|
|
|
|
|
|
(set-face-background 'dropdown-list-selection-face "grey") |
336
|
|
|
|
|
|
|
|
337
|
|
|
|
|
|
|
|
338
|
|
|
|
|
|
|
;; ** Misc Config ** |
339
|
|
|
|
|
|
|
|
340
|
|
|
|
|
|
|
;; Run calls to perly_sense as a prepared shell command. Experimental |
341
|
|
|
|
|
|
|
;; optimization, please try it out. |
342
|
|
|
|
|
|
|
(setq ps/use-prepare-shell-command t) |
343
|
|
|
|
|
|
|
|
344
|
|
|
|
|
|
|
;; *** PerlySense End *** |
345
|
|
|
|
|
|
|
|
346
|
|
|
|
|
|
|
|
347
|
|
|
|
|
|
|
|
348
|
|
|
|
|
|
|
=head2 Emacs Configuration |
349
|
|
|
|
|
|
|
|
350
|
|
|
|
|
|
|
The most important config you can change is the prefix key. |
351
|
|
|
|
|
|
|
|
352
|
|
|
|
|
|
|
The default, \C-o, seemed to have a rater low useful-to-keystroke |
353
|
|
|
|
|
|
|
ratio and so was a strong candidate for stealing for this much more |
354
|
|
|
|
|
|
|
important purpose :) Now, the I<proper> way of doing this is of course |
355
|
|
|
|
|
|
|
to some kind of C-c prefix. You decide. |
356
|
|
|
|
|
|
|
|
357
|
|
|
|
|
|
|
If you want to use flymake to do background syntax and Perl::Critic |
358
|
|
|
|
|
|
|
checks, set ps/load-flymake to t (this is a very nifty thing, |
359
|
|
|
|
|
|
|
so yes you want to do this) and configure the colors to your liking. |
360
|
|
|
|
|
|
|
|
361
|
|
|
|
|
|
|
Note: This also needs to be enabled on a per-project basis (see |
362
|
|
|
|
|
|
|
below). |
363
|
|
|
|
|
|
|
|
364
|
|
|
|
|
|
|
Once you have restarted Emacs, you might want to browse around the |
365
|
|
|
|
|
|
|
customizations by doing |
366
|
|
|
|
|
|
|
|
367
|
|
|
|
|
|
|
M-x customize-group perly-sense |
368
|
|
|
|
|
|
|
|
369
|
|
|
|
|
|
|
|
370
|
|
|
|
|
|
|
|
371
|
|
|
|
|
|
|
=head1 GETTING STARTED WITH EMACS |
372
|
|
|
|
|
|
|
|
373
|
|
|
|
|
|
|
This is quite a handfull of new features, and you're not likely to be |
374
|
|
|
|
|
|
|
able to use them efficiently from day one. Remember, Emacs is all |
375
|
|
|
|
|
|
|
about acquiring finger memory, one feature at a time. |
376
|
|
|
|
|
|
|
|
377
|
|
|
|
|
|
|
These are the ones I use every day so they may be a good start: |
378
|
|
|
|
|
|
|
|
379
|
|
|
|
|
|
|
=over 4 |
380
|
|
|
|
|
|
|
|
381
|
|
|
|
|
|
|
=item * Go to Module |
382
|
|
|
|
|
|
|
|
383
|
|
|
|
|
|
|
=item * Go to base class |
384
|
|
|
|
|
|
|
|
385
|
|
|
|
|
|
|
=item * Document Class Hierarchy |
386
|
|
|
|
|
|
|
|
387
|
|
|
|
|
|
|
=back |
388
|
|
|
|
|
|
|
|
389
|
|
|
|
|
|
|
=over 4 |
390
|
|
|
|
|
|
|
|
391
|
|
|
|
|
|
|
=item * Go to Version Control |
392
|
|
|
|
|
|
|
|
393
|
|
|
|
|
|
|
=back |
394
|
|
|
|
|
|
|
|
395
|
|
|
|
|
|
|
=over 4 |
396
|
|
|
|
|
|
|
|
397
|
|
|
|
|
|
|
=item * Find with Ack |
398
|
|
|
|
|
|
|
|
399
|
|
|
|
|
|
|
=item * Find sub declerations |
400
|
|
|
|
|
|
|
|
401
|
|
|
|
|
|
|
=back |
402
|
|
|
|
|
|
|
|
403
|
|
|
|
|
|
|
=over 4 |
404
|
|
|
|
|
|
|
|
405
|
|
|
|
|
|
|
=item * Run tests, Re-run tests |
406
|
|
|
|
|
|
|
|
407
|
|
|
|
|
|
|
=item * Assist with Test count |
408
|
|
|
|
|
|
|
|
409
|
|
|
|
|
|
|
=back |
410
|
|
|
|
|
|
|
|
411
|
|
|
|
|
|
|
|
412
|
|
|
|
|
|
|
=head2 Reading Docs |
413
|
|
|
|
|
|
|
|
414
|
|
|
|
|
|
|
=head3 Smart docs |
415
|
|
|
|
|
|
|
|
416
|
|
|
|
|
|
|
|
417
|
|
|
|
|
|
|
=for html <p>[ <a href="http://search.cpan.org/src/JOHANL/Devel-PerlySense-0.0183/doc/smart_docs_method.html">Screenshot</a> ]<p> |
418
|
|
|
|
|
|
|
|
419
|
|
|
|
|
|
|
|
420
|
|
|
|
|
|
|
C<C-o C-d> is the "Smart docs" command. It brings up POD documentation |
421
|
|
|
|
|
|
|
for what's at point. |
422
|
|
|
|
|
|
|
|
423
|
|
|
|
|
|
|
Put the cursor on the C<method> word of a C<$self-E<gt>method> call |
424
|
|
|
|
|
|
|
and press C<C-o C-d> and wait until a documentation hint for the |
425
|
|
|
|
|
|
|
method call is displayed briefly in the echo area. PerlySense will |
426
|
|
|
|
|
|
|
look in base classes if the method can't be found in the current |
427
|
|
|
|
|
|
|
class. |
428
|
|
|
|
|
|
|
|
429
|
|
|
|
|
|
|
Put the cursor on the C<method> word of an $object-E<gt>method call |
430
|
|
|
|
|
|
|
and press C<C-o C-d> to see the docs hint. PerlySense will look |
431
|
|
|
|
|
|
|
through all your C<use>d modules (and their base classes) for the |
432
|
|
|
|
|
|
|
method call and try to identify the best match. |
433
|
|
|
|
|
|
|
|
434
|
|
|
|
|
|
|
Note! The first time each module is parsed this will take a second or |
435
|
|
|
|
|
|
|
two, and the very first time you run the command with lots of "use" |
436
|
|
|
|
|
|
|
modules it's bound to take a lot longer than that. |
437
|
|
|
|
|
|
|
|
438
|
|
|
|
|
|
|
Put the cursor on a module name and press C<C-o C-d> to bring up a new |
439
|
|
|
|
|
|
|
buffer with the POD for that module (this is similar to the cperl-mode |
440
|
|
|
|
|
|
|
feature, only a) not as good, but b) it works on Windows). |
441
|
|
|
|
|
|
|
|
442
|
|
|
|
|
|
|
Press C<C-o C-d> with nothing under the cursor brings up a POD buffer |
443
|
|
|
|
|
|
|
for the current file. |
444
|
|
|
|
|
|
|
|
445
|
|
|
|
|
|
|
|
446
|
|
|
|
|
|
|
=head3 Document Inheritance |
447
|
|
|
|
|
|
|
|
448
|
|
|
|
|
|
|
C<C-o d i> will briefly display the Inheritance hierarchy for the |
449
|
|
|
|
|
|
|
current Class in the echo area. Example: |
450
|
|
|
|
|
|
|
|
451
|
|
|
|
|
|
|
[ DBIx::Class::Componentised ] |
452
|
|
|
|
|
|
|
[ DBIx::Class ] --> [ Class::Data::Accessor ] |
453
|
|
|
|
|
|
|
[<CatalystX::FeedMe::DBIC::FeedItem>] |
454
|
|
|
|
|
|
|
|
455
|
|
|
|
|
|
|
|
456
|
|
|
|
|
|
|
=head3 Document Used Modules |
457
|
|
|
|
|
|
|
|
458
|
|
|
|
|
|
|
C<C-o d u> will briefly display the list of modules used from the |
459
|
|
|
|
|
|
|
current buffer in the echo area. Example: |
460
|
|
|
|
|
|
|
|
461
|
|
|
|
|
|
|
[ Carp ] [ File::Spec ] [ Win32::OLE::Const ] |
462
|
|
|
|
|
|
|
[ Class::MethodMaker ] [ File::Temp ] [ Win32::Word::Writer::Table ] |
463
|
|
|
|
|
|
|
[ Data::Dumper ] [ Win32::OLE ] |
464
|
|
|
|
|
|
|
|
465
|
|
|
|
|
|
|
|
466
|
|
|
|
|
|
|
|
467
|
|
|
|
|
|
|
=head2 Browsing Code |
468
|
|
|
|
|
|
|
|
469
|
|
|
|
|
|
|
=head3 Smart go to |
470
|
|
|
|
|
|
|
|
471
|
|
|
|
|
|
|
C<C-o C-g> is the "Smart go to" command. It's similar to Smart Docs, |
472
|
|
|
|
|
|
|
but instead of bringing the docs to you, it brings you to the |
473
|
|
|
|
|
|
|
definition of what's at point. |
474
|
|
|
|
|
|
|
|
475
|
|
|
|
|
|
|
The definition can be either the sub declaration, or if the |
476
|
|
|
|
|
|
|
declaration can't be found (like for auto-generated getters/setters, |
477
|
|
|
|
|
|
|
autoloaded subs etc), the POD documentation for the sub. |
478
|
|
|
|
|
|
|
|
479
|
|
|
|
|
|
|
Before you go anywhere the mark is set. Go back to earlier marks |
480
|
|
|
|
|
|
|
globally with C-x C-SPC, or locally with C-u C-SPC. |
481
|
|
|
|
|
|
|
|
482
|
|
|
|
|
|
|
|
483
|
|
|
|
|
|
|
=head3 Go to Module |
484
|
|
|
|
|
|
|
|
485
|
|
|
|
|
|
|
C<C-o g m> -- Go to Module at point. Useful if "Smart go to" can't |
486
|
|
|
|
|
|
|
identify exactly what's at point. |
487
|
|
|
|
|
|
|
|
488
|
|
|
|
|
|
|
Default is the selected text, or the |
489
|
|
|
|
|
|
|
Module at point. |
490
|
|
|
|
|
|
|
|
491
|
|
|
|
|
|
|
|
492
|
|
|
|
|
|
|
=head3 Go to Base Class |
493
|
|
|
|
|
|
|
|
494
|
|
|
|
|
|
|
C<C-o g b> takes you up one level in the inheritance hierarchy. If the |
495
|
|
|
|
|
|
|
current class has many base classes, you'll have to choose which one |
496
|
|
|
|
|
|
|
to go to. |
497
|
|
|
|
|
|
|
|
498
|
|
|
|
|
|
|
If the current method is implemented in that base class, go to the sub |
499
|
|
|
|
|
|
|
definition. |
500
|
|
|
|
|
|
|
|
501
|
|
|
|
|
|
|
After going to the Base Class, the Inheritance tree of that class is |
502
|
|
|
|
|
|
|
displayed in the echo area so you can see where you ended up. |
503
|
|
|
|
|
|
|
|
504
|
|
|
|
|
|
|
|
505
|
|
|
|
|
|
|
=head3 Go to the 'new' method |
506
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
C<C-o g n> takes you to the definition of the 'new' method of the |
508
|
|
|
|
|
|
|
current class (in this class, or a parent class). But if you're |
509
|
|
|
|
|
|
|
unlucky, it might take you to your OO helper module's default new. |
510
|
|
|
|
|
|
|
|
511
|
|
|
|
|
|
|
|
512
|
|
|
|
|
|
|
=head3 Go To 'use Module' section |
513
|
|
|
|
|
|
|
|
514
|
|
|
|
|
|
|
C<C-o g u> takes you to the line below the last 'use Module' statement |
515
|
|
|
|
|
|
|
in the the current buffer. |
516
|
|
|
|
|
|
|
|
517
|
|
|
|
|
|
|
|
518
|
|
|
|
|
|
|
=head3 Go to Version Control |
519
|
|
|
|
|
|
|
|
520
|
|
|
|
|
|
|
C<C-o g v> -- Go to the Project view for the current Version Control |
521
|
|
|
|
|
|
|
system. This typically displays the change status of the files in the |
522
|
|
|
|
|
|
|
project. A dired of the Project dir is used in lieu of a VCS. |
523
|
|
|
|
|
|
|
|
524
|
|
|
|
|
|
|
First, try to go to any existing VC project buffer. |
525
|
|
|
|
|
|
|
|
526
|
|
|
|
|
|
|
If there is no VC buffer open, find out what VCS is used, and display |
527
|
|
|
|
|
|
|
the Project view. |
528
|
|
|
|
|
|
|
|
529
|
|
|
|
|
|
|
Supported VC systems: |
530
|
|
|
|
|
|
|
|
531
|
|
|
|
|
|
|
=over 4 |
532
|
|
|
|
|
|
|
|
533
|
|
|
|
|
|
|
=item * Subversion -- Quick intro to *svn-status* |
534
|
|
|
|
|
|
|
|
535
|
|
|
|
|
|
|
_ (underscore) - display only the changed files (toggle) |
536
|
|
|
|
|
|
|
|
537
|
|
|
|
|
|
|
n, p, m, u -- next, previous, mark, unmark |
538
|
|
|
|
|
|
|
|
539
|
|
|
|
|
|
|
E -- diff the changes in the current file |
540
|
|
|
|
|
|
|
|
541
|
|
|
|
|
|
|
c -- commit file(s) |
542
|
|
|
|
|
|
|
|
543
|
|
|
|
|
|
|
r -- revert file(s) |
544
|
|
|
|
|
|
|
|
545
|
|
|
|
|
|
|
X v -- resolve conflict (or X X, I'm not sure what the difference is) |
546
|
|
|
|
|
|
|
|
547
|
|
|
|
|
|
|
etc, etc, etc, do a C-h m to see all the goodies. |
548
|
|
|
|
|
|
|
|
549
|
|
|
|
|
|
|
See also: |
550
|
|
|
|
|
|
|
|
551
|
|
|
|
|
|
|
=over 4 |
552
|
|
|
|
|
|
|
|
553
|
|
|
|
|
|
|
=item * L<http://www.credmp.org/index.php/2007/12/08/emacs-hidden-gems-version-control/>, |
554
|
|
|
|
|
|
|
|
555
|
|
|
|
|
|
|
=item * L<http://www.emacsblog.org/2007/05/17/package-faves-psvn/> |
556
|
|
|
|
|
|
|
|
557
|
|
|
|
|
|
|
|
558
|
|
|
|
|
|
|
=back |
559
|
|
|
|
|
|
|
|
560
|
|
|
|
|
|
|
|
561
|
|
|
|
|
|
|
=item * Git -- Magit |
562
|
|
|
|
|
|
|
|
563
|
|
|
|
|
|
|
This requires you to have Magit installed. Download and manual at: |
564
|
|
|
|
|
|
|
L<http://zagadka.vm.bytemark.co.uk/magit/>. |
565
|
|
|
|
|
|
|
|
566
|
|
|
|
|
|
|
When you switch to an existing Magit status buffer the status is |
567
|
|
|
|
|
|
|
refreshed automatically to display the current status. |
568
|
|
|
|
|
|
|
|
569
|
|
|
|
|
|
|
If there are many *magit: NAME* buffers open, the first existing one |
570
|
|
|
|
|
|
|
will be used (whichever that might be). |
571
|
|
|
|
|
|
|
|
572
|
|
|
|
|
|
|
|
573
|
|
|
|
|
|
|
=back |
574
|
|
|
|
|
|
|
|
575
|
|
|
|
|
|
|
|
576
|
|
|
|
|
|
|
=head3 Go to Project's Other Files |
577
|
|
|
|
|
|
|
|
578
|
|
|
|
|
|
|
C<C-o g p o> -- Navigate to I<other> source files in the project that |
579
|
|
|
|
|
|
|
correspond to the current file. |
580
|
|
|
|
|
|
|
|
581
|
|
|
|
|
|
|
This is useful if you have similarly named files in different parts of |
582
|
|
|
|
|
|
|
the source tree that belong to each other, as is common in projects |
583
|
|
|
|
|
|
|
with an MVC structure (e.g. those based on L<Catalyst>). |
584
|
|
|
|
|
|
|
|
585
|
|
|
|
|
|
|
This requires that you have a C<.corresponding_file> config file in |
586
|
|
|
|
|
|
|
the C<.PerlySenseProject> or project root directory (or your home |
587
|
|
|
|
|
|
|
directory). |
588
|
|
|
|
|
|
|
|
589
|
|
|
|
|
|
|
See L<File::Corresponding> for details. |
590
|
|
|
|
|
|
|
|
591
|
|
|
|
|
|
|
|
592
|
|
|
|
|
|
|
|
593
|
|
|
|
|
|
|
=head2 Finding Code |
594
|
|
|
|
|
|
|
|
595
|
|
|
|
|
|
|
|
596
|
|
|
|
|
|
|
=head3 Find with Ack |
597
|
|
|
|
|
|
|
|
598
|
|
|
|
|
|
|
|
599
|
|
|
|
|
|
|
=for html <p>[ <a href="http://search.cpan.org/src/JOHANL/Devel-PerlySense-0.0183/doc/find_with_ack.html">Screenshot</a> ]<p> |
600
|
|
|
|
|
|
|
|
601
|
|
|
|
|
|
|
|
602
|
|
|
|
|
|
|
C<C-o f a> -- Ack through the source and display the hits in a |
603
|
|
|
|
|
|
|
B<*grep*> buffer. L<ack> is like grep, but more suitable for |
604
|
|
|
|
|
|
|
development. |
605
|
|
|
|
|
|
|
|
606
|
|
|
|
|
|
|
The search takes place from the Project directory. Before running ack |
607
|
|
|
|
|
|
|
you'll get to edit the command line with a sensible default chosen from: |
608
|
|
|
|
|
|
|
|
609
|
|
|
|
|
|
|
=over 4 |
610
|
|
|
|
|
|
|
|
611
|
|
|
|
|
|
|
=item * the active region |
612
|
|
|
|
|
|
|
|
613
|
|
|
|
|
|
|
=item * the word at point (with the C<-w> whole word option) |
614
|
|
|
|
|
|
|
|
615
|
|
|
|
|
|
|
=back |
616
|
|
|
|
|
|
|
|
617
|
|
|
|
|
|
|
When editing the ack command you can use the following keys to set options |
618
|
|
|
|
|
|
|
|
619
|
|
|
|
|
|
|
|---------+--------+---------------+------------------------------------------| |
620
|
|
|
|
|
|
|
| "C-o w" | toggle | -w | Whole word | |
621
|
|
|
|
|
|
|
| "C-o q" | toggle | -Q | Quote metacharacters, pattern is literal | |
622
|
|
|
|
|
|
|
| "C-o i" | toggle | -i | Ignore case | |
623
|
|
|
|
|
|
|
| "C-o p" | use | --perl | | |
624
|
|
|
|
|
|
|
| "C-o a" | use | --all | Ack version < 2.0 | |
625
|
|
|
|
|
|
|
| "C-o k" | use | --known-types | | |
626
|
|
|
|
|
|
|
| "C-o s" | use | --sql | | |
627
|
|
|
|
|
|
|
|---------+--------+---------------+------------------------------------------| |
628
|
|
|
|
|
|
|
|
629
|
|
|
|
|
|
|
To search for all files, toggle the current file type off (typically |
630
|
|
|
|
|
|
|
--perl, so type C<C-o p> to toggle it off). |
631
|
|
|
|
|
|
|
|
632
|
|
|
|
|
|
|
For details, refer to the L<ack> documentation (the program was |
633
|
|
|
|
|
|
|
installed as a dependency of PerlySense). |
634
|
|
|
|
|
|
|
|
635
|
|
|
|
|
|
|
Remember that earlier searches are available in the command history, |
636
|
|
|
|
|
|
|
just like with grep. |
637
|
|
|
|
|
|
|
|
638
|
|
|
|
|
|
|
Tip: You can jump from a source file to the next hit with C<C-c C-c> |
639
|
|
|
|
|
|
|
(type C<C-h m> in the B<*grep*> buffer to see the mode documentation). |
640
|
|
|
|
|
|
|
|
641
|
|
|
|
|
|
|
Tip: if you need to find something else while browsing the B<*grep*> |
642
|
|
|
|
|
|
|
buffer, you can easily rename the current B<*grep*> buffer to |
643
|
|
|
|
|
|
|
something else using C<M-x rename-buffer>. |
644
|
|
|
|
|
|
|
|
645
|
|
|
|
|
|
|
|
646
|
|
|
|
|
|
|
=head3 Find sub declarations |
647
|
|
|
|
|
|
|
|
648
|
|
|
|
|
|
|
|
649
|
|
|
|
|
|
|
=for html <p>[ <a href="http://search.cpan.org/src/JOHANL/Devel-PerlySense-0.0183/doc/find_sub_declaration.html">Screenshot</a> ]<p> |
650
|
|
|
|
|
|
|
|
651
|
|
|
|
|
|
|
|
652
|
|
|
|
|
|
|
C<C-o f s> -- Ack the Project for I<sub declarations> of the method, |
653
|
|
|
|
|
|
|
or word at point. |
654
|
|
|
|
|
|
|
|
655
|
|
|
|
|
|
|
I.e. look for lines with C<sub NAME>. |
656
|
|
|
|
|
|
|
|
657
|
|
|
|
|
|
|
The point can be either on the method (C<$self-E<gt>st|ore>), or on |
658
|
|
|
|
|
|
|
the object (C<$us|er_agent-E<gt>get()>). |
659
|
|
|
|
|
|
|
|
660
|
|
|
|
|
|
|
|
661
|
|
|
|
|
|
|
=head3 Find method calls |
662
|
|
|
|
|
|
|
|
663
|
|
|
|
|
|
|
|
664
|
|
|
|
|
|
|
=for html <p>[ <a href="http://search.cpan.org/src/JOHANL/Devel-PerlySense-0.0183/doc/find_method_calls.html">Screenshot</a> ]<p> |
665
|
|
|
|
|
|
|
|
666
|
|
|
|
|
|
|
|
667
|
|
|
|
|
|
|
C<C-o f c> -- Ack the Project for I<method calls> to the method, or |
668
|
|
|
|
|
|
|
word at point. |
669
|
|
|
|
|
|
|
|
670
|
|
|
|
|
|
|
I.e. look for lines with C<-E<gt>NAME>. |
671
|
|
|
|
|
|
|
|
672
|
|
|
|
|
|
|
|
673
|
|
|
|
|
|
|
=head3 Go to Find-buffer |
674
|
|
|
|
|
|
|
|
675
|
|
|
|
|
|
|
Invoke C<C-o g f> to go to the B<*grep*> buffer. |
676
|
|
|
|
|
|
|
|
677
|
|
|
|
|
|
|
|
678
|
|
|
|
|
|
|
|
679
|
|
|
|
|
|
|
=head2 Class Overview |
680
|
|
|
|
|
|
|
|
681
|
|
|
|
|
|
|
Pressing C<C-o C-o> will bring up the Class Overview of the Class name |
682
|
|
|
|
|
|
|
at point (not yet implemented), or otherwise the current Class (the |
683
|
|
|
|
|
|
|
active Package). |
684
|
|
|
|
|
|
|
|
685
|
|
|
|
|
|
|
Example class CatalystX::FeedMe::Controller::Feed |
686
|
|
|
|
|
|
|
|
687
|
|
|
|
|
|
|
* Inheritance * |
688
|
|
|
|
|
|
|
[ Class::Accessor ] |
689
|
|
|
|
|
|
|
+> [ Class::Accessor::Fast ] <-----+ |
690
|
|
|
|
|
|
|
| [ Catalyst::AttrContainer ] ------+---------------------------+ |
691
|
|
|
|
|
|
|
| | | v |
692
|
|
|
|
|
|
|
+- [ Catalyst::Base ] --> [ Catalyst::Component ] --> [ Class::Data::Inheritable ] |
693
|
|
|
|
|
|
|
[ Catalyst::Controller ] |
694
|
|
|
|
|
|
|
[<CatalystX::FeedMe::Controller::Feed>] |
695
|
|
|
|
|
|
|
|
696
|
|
|
|
|
|
|
* Uses * |
697
|
|
|
|
|
|
|
[ Data::Dumper ] [ XML::Atom::Syndication::Content ] [ XML::Atom::Syndication::Feed ] |
698
|
|
|
|
|
|
|
[ Template::Filters ] [ XML::Atom::Syndication::Entry ] [ XML::Atom::Syndication::Link ] |
699
|
|
|
|
|
|
|
|
700
|
|
|
|
|
|
|
* NeighbourHood * |
701
|
|
|
|
|
|
|
[ CatalystX::FeedMe::DBIC ] [<CatalystX::FeedMe::Controller::Feed >] -none- |
702
|
|
|
|
|
|
|
[ CatalystX::FeedMe::Controller::FeedItem ] |
703
|
|
|
|
|
|
|
[ CatalystX::FeedMe::Controller::Homepage ] |
704
|
|
|
|
|
|
|
[ CatalystX::FeedMe::Controller::Root ] |
705
|
|
|
|
|
|
|
|
706
|
|
|
|
|
|
|
* Bookmarks * |
707
|
|
|
|
|
|
|
- Todo |
708
|
|
|
|
|
|
|
Feed.pm:83: remove duplication |
709
|
|
|
|
|
|
|
|
710
|
|
|
|
|
|
|
* API * |
711
|
|
|
|
|
|
|
\>mutator_name_for |
712
|
|
|
|
|
|
|
->new |
713
|
|
|
|
|
|
|
->path_prefix |
714
|
|
|
|
|
|
|
... |
715
|
|
|
|
|
|
|
|
716
|
|
|
|
|
|
|
|
717
|
|
|
|
|
|
|
=head3 Overview sections |
718
|
|
|
|
|
|
|
|
719
|
|
|
|
|
|
|
In addition to the full Overview, each section may be displayed |
720
|
|
|
|
|
|
|
individually: |
721
|
|
|
|
|
|
|
|
722
|
|
|
|
|
|
|
=over 4 |
723
|
|
|
|
|
|
|
|
724
|
|
|
|
|
|
|
=item * C-o o i -- Inheritance |
725
|
|
|
|
|
|
|
|
726
|
|
|
|
|
|
|
=item * C-o o a -- API |
727
|
|
|
|
|
|
|
|
728
|
|
|
|
|
|
|
=item * C-o o b -- Bookmarks |
729
|
|
|
|
|
|
|
|
730
|
|
|
|
|
|
|
=item * C-o o u -- Uses |
731
|
|
|
|
|
|
|
|
732
|
|
|
|
|
|
|
=item * C-o o h -- NeighbourHood |
733
|
|
|
|
|
|
|
|
734
|
|
|
|
|
|
|
=back |
735
|
|
|
|
|
|
|
|
736
|
|
|
|
|
|
|
|
737
|
|
|
|
|
|
|
The B<Inheritance> section shows all Base classes of the |
738
|
|
|
|
|
|
|
Class. Inheriting from something like Catalyst is hopefully the |
739
|
|
|
|
|
|
|
hairiest you'll see. Classes inherit from their parents upwards in the |
740
|
|
|
|
|
|
|
diagram unless there is an arrow pointing elsewhere. |
741
|
|
|
|
|
|
|
|
742
|
|
|
|
|
|
|
The B<Uses> section shows all used modules in the Class. |
743
|
|
|
|
|
|
|
|
744
|
|
|
|
|
|
|
The B<NeighbourHood> section shows three columns (1: parent dir, 2: |
745
|
|
|
|
|
|
|
current dir, 3: subdir for the current class) with Classes located |
746
|
|
|
|
|
|
|
nearby (this can be bizarrely huge (and take a long time) if you |
747
|
|
|
|
|
|
|
browse your site_lib or similar). |
748
|
|
|
|
|
|
|
|
749
|
|
|
|
|
|
|
(This was disabled for having a bad time/useful ratio. Use C-o o h to |
750
|
|
|
|
|
|
|
bring up only the NeighbourHood). |
751
|
|
|
|
|
|
|
|
752
|
|
|
|
|
|
|
The B<Bookmarks> section shows matches for bookmark definitions you |
753
|
|
|
|
|
|
|
have defined in the Project config (see below). |
754
|
|
|
|
|
|
|
|
755
|
|
|
|
|
|
|
the B<API> section shows things that look like methods and properties |
756
|
|
|
|
|
|
|
of the class (sub declarations, $self method calls, |
757
|
|
|
|
|
|
|
$self-E<gt>{hash_ref_keys}): |
758
|
|
|
|
|
|
|
|
759
|
|
|
|
|
|
|
->method_in_this_class |
760
|
|
|
|
|
|
|
\>method_in_base_class (note the arrow coming from above) |
761
|
|
|
|
|
|
|
|
762
|
|
|
|
|
|
|
Private methods (named with a leading _) are displayed as regular |
763
|
|
|
|
|
|
|
methods. Same goes for private methods in base classes, except when |
764
|
|
|
|
|
|
|
the base class is outside of your Project (like for CPAN modules). |
765
|
|
|
|
|
|
|
|
766
|
|
|
|
|
|
|
Why is this? |
767
|
|
|
|
|
|
|
|
768
|
|
|
|
|
|
|
If it's your code base you're interested in everything, but if you |
769
|
|
|
|
|
|
|
inherit from a CPAN module, you don't care (you even shouldn't care) |
770
|
|
|
|
|
|
|
about the implementation of that module. |
771
|
|
|
|
|
|
|
|
772
|
|
|
|
|
|
|
Note that you can still see the private methods of those modules by |
773
|
|
|
|
|
|
|
doing a Class Overview on them, or any of the modules outside your |
774
|
|
|
|
|
|
|
current Project (thereby changing the current Project to the directory |
775
|
|
|
|
|
|
|
where those modules are installed). |
776
|
|
|
|
|
|
|
|
777
|
|
|
|
|
|
|
|
778
|
|
|
|
|
|
|
=head3 Key bindings |
779
|
|
|
|
|
|
|
|
780
|
|
|
|
|
|
|
When in the Class Overview buffer: |
781
|
|
|
|
|
|
|
|
782
|
|
|
|
|
|
|
g -- Go to the file of the thing at point (Module/Method/Bookmark) |
783
|
|
|
|
|
|
|
|
784
|
|
|
|
|
|
|
d -- Documentation for the thing at point (Module/Method) |
785
|
|
|
|
|
|
|
|
786
|
|
|
|
|
|
|
c -- Class Overview for the thing at point. RET does the same. |
787
|
|
|
|
|
|
|
|
788
|
|
|
|
|
|
|
I -- Move point to the Inheritance heading in the buffer. |
789
|
|
|
|
|
|
|
|
790
|
|
|
|
|
|
|
U -- Move point to the Uses heading in the buffer. |
791
|
|
|
|
|
|
|
|
792
|
|
|
|
|
|
|
H -- Move point to the NeighbourHood heading (mnemonic: 'Hood). |
793
|
|
|
|
|
|
|
|
794
|
|
|
|
|
|
|
B -- Move point to the Bookmarks heading. |
795
|
|
|
|
|
|
|
|
796
|
|
|
|
|
|
|
A -- Move point to the API heading. |
797
|
|
|
|
|
|
|
|
798
|
|
|
|
|
|
|
N -- Move point to the '-E<gt>new' method in the buffer (if any). |
799
|
|
|
|
|
|
|
|
800
|
|
|
|
|
|
|
q -- Quit the Class Overview buffer. |
801
|
|
|
|
|
|
|
|
802
|
|
|
|
|
|
|
|
803
|
|
|
|
|
|
|
|
804
|
|
|
|
|
|
|
=head2 Testing |
805
|
|
|
|
|
|
|
|
806
|
|
|
|
|
|
|
|
807
|
|
|
|
|
|
|
=for html <p>[ <a href="http://search.cpan.org/src/JOHANL/Devel-PerlySense-0.0183/doc/testing.html">Screenshot</a> ]<p> |
808
|
|
|
|
|
|
|
|
809
|
|
|
|
|
|
|
|
810
|
|
|
|
|
|
|
=head3 Run File |
811
|
|
|
|
|
|
|
|
812
|
|
|
|
|
|
|
C<C-o C-r> -- Run the file of the current buffer using the Compilation |
813
|
|
|
|
|
|
|
mode. |
814
|
|
|
|
|
|
|
|
815
|
|
|
|
|
|
|
Files are run according to the source type, which is determined by the |
816
|
|
|
|
|
|
|
file name (see the config file). The default for .t files is to run |
817
|
|
|
|
|
|
|
"prove -v", for .pm files "perl -c", etc. This can be configured per |
818
|
|
|
|
|
|
|
Project (see below). |
819
|
|
|
|
|
|
|
|
820
|
|
|
|
|
|
|
Files can also be run using an Alternate Command using C<C-u C-o C-r> |
821
|
|
|
|
|
|
|
if you have specified one in the config file. This might be useful if |
822
|
|
|
|
|
|
|
you want to re-generate or restart something before running the file, |
823
|
|
|
|
|
|
|
but only sometimes. Or, maybe you want to run some tests without the |
824
|
|
|
|
|
|
|
-v flag or something. |
825
|
|
|
|
|
|
|
|
826
|
|
|
|
|
|
|
The file is run from the Project root directory or from the file |
827
|
|
|
|
|
|
|
directory depending on the file type, and the @INC is set |
828
|
|
|
|
|
|
|
appropriately. You can also specify additional @INC directories in the |
829
|
|
|
|
|
|
|
Project config. |
830
|
|
|
|
|
|
|
|
831
|
|
|
|
|
|
|
Note that you can configure whatever type of run profile you like, |
832
|
|
|
|
|
|
|
not just Perl source files. |
833
|
|
|
|
|
|
|
|
834
|
|
|
|
|
|
|
As a taste of what's possible, imagine that you have a test framework |
835
|
|
|
|
|
|
|
with .yml acceptance test data files and a corresponding yml-runner.pl |
836
|
|
|
|
|
|
|
script. You can set up the config so you can type C<C-o C-r> while |
837
|
|
|
|
|
|
|
editing the .yaml file to run that test. And if you need to regenerate |
838
|
|
|
|
|
|
|
some fixtures or something before running the yml test, you can |
839
|
|
|
|
|
|
|
configure the Alternate Command to do that (run with C<C-u C-o |
840
|
|
|
|
|
|
|
C-r>). Refer to the L<Devel::PerlySense::Cookbook> for details. |
841
|
|
|
|
|
|
|
|
842
|
|
|
|
|
|
|
If any warnings, errors or test failures are encountered, they are |
843
|
|
|
|
|
|
|
highlighted in the B<*compilation*> buffer. Press RET on a highlighted |
844
|
|
|
|
|
|
|
line to go to the source. Jump between errors with Tab. |
845
|
|
|
|
|
|
|
|
846
|
|
|
|
|
|
|
Use C-c C-c to move from one error to the next while editing. |
847
|
|
|
|
|
|
|
|
848
|
|
|
|
|
|
|
If you wish to start many runs at the same time, rename the |
849
|
|
|
|
|
|
|
compilation buffer with C<M-x rename-buffer>. |
850
|
|
|
|
|
|
|
|
851
|
|
|
|
|
|
|
|
852
|
|
|
|
|
|
|
=head3 Re-run File |
853
|
|
|
|
|
|
|
|
854
|
|
|
|
|
|
|
Invoke C<C-o C-r> from within the B<*compilation*> buffer to re-run |
855
|
|
|
|
|
|
|
(C<M-x recompile>) the file. Useful when you have skipped around the |
856
|
|
|
|
|
|
|
source fixing errors and the .t file isn't visible. |
857
|
|
|
|
|
|
|
|
858
|
|
|
|
|
|
|
C<C-o r r> -- If not even the B<*compilation*> buffer is visible, |
859
|
|
|
|
|
|
|
issue Re-Run File from anywhere to bring it up and re-run. |
860
|
|
|
|
|
|
|
|
861
|
|
|
|
|
|
|
Note: this will re-run whatever is displayed in the B<*compilation*> |
862
|
|
|
|
|
|
|
buffer. |
863
|
|
|
|
|
|
|
|
864
|
|
|
|
|
|
|
|
865
|
|
|
|
|
|
|
=head3 Run File under Devel::CoverX::Covered |
866
|
|
|
|
|
|
|
|
867
|
|
|
|
|
|
|
C<C-o r c> -- This is the same as Run File, but collect test coverage |
868
|
|
|
|
|
|
|
information using Devel::CoverX::Covered. |
869
|
|
|
|
|
|
|
|
870
|
|
|
|
|
|
|
Note: Currently this only works with Unix like shells. |
871
|
|
|
|
|
|
|
|
872
|
|
|
|
|
|
|
|
873
|
|
|
|
|
|
|
|
874
|
|
|
|
|
|
|
=head3 Go to Run-buffer |
875
|
|
|
|
|
|
|
|
876
|
|
|
|
|
|
|
Invoke C<C-o g r> to go to the B<*compilation*> buffer. |
877
|
|
|
|
|
|
|
|
878
|
|
|
|
|
|
|
|
879
|
|
|
|
|
|
|
=head3 Edit Test Count |
880
|
|
|
|
|
|
|
|
881
|
|
|
|
|
|
|
C<C-o e t c> -- Increase the test count number in the line resembling |
882
|
|
|
|
|
|
|
|
883
|
|
|
|
|
|
|
use Test::More tests => 43; |
884
|
|
|
|
|
|
|
|
885
|
|
|
|
|
|
|
without moving point. The current and new test count is reported in |
886
|
|
|
|
|
|
|
the echo area. |
887
|
|
|
|
|
|
|
|
888
|
|
|
|
|
|
|
Increase with the numeric argument (e.g. C<C-u -2 C-o e t c>), or |
889
|
|
|
|
|
|
|
default 1. |
890
|
|
|
|
|
|
|
|
891
|
|
|
|
|
|
|
|
892
|
|
|
|
|
|
|
=head3 Assist With Test Count |
893
|
|
|
|
|
|
|
|
894
|
|
|
|
|
|
|
C<C-o a t> -- If the test count in a .t file is out of sync with |
895
|
|
|
|
|
|
|
what's correctly reported when running the test in the |
896
|
|
|
|
|
|
|
B<*compilation*> buffer (see Run File), use this command to update the |
897
|
|
|
|
|
|
|
.t file. |
898
|
|
|
|
|
|
|
|
899
|
|
|
|
|
|
|
This updates the |
900
|
|
|
|
|
|
|
|
901
|
|
|
|
|
|
|
use Test::More tests => 43; |
902
|
|
|
|
|
|
|
|
903
|
|
|
|
|
|
|
line in the current buffer, so be sure to only run this when the |
904
|
|
|
|
|
|
|
B<*compilation*> buffer contains the run result of this buffer. |
905
|
|
|
|
|
|
|
|
906
|
|
|
|
|
|
|
|
907
|
|
|
|
|
|
|
=head3 Run Single Test::Class Method |
908
|
|
|
|
|
|
|
|
909
|
|
|
|
|
|
|
If you use L<Test::Class> to write your tests, you may sometimes want |
910
|
|
|
|
|
|
|
to run L<just a single test method|Test::Clas/RUNNING_INDIVIDUAL_TESTS>. |
911
|
|
|
|
|
|
|
|
912
|
|
|
|
|
|
|
Hit C<C-o r m> to mark the current sub as the current test method, and |
913
|
|
|
|
|
|
|
C<C-o r m> again to unmark it. This will set the $TEST_METHOD |
914
|
|
|
|
|
|
|
environment variable during program runs, so when you run this test |
915
|
|
|
|
|
|
|
class, only the marked method will be run. |
916
|
|
|
|
|
|
|
|
917
|
|
|
|
|
|
|
The current test method is indicated with a "Test::Class -->" next to |
918
|
|
|
|
|
|
|
it. |
919
|
|
|
|
|
|
|
|
920
|
|
|
|
|
|
|
|
921
|
|
|
|
|
|
|
=head3 Go to Tests - Other Files |
922
|
|
|
|
|
|
|
|
923
|
|
|
|
|
|
|
|
924
|
|
|
|
|
|
|
=for html <p>[ <a href="http://search.cpan.org/src/JOHANL/Devel-PerlySense-0.0183/doc/goto_tests.html">Screenshot</a> ]<p> |
925
|
|
|
|
|
|
|
|
926
|
|
|
|
|
|
|
|
927
|
|
|
|
|
|
|
C<C-o g t o> -- In a test file, navigate to the source files that are |
928
|
|
|
|
|
|
|
covered by that test file. |
929
|
|
|
|
|
|
|
|
930
|
|
|
|
|
|
|
In a source file, navigate to test files covering the file. If the |
931
|
|
|
|
|
|
|
point is on a line with a sub declaration, the list of test files is |
932
|
|
|
|
|
|
|
limited to those that cover that particular sub. |
933
|
|
|
|
|
|
|
|
934
|
|
|
|
|
|
|
This requires that L<Devel::CoverX::Covered> is installed and a |
935
|
|
|
|
|
|
|
L<Devel::Cover> cover_db in the project root directory. |
936
|
|
|
|
|
|
|
|
937
|
|
|
|
|
|
|
You can build the coverage database either as a (very slow) separate |
938
|
|
|
|
|
|
|
test run, or by running individual files with C<C-o r c>. |
939
|
|
|
|
|
|
|
|
940
|
|
|
|
|
|
|
See L<Devel::CoverX::Covered> for details. |
941
|
|
|
|
|
|
|
|
942
|
|
|
|
|
|
|
|
943
|
|
|
|
|
|
|
=head3 Go to Error line |
944
|
|
|
|
|
|
|
|
945
|
|
|
|
|
|
|
If you run tests in a regular shell (inside Emacs or in a terminal |
946
|
|
|
|
|
|
|
window), this may be handy. |
947
|
|
|
|
|
|
|
|
948
|
|
|
|
|
|
|
C<C-o g e> -- If point is located on an error line from a syntax |
949
|
|
|
|
|
|
|
error, or a stack trace from the debugger or similar, go to that |
950
|
|
|
|
|
|
|
file+line. |
951
|
|
|
|
|
|
|
|
952
|
|
|
|
|
|
|
If no file name can be found, prompt for a piece of text that contains |
953
|
|
|
|
|
|
|
the file+line spec. The kill ring or clipboard text is used as default |
954
|
|
|
|
|
|
|
if available (so it's easy to just copy the error line from the |
955
|
|
|
|
|
|
|
terminal, run this command and hit return to accept the default text). |
956
|
|
|
|
|
|
|
|
957
|
|
|
|
|
|
|
|
958
|
|
|
|
|
|
|
|
959
|
|
|
|
|
|
|
=head2 Debugging Code |
960
|
|
|
|
|
|
|
|
961
|
|
|
|
|
|
|
=head3 Run File in Debugger |
962
|
|
|
|
|
|
|
|
963
|
|
|
|
|
|
|
C<C-o r d> -- Run the file of the current buffer using the Emacs |
964
|
|
|
|
|
|
|
integrated Perl debugger. This the same as the excellent C<M-x |
965
|
|
|
|
|
|
|
perldb>, except a few annoyances are fixed, like the include |
966
|
|
|
|
|
|
|
directories, the working directory, the default command line etc. |
967
|
|
|
|
|
|
|
|
968
|
|
|
|
|
|
|
Note that if you have spaces in your file names, this might not work |
969
|
|
|
|
|
|
|
(it's a perldb thing). |
970
|
|
|
|
|
|
|
|
971
|
|
|
|
|
|
|
The debugger is started according to the file source type, which is |
972
|
|
|
|
|
|
|
determined by the file name (see the config file). |
973
|
|
|
|
|
|
|
|
974
|
|
|
|
|
|
|
You can also use C<C-u C-o r d> to Debug with an Alternate Command, |
975
|
|
|
|
|
|
|
just like with Run File. |
976
|
|
|
|
|
|
|
|
977
|
|
|
|
|
|
|
This can all be configured similar to how files are run (see above). |
978
|
|
|
|
|
|
|
|
979
|
|
|
|
|
|
|
Most files are run from the Project root directory by default. |
980
|
|
|
|
|
|
|
|
981
|
|
|
|
|
|
|
|
982
|
|
|
|
|
|
|
=head3 Commands and key bindings |
983
|
|
|
|
|
|
|
|
984
|
|
|
|
|
|
|
Commonly used commands: |
985
|
|
|
|
|
|
|
|
986
|
|
|
|
|
|
|
|-------------+------+-------------------------| |
987
|
|
|
|
|
|
|
| Source | DB | Command | |
988
|
|
|
|
|
|
|
|-------------+------+-------------------------| |
989
|
|
|
|
|
|
|
| C-x C-a C-n | n | Next line (step over) | |
990
|
|
|
|
|
|
|
| C-x C-a C-s | s | Step into | |
991
|
|
|
|
|
|
|
| | RET | Repeat last n or s | |
992
|
|
|
|
|
|
|
| C-x C-a C-r | r | Return from sub | |
993
|
|
|
|
|
|
|
| C-x C-a C-u | | Run to (Until) point | |
994
|
|
|
|
|
|
|
| | x $v | Dump variable $v | |
995
|
|
|
|
|
|
|
| | T | Stack trace | |
996
|
|
|
|
|
|
|
| | y | Dump lexicals (mY vars) | |
997
|
|
|
|
|
|
|
| | R | Restart | |
998
|
|
|
|
|
|
|
| | m $o | List methods of $o | |
999
|
|
|
|
|
|
|
|-------------+------+-------------------------| |
1000
|
|
|
|
|
|
|
|
1001
|
|
|
|
|
|
|
|
1002
|
|
|
|
|
|
|
=head3 Dumping objects |
1003
|
|
|
|
|
|
|
|
1004
|
|
|
|
|
|
|
x $VAR |
1005
|
|
|
|
|
|
|
|
1006
|
|
|
|
|
|
|
to print/dump objects. |
1007
|
|
|
|
|
|
|
|
1008
|
|
|
|
|
|
|
See L<http://use.perl.org/~jplindstrom/journal/34427> for how to deal |
1009
|
|
|
|
|
|
|
with large objects (put the C<.perldb> file in $HOME or the project |
1010
|
|
|
|
|
|
|
root dir). |
1011
|
|
|
|
|
|
|
|
1012
|
|
|
|
|
|
|
|
1013
|
|
|
|
|
|
|
=head3 Breakpoints |
1014
|
|
|
|
|
|
|
|
1015
|
|
|
|
|
|
|
Create a programmatic breakpoint like this |
1016
|
|
|
|
|
|
|
|
1017
|
|
|
|
|
|
|
$DB::single = 1; |
1018
|
|
|
|
|
|
|
|
1019
|
|
|
|
|
|
|
|
1020
|
|
|
|
|
|
|
=head3 More Documentation |
1021
|
|
|
|
|
|
|
|
1022
|
|
|
|
|
|
|
Once the debugger is started, refer to the Gud menu for a few useful |
1023
|
|
|
|
|
|
|
commands and key bindings (gud = Grand Unified Debugger). See also: |
1024
|
|
|
|
|
|
|
L<http://www.gnu.org/software/emacs/manual/html_node/emacs/Debuggers.html> |
1025
|
|
|
|
|
|
|
|
1026
|
|
|
|
|
|
|
Since the Perl debugger command line is available, make sure you read |
1027
|
|
|
|
|
|
|
up on that too: L<http://perldoc.perl.org/perldebug.html> (especially |
1028
|
|
|
|
|
|
|
the E<lt>E<lt>, {{, etc. are more useful than they might seem at |
1029
|
|
|
|
|
|
|
first). |
1030
|
|
|
|
|
|
|
|
1031
|
|
|
|
|
|
|
|
1032
|
|
|
|
|
|
|
|
1033
|
|
|
|
|
|
|
=head2 Displaying Code |
1034
|
|
|
|
|
|
|
|
1035
|
|
|
|
|
|
|
=head3 Flymake Introduction |
1036
|
|
|
|
|
|
|
|
1037
|
|
|
|
|
|
|
|
1038
|
|
|
|
|
|
|
=for html <p>[ <a href="http://search.cpan.org/src/JOHANL/Devel-PerlySense-0.0183/doc/flymake.html">Screenshot</a> ]<p> |
1039
|
|
|
|
|
|
|
|
1040
|
|
|
|
|
|
|
|
1041
|
|
|
|
|
|
|
"Flymake performs on-the-fly syntax checks of the files being edited |
1042
|
|
|
|
|
|
|
using the external syntax check tool (usually the compiler). |
1043
|
|
|
|
|
|
|
Highlights erroneous lines and displays associated error messages." |
1044
|
|
|
|
|
|
|
|
1045
|
|
|
|
|
|
|
Flymake is included in Emacs 22 (or available from |
1046
|
|
|
|
|
|
|
http://flymake.sourceforge.net/, put flymake.el somewhere in your |
1047
|
|
|
|
|
|
|
load-path. [[[explain how to fix brokenness?]]] ). |
1048
|
|
|
|
|
|
|
|
1049
|
|
|
|
|
|
|
PerlySense uses flymake to check syntax, Perl Critic, etc. |
1050
|
|
|
|
|
|
|
|
1051
|
|
|
|
|
|
|
Having Perl::Critic enabled will also speed up other operations by |
1052
|
|
|
|
|
|
|
caching information. |
1053
|
|
|
|
|
|
|
|
1054
|
|
|
|
|
|
|
Three inconveniences with vanilla Flymake are fixed: |
1055
|
|
|
|
|
|
|
|
1056
|
|
|
|
|
|
|
|
1057
|
|
|
|
|
|
|
=over 4 |
1058
|
|
|
|
|
|
|
|
1059
|
|
|
|
|
|
|
=item * no proper @INC |
1060
|
|
|
|
|
|
|
|
1061
|
|
|
|
|
|
|
=item * only .pl files |
1062
|
|
|
|
|
|
|
|
1063
|
|
|
|
|
|
|
=item * "perl -c" warns about redefined subs for |
1064
|
|
|
|
|
|
|
recursively used modules (which is perfectly fine Perl) |
1065
|
|
|
|
|
|
|
|
1066
|
|
|
|
|
|
|
=back |
1067
|
|
|
|
|
|
|
|
1068
|
|
|
|
|
|
|
|
1069
|
|
|
|
|
|
|
Syntax errors and warnings both use the error face. |
1070
|
|
|
|
|
|
|
|
1071
|
|
|
|
|
|
|
L<Perl::Critic> violations use the warning face. |
1072
|
|
|
|
|
|
|
|
1073
|
|
|
|
|
|
|
|
1074
|
|
|
|
|
|
|
|
1075
|
|
|
|
|
|
|
=head3 Enabling Flymake |
1076
|
|
|
|
|
|
|
|
1077
|
|
|
|
|
|
|
First off, flymake itself needs to be enabled. Refer to the Emacs |
1078
|
|
|
|
|
|
|
Installation description above. |
1079
|
|
|
|
|
|
|
|
1080
|
|
|
|
|
|
|
This will enable Flymake for all cperl-mode buffers, causing Emacs to |
1081
|
|
|
|
|
|
|
call perly_sense for each check. |
1082
|
|
|
|
|
|
|
|
1083
|
|
|
|
|
|
|
I<PerlySense won't do anything at this point though>. You still need |
1084
|
|
|
|
|
|
|
to configure what should happen during a flymake. |
1085
|
|
|
|
|
|
|
|
1086
|
|
|
|
|
|
|
Create a PerlySense Project directory (see below) and look in the |
1087
|
|
|
|
|
|
|
project.yml file for instructions on how to configure Flymake |
1088
|
|
|
|
|
|
|
activities. |
1089
|
|
|
|
|
|
|
|
1090
|
|
|
|
|
|
|
Set "syntax" and/or "critic" to 1 to enable them. |
1091
|
|
|
|
|
|
|
|
1092
|
|
|
|
|
|
|
B<The primary reason "syntax" is turned off by default is that it's a |
1093
|
|
|
|
|
|
|
potential security hole>; running "perl -c" on a file will not only |
1094
|
|
|
|
|
|
|
check the syntax; BEGIN and CHECK blocks are also executed. Doing that |
1095
|
|
|
|
|
|
|
on random code may be considered... baaad. |
1096
|
|
|
|
|
|
|
|
1097
|
|
|
|
|
|
|
This way you can have Flymake enabled globally and still not run "perl |
1098
|
|
|
|
|
|
|
-c" on everything that happens to be in a buffer. |
1099
|
|
|
|
|
|
|
|
1100
|
|
|
|
|
|
|
|
1101
|
|
|
|
|
|
|
|
1102
|
|
|
|
|
|
|
=head3 Using Flymake |
1103
|
|
|
|
|
|
|
|
1104
|
|
|
|
|
|
|
In the Project config file there are some hints on how to customize |
1105
|
|
|
|
|
|
|
Flymake, when it should run, etc. You can also customize it with C<M-x |
1106
|
|
|
|
|
|
|
customize-group flymake>. |
1107
|
|
|
|
|
|
|
|
1108
|
|
|
|
|
|
|
(Personally I find the nagging while I type very distracting, but I |
1109
|
|
|
|
|
|
|
welcome the immediate feedback whenever I save the file. YMMV.) |
1110
|
|
|
|
|
|
|
|
1111
|
|
|
|
|
|
|
Look in the mode line for hints on whether there are any errors or |
1112
|
|
|
|
|
|
|
warnings. |
1113
|
|
|
|
|
|
|
|
1114
|
|
|
|
|
|
|
C<C-o s n> -- Go to the next Source error/warning. |
1115
|
|
|
|
|
|
|
|
1116
|
|
|
|
|
|
|
Display the error in the minibuffer. If the warning is from a |
1117
|
|
|
|
|
|
|
Perl::Critic module, copy the module name into the kill-ring, so you |
1118
|
|
|
|
|
|
|
easily can yank it into the .perlcritic config file to disable |
1119
|
|
|
|
|
|
|
it. (not implemented) |
1120
|
|
|
|
|
|
|
|
1121
|
|
|
|
|
|
|
C<C-o s p> -- Go to the previous Source error/warning. |
1122
|
|
|
|
|
|
|
|
1123
|
|
|
|
|
|
|
C<C-o s s> -- Display the error/warning text of the current line in a |
1124
|
|
|
|
|
|
|
popup. Or display the error in the minibuffer if the display isn't |
1125
|
|
|
|
|
|
|
graphical, or if the ps/flymake-prefer-errors-in-minibuffer variable |
1126
|
|
|
|
|
|
|
is customized to a true value. |
1127
|
|
|
|
|
|
|
|
1128
|
|
|
|
|
|
|
|
1129
|
|
|
|
|
|
|
|
1130
|
|
|
|
|
|
|
=head3 Code Coverage Visualization Introduction |
1131
|
|
|
|
|
|
|
|
1132
|
|
|
|
|
|
|
|
1133
|
|
|
|
|
|
|
=for html <p>[ <a href="http://search.cpan.org/src/JOHANL/Devel-PerlySense-0.0183/doc/code_coverage.html">Screenshot</a> ]<p> |
1134
|
|
|
|
|
|
|
|
1135
|
|
|
|
|
|
|
|
1136
|
|
|
|
|
|
|
If you have a test suite, you might like this. You should have tests. |
1137
|
|
|
|
|
|
|
|
1138
|
|
|
|
|
|
|
If you run Devel::Cover, you'll be happy. You should know your code |
1139
|
|
|
|
|
|
|
coverage. |
1140
|
|
|
|
|
|
|
|
1141
|
|
|
|
|
|
|
PerlySense can display the code coverage in the source buffer. |
1142
|
|
|
|
|
|
|
|
1143
|
|
|
|
|
|
|
Currently supported is subroutine coverage, i.e. whether a sub is |
1144
|
|
|
|
|
|
|
covered by tests or not. |
1145
|
|
|
|
|
|
|
|
1146
|
|
|
|
|
|
|
Covered subs are displayed with a discreet green underline, uncovered |
1147
|
|
|
|
|
|
|
subs get a red underline. |
1148
|
|
|
|
|
|
|
|
1149
|
|
|
|
|
|
|
|
1150
|
|
|
|
|
|
|
|
1151
|
|
|
|
|
|
|
|
1152
|
|
|
|
|
|
|
=head3 Coverage Visualization Setup |
1153
|
|
|
|
|
|
|
|
1154
|
|
|
|
|
|
|
PerlySense uses L<Devel::CoverX::Covered> to manage the coverage |
1155
|
|
|
|
|
|
|
data. Refer to that documentation for how to run your test suite with |
1156
|
|
|
|
|
|
|
L<Devel::Cover> and generate a "covered" database. |
1157
|
|
|
|
|
|
|
|
1158
|
|
|
|
|
|
|
The "covered" database should reside in your project root dir and |
1159
|
|
|
|
|
|
|
contain files with file names relative to the project root dir (that's |
1160
|
|
|
|
|
|
|
ordinarily the case). |
1161
|
|
|
|
|
|
|
|
1162
|
|
|
|
|
|
|
Note: Running the test suite with Devel::Cover can be very, very |
1163
|
|
|
|
|
|
|
slow. A nightly build is usually a good idea. |
1164
|
|
|
|
|
|
|
|
1165
|
|
|
|
|
|
|
You can also collect / undate coverage information for indivual test |
1166
|
|
|
|
|
|
|
files with C<C-o r c>. This is the easiest way to just try it out. |
1167
|
|
|
|
|
|
|
|
1168
|
|
|
|
|
|
|
You might want to add the following to be ignored by your VCS |
1169
|
|
|
|
|
|
|
(e.g. .gitignore): |
1170
|
|
|
|
|
|
|
|
1171
|
|
|
|
|
|
|
/cover_db/* |
1172
|
|
|
|
|
|
|
/covered/* |
1173
|
|
|
|
|
|
|
|
1174
|
|
|
|
|
|
|
|
1175
|
|
|
|
|
|
|
|
1176
|
|
|
|
|
|
|
=head3 Using Coverage Visualization |
1177
|
|
|
|
|
|
|
|
1178
|
|
|
|
|
|
|
You can toggle Visualization with C<C-o C-v> at any time when editing. |
1179
|
|
|
|
|
|
|
|
1180
|
|
|
|
|
|
|
You can also enable Visualization by default in the install script |
1181
|
|
|
|
|
|
|
(see above), or via C<M-x customize-variable |
1182
|
|
|
|
|
|
|
ps/enable-test-coverage-visualization>. |
1183
|
|
|
|
|
|
|
|
1184
|
|
|
|
|
|
|
Whenever Visualization is enabled, PerlySense will try to fetch |
1185
|
|
|
|
|
|
|
coverage information just after a file is opened and highlight the |
1186
|
|
|
|
|
|
|
word "sub" for each subroutine in the buffer. |
1187
|
|
|
|
|
|
|
|
1188
|
|
|
|
|
|
|
=over 4 |
1189
|
|
|
|
|
|
|
|
1190
|
|
|
|
|
|
|
=item * A green underline means that the sub was entered at least |
1191
|
|
|
|
|
|
|
once. This does not mean all lines in the sub was covered. |
1192
|
|
|
|
|
|
|
|
1193
|
|
|
|
|
|
|
=item * A red underline means the sub wasn't covered at all. Time to write |
1194
|
|
|
|
|
|
|
more tests! |
1195
|
|
|
|
|
|
|
|
1196
|
|
|
|
|
|
|
=item * No underline means that the sub isn't in the coverage |
1197
|
|
|
|
|
|
|
database. Maybe the sub was added after the test run, maybe |
1198
|
|
|
|
|
|
|
Devel::Cover didn't manage to capture any coverage information for the |
1199
|
|
|
|
|
|
|
sub. |
1200
|
|
|
|
|
|
|
|
1201
|
|
|
|
|
|
|
If you really think the sub should be covered, generate a HTML report |
1202
|
|
|
|
|
|
|
with L<Devel::Cover> and investigate further. |
1203
|
|
|
|
|
|
|
|
1204
|
|
|
|
|
|
|
=back |
1205
|
|
|
|
|
|
|
|
1206
|
|
|
|
|
|
|
The point of the visualization is to provide an ambient feeling of |
1207
|
|
|
|
|
|
|
what's covered or not. Too much detail and color all over the place |
1208
|
|
|
|
|
|
|
and the source turns into a christmas tree! But if you browse past a |
1209
|
|
|
|
|
|
|
complex method and see that it isn't tested, that should ring a bell. |
1210
|
|
|
|
|
|
|
|
1211
|
|
|
|
|
|
|
To increase this effect you may want to only highlight subs with bad |
1212
|
|
|
|
|
|
|
coverage (customize the variable |
1213
|
|
|
|
|
|
|
C<ps/only-highlight-bad-sub-coverage>) |
1214
|
|
|
|
|
|
|
|
1215
|
|
|
|
|
|
|
Note that you can hit C<C-o g t o> -- "Go To Tests - Other Files" to |
1216
|
|
|
|
|
|
|
see what test files are covering I<this file>. If you run the command |
1217
|
|
|
|
|
|
|
with the cursor on a "sub" line, you'll get only the tests that cover |
1218
|
|
|
|
|
|
|
I<that particular subroutine>. |
1219
|
|
|
|
|
|
|
|
1220
|
|
|
|
|
|
|
|
1221
|
|
|
|
|
|
|
|
1222
|
|
|
|
|
|
|
=head2 Editing Code |
1223
|
|
|
|
|
|
|
|
1224
|
|
|
|
|
|
|
Editing code includes both smaller editing tasks and refactorings to |
1225
|
|
|
|
|
|
|
restucture the code. |
1226
|
|
|
|
|
|
|
|
1227
|
|
|
|
|
|
|
|
1228
|
|
|
|
|
|
|
=head3 Edit - Copy Package Name |
1229
|
|
|
|
|
|
|
|
1230
|
|
|
|
|
|
|
C<C-o e c p> -- Copy the current package statement name to the |
1231
|
|
|
|
|
|
|
clipboard (kill-ring) and display it in the echo area. If there is no |
1232
|
|
|
|
|
|
|
package statement, try to get the package name from the file name. |
1233
|
|
|
|
|
|
|
|
1234
|
|
|
|
|
|
|
|
1235
|
|
|
|
|
|
|
|
1236
|
|
|
|
|
|
|
=head3 Edit - Copy Package Name From File Name |
1237
|
|
|
|
|
|
|
|
1238
|
|
|
|
|
|
|
C<C-o e c P> -- Assuming the file is a Perl module in a lib directory, |
1239
|
|
|
|
|
|
|
copy the corrsponding package name to the clipboard (kill-ring) and |
1240
|
|
|
|
|
|
|
display it in the echo area. |
1241
|
|
|
|
|
|
|
|
1242
|
|
|
|
|
|
|
Useful when you've just created an empty new Perl module .pm file. |
1243
|
|
|
|
|
|
|
|
1244
|
|
|
|
|
|
|
|
1245
|
|
|
|
|
|
|
|
1246
|
|
|
|
|
|
|
=head3 Edit - Copy Sub Name |
1247
|
|
|
|
|
|
|
|
1248
|
|
|
|
|
|
|
C<C-o e c s> -- Copy the current sub name to the clipboard (kill-ring) |
1249
|
|
|
|
|
|
|
and display it in the echo area. |
1250
|
|
|
|
|
|
|
|
1251
|
|
|
|
|
|
|
|
1252
|
|
|
|
|
|
|
|
1253
|
|
|
|
|
|
|
=head3 Edit - Copy File Name |
1254
|
|
|
|
|
|
|
|
1255
|
|
|
|
|
|
|
C<C-o e c f> -- Copy the current file name to the clipboard |
1256
|
|
|
|
|
|
|
(kill-ring) and display it in the echo area. |
1257
|
|
|
|
|
|
|
|
1258
|
|
|
|
|
|
|
|
1259
|
|
|
|
|
|
|
|
1260
|
|
|
|
|
|
|
=head3 Edit - Add 'use Module' Statement |
1261
|
|
|
|
|
|
|
|
1262
|
|
|
|
|
|
|
C<C-o e a u> -- Set mark and add a 'use My::Module;' statement to the |
1263
|
|
|
|
|
|
|
end of the 'use Module' section at the top of the file. |
1264
|
|
|
|
|
|
|
|
1265
|
|
|
|
|
|
|
The default module is the selected text, or the module at point (point |
1266
|
|
|
|
|
|
|
may be on a method call of the module). |
1267
|
|
|
|
|
|
|
|
1268
|
|
|
|
|
|
|
This is typically useful when you realize you're using a module |
1269
|
|
|
|
|
|
|
already, but without a use-statement. But you don't want to leave |
1270
|
|
|
|
|
|
|
where you are just to fiddle with adding it. |
1271
|
|
|
|
|
|
|
|
1272
|
|
|
|
|
|
|
So hit C<C-o e a u> to add it, see that it got added at a good place |
1273
|
|
|
|
|
|
|
and hit C-u C-SPC to return to where you were, and continue doing what |
1274
|
|
|
|
|
|
|
you where doing. |
1275
|
|
|
|
|
|
|
|
1276
|
|
|
|
|
|
|
|
1277
|
|
|
|
|
|
|
|
1278
|
|
|
|
|
|
|
=head3 Edit - Move 'use Module' Statement |
1279
|
|
|
|
|
|
|
|
1280
|
|
|
|
|
|
|
C<C-o e m u> -- If point is on a line with a single 'use Module' |
1281
|
|
|
|
|
|
|
statement, set mark and move that statement to the end of the 'use |
1282
|
|
|
|
|
|
|
Module' section at the top of the file. |
1283
|
|
|
|
|
|
|
|
1284
|
|
|
|
|
|
|
This is typically useful for when you encounter a stray 'use Module' |
1285
|
|
|
|
|
|
|
in the middle of the file. |
1286
|
|
|
|
|
|
|
|
1287
|
|
|
|
|
|
|
So type the 'use Module' statement, hit C<C-o e m u> to move it, see |
1288
|
|
|
|
|
|
|
that it got moved to a good place and hit C-u C-SPC to return to where |
1289
|
|
|
|
|
|
|
you were, and continue doing what you where doing. |
1290
|
|
|
|
|
|
|
|
1291
|
|
|
|
|
|
|
|
1292
|
|
|
|
|
|
|
=head3 Edit/Refactor - Extract Variable |
1293
|
|
|
|
|
|
|
|
1294
|
|
|
|
|
|
|
C<C-o e e v> -- Do the refactoring Extract Variable of the active region. |
1295
|
|
|
|
|
|
|
|
1296
|
|
|
|
|
|
|
For example, in this piece of code: |
1297
|
|
|
|
|
|
|
|
1298
|
|
|
|
|
|
|
my $syntax = $self->perlysense->config->{external}->{editor}->{emacs}->{flymake}->{syntax}; |
1299
|
|
|
|
|
|
|
my $critic = $self->perlysense->config->{external}->{editor}->{emacs}->{flymake}->{critic}; |
1300
|
|
|
|
|
|
|
|
1301
|
|
|
|
|
|
|
Select a piece of code (on either of the lines) that is duplicated a |
1302
|
|
|
|
|
|
|
lot and hit C<C-o e e v>. In this case this seems to be the common |
1303
|
|
|
|
|
|
|
part: |
1304
|
|
|
|
|
|
|
|
1305
|
|
|
|
|
|
|
$self->perlysense->config->{external}->{editor}->{emacs}->{flymake} |
1306
|
|
|
|
|
|
|
|
1307
|
|
|
|
|
|
|
You will be asked for a variable name to put this in. The default is |
1308
|
|
|
|
|
|
|
the last word in the selected code ($flymake). |
1309
|
|
|
|
|
|
|
|
1310
|
|
|
|
|
|
|
All occurrences of the selection will now be replaced with $flymake, |
1311
|
|
|
|
|
|
|
and the new variable $flymake will be declared just before the |
1312
|
|
|
|
|
|
|
earliest usage. |
1313
|
|
|
|
|
|
|
|
1314
|
|
|
|
|
|
|
my $flymake = $self->perlysense->config->{external}->{editor}->{emacs}->{flymake}; |
1315
|
|
|
|
|
|
|
my $syntax = $flymake->{syntax}; |
1316
|
|
|
|
|
|
|
my $critic = $flymake->{critic}; |
1317
|
|
|
|
|
|
|
|
1318
|
|
|
|
|
|
|
Before the edit, the C<mark> was pushed at the location where you |
1319
|
|
|
|
|
|
|
started, so you can hit C<C-u C-SPC> to jump back. |
1320
|
|
|
|
|
|
|
|
1321
|
|
|
|
|
|
|
After the edit, the point is left at the new variable declaration so |
1322
|
|
|
|
|
|
|
you can ensure that it is in a reasonable location. It's not unusual |
1323
|
|
|
|
|
|
|
to need to move it to an outer scope in order for all the usages to be |
1324
|
|
|
|
|
|
|
covered by the declaration. |
1325
|
|
|
|
|
|
|
|
1326
|
|
|
|
|
|
|
Now you need to ensure this edit makes sense. Both replacements and |
1327
|
|
|
|
|
|
|
the declaration are highlighted, so it's easy to see what was |
1328
|
|
|
|
|
|
|
changed. |
1329
|
|
|
|
|
|
|
|
1330
|
|
|
|
|
|
|
Once you've eye-balled the edits, hit C<C-o e h> to remove the |
1331
|
|
|
|
|
|
|
Highlights. |
1332
|
|
|
|
|
|
|
|
1333
|
|
|
|
|
|
|
Note that the replacement is syntax unaware, so you'll have to ensure |
1334
|
|
|
|
|
|
|
it's syntactically correct yourself (althugh most of the time it works |
1335
|
|
|
|
|
|
|
just fine). |
1336
|
|
|
|
|
|
|
|
1337
|
|
|
|
|
|
|
In this particular example, had there been no arrows between the hash |
1338
|
|
|
|
|
|
|
keys, the final code would have looked like this: |
1339
|
|
|
|
|
|
|
|
1340
|
|
|
|
|
|
|
my $flymake = $self->perlysense->config->{external}{editor}{emacs}{flymake}; |
1341
|
|
|
|
|
|
|
my $syntax = $flymake{syntax}; |
1342
|
|
|
|
|
|
|
my $critic = $flymake{critic}; |
1343
|
|
|
|
|
|
|
|
1344
|
|
|
|
|
|
|
and that clearly isn't equivalent Perl code, the flymake hashref |
1345
|
|
|
|
|
|
|
having been converted to a hash. This is probably the most common |
1346
|
|
|
|
|
|
|
failure mode though, and shouldn't happen that often. Now you know. |
1347
|
|
|
|
|
|
|
|
1348
|
|
|
|
|
|
|
By default, only the current subroutine is changed. Invoke with the |
1349
|
|
|
|
|
|
|
prefix arg to change the entire buffer: C<C-u C-o e e v>. |
1350
|
|
|
|
|
|
|
|
1351
|
|
|
|
|
|
|
Cool usages for Extract Variable: |
1352
|
|
|
|
|
|
|
|
1353
|
|
|
|
|
|
|
=over 4 |
1354
|
|
|
|
|
|
|
|
1355
|
|
|
|
|
|
|
=item * Remove duplicated code (duh), beause duplication is just shoddy. |
1356
|
|
|
|
|
|
|
|
1357
|
|
|
|
|
|
|
=item * Rename variable - Extract Varable, then just delete the declaration. |
1358
|
|
|
|
|
|
|
|
1359
|
|
|
|
|
|
|
=item * C<print "So, you want to make a $object-E<gt>method_call inside a string\n";> |
1360
|
|
|
|
|
|
|
|
1361
|
|
|
|
|
|
|
But that doesn't work obviously. So you mark C<$object-E<gt>method_call> |
1362
|
|
|
|
|
|
|
and extract it, and end up with this: |
1363
|
|
|
|
|
|
|
|
1364
|
|
|
|
|
|
|
my $method_call = $object->method_call; |
1365
|
|
|
|
|
|
|
print "So, you want to make a $method_call inside a string\n"; |
1366
|
|
|
|
|
|
|
|
1367
|
|
|
|
|
|
|
Nice! |
1368
|
|
|
|
|
|
|
|
1369
|
|
|
|
|
|
|
=back |
1370
|
|
|
|
|
|
|
|
1371
|
|
|
|
|
|
|
|
1372
|
|
|
|
|
|
|
|
1373
|
|
|
|
|
|
|
=head3 Edit -- Find Callers |
1374
|
|
|
|
|
|
|
|
1375
|
|
|
|
|
|
|
C<C-o e f c> -- Find callers of a method in the current project, and |
1376
|
|
|
|
|
|
|
insert the package->sub as a call tree in a comment. |
1377
|
|
|
|
|
|
|
|
1378
|
|
|
|
|
|
|
This is for understanding where in the code base method calls |
1379
|
|
|
|
|
|
|
originate. |
1380
|
|
|
|
|
|
|
|
1381
|
|
|
|
|
|
|
If point is in a comment on something that looks like a method call, |
1382
|
|
|
|
|
|
|
look for that method. This can be in source code, or in a comment with |
1383
|
|
|
|
|
|
|
callers. Insert the comment with callers above the current line. |
1384
|
|
|
|
|
|
|
|
1385
|
|
|
|
|
|
|
Otherwise, look for callers to the current sub. Insert the comment |
1386
|
|
|
|
|
|
|
with callers above the sub declaration. |
1387
|
|
|
|
|
|
|
|
1388
|
|
|
|
|
|
|
Example: Point is in the sub C<price>: |
1389
|
|
|
|
|
|
|
|
1390
|
|
|
|
|
|
|
package MyApp::Book; |
1391
|
|
|
|
|
|
|
|
1392
|
|
|
|
|
|
|
sub price { |
1393
|
|
|
|
|
|
|
| |
1394
|
|
|
|
|
|
|
|
1395
|
|
|
|
|
|
|
Hit C<C-o e f c> and PerlySense will insert the three places where the |
1396
|
|
|
|
|
|
|
price method is called: |
1397
|
|
|
|
|
|
|
|
1398
|
|
|
|
|
|
|
package MyApp::Book; |
1399
|
|
|
|
|
|
|
|
1400
|
|
|
|
|
|
|
# MyApp::Book->discount_price |
1401
|
|
|
|
|
|
|
# MyApp::User->total_book_cost |
1402
|
|
|
|
|
|
|
# |MyApp::Author->daily_total_income |
1403
|
|
|
|
|
|
|
# MyApp::Book->price |
1404
|
|
|
|
|
|
|
sub price { |
1405
|
|
|
|
|
|
|
|
1406
|
|
|
|
|
|
|
Let's assume the method call chain for total_book_cost is interesting, |
1407
|
|
|
|
|
|
|
so put the cursor on that line and again hit C<C-o e f c>. The callers |
1408
|
|
|
|
|
|
|
for that method is now inserted on the line above. |
1409
|
|
|
|
|
|
|
|
1410
|
|
|
|
|
|
|
package MyApp::Book; |
1411
|
|
|
|
|
|
|
|
1412
|
|
|
|
|
|
|
# MyApp::Book->discount_price |
1413
|
|
|
|
|
|
|
# MyApp::Controller::User->user_details |
1414
|
|
|
|
|
|
|
# |MyApp::User->total_cost |
1415
|
|
|
|
|
|
|
# MyApp::User->total_book_cost |
1416
|
|
|
|
|
|
|
# MyApp::Author->daily_total_income |
1417
|
|
|
|
|
|
|
# MyApp::Book->price |
1418
|
|
|
|
|
|
|
sub price { |
1419
|
|
|
|
|
|
|
|
1420
|
|
|
|
|
|
|
You can go on like this and add more callers to investigate the code |
1421
|
|
|
|
|
|
|
structure. |
1422
|
|
|
|
|
|
|
|
1423
|
|
|
|
|
|
|
The cursor is placed conveniently to make it easy to add subsequent |
1424
|
|
|
|
|
|
|
callers to the call tree. |
1425
|
|
|
|
|
|
|
|
1426
|
|
|
|
|
|
|
If the same caller is already present in the comment, it is marked |
1427
|
|
|
|
|
|
|
with a * to indicate that there's no point following them. |
1428
|
|
|
|
|
|
|
|
1429
|
|
|
|
|
|
|
Caveat: The method of identifying callers works by method names alone, |
1430
|
|
|
|
|
|
|
so there might be false positives, or uninteresting callers added to |
1431
|
|
|
|
|
|
|
the list. Delete those lines to avoid clutter. |
1432
|
|
|
|
|
|
|
|
1433
|
|
|
|
|
|
|
|
1434
|
|
|
|
|
|
|
|
1435
|
|
|
|
|
|
|
=head3 Edit -- Visualize Callers |
1436
|
|
|
|
|
|
|
|
1437
|
|
|
|
|
|
|
C<C-o e v c> -- Visualize callers in a call tree comment (collected |
1438
|
|
|
|
|
|
|
using Find Callers above) by drawing it using GraphViz. |
1439
|
|
|
|
|
|
|
|
1440
|
|
|
|
|
|
|
Put the cursor in a comment with the call tree and hit C<C-o e v |
1441
|
|
|
|
|
|
|
c>. PerlySense will create a temporary .dot file and let GraphViz |
1442
|
|
|
|
|
|
|
render it into a nice .png image, which will be opened. |
1443
|
|
|
|
|
|
|
|
1444
|
|
|
|
|
|
|
If you're running a graphical Emacs it might even look pretty. |
1445
|
|
|
|
|
|
|
|
1446
|
|
|
|
|
|
|
This requires GraphViz' C<dot> binary to be installed: |
1447
|
|
|
|
|
|
|
|
1448
|
|
|
|
|
|
|
sudo apt-get install graphviz # Debian / Ubuntu |
1449
|
|
|
|
|
|
|
sudo yum install graphviz # Redhat / CentOS |
1450
|
|
|
|
|
|
|
|
1451
|
|
|
|
|
|
|
on OSX, try brew something. |
1452
|
|
|
|
|
|
|
|
1453
|
|
|
|
|
|
|
|
1454
|
|
|
|
|
|
|
=head3 Assist With -- Regex |
1455
|
|
|
|
|
|
|
|
1456
|
|
|
|
|
|
|
|
1457
|
|
|
|
|
|
|
=for html <p>[ <a href="http://search.cpan.org/src/JOHANL/Devel-PerlySense-0.0183/doc/regex_tool.html">Screenshot</a> ]<p> |
1458
|
|
|
|
|
|
|
|
1459
|
|
|
|
|
|
|
|
1460
|
|
|
|
|
|
|
Hit C<C-o a r> to bring up the Regex Tool which will let you compose a |
1461
|
|
|
|
|
|
|
Perl regular expression interactively with matching text highlighed. |
1462
|
|
|
|
|
|
|
|
1463
|
|
|
|
|
|
|
The Regex Tool appears in a new frame with three buffers: B<*Regex*>, |
1464
|
|
|
|
|
|
|
B<*Text*> and B<*Groups*>. |
1465
|
|
|
|
|
|
|
|
1466
|
|
|
|
|
|
|
If point is on a regular expression in the source code, that regex |
1467
|
|
|
|
|
|
|
will be used to pre-populate the B<*Regex*> buffer. (Not yet |
1468
|
|
|
|
|
|
|
implemented) |
1469
|
|
|
|
|
|
|
|
1470
|
|
|
|
|
|
|
If there is a comment block just above the regex, it will be used to |
1471
|
|
|
|
|
|
|
pre-populate the B<*Text*> buffer. Note that it is very handy to |
1472
|
|
|
|
|
|
|
document the regex with some sample input, so this is a good idea in |
1473
|
|
|
|
|
|
|
general. (Not yet implemented) |
1474
|
|
|
|
|
|
|
|
1475
|
|
|
|
|
|
|
The contents of the B<*Regex*> buffer should look e.g. like this: |
1476
|
|
|
|
|
|
|
|
1477
|
|
|
|
|
|
|
/ part \s (\w+) \s no:(\d) /xgm |
1478
|
|
|
|
|
|
|
|
1479
|
|
|
|
|
|
|
=over 4 |
1480
|
|
|
|
|
|
|
|
1481
|
|
|
|
|
|
|
=item * |
1482
|
|
|
|
|
|
|
|
1483
|
|
|
|
|
|
|
You can use all the usual delimiters, such as / | {} () ", etc. |
1484
|
|
|
|
|
|
|
|
1485
|
|
|
|
|
|
|
=item * |
1486
|
|
|
|
|
|
|
|
1487
|
|
|
|
|
|
|
You can put Perl comments below the regex to temporarily store chunks |
1488
|
|
|
|
|
|
|
of regex code during prototyping. |
1489
|
|
|
|
|
|
|
|
1490
|
|
|
|
|
|
|
=item * |
1491
|
|
|
|
|
|
|
|
1492
|
|
|
|
|
|
|
The modifiers work as expected, including /x and /g . |
1493
|
|
|
|
|
|
|
|
1494
|
|
|
|
|
|
|
=back |
1495
|
|
|
|
|
|
|
|
1496
|
|
|
|
|
|
|
The results in the B<*Groups*> buffer are updated as you type in |
1497
|
|
|
|
|
|
|
either the B<*Regex*> or B<*Text*> buffer. |
1498
|
|
|
|
|
|
|
|
1499
|
|
|
|
|
|
|
Use C-c C-c to force an update. |
1500
|
|
|
|
|
|
|
|
1501
|
|
|
|
|
|
|
Use C-c C-k to quit all the regex-tool buffers and remove the frame. |
1502
|
|
|
|
|
|
|
|
1503
|
|
|
|
|
|
|
|
1504
|
|
|
|
|
|
|
|
1505
|
|
|
|
|
|
|
=head1 THE PERLYSENSE USER DIRECTORY |
1506
|
|
|
|
|
|
|
|
1507
|
|
|
|
|
|
|
PerlySense keeps a per-user directory to store cache files, logs, |
1508
|
|
|
|
|
|
|
etc. The C<.PerlySense> user directory is located under the first |
1509
|
|
|
|
|
|
|
available of these environment variables: |
1510
|
|
|
|
|
|
|
|
1511
|
|
|
|
|
|
|
$APPDATA |
1512
|
|
|
|
|
|
|
$ALLUSERSPROFILE |
1513
|
|
|
|
|
|
|
$USERPROFILE |
1514
|
|
|
|
|
|
|
$HOME |
1515
|
|
|
|
|
|
|
$TEMP |
1516
|
|
|
|
|
|
|
$TMP |
1517
|
|
|
|
|
|
|
|
1518
|
|
|
|
|
|
|
|
1519
|
|
|
|
|
|
|
Run |
1520
|
|
|
|
|
|
|
|
1521
|
|
|
|
|
|
|
perly_sense info |
1522
|
|
|
|
|
|
|
|
1523
|
|
|
|
|
|
|
to see which directory is actually being used. |
1524
|
|
|
|
|
|
|
|
1525
|
|
|
|
|
|
|
|
1526
|
|
|
|
|
|
|
|
1527
|
|
|
|
|
|
|
=head1 PROJECTS |
1528
|
|
|
|
|
|
|
|
1529
|
|
|
|
|
|
|
PerlySense has the concept of a Project root directory. |
1530
|
|
|
|
|
|
|
|
1531
|
|
|
|
|
|
|
Basically, this is where all the source lives, and where your program |
1532
|
|
|
|
|
|
|
can go to find modules that are used. This is from where tests are run |
1533
|
|
|
|
|
|
|
and files are found. |
1534
|
|
|
|
|
|
|
|
1535
|
|
|
|
|
|
|
You can specify the Project root dir explicitly for your |
1536
|
|
|
|
|
|
|
applications. But if you don't, PerlySense will try and figure out |
1537
|
|
|
|
|
|
|
what the Project root directory is from the context of the surrounding |
1538
|
|
|
|
|
|
|
code. |
1539
|
|
|
|
|
|
|
|
1540
|
|
|
|
|
|
|
This means you can browse source code anywhere on your hard drive |
1541
|
|
|
|
|
|
|
(e.g. @INC) without any special setup or configuration. Most things |
1542
|
|
|
|
|
|
|
will just work, without any hassle. |
1543
|
|
|
|
|
|
|
|
1544
|
|
|
|
|
|
|
If you follow the standard directory structure for CPAN modules, the |
1545
|
|
|
|
|
|
|
Project directory is typically the one which contains the Makefile.PL, |
1546
|
|
|
|
|
|
|
the lib, bin, and t directory, etc. |
1547
|
|
|
|
|
|
|
|
1548
|
|
|
|
|
|
|
|
1549
|
|
|
|
|
|
|
|
1550
|
|
|
|
|
|
|
=head2 Identifying a Project root directory |
1551
|
|
|
|
|
|
|
|
1552
|
|
|
|
|
|
|
The fastest and most solid way for PerlySense to know which is the |
1553
|
|
|
|
|
|
|
Project directory is to create a C<.PerlySenseProject> directory with |
1554
|
|
|
|
|
|
|
a config file in it. This is highly recommended for all of your own |
1555
|
|
|
|
|
|
|
projects. |
1556
|
|
|
|
|
|
|
|
1557
|
|
|
|
|
|
|
The complete project identification strategy is as follows: |
1558
|
|
|
|
|
|
|
|
1559
|
|
|
|
|
|
|
|
1560
|
|
|
|
|
|
|
=over 4 |
1561
|
|
|
|
|
|
|
|
1562
|
|
|
|
|
|
|
=item * |
1563
|
|
|
|
|
|
|
|
1564
|
|
|
|
|
|
|
First, if there is any directory upwards in the dirctory path with a |
1565
|
|
|
|
|
|
|
C<.PerlySenseProject> dir in it, that is the Project directory. |
1566
|
|
|
|
|
|
|
|
1567
|
|
|
|
|
|
|
|
1568
|
|
|
|
|
|
|
=item * |
1569
|
|
|
|
|
|
|
|
1570
|
|
|
|
|
|
|
Second, PerlySense will try figure out from where the current file (if |
1571
|
|
|
|
|
|
|
any) was being required/used given the contained package names or used |
1572
|
|
|
|
|
|
|
modules. |
1573
|
|
|
|
|
|
|
|
1574
|
|
|
|
|
|
|
|
1575
|
|
|
|
|
|
|
=item * |
1576
|
|
|
|
|
|
|
|
1577
|
|
|
|
|
|
|
Third, if that doesn't work, PerlySense will look for C<lib> and C<t> |
1578
|
|
|
|
|
|
|
directories. |
1579
|
|
|
|
|
|
|
|
1580
|
|
|
|
|
|
|
=back |
1581
|
|
|
|
|
|
|
|
1582
|
|
|
|
|
|
|
If that doesn't work, PerlySense is lost and you really do need to |
1583
|
|
|
|
|
|
|
create an explicit Project directory by running the following command |
1584
|
|
|
|
|
|
|
in your intended Project root directory (that would typically be the |
1585
|
|
|
|
|
|
|
directory which has a C<lib> directory in it): |
1586
|
|
|
|
|
|
|
|
1587
|
|
|
|
|
|
|
perly_sense create_project |
1588
|
|
|
|
|
|
|
|
1589
|
|
|
|
|
|
|
Any existing C<.PerlySenseProject/project.yml> config file will be |
1590
|
|
|
|
|
|
|
renamed. |
1591
|
|
|
|
|
|
|
|
1592
|
|
|
|
|
|
|
Note that this all means that the current Project depends on which |
1593
|
|
|
|
|
|
|
file you are looking at. If it's a file within the directory tree |
1594
|
|
|
|
|
|
|
under a C<.PerlySenseProject> directory, that's what the current |
1595
|
|
|
|
|
|
|
Project is. But if you from that file do a Class Overview on an |
1596
|
|
|
|
|
|
|
installed CPAN module, the current Project is deduced from that .pm |
1597
|
|
|
|
|
|
|
file, typically making the current Project be the C<lib> or |
1598
|
|
|
|
|
|
|
C<site_lib> of your local CPAN installation. |
1599
|
|
|
|
|
|
|
|
1600
|
|
|
|
|
|
|
|
1601
|
|
|
|
|
|
|
|
1602
|
|
|
|
|
|
|
=head2 Project Configuration |
1603
|
|
|
|
|
|
|
|
1604
|
|
|
|
|
|
|
The Project has a .PerlySenseProject/project.yml config file. Here you |
1605
|
|
|
|
|
|
|
can change the name of the Project, add extra @INC directories, etc. |
1606
|
|
|
|
|
|
|
|
1607
|
|
|
|
|
|
|
There is a yaml-mode for Emacs, but I haven't got it to work properly |
1608
|
|
|
|
|
|
|
(unless an infinite loop counts as "properly" these days). The |
1609
|
|
|
|
|
|
|
shell-script-mode is good enough. |
1610
|
|
|
|
|
|
|
|
1611
|
|
|
|
|
|
|
The config file documentation is where it belongs, in the config file, |
1612
|
|
|
|
|
|
|
so just take a look at it. |
1613
|
|
|
|
|
|
|
|
1614
|
|
|
|
|
|
|
|
1615
|
|
|
|
|
|
|
|
1616
|
|
|
|
|
|
|
=head2 perly_sense Project commands |
1617
|
|
|
|
|
|
|
|
1618
|
|
|
|
|
|
|
|
1619
|
|
|
|
|
|
|
perly_sense create_project [--dir=DIR] |
1620
|
|
|
|
|
|
|
|
1621
|
|
|
|
|
|
|
Create a PerlySense project in DIR (default is current dir). |
1622
|
|
|
|
|
|
|
|
1623
|
|
|
|
|
|
|
|
1624
|
|
|
|
|
|
|
|
1625
|
|
|
|
|
|
|
perly_sense process_project |
1626
|
|
|
|
|
|
|
|
1627
|
|
|
|
|
|
|
Cache all modules in the project. (not implemented) |
1628
|
|
|
|
|
|
|
|
1629
|
|
|
|
|
|
|
|
1630
|
|
|
|
|
|
|
|
1631
|
|
|
|
|
|
|
=head1 BOOKMARKS |
1632
|
|
|
|
|
|
|
|
1633
|
|
|
|
|
|
|
Bookmarks are regexes that may match against a single line. Each |
1634
|
|
|
|
|
|
|
bookmark definition has a name/moniker under which the matches are |
1635
|
|
|
|
|
|
|
grouped in the Class Overview display. |
1636
|
|
|
|
|
|
|
|
1637
|
|
|
|
|
|
|
The primary point of Bookmarks is to highlight unusual things in the |
1638
|
|
|
|
|
|
|
source. The secondary to make it easy for you go navigate to them. |
1639
|
|
|
|
|
|
|
|
1640
|
|
|
|
|
|
|
This can be anything you like, but things that come to mind are: |
1641
|
|
|
|
|
|
|
|
1642
|
|
|
|
|
|
|
=over 4 |
1643
|
|
|
|
|
|
|
|
1644
|
|
|
|
|
|
|
=item * TODO comments |
1645
|
|
|
|
|
|
|
|
1646
|
|
|
|
|
|
|
=item * FIXME/XXX/HACK comments |
1647
|
|
|
|
|
|
|
|
1648
|
|
|
|
|
|
|
=item * Things you don't want left in the code, like |
1649
|
|
|
|
|
|
|
|
1650
|
|
|
|
|
|
|
Breakpoints ($DB::single = 1) |
1651
|
|
|
|
|
|
|
|
1652
|
|
|
|
|
|
|
Debugging warn/print statements |
1653
|
|
|
|
|
|
|
|
1654
|
|
|
|
|
|
|
=back |
1655
|
|
|
|
|
|
|
|
1656
|
|
|
|
|
|
|
|
1657
|
|
|
|
|
|
|
=head2 Configuration |
1658
|
|
|
|
|
|
|
|
1659
|
|
|
|
|
|
|
Bookmarks are defined in the Project Config file (technical details |
1660
|
|
|
|
|
|
|
are documented there). |
1661
|
|
|
|
|
|
|
|
1662
|
|
|
|
|
|
|
|
1663
|
|
|
|
|
|
|
|
1664
|
|
|
|
|
|
|
=head1 KEY BINDING CONVENTIONS |
1665
|
|
|
|
|
|
|
|
1666
|
|
|
|
|
|
|
There is a system behind the chosen key bindings in |
1667
|
|
|
|
|
|
|
PerlySense. Knowing the conventions will make it easier to remember |
1668
|
|
|
|
|
|
|
everything. |
1669
|
|
|
|
|
|
|
|
1670
|
|
|
|
|
|
|
=head2 Convention: Action based |
1671
|
|
|
|
|
|
|
|
1672
|
|
|
|
|
|
|
The first level after the prefix key (C<C-o> by default) is always an |
1673
|
|
|
|
|
|
|
Action, e.g. Run, or Document. |
1674
|
|
|
|
|
|
|
|
1675
|
|
|
|
|
|
|
(In the case of C<C-o C-d> for Document you can either think of it as |
1676
|
|
|
|
|
|
|
"Document this for me!" or "Give me Documentation!".) |
1677
|
|
|
|
|
|
|
|
1678
|
|
|
|
|
|
|
With a verb at the first level rather than a noun, the Action can be |
1679
|
|
|
|
|
|
|
context sensitive, "smart", or DWIMy. |
1680
|
|
|
|
|
|
|
|
1681
|
|
|
|
|
|
|
|
1682
|
|
|
|
|
|
|
=over 4 |
1683
|
|
|
|
|
|
|
|
1684
|
|
|
|
|
|
|
=item Smart Goto goes to whatever is under the cursor, be it a module |
1685
|
|
|
|
|
|
|
name, a method call, a file name, or an error message. |
1686
|
|
|
|
|
|
|
|
1687
|
|
|
|
|
|
|
=item Run runs the file differently depending on what kind of file is |
1688
|
|
|
|
|
|
|
open (tests are "proved", modules are syntax checked, scripts are run, |
1689
|
|
|
|
|
|
|
etc). |
1690
|
|
|
|
|
|
|
|
1691
|
|
|
|
|
|
|
=back |
1692
|
|
|
|
|
|
|
|
1693
|
|
|
|
|
|
|
|
1694
|
|
|
|
|
|
|
=head2 Convention: The Action as a Gateway |
1695
|
|
|
|
|
|
|
|
1696
|
|
|
|
|
|
|
The first level indicates the Action to perform, and has the Ctrl |
1697
|
|
|
|
|
|
|
modifier as a "Smart" / DWIMy modifier. This is both so it's easy to |
1698
|
|
|
|
|
|
|
type C<C-o C-r> without releasing the Ctrl key, and to provide a |
1699
|
|
|
|
|
|
|
gateway to more specific actions when typing the key without Ctrl. |
1700
|
|
|
|
|
|
|
|
1701
|
|
|
|
|
|
|
E.g. C<C-o C-r> means "Run file", C<C-o r r> means "Run - Re-run". |
1702
|
|
|
|
|
|
|
|
1703
|
|
|
|
|
|
|
E.g. C<C-o C-g> means "Smart Goto", C<C-o g b> means "Goto - Base |
1704
|
|
|
|
|
|
|
Class", C-o g s means "Goto - SUPER Method". |
1705
|
|
|
|
|
|
|
|
1706
|
|
|
|
|
|
|
|
1707
|
|
|
|
|
|
|
|
1708
|
|
|
|
|
|
|
=head2 The Main Actions Areas |
1709
|
|
|
|
|
|
|
|
1710
|
|
|
|
|
|
|
(some of the main areas have no implementations yet) |
1711
|
|
|
|
|
|
|
|
1712
|
|
|
|
|
|
|
=over 4 |
1713
|
|
|
|
|
|
|
|
1714
|
|
|
|
|
|
|
=item * r -- Run |
1715
|
|
|
|
|
|
|
|
1716
|
|
|
|
|
|
|
Run files in various ways. |
1717
|
|
|
|
|
|
|
|
1718
|
|
|
|
|
|
|
|
1719
|
|
|
|
|
|
|
=item * g -- Go to |
1720
|
|
|
|
|
|
|
|
1721
|
|
|
|
|
|
|
Navigate to various locations in the source. |
1722
|
|
|
|
|
|
|
|
1723
|
|
|
|
|
|
|
|
1724
|
|
|
|
|
|
|
=item * d -- Document |
1725
|
|
|
|
|
|
|
|
1726
|
|
|
|
|
|
|
Bring up documentation. |
1727
|
|
|
|
|
|
|
|
1728
|
|
|
|
|
|
|
|
1729
|
|
|
|
|
|
|
=item * f -- Find |
1730
|
|
|
|
|
|
|
|
1731
|
|
|
|
|
|
|
Find/search and display things in the source. |
1732
|
|
|
|
|
|
|
|
1733
|
|
|
|
|
|
|
|
1734
|
|
|
|
|
|
|
=item * o -- Overview |
1735
|
|
|
|
|
|
|
|
1736
|
|
|
|
|
|
|
Bring up an overview of things. |
1737
|
|
|
|
|
|
|
|
1738
|
|
|
|
|
|
|
|
1739
|
|
|
|
|
|
|
=item * m -- forMat |
1740
|
|
|
|
|
|
|
|
1741
|
|
|
|
|
|
|
Reformat source. |
1742
|
|
|
|
|
|
|
|
1743
|
|
|
|
|
|
|
|
1744
|
|
|
|
|
|
|
=item * e -- Edit & Refactor |
1745
|
|
|
|
|
|
|
|
1746
|
|
|
|
|
|
|
Perform smaller convenience editing task, as well as refactorings -- |
1747
|
|
|
|
|
|
|
restructuring edits that don't impact functionality/behaviour. |
1748
|
|
|
|
|
|
|
|
1749
|
|
|
|
|
|
|
=item * A -- Assist |
1750
|
|
|
|
|
|
|
|
1751
|
|
|
|
|
|
|
Solve very context sensitive problems. |
1752
|
|
|
|
|
|
|
|
1753
|
|
|
|
|
|
|
=back |
1754
|
|
|
|
|
|
|
|
1755
|
|
|
|
|
|
|
|
1756
|
|
|
|
|
|
|
=head2 Explore Emacs key bindings |
1757
|
|
|
|
|
|
|
|
1758
|
|
|
|
|
|
|
Remember that you can use the usual Emacs feature to display possible |
1759
|
|
|
|
|
|
|
key stroke completions by hitting C-h whenever in the key stroke |
1760
|
|
|
|
|
|
|
sequence. |
1761
|
|
|
|
|
|
|
|
1762
|
|
|
|
|
|
|
E.g. Hitting C<C-o g C-h> will list all available key strokes starting |
1763
|
|
|
|
|
|
|
wiht C<C-o g>. |
1764
|
|
|
|
|
|
|
|
1765
|
|
|
|
|
|
|
|
1766
|
|
|
|
|
|
|
|
1767
|
|
|
|
|
|
|
=head2 Changing key bindings |
1768
|
|
|
|
|
|
|
|
1769
|
|
|
|
|
|
|
Some key bindings may change over time as I figure out what works and |
1770
|
|
|
|
|
|
|
what doesn't. Some key bindings may be reorganized to make more sense |
1771
|
|
|
|
|
|
|
or to just work better. |
1772
|
|
|
|
|
|
|
|
1773
|
|
|
|
|
|
|
|
1774
|
|
|
|
|
|
|
|
1775
|
|
|
|
|
|
|
=head1 IN CLOSING -- ON PARSING PERL |
1776
|
|
|
|
|
|
|
|
1777
|
|
|
|
|
|
|
Since Perl is so dynamic, a perfect static analysis of the source is |
1778
|
|
|
|
|
|
|
impossible. But not unusably so. Well, hopefully. Most of the time. |
1779
|
|
|
|
|
|
|
|
1780
|
|
|
|
|
|
|
Because of this PerlySense is not about exact rules, but about |
1781
|
|
|
|
|
|
|
heuristics and a 90% solution that isn't perfect, but good-enough. |
1782
|
|
|
|
|
|
|
|
1783
|
|
|
|
|
|
|
PerlySense tries to take advantage of the fact that Perl code is more |
1784
|
|
|
|
|
|
|
than the plain source file. The source lives in a context of POD and a |
1785
|
|
|
|
|
|
|
directory structure and common Perl idioms. |
1786
|
|
|
|
|
|
|
|
1787
|
|
|
|
|
|
|
Sometimes when PerlySense can't make a decision, you're expected to |
1788
|
|
|
|
|
|
|
chip in and tell it what you meant. |
1789
|
|
|
|
|
|
|
|
1790
|
|
|
|
|
|
|
Sometimes it won't work at all. |
1791
|
|
|
|
|
|
|
|
1792
|
|
|
|
|
|
|
Such is the way of dynamic languages. |
1793
|
|
|
|
|
|
|
|
1794
|
|
|
|
|
|
|
If it works for you, brilliant, use it to be more productive. If |
1795
|
|
|
|
|
|
|
not... well, there's always Java >:) |
1796
|
|
|
|
|
|
|
|
1797
|
|
|
|
|
|
|
|
1798
|
|
|
|
|
|
|
|
1799
|
|
|
|
|
|
|
=head2 Syntax Parsing Modules |
1800
|
|
|
|
|
|
|
|
1801
|
|
|
|
|
|
|
PerlySense provides a plugin architecture for supporting custom syntax |
1802
|
|
|
|
|
|
|
provided by OO modules such as L<Moose>, or L<Class::Accessor>. |
1803
|
|
|
|
|
|
|
|
1804
|
|
|
|
|
|
|
Currently Moose is supported via the |
1805
|
|
|
|
|
|
|
L<Devel::PerlySense::Plugin::Syntax::Moose> module. |
1806
|
|
|
|
|
|
|
|
1807
|
|
|
|
|
|
|
|
1808
|
|
|
|
|
|
|
|
1809
|
|
|
|
|
|
|
=head1 MORE DOCUMENTATION |
1810
|
|
|
|
|
|
|
|
1811
|
|
|
|
|
|
|
L<Devel::PerlySense::Cookbook> |
1812
|
|
|
|
|
|
|
|
1813
|
|
|
|
|
|
|
|
1814
|
|
|
|
|
|
|
|
1815
|
|
|
|
|
|
|
=head1 SEE ALSO |
1816
|
|
|
|
|
|
|
|
1817
|
|
|
|
|
|
|
L<sepia> - similar effort |
1818
|
|
|
|
|
|
|
|
1819
|
|
|
|
|
|
|
L<PPI> - excellent for parsing Perl |
1820
|
|
|
|
|
|
|
|
1821
|
|
|
|
|
|
|
L<CPANXR> - also uses PPI for cross referencing the CPAN |
1822
|
|
|
|
|
|
|
|
1823
|
|
|
|
|
|
|
L<http://www.DarSerMan.com/Perl/Oasis/> - Win32 class |
1824
|
|
|
|
|
|
|
browser/IDE. Earlier (a lot) work by me. |
1825
|
|
|
|
|
|
|
|
1826
|
|
|
|
|
|
|
L<http://www.perl.com/lpt/a/955> - Article "Perl Needs Better Tools" |
1827
|
|
|
|
|
|
|
|
1828
|
|
|
|
|
|
|
L<http://media.pragprog.com/articles/mar_02_archeology.pdf> - Article "Software Archeology" |
1829
|
|
|
|
|
|
|
|
1830
|
|
|
|
|
|
|
L<http://www.newartisans.com/downloads_files/regex-tool.el> - Regex Tool |
1831
|
|
|
|
|
|
|
|
1832
|
|
|
|
|
|
|
L<http://vimdoc.sourceforge.net/htmldoc/eval.html#Dictionaries> - Vim native data structure |
1833
|
|
|
|
|
|
|
|
1834
|
|
|
|
|
|
|
|
1835
|
|
|
|
|
|
|
|
1836
|
|
|
|
|
|
|
=encoding utf8 |
1837
|
|
|
|
|
|
|
|
1838
|
|
|
|
|
|
|
=head1 AUTHOR |
1839
|
|
|
|
|
|
|
|
1840
|
|
|
|
|
|
|
Johan Lindstrom, C<< <johanl buzzwordninja.com> >> |
1841
|
|
|
|
|
|
|
|
1842
|
|
|
|
|
|
|
=head1 CONTRIBUTIONS, BUGS, AND CAVEATS |
1843
|
|
|
|
|
|
|
|
1844
|
|
|
|
|
|
|
=head2 CONTRIBUTIONS |
1845
|
|
|
|
|
|
|
|
1846
|
|
|
|
|
|
|
If you want to hack on PerlySense, fork the project at GitHub: |
1847
|
|
|
|
|
|
|
L<https://github.com/jplindstrom/p5-Devel-PerlySense> |
1848
|
|
|
|
|
|
|
|
1849
|
|
|
|
|
|
|
|
1850
|
|
|
|
|
|
|
=head2 BUG REPORTS |
1851
|
|
|
|
|
|
|
|
1852
|
|
|
|
|
|
|
Please report any bugs or feature requests to |
1853
|
|
|
|
|
|
|
C<bug-devel-perlysense@rt.cpan.org>, or through the web interface at |
1854
|
|
|
|
|
|
|
L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Devel-PerlySense>. |
1855
|
|
|
|
|
|
|
I will be notified, and then you'll automatically be notified of progress on |
1856
|
|
|
|
|
|
|
your bug as I make changes. |
1857
|
|
|
|
|
|
|
|
1858
|
|
|
|
|
|
|
|
1859
|
|
|
|
|
|
|
=head2 CAVEATS |
1860
|
|
|
|
|
|
|
|
1861
|
|
|
|
|
|
|
Tab/space isn't supported by PPI yet, but it's supposed to be. So |
1862
|
|
|
|
|
|
|
using Tab instead of spaces won't work properly. |
1863
|
|
|
|
|
|
|
|
1864
|
|
|
|
|
|
|
|
1865
|
|
|
|
|
|
|
|
1866
|
|
|
|
|
|
|
=head2 KNOWN BUGS |
1867
|
|
|
|
|
|
|
|
1868
|
|
|
|
|
|
|
PPI is kinda slow for large documents. Lots of objects being created etc. |
1869
|
|
|
|
|
|
|
|
1870
|
|
|
|
|
|
|
There are certainly edge cases. Bug reports with failing tests |
1871
|
|
|
|
|
|
|
appreciated :) |
1872
|
|
|
|
|
|
|
|
1873
|
|
|
|
|
|
|
There is one known infinite loop. |
1874
|
|
|
|
|
|
|
|
1875
|
|
|
|
|
|
|
|
1876
|
|
|
|
|
|
|
|
1877
|
|
|
|
|
|
|
=head1 ACKNOWLEDGEMENTS |
1878
|
|
|
|
|
|
|
|
1879
|
|
|
|
|
|
|
Peter Liljenberg and Phil Jackson for their elisp fu. |
1880
|
|
|
|
|
|
|
|
1881
|
|
|
|
|
|
|
Jonathan Rockway for cool ideas: |
1882
|
|
|
|
|
|
|
L<http://blog.jrock.us/articles/Increment%20test%20counter.pod> |
1883
|
|
|
|
|
|
|
|
1884
|
|
|
|
|
|
|
John Wiegley for the regex-tool L<http://www.newartisans.com/downloads_files/regex-tool.el> |
1885
|
|
|
|
|
|
|
|
1886
|
|
|
|
|
|
|
Jaeyoun Chung for dropdown-list L<http://www.emacswiki.org/cgi-bin/wiki/dropdown-list.el> |
1887
|
|
|
|
|
|
|
|
1888
|
|
|
|
|
|
|
|
1889
|
|
|
|
|
|
|
|
1890
|
|
|
|
|
|
|
=head1 COPYRIGHT & LICENSE |
1891
|
|
|
|
|
|
|
|
1892
|
|
|
|
|
|
|
Copyright 2007 Johan Lindstrom, All Rights Reserved. |
1893
|
|
|
|
|
|
|
|
1894
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify it |
1895
|
|
|
|
|
|
|
under the same terms as Perl itself. |
1896
|
|
|
|
|
|
|
|
1897
|
|
|
|
|
|
|
=cut |
1898
|
|
|
|
|
|
|
|
1899
|
|
|
|
|
|
|
|
1900
|
|
|
|
|
|
|
|
1901
|
|
|
|
|
|
|
|
1902
|
|
|
|
|
|
|
|
1903
|
68
|
|
|
68
|
|
46290
|
use strict; |
|
68
|
|
|
|
|
91
|
|
|
68
|
|
|
|
|
1800
|
|
1904
|
68
|
|
|
68
|
|
230
|
use warnings; |
|
68
|
|
|
|
|
72
|
|
|
68
|
|
|
|
|
1831
|
|
1905
|
68
|
|
|
68
|
|
21392
|
use utf8; |
|
68
|
|
|
|
|
385
|
|
|
68
|
|
|
|
|
351
|
|
1906
|
|
|
|
|
|
|
|
1907
|
|
|
|
|
|
|
package Devel::PerlySense; |
1908
|
|
|
|
|
|
|
$Devel::PerlySense::VERSION = '0.0217'; |
1909
|
|
|
|
|
|
|
|
1910
|
|
|
|
|
|
|
|
1911
|
68
|
|
|
68
|
|
18600
|
use Spiffy -Base; |
|
68
|
|
|
|
|
164905
|
|
|
68
|
|
|
|
|
375
|
|
1912
|
68
|
|
|
68
|
|
294819
|
use Carp; |
|
68
|
|
|
68
|
|
99
|
|
|
68
|
|
|
68
|
|
1251
|
|
|
68
|
|
|
|
|
210
|
|
|
68
|
|
|
|
|
76
|
|
|
68
|
|
|
|
|
1390
|
|
|
68
|
|
|
|
|
213
|
|
|
68
|
|
|
|
|
68
|
|
|
68
|
|
|
|
|
3112
|
|
1913
|
68
|
|
|
68
|
|
7623
|
use Data::Dumper; |
|
68
|
|
|
|
|
64477
|
|
|
68
|
|
|
|
|
2566
|
|
1914
|
68
|
|
|
68
|
|
426
|
use File::Basename; |
|
68
|
|
|
|
|
80
|
|
|
68
|
|
|
|
|
3032
|
|
1915
|
68
|
|
|
68
|
|
240
|
use File::Path; |
|
68
|
|
|
|
|
80
|
|
|
68
|
|
|
|
|
2520
|
|
1916
|
68
|
|
|
68
|
|
27744
|
use File::Find::Rule; |
|
68
|
|
|
|
|
354014
|
|
|
68
|
|
|
|
|
413
|
|
1917
|
68
|
|
|
68
|
|
25553
|
use Path::Class qw/dir file/; |
|
68
|
|
|
|
|
1517565
|
|
|
68
|
|
|
|
|
3687
|
|
1918
|
68
|
|
|
68
|
|
45893
|
use Path::Tiny; |
|
68
|
|
|
|
|
527226
|
|
|
68
|
|
|
|
|
3459
|
|
1919
|
68
|
|
|
68
|
|
34192
|
use Pod::Text; |
|
68
|
|
|
|
|
2036541
|
|
|
68
|
|
|
|
|
4528
|
|
1920
|
68
|
|
|
68
|
|
21035
|
use IO::String; |
|
68
|
|
|
|
|
77446
|
|
|
68
|
|
|
|
|
1724
|
|
1921
|
68
|
|
|
68
|
|
25861
|
use Cache::Cache; |
|
68
|
|
|
|
|
14930
|
|
|
68
|
|
|
|
|
2522
|
|
1922
|
68
|
|
|
68
|
|
32477
|
use Storable qw/freeze thaw/; |
|
68
|
|
|
|
|
144719
|
|
|
68
|
|
|
|
|
4042
|
|
1923
|
68
|
|
|
68
|
|
345
|
use List::Util qw/ first /; |
|
68
|
|
|
|
|
95
|
|
|
68
|
|
|
|
|
4970
|
|
1924
|
68
|
|
|
68
|
|
20390
|
use List::MoreUtils qw/ uniq /; |
|
68
|
|
|
|
|
304046
|
|
|
68
|
|
|
|
|
551
|
|
1925
|
|
|
|
|
|
|
|
1926
|
68
|
|
|
68
|
|
47129
|
use Devel::TimeThis; |
|
68
|
|
|
|
|
101
|
|
|
68
|
|
|
|
|
1658
|
|
1927
|
|
|
|
|
|
|
|
1928
|
68
|
|
|
68
|
|
14725
|
use Devel::PerlySense::Util; |
|
68
|
|
|
|
|
131
|
|
|
68
|
|
|
|
|
3482
|
|
1929
|
68
|
|
|
68
|
|
16138
|
use Devel::PerlySense::Util::Log; |
|
68
|
|
|
|
|
888
|
|
|
68
|
|
|
|
|
4337
|
|
1930
|
68
|
|
|
68
|
|
17813
|
use Devel::PerlySense::Project; |
|
68
|
|
|
|
|
143
|
|
|
68
|
|
|
|
|
435
|
|
1931
|
68
|
|
|
68
|
|
33777
|
use Devel::PerlySense::Project::Unknown; |
|
68
|
|
|
|
|
115
|
|
|
68
|
|
|
|
|
350
|
|
1932
|
68
|
|
|
68
|
|
13324
|
use Devel::PerlySense::Config::Project; |
|
68
|
|
|
|
|
89
|
|
|
68
|
|
|
|
|
345
|
|
1933
|
68
|
|
|
68
|
|
10045
|
use Devel::PerlySense::Home; |
|
68
|
|
|
|
|
83
|
|
|
68
|
|
|
|
|
305
|
|
1934
|
68
|
|
|
68
|
|
25034
|
use Devel::PerlySense::Class; |
|
68
|
|
|
|
|
140
|
|
|
68
|
|
|
|
|
410
|
|
1935
|
68
|
|
|
68
|
|
12151
|
use Devel::PerlySense::Document; |
|
68
|
|
|
|
|
91
|
|
|
68
|
|
|
|
|
277
|
|
1936
|
68
|
|
|
68
|
|
10350
|
use Devel::PerlySense::Document::Location; |
|
68
|
|
|
|
|
85
|
|
|
68
|
|
|
|
|
258
|
|
1937
|
68
|
|
|
68
|
|
28531
|
use Devel::PerlySense::BookmarkConfig; |
|
68
|
|
|
|
|
127
|
|
|
68
|
|
|
|
|
352
|
|
1938
|
68
|
|
|
68
|
|
29502
|
use Devel::PerlySense::CallTree; |
|
68
|
|
|
|
|
179
|
|
|
68
|
|
|
|
|
2218
|
|
1939
|
68
|
|
|
68
|
|
22332
|
use Devel::PerlySense::CallTree::Graph; |
|
68
|
|
|
|
|
151
|
|
|
68
|
|
|
|
|
220685
|
|
1940
|
|
|
|
|
|
|
|
1941
|
|
|
|
|
|
|
|
1942
|
|
|
|
|
|
|
|
1943
|
|
|
|
|
|
|
|
1944
|
|
|
|
|
|
|
=head1 *** THE FOLLOWING IS DEVELOPER DOCUMENTATION *** |
1945
|
|
|
|
|
|
|
|
1946
|
|
|
|
|
|
|
|
1947
|
|
|
|
|
|
|
|
1948
|
|
|
|
|
|
|
|
1949
|
|
|
|
|
|
|
|
1950
|
|
|
|
|
|
|
=head1 PROPERTIES |
1951
|
|
|
|
|
|
|
|
1952
|
|
|
|
|
|
|
=head2 oCache |
1953
|
|
|
|
|
|
|
|
1954
|
|
|
|
|
|
|
Cache::Cache object, or undef if no cache is active. |
1955
|
|
|
|
|
|
|
|
1956
|
|
|
|
|
|
|
Default: undef |
1957
|
|
|
|
|
|
|
|
1958
|
|
|
|
|
|
|
=cut |
1959
|
|
|
|
|
|
|
field "oCache" => undef; |
1960
|
|
|
|
|
|
|
|
1961
|
|
|
|
|
|
|
|
1962
|
|
|
|
|
|
|
|
1963
|
|
|
|
|
|
|
|
1964
|
|
|
|
|
|
|
|
1965
|
|
|
|
|
|
|
=head2 oProject |
1966
|
|
|
|
|
|
|
|
1967
|
|
|
|
|
|
|
Devel::PerlySense::Project object. |
1968
|
|
|
|
|
|
|
|
1969
|
|
|
|
|
|
|
Default: A Devel::PerlySense::Project::Unknown object. |
1970
|
|
|
|
|
|
|
|
1971
|
|
|
|
|
|
|
=cut |
1972
|
|
|
|
|
|
|
field "oProject" => undef; |
1973
|
|
|
|
|
|
|
|
1974
|
|
|
|
|
|
|
|
1975
|
|
|
|
|
|
|
|
1976
|
|
|
|
|
|
|
|
1977
|
|
|
|
|
|
|
=head2 oHome |
1978
|
|
|
|
|
|
|
|
1979
|
|
|
|
|
|
|
Devel::PerlySense::Home object. |
1980
|
|
|
|
|
|
|
|
1981
|
|
|
|
|
|
|
Default: A newly created Home object. |
1982
|
|
|
|
|
|
|
|
1983
|
|
|
|
|
|
|
=cut |
1984
|
|
|
|
|
|
|
field "oHome" => Devel::PerlySense::Home->new(); |
1985
|
|
|
|
|
|
|
|
1986
|
|
|
|
|
|
|
|
1987
|
|
|
|
|
|
|
|
1988
|
|
|
|
|
|
|
|
1989
|
|
|
|
|
|
|
|
1990
|
|
|
|
|
|
|
=head2 rhConfig |
1991
|
|
|
|
|
|
|
|
1992
|
|
|
|
|
|
|
Hash ref with the current config. |
1993
|
|
|
|
|
|
|
|
1994
|
|
|
|
|
|
|
If there is a known Project, it reflects the Project's config, |
1995
|
|
|
|
|
|
|
otherwise it's the default config. |
1996
|
|
|
|
|
|
|
|
1997
|
|
|
|
|
|
|
Readonly. Note that the _entire_ data structure is readonly. Each time |
1998
|
|
|
|
|
|
|
you change/add/remove a value from it, a kitten is slain. So, dude, |
1999
|
|
|
|
|
|
|
just don't go there! |
2000
|
|
|
|
|
|
|
|
2001
|
|
|
|
|
|
|
=cut |
2002
|
1360
|
|
|
1360
|
1
|
7244
|
sub rhConfig { |
2003
|
1360
|
|
|
|
|
17348
|
return $self->oProject->rhConfig; |
2004
|
|
|
|
|
|
|
} |
2005
|
|
|
|
|
|
|
|
2006
|
|
|
|
|
|
|
|
2007
|
|
|
|
|
|
|
|
2008
|
|
|
|
|
|
|
|
2009
|
|
|
|
|
|
|
|
2010
|
|
|
|
|
|
|
=head2 VERSION |
2011
|
|
|
|
|
|
|
|
2012
|
|
|
|
|
|
|
The $VERSION of this module. |
2013
|
|
|
|
|
|
|
|
2014
|
|
|
|
|
|
|
=cut |
2015
|
78
|
|
|
78
|
1
|
80
|
sub VERSION { |
2016
|
|
|
|
|
|
|
# This variable is created by Dist::Zilla during release |
2017
|
78
|
|
50
|
|
|
292
|
return $Devel::PerlySense::VERSION || "0.0001DEV"; |
2018
|
|
|
|
|
|
|
} |
2019
|
|
|
|
|
|
|
|
2020
|
|
|
|
|
|
|
|
2021
|
|
|
|
|
|
|
|
2022
|
|
|
|
|
|
|
|
2023
|
|
|
|
|
|
|
|
2024
|
|
|
|
|
|
|
=head2 oBookmarkConfig |
2025
|
|
|
|
|
|
|
|
2026
|
|
|
|
|
|
|
Devel::PerlySense::BookmarkConfig object. |
2027
|
|
|
|
|
|
|
|
2028
|
|
|
|
|
|
|
=cut |
2029
|
|
|
|
|
|
|
field "oBookmarkConfig" => undef; |
2030
|
|
|
|
|
|
|
|
2031
|
|
|
|
|
|
|
|
2032
|
|
|
|
|
|
|
|
2033
|
|
|
|
|
|
|
|
2034
|
|
|
|
|
|
|
|
2035
|
|
|
|
|
|
|
=head2 rhFileDocumentCache |
2036
|
|
|
|
|
|
|
|
2037
|
|
|
|
|
|
|
Hash ref with (keys: absolute file names; keys: Document objects). |
2038
|
|
|
|
|
|
|
|
2039
|
|
|
|
|
|
|
=cut |
2040
|
|
|
|
|
|
|
field "rhFileDocumentCache" => {}; |
2041
|
|
|
|
|
|
|
|
2042
|
|
|
|
|
|
|
|
2043
|
|
|
|
|
|
|
|
2044
|
|
|
|
|
|
|
|
2045
|
|
|
|
|
|
|
|
2046
|
|
|
|
|
|
|
=head1 API METHODS |
2047
|
|
|
|
|
|
|
|
2048
|
|
|
|
|
|
|
=head2 new() |
2049
|
|
|
|
|
|
|
|
2050
|
|
|
|
|
|
|
Create new PerlySense object. |
2051
|
|
|
|
|
|
|
|
2052
|
|
|
|
|
|
|
=cut |
2053
|
|
|
|
|
|
|
sub new() { |
2054
|
92
|
|
|
92
|
1
|
125597
|
my $self = bless {}, shift; |
2055
|
92
|
|
|
|
|
570
|
$self->oBookmarkConfig(Devel::PerlySense::BookmarkConfig->new( oPerlySense => $self )); |
2056
|
92
|
|
|
|
|
1033
|
$self->oProject(Devel::PerlySense::Project::Unknown->new( oPerlySense => $self )); |
2057
|
92
|
|
|
|
|
4309
|
return($self); |
2058
|
|
|
|
|
|
|
} |
2059
|
|
|
|
|
|
|
|
2060
|
|
|
|
|
|
|
|
2061
|
|
|
|
|
|
|
|
2062
|
|
|
|
|
|
|
|
2063
|
|
|
|
|
|
|
|
2064
|
|
|
|
|
|
|
=head2 setFindProject([file => $file], [dir => $dir]) |
2065
|
|
|
|
|
|
|
|
2066
|
|
|
|
|
|
|
Identify a project given the $file or $dir, and set the oProject |
2067
|
|
|
|
|
|
|
property. |
2068
|
|
|
|
|
|
|
|
2069
|
|
|
|
|
|
|
If there is already a project defined, don't change it. |
2070
|
|
|
|
|
|
|
|
2071
|
|
|
|
|
|
|
If no project was found, don't change oProject. |
2072
|
|
|
|
|
|
|
|
2073
|
|
|
|
|
|
|
Return 1 if there is a valid project, else 0. |
2074
|
|
|
|
|
|
|
|
2075
|
|
|
|
|
|
|
Die on errors. |
2076
|
|
|
|
|
|
|
|
2077
|
|
|
|
|
|
|
=cut |
2078
|
733
|
|
|
733
|
1
|
2162
|
sub setFindProject { |
2079
|
733
|
100
|
|
|
|
12629
|
if( ! $self->oProject->isa("Devel::PerlySense::Project::Unknown")) { |
2080
|
677
|
|
|
|
|
7003
|
return 1; |
2081
|
|
|
|
|
|
|
} |
2082
|
|
|
|
|
|
|
|
2083
|
56
|
100
|
|
|
|
997
|
my $oProject = Devel::PerlySense::Project->newFromLocation( |
2084
|
|
|
|
|
|
|
@_, |
2085
|
|
|
|
|
|
|
oPerlySense => $self, |
2086
|
|
|
|
|
|
|
) or return 0; |
2087
|
55
|
|
|
|
|
4313
|
$self->oProject($oProject); |
2088
|
|
|
|
|
|
|
|
2089
|
55
|
|
|
|
|
889
|
return(1); |
2090
|
|
|
|
|
|
|
} |
2091
|
|
|
|
|
|
|
|
2092
|
|
|
|
|
|
|
|
2093
|
|
|
|
|
|
|
|
2094
|
|
|
|
|
|
|
|
2095
|
|
|
|
|
|
|
|
2096
|
|
|
|
|
|
|
=head2 oDocumentParseFile($file) |
2097
|
|
|
|
|
|
|
|
2098
|
|
|
|
|
|
|
Parse $file into a new PerlySense::Document object. |
2099
|
|
|
|
|
|
|
|
2100
|
|
|
|
|
|
|
Return the new object. |
2101
|
|
|
|
|
|
|
|
2102
|
|
|
|
|
|
|
If $file was already parsed by this PerlySense object, cache that |
2103
|
|
|
|
|
|
|
instance of the Document and return that instead of parsing it again. |
2104
|
|
|
|
|
|
|
|
2105
|
|
|
|
|
|
|
Die on errors (like if the file wasn't found). |
2106
|
|
|
|
|
|
|
|
2107
|
|
|
|
|
|
|
=cut |
2108
|
213
|
|
|
213
|
1
|
300
|
sub oDocumentParseFile { |
2109
|
213
|
|
|
|
|
338
|
my ($file) = @_; |
2110
|
|
|
|
|
|
|
|
2111
|
|
|
|
|
|
|
# Stop recursive lookups |
2112
|
213
|
100
|
|
|
|
4698
|
if( exists $self->rhFileDocumentCache->{$file}) { |
2113
|
130
|
50
|
|
|
|
2674
|
if(! defined $self->rhFileDocumentCache->{$file}) { |
2114
|
0
|
|
|
|
|
0
|
die("Tried to parse ($file) recursively\n"); |
2115
|
|
|
|
|
|
|
} |
2116
|
|
|
|
|
|
|
} |
2117
|
213
|
|
|
|
|
3861
|
$self->rhFileDocumentCache->{$file} = undef; |
2118
|
|
|
|
|
|
|
|
2119
|
213
|
|
33
|
|
|
5283
|
my $oDocument = $self->rhFileDocumentCache->{$file} ||= do { |
2120
|
213
|
|
|
|
|
1068752
|
my $oDocumentNew = Devel::PerlySense::Document->new(oPerlySense => $self); |
2121
|
213
|
|
|
|
|
792
|
$oDocumentNew->parse(file => $file); |
2122
|
212
|
|
|
|
|
894
|
$oDocumentNew; |
2123
|
|
|
|
|
|
|
}; |
2124
|
|
|
|
|
|
|
|
2125
|
212
|
|
|
|
|
1004
|
return($oDocument); |
2126
|
|
|
|
|
|
|
} |
2127
|
|
|
|
|
|
|
|
2128
|
|
|
|
|
|
|
|
2129
|
|
|
|
|
|
|
|
2130
|
|
|
|
|
|
|
|
2131
|
|
|
|
|
|
|
|
2132
|
|
|
|
|
|
|
=head2 clearInMemoryDocumentCache() |
2133
|
|
|
|
|
|
|
|
2134
|
|
|
|
|
|
|
Clear the rhFileDocumentCache property. |
2135
|
|
|
|
|
|
|
|
2136
|
|
|
|
|
|
|
Return 1. |
2137
|
|
|
|
|
|
|
|
2138
|
|
|
|
|
|
|
=cut |
2139
|
0
|
|
|
0
|
1
|
0
|
sub clearInMemoryDocumentCache { |
2140
|
0
|
|
|
|
|
0
|
$self->rhFileDocumentCache( {} ); |
2141
|
0
|
|
|
|
|
0
|
return 1; |
2142
|
|
|
|
|
|
|
} |
2143
|
|
|
|
|
|
|
|
2144
|
|
|
|
|
|
|
|
2145
|
|
|
|
|
|
|
|
2146
|
|
|
|
|
|
|
|
2147
|
|
|
|
|
|
|
|
2148
|
|
|
|
|
|
|
=head2 podFromFile(file => $file) |
2149
|
|
|
|
|
|
|
|
2150
|
|
|
|
|
|
|
Return the pod in $file as text, or die on errors. |
2151
|
|
|
|
|
|
|
|
2152
|
|
|
|
|
|
|
Die if $file doesn't exist. |
2153
|
|
|
|
|
|
|
|
2154
|
|
|
|
|
|
|
=cut |
2155
|
5
|
|
|
5
|
1
|
6
|
sub podFromFile { |
2156
|
5
|
|
|
|
|
18
|
my ($file) = Devel::PerlySense::Util::aNamedArg(["file"], @_); |
2157
|
|
|
|
|
|
|
|
2158
|
5
|
50
|
|
|
|
285
|
open(my $fhIn, "<", $file) or die("Could not open file ($file): $!\n"); |
2159
|
|
|
|
|
|
|
|
2160
|
5
|
|
|
|
|
10
|
my $textPod = ""; |
2161
|
5
|
|
|
|
|
39
|
my $fhOut = IO::String->new($textPod); |
2162
|
5
|
|
|
|
|
249
|
Pod::Text->new()->parse_from_filehandle($fhIn, $fhOut); |
2163
|
|
|
|
|
|
|
|
2164
|
5
|
|
|
|
|
208158
|
return($textPod); |
2165
|
|
|
|
|
|
|
} |
2166
|
|
|
|
|
|
|
|
2167
|
|
|
|
|
|
|
|
2168
|
|
|
|
|
|
|
|
2169
|
|
|
|
|
|
|
|
2170
|
|
|
|
|
|
|
|
2171
|
|
|
|
|
|
|
=head2 oLocationSmartGoTo(file => $fileOrigin, row => $row, col => $row) |
2172
|
|
|
|
|
|
|
|
2173
|
|
|
|
|
|
|
Look in $file at location $row/$col and determine what is |
2174
|
|
|
|
|
|
|
there. Depending on what's there, find the source |
2175
|
|
|
|
|
|
|
declaration/whatever, find it and return an |
2176
|
|
|
|
|
|
|
Devel::PerlySense::Document::Location object. |
2177
|
|
|
|
|
|
|
|
2178
|
|
|
|
|
|
|
Currently supported: |
2179
|
|
|
|
|
|
|
|
2180
|
|
|
|
|
|
|
$self->method, look in current file and base classes. If no sub can |
2181
|
|
|
|
|
|
|
be found, look for POD. |
2182
|
|
|
|
|
|
|
|
2183
|
|
|
|
|
|
|
shift->method for subs that don't have a $self. Same as |
2184
|
|
|
|
|
|
|
$self->method. |
2185
|
|
|
|
|
|
|
|
2186
|
|
|
|
|
|
|
$object->method, look in current file and used modules. If no sub |
2187
|
|
|
|
|
|
|
can be found, look for POD. |
2188
|
|
|
|
|
|
|
|
2189
|
|
|
|
|
|
|
Module::Name (bareword) |
2190
|
|
|
|
|
|
|
|
2191
|
|
|
|
|
|
|
Module::Name (as the only contents of a string literal) |
2192
|
|
|
|
|
|
|
|
2193
|
|
|
|
|
|
|
If there's nothing at $row/col, or if the source can't be found, |
2194
|
|
|
|
|
|
|
return undef. |
2195
|
|
|
|
|
|
|
|
2196
|
|
|
|
|
|
|
Die if $file doesn't exist, or on other errors. |
2197
|
|
|
|
|
|
|
|
2198
|
|
|
|
|
|
|
=cut |
2199
|
16
|
|
|
16
|
1
|
8354
|
sub oLocationSmartGoTo { |
2200
|
16
|
|
|
|
|
65
|
my ($file, $row, $col) = Devel::PerlySense::Util::aNamedArg(["file", "row", "col"], @_); |
2201
|
16
|
|
|
|
|
90
|
debug("oLocationSmartGoTo file($file) row($row) col($col)"); |
2202
|
|
|
|
|
|
|
|
2203
|
16
|
|
|
|
|
48
|
my $oDocument = $self->oDocumentParseFile($file); |
2204
|
|
|
|
|
|
|
|
2205
|
|
|
|
|
|
|
{ |
2206
|
16
|
100
|
|
|
|
22
|
if(my $method = $oDocument->selfMethodCallAt(row => $row, col => $col)) { |
|
16
|
|
|
|
|
64
|
|
2207
|
3
|
|
|
|
|
51
|
my $oLocation = $oDocument->oLocationSubDefinition(row => $row, name => $method); |
2208
|
3
|
50
|
|
|
|
25
|
$oLocation and return($oLocation); |
2209
|
|
|
|
|
|
|
} |
2210
|
|
|
|
|
|
|
} |
2211
|
|
|
|
|
|
|
|
2212
|
13
|
|
|
|
|
48
|
my ($module, $method) = $oDocument->moduleMethodCallAt(row => $row, col => $col); |
2213
|
13
|
100
|
66
|
|
|
78
|
if($module && $method) { |
2214
|
3
|
100
|
|
|
|
147
|
if(my $oDocumentDest = $self->oDocumentFindModule(nameModule => $module, dirOrigin => dirname($file))) { |
2215
|
2
|
|
|
|
|
9
|
my $oLocation = $oDocumentDest->oLocationSubDefinition(row => $row, name => $method); |
2216
|
2
|
50
|
|
|
|
21
|
$oLocation and return($oLocation); |
2217
|
|
|
|
|
|
|
} |
2218
|
|
|
|
|
|
|
} |
2219
|
|
|
|
|
|
|
|
2220
|
|
|
|
|
|
|
|
2221
|
11
|
|
|
|
|
49
|
my ($oObject, $oMethod, $oLocationSub) = $oDocument->aObjectMethodCallAt(row => $row, col => $col); |
2222
|
11
|
50
|
66
|
|
|
64
|
if($oObject && $oMethod && $oLocationSub) { |
|
|
|
66
|
|
|
|
|
2223
|
4
|
|
|
|
|
11
|
debug("Looking for $oObject->$oMethod"); |
2224
|
4
|
|
|
|
|
11
|
my @aMethodCall = $oDocument->aMethodCallOf( |
2225
|
|
|
|
|
|
|
nameObject => "$oObject", |
2226
|
|
|
|
|
|
|
oLocationWithin => $oLocationSub, |
2227
|
|
|
|
|
|
|
); |
2228
|
4
|
|
|
|
|
18
|
my @aNameModuleUse = $oDocument->aNameModuleUse(); #Add all known modules, not just the ones explicitly stated |
2229
|
4
|
|
|
|
|
15
|
my @aDocumentDest = $self->aDocumentFindModuleWithInterface( |
2230
|
|
|
|
|
|
|
raNameModule => \@aNameModuleUse, |
2231
|
|
|
|
|
|
|
raMethodRequired => [ "$oMethod" ] , |
2232
|
|
|
|
|
|
|
raMethodNice => \@aMethodCall, |
2233
|
|
|
|
|
|
|
dirOrigin => dirname($file), |
2234
|
|
|
|
|
|
|
); |
2235
|
4
|
50
|
|
|
|
14
|
if(@aDocumentDest) { |
2236
|
4
|
|
|
|
|
8
|
debug("Possible matching modules:\n" . join("\n", map { " * $_" } map { @{$_->oMeta->raPackage} } @aDocumentDest)); |
|
4
|
|
|
|
|
92
|
|
|
4
|
|
|
|
|
18
|
|
|
4
|
|
|
|
|
60
|
|
2237
|
4
|
|
|
|
|
15
|
my $oLocation = $aDocumentDest[0]->oLocationSubDefinition( |
2238
|
|
|
|
|
|
|
row => $row, |
2239
|
|
|
|
|
|
|
name => "$oMethod", |
2240
|
|
|
|
|
|
|
); |
2241
|
4
|
50
|
|
|
|
42
|
$oLocation and return($oLocation); |
2242
|
|
|
|
|
|
|
} |
2243
|
|
|
|
|
|
|
} |
2244
|
|
|
|
|
|
|
|
2245
|
|
|
|
|
|
|
|
2246
|
7
|
100
|
|
|
|
26
|
if(my $module = $oDocument->moduleAt(row => $row, col => $col)) { |
2247
|
3
|
50
|
|
|
|
140
|
my $file = $self->fileFindModule(nameModule => $module, dirOrigin => dirname($file)) |
2248
|
|
|
|
|
|
|
or return(undef); |
2249
|
|
|
|
|
|
|
|
2250
|
3
|
|
|
|
|
257
|
my $oLocation = Devel::PerlySense::Document::Location->new(file => $file, row => 1, col => 1); |
2251
|
3
|
|
|
|
|
75
|
return($oLocation); |
2252
|
|
|
|
|
|
|
} |
2253
|
|
|
|
|
|
|
|
2254
|
4
|
|
|
|
|
29
|
return(undef); |
2255
|
|
|
|
|
|
|
} |
2256
|
|
|
|
|
|
|
|
2257
|
|
|
|
|
|
|
|
2258
|
|
|
|
|
|
|
|
2259
|
|
|
|
|
|
|
|
2260
|
|
|
|
|
|
|
|
2261
|
|
|
|
|
|
|
=head2 oLocationSmartDoc(file => $fileOrigin, row => $row, col => $row) |
2262
|
|
|
|
|
|
|
|
2263
|
|
|
|
|
|
|
Look in $file at location $row/$col and determine what is |
2264
|
|
|
|
|
|
|
there. Depending on what's there, find the documentation for it and |
2265
|
|
|
|
|
|
|
return a Document::Location object with the following rhProperty keys set: |
2266
|
|
|
|
|
|
|
|
2267
|
|
|
|
|
|
|
text - the docs text |
2268
|
|
|
|
|
|
|
found - "method" | "module" |
2269
|
|
|
|
|
|
|
docType - "hint" | "document" |
2270
|
|
|
|
|
|
|
name - the name of the thing found |
2271
|
|
|
|
|
|
|
|
2272
|
|
|
|
|
|
|
|
2273
|
|
|
|
|
|
|
Currently supported: |
2274
|
|
|
|
|
|
|
|
2275
|
|
|
|
|
|
|
Same as for oLocationSmartGoTo |
2276
|
|
|
|
|
|
|
|
2277
|
|
|
|
|
|
|
If there's nothing at $row/col, use the current document. |
2278
|
|
|
|
|
|
|
|
2279
|
|
|
|
|
|
|
Die if $file doesn't exist, or on other errors. |
2280
|
|
|
|
|
|
|
|
2281
|
|
|
|
|
|
|
=cut |
2282
|
|
|
|
|
|
|
#Rework this so it can deal with HTML output as well |
2283
|
9
|
|
|
9
|
1
|
14641
|
sub oLocationSmartDoc { |
2284
|
9
|
|
|
|
|
46
|
my ($file, $row, $col) = Devel::PerlySense::Util::aNamedArg(["file", "row", "col"], @_); |
2285
|
|
|
|
|
|
|
|
2286
|
9
|
|
|
|
|
34
|
my $oDocument = $self->oDocumentParseFile($file); |
2287
|
|
|
|
|
|
|
|
2288
|
|
|
|
|
|
|
#$self->method |
2289
|
9
|
100
|
|
|
|
44
|
if(my $method = $oDocument->selfMethodCallAt(row => $row, col => $col)) { |
2290
|
2
|
|
|
|
|
41
|
return( $self->oLocationMethodDocFromDocument($oDocument, $method) ); |
2291
|
|
|
|
|
|
|
} |
2292
|
|
|
|
|
|
|
|
2293
|
|
|
|
|
|
|
#Module::Name->method |
2294
|
7
|
|
|
|
|
28
|
my ($module, $method) = $oDocument->moduleMethodCallAt(row => $row, col => $col); |
2295
|
7
|
100
|
66
|
|
|
53
|
if($module && $method) { |
2296
|
2
|
50
|
|
|
|
107
|
if(my $oDocumentDest = $self->oDocumentFindModule(nameModule => $module, dirOrigin => dirname($file))) { |
2297
|
2
|
|
|
|
|
11
|
return( $self->oLocationMethodDocFromDocument($oDocumentDest, $method) ); |
2298
|
|
|
|
|
|
|
} |
2299
|
|
|
|
|
|
|
} |
2300
|
|
|
|
|
|
|
|
2301
|
|
|
|
|
|
|
#$object->method |
2302
|
5
|
|
|
|
|
21
|
my ($oObject, $oMethod, $oLocationSub) = $oDocument->aObjectMethodCallAt(row => $row, col => $col); |
2303
|
5
|
50
|
66
|
|
|
20
|
if($oObject && $oMethod && $oLocationSub) { |
|
|
|
66
|
|
|
|
|
2304
|
1
|
|
|
|
|
3
|
my @aMethodCall = $oDocument->aMethodCallOf(nameObject => "$oObject", oLocationWithin => $oLocationSub); |
2305
|
1
|
|
|
|
|
5
|
my @aNameModuleUse = $oDocument->aNameModuleUse(); |
2306
|
1
|
|
|
|
|
4
|
my @aDocumentDest = $self->aDocumentFindModuleWithInterface(raNameModule => \@aNameModuleUse, raMethodRequired => [ "$oMethod" ] , raMethodNice => \@aMethodCall, dirOrigin => dirname($file)); |
2307
|
1
|
50
|
|
|
|
9
|
if(@aDocumentDest) { |
2308
|
|
|
|
|
|
|
###TODO: report all possible methods, and let the user chose from them in the editor |
2309
|
1
|
|
|
|
|
5
|
return( $self->oLocationMethodDocFromDocument($aDocumentDest[0], "$oMethod") ); |
2310
|
|
|
|
|
|
|
} |
2311
|
|
|
|
|
|
|
} |
2312
|
|
|
|
|
|
|
|
2313
|
|
|
|
|
|
|
#Module::Name |
2314
|
4
|
100
|
|
|
|
17
|
if(my $module = $oDocument->moduleAt(row => $row, col => $col)) { |
2315
|
1
|
50
|
|
|
|
48
|
my $file = $self->fileFindModule(nameModule => $module, dirOrigin => dirname($file)) |
2316
|
|
|
|
|
|
|
or return(undef); |
2317
|
|
|
|
|
|
|
|
2318
|
1
|
|
|
|
|
128
|
my $oLocation = Devel::PerlySense::Document::Location->new(file => $file, row => 1, col => 1); |
2319
|
1
|
|
|
|
|
13
|
$oLocation->rhProperty->{found} = "module"; |
2320
|
1
|
|
|
|
|
17
|
$oLocation->rhProperty->{docType} = "document"; |
2321
|
1
|
|
|
|
|
17
|
$oLocation->rhProperty->{name} = "$module"; |
2322
|
1
|
50
|
|
|
|
7
|
$oLocation->rhProperty->{text} = $self->podFromFile(file => $file) or return(undef); |
2323
|
1
|
|
|
|
|
53
|
return($oLocation); |
2324
|
|
|
|
|
|
|
} |
2325
|
|
|
|
|
|
|
|
2326
|
|
|
|
|
|
|
#Fail to docs about this current file |
2327
|
3
|
50
|
|
|
|
12
|
if($oDocument->isEmptyAt(row => $row, col => $col)) { |
2328
|
3
|
|
|
|
|
10
|
my $oLocation = Devel::PerlySense::Document::Location->new(file => $file, row => 1, col => 1); |
2329
|
3
|
|
|
|
|
35
|
$oLocation->rhProperty->{found} = "module"; |
2330
|
3
|
|
|
|
|
46
|
$oLocation->rhProperty->{docType} = "document"; |
2331
|
3
|
|
|
|
|
22
|
$oLocation->rhProperty->{name} = $oDocument->packageAt(row => $row); |
2332
|
3
|
50
|
|
|
|
99
|
$oLocation->rhProperty->{text} = $self->podFromFile(file => $file) or return(undef); |
2333
|
3
|
|
|
|
|
241
|
return($oLocation); |
2334
|
|
|
|
|
|
|
} |
2335
|
|
|
|
|
|
|
|
2336
|
0
|
|
|
|
|
0
|
return(undef); |
2337
|
|
|
|
|
|
|
} |
2338
|
|
|
|
|
|
|
|
2339
|
|
|
|
|
|
|
|
2340
|
|
|
|
|
|
|
|
2341
|
|
|
|
|
|
|
|
2342
|
|
|
|
|
|
|
|
2343
|
|
|
|
|
|
|
=head2 oLocationMethodDocFromDocument($oDocument, $method) |
2344
|
|
|
|
|
|
|
|
2345
|
|
|
|
|
|
|
Look in $oDocument and find the documentation for it and |
2346
|
|
|
|
|
|
|
return a Document::Location object with the following rhProperty keys set: |
2347
|
|
|
|
|
|
|
|
2348
|
|
|
|
|
|
|
text - the docs text |
2349
|
|
|
|
|
|
|
found - "method" | "module" |
2350
|
|
|
|
|
|
|
docType - "hint" | "document" |
2351
|
|
|
|
|
|
|
name - the name of the thing found |
2352
|
|
|
|
|
|
|
|
2353
|
|
|
|
|
|
|
If possible, also set "pod" and "podHeading". |
2354
|
|
|
|
|
|
|
|
2355
|
|
|
|
|
|
|
Return undef if no doc could be found. |
2356
|
|
|
|
|
|
|
|
2357
|
|
|
|
|
|
|
Currently, only POD is regarded as documentation. Todo: fail to |
2358
|
|
|
|
|
|
|
listing an example/abstracted invocation of the method. |
2359
|
|
|
|
|
|
|
|
2360
|
|
|
|
|
|
|
Die on errors. |
2361
|
|
|
|
|
|
|
|
2362
|
|
|
|
|
|
|
=cut |
2363
|
84
|
|
|
84
|
1
|
2981
|
sub oLocationMethodDocFromDocument { |
2364
|
84
|
|
|
|
|
115
|
my ($oDocument, $method) = @_; |
2365
|
84
|
|
|
|
|
246
|
my $oLocation = $oDocument->oLocationPod(name => $method, lookFor => "method"); |
2366
|
84
|
|
|
|
|
379
|
return( $self->oLocationRenderPodToText($oLocation) ); |
2367
|
|
|
|
|
|
|
} |
2368
|
|
|
|
|
|
|
|
2369
|
|
|
|
|
|
|
|
2370
|
|
|
|
|
|
|
|
2371
|
|
|
|
|
|
|
|
2372
|
|
|
|
|
|
|
|
2373
|
|
|
|
|
|
|
=head2 oLocationMethodDefinitionFromDocument(oDocument => $oDocument, nameClass => $nameClass, nameMethod => $method) |
2374
|
|
|
|
|
|
|
|
2375
|
|
|
|
|
|
|
Look in $oDocument and find the declaration for $nameMmethod and |
2376
|
|
|
|
|
|
|
return a Document::Location object. |
2377
|
|
|
|
|
|
|
|
2378
|
|
|
|
|
|
|
Return undef if no declaration could be found. |
2379
|
|
|
|
|
|
|
|
2380
|
|
|
|
|
|
|
Die on errors. |
2381
|
|
|
|
|
|
|
|
2382
|
|
|
|
|
|
|
=cut |
2383
|
2
|
|
|
2
|
1
|
37
|
sub oLocationMethodDefinitionFromDocument { |
2384
|
2
|
|
|
|
|
8
|
my ($oDocument, $nameClass, $nameMethod) = Devel::PerlySense::Util::aNamedArg(["oDocument", "nameClass", "nameMethod"], @_); |
2385
|
2
|
|
|
|
|
9
|
my $oLocation = $oDocument->oLocationSubDefinition( |
2386
|
|
|
|
|
|
|
package => $nameClass, |
2387
|
|
|
|
|
|
|
name => $nameMethod, |
2388
|
|
|
|
|
|
|
); |
2389
|
|
|
|
|
|
|
} |
2390
|
|
|
|
|
|
|
|
2391
|
|
|
|
|
|
|
|
2392
|
|
|
|
|
|
|
|
2393
|
|
|
|
|
|
|
|
2394
|
|
|
|
|
|
|
|
2395
|
|
|
|
|
|
|
=head2 rhRegexExample(file => $fileOrigin, row => $row, col => $row) |
2396
|
|
|
|
|
|
|
|
2397
|
|
|
|
|
|
|
Look in $file at location $row/$col and find the regex located there, |
2398
|
|
|
|
|
|
|
and possibly the example comment preceeding it. |
2399
|
|
|
|
|
|
|
|
2400
|
|
|
|
|
|
|
Return hash ref with (keys: regex, example; values: source |
2401
|
|
|
|
|
|
|
string). The source string is an empty string if nothing found. |
2402
|
|
|
|
|
|
|
|
2403
|
|
|
|
|
|
|
If there is an example string in a comment, return the example without |
2404
|
|
|
|
|
|
|
the comment # |
2405
|
|
|
|
|
|
|
|
2406
|
|
|
|
|
|
|
Die if $file doesn't exist, or on other errors. |
2407
|
|
|
|
|
|
|
|
2408
|
|
|
|
|
|
|
=cut |
2409
|
0
|
|
|
0
|
1
|
0
|
sub rhRegexExample { |
2410
|
0
|
|
|
|
|
0
|
my ($file, $row, $col) = Devel::PerlySense::Util::aNamedArg(["file", "row", "col"], @_); |
2411
|
|
|
|
|
|
|
|
2412
|
0
|
|
|
|
|
0
|
my $oDocument = $self->oDocumentParseFile($file); |
2413
|
|
|
|
|
|
|
|
2414
|
0
|
|
|
|
|
0
|
return $oDocument->rhRegexExampleAt(row => $row, col => $col); |
2415
|
|
|
|
|
|
|
} |
2416
|
|
|
|
|
|
|
|
2417
|
|
|
|
|
|
|
|
2418
|
|
|
|
|
|
|
|
2419
|
|
|
|
|
|
|
|
2420
|
|
|
|
|
|
|
|
2421
|
|
|
|
|
|
|
=head2 raFileTestOther(file => $fileSource, [sub => $sub]) |
2422
|
|
|
|
|
|
|
|
2423
|
|
|
|
|
|
|
Return array ref with file names of files related to $file and |
2424
|
|
|
|
|
|
|
possibly $sub, i.e. the "other" files related to $file. |
2425
|
|
|
|
|
|
|
|
2426
|
|
|
|
|
|
|
If $file is a source file, return test files, and vice verca. |
2427
|
|
|
|
|
|
|
|
2428
|
|
|
|
|
|
|
$sub is only ever active when $fileSource is a source file. |
2429
|
|
|
|
|
|
|
|
2430
|
|
|
|
|
|
|
Die if Devel::CoverX::Covered isn't installed. |
2431
|
|
|
|
|
|
|
|
2432
|
|
|
|
|
|
|
=cut |
2433
|
0
|
|
|
0
|
1
|
0
|
sub raFileTestOther { |
2434
|
0
|
|
|
|
|
0
|
my ($file, $sub) = Devel::PerlySense::Util::aNamedArg(["file", "sub"], @_); |
2435
|
0
|
0
|
|
|
|
0
|
$self->setFindProject(file => $file) or die("Could not identify any PerlySense Project\n"); |
2436
|
0
|
|
|
|
|
0
|
return $self->oProject->raFileTestOther(file => $file, sub => $sub); |
2437
|
|
|
|
|
|
|
} |
2438
|
|
|
|
|
|
|
|
2439
|
|
|
|
|
|
|
|
2440
|
|
|
|
|
|
|
|
2441
|
|
|
|
|
|
|
|
2442
|
|
|
|
|
|
|
|
2443
|
|
|
|
|
|
|
=head2 raFileProjectOther(file => $fileSource) |
2444
|
|
|
|
|
|
|
|
2445
|
|
|
|
|
|
|
Return array ref with file names of files related to $file, i.e. the |
2446
|
|
|
|
|
|
|
files corresponding to $file according to the .corresponding_files |
2447
|
|
|
|
|
|
|
config file.. |
2448
|
|
|
|
|
|
|
|
2449
|
|
|
|
|
|
|
Die if there is no config file. |
2450
|
|
|
|
|
|
|
|
2451
|
|
|
|
|
|
|
=cut |
2452
|
0
|
|
|
0
|
1
|
0
|
sub raFileProjectOther { |
2453
|
0
|
|
|
|
|
0
|
my ($file, $sub) = Devel::PerlySense::Util::aNamedArg(["file"], @_); |
2454
|
0
|
0
|
|
|
|
0
|
$self->setFindProject(file => $file) or die("Could not identify any PerlySense Project\n"); |
2455
|
0
|
|
|
|
|
0
|
return $self->oProject->raFileProjectOther(file => $file); |
2456
|
|
|
|
|
|
|
} |
2457
|
|
|
|
|
|
|
|
2458
|
|
|
|
|
|
|
|
2459
|
|
|
|
|
|
|
|
2460
|
|
|
|
|
|
|
|
2461
|
|
|
|
|
|
|
|
2462
|
|
|
|
|
|
|
=head2 rhRunFile(file => $fileSource, [ keyConfigCommand => "command" ]) |
2463
|
|
|
|
|
|
|
|
2464
|
|
|
|
|
|
|
Figure out what type of source file $fileSource is, and how it should |
2465
|
|
|
|
|
|
|
be run. |
2466
|
|
|
|
|
|
|
|
2467
|
|
|
|
|
|
|
The settings in the Project's config->{run_file} is used to determine |
2468
|
|
|
|
|
|
|
the details. |
2469
|
|
|
|
|
|
|
|
2470
|
|
|
|
|
|
|
Return hash ref with (keys: "dir_run_from", "command_run", |
2471
|
|
|
|
|
|
|
"type_source_file"), or die on errors (like if no Project could be |
2472
|
|
|
|
|
|
|
found). |
2473
|
|
|
|
|
|
|
|
2474
|
|
|
|
|
|
|
dir_run_from is an absolute file name which should be the cwd when |
2475
|
|
|
|
|
|
|
command_run is executed. |
2476
|
|
|
|
|
|
|
|
2477
|
|
|
|
|
|
|
type_source_file is something like "Test", "Module". |
2478
|
|
|
|
|
|
|
|
2479
|
|
|
|
|
|
|
=cut |
2480
|
4
|
|
|
4
|
1
|
5
|
sub rhRunFile { |
2481
|
4
|
|
|
|
|
14
|
my ($file) = Devel::PerlySense::Util::aNamedArg(["file"], @_); |
2482
|
|
|
|
|
|
|
|
2483
|
4
|
50
|
|
|
|
10
|
$self->setFindProject(file => $file) |
2484
|
|
|
|
|
|
|
or die("Could not identify any PerlySense Project\n"); |
2485
|
|
|
|
|
|
|
|
2486
|
4
|
|
|
|
|
51
|
return $self->oProject->rhRunFile(@_); |
2487
|
|
|
|
|
|
|
} |
2488
|
|
|
|
|
|
|
|
2489
|
|
|
|
|
|
|
|
2490
|
|
|
|
|
|
|
|
2491
|
|
|
|
|
|
|
|
2492
|
|
|
|
|
|
|
|
2493
|
|
|
|
|
|
|
=head2 rhDebugFile(file => $fileSource, [ keyConfigCommand => "command" ]) |
2494
|
|
|
|
|
|
|
|
2495
|
|
|
|
|
|
|
Figure out what type of source file $fileSource is, and how it should |
2496
|
|
|
|
|
|
|
be debugged. |
2497
|
|
|
|
|
|
|
|
2498
|
|
|
|
|
|
|
The settings in the Project's config->{debug_file} is used to determine |
2499
|
|
|
|
|
|
|
the details. |
2500
|
|
|
|
|
|
|
|
2501
|
|
|
|
|
|
|
Return hash ref with (keys: "dir_debug_from", "command_debug", |
2502
|
|
|
|
|
|
|
"type_source_file"), or die on errors (like if no Project could be |
2503
|
|
|
|
|
|
|
found). |
2504
|
|
|
|
|
|
|
|
2505
|
|
|
|
|
|
|
dir_debug_from is an absolute file name which should be the cwd when |
2506
|
|
|
|
|
|
|
command_debug is executed. |
2507
|
|
|
|
|
|
|
|
2508
|
|
|
|
|
|
|
type_source_file is something like "Test", "Module". |
2509
|
|
|
|
|
|
|
|
2510
|
|
|
|
|
|
|
=cut |
2511
|
1
|
|
|
1
|
1
|
2
|
sub rhDebugFile { |
2512
|
1
|
|
|
|
|
4
|
my ($file) = Devel::PerlySense::Util::aNamedArg(["file"], @_); |
2513
|
|
|
|
|
|
|
|
2514
|
1
|
50
|
|
|
|
4
|
$self->setFindProject(file => $file) |
2515
|
|
|
|
|
|
|
or die("Could not identify any PerlySense Project\n"); |
2516
|
|
|
|
|
|
|
|
2517
|
1
|
|
|
|
|
13
|
return $self->oProject->rhDebugFile(@_); |
2518
|
|
|
|
|
|
|
} |
2519
|
|
|
|
|
|
|
|
2520
|
|
|
|
|
|
|
|
2521
|
|
|
|
|
|
|
|
2522
|
|
|
|
|
|
|
|
2523
|
|
|
|
|
|
|
|
2524
|
|
|
|
|
|
|
=head2 flymakeFile(file => $fileSource) |
2525
|
|
|
|
|
|
|
|
2526
|
|
|
|
|
|
|
Do a flymake run with $fileSource according to the flymake config and |
2527
|
|
|
|
|
|
|
output the result to STDOUT and STDERR. |
2528
|
|
|
|
|
|
|
|
2529
|
|
|
|
|
|
|
=cut |
2530
|
0
|
|
|
0
|
1
|
0
|
sub flymakeFile { |
2531
|
0
|
|
|
|
|
0
|
my ($file) = Devel::PerlySense::Util::aNamedArg(["file"], @_); |
2532
|
|
|
|
|
|
|
|
2533
|
0
|
0
|
|
|
|
0
|
$self->setFindProject(file => $file) |
2534
|
|
|
|
|
|
|
or die("Could not identify any PerlySense Project\n"); |
2535
|
|
|
|
|
|
|
|
2536
|
0
|
|
|
|
|
0
|
return $self->oProject->flymakeFile(file => $file); |
2537
|
|
|
|
|
|
|
} |
2538
|
|
|
|
|
|
|
|
2539
|
|
|
|
|
|
|
|
2540
|
|
|
|
|
|
|
|
2541
|
|
|
|
|
|
|
|
2542
|
|
|
|
|
|
|
|
2543
|
|
|
|
|
|
|
=head2 rhSubCovered(file => $fileSource) |
2544
|
|
|
|
|
|
|
|
2545
|
|
|
|
|
|
|
Do a "covered subs" call with $fileSource in the current project. |
2546
|
|
|
|
|
|
|
|
2547
|
|
|
|
|
|
|
Return hash ref with (keys: sub name; keys: quality). |
2548
|
|
|
|
|
|
|
|
2549
|
|
|
|
|
|
|
=cut |
2550
|
0
|
|
|
0
|
1
|
0
|
sub rhSubCovered { |
2551
|
0
|
|
|
|
|
0
|
my ($file) = Devel::PerlySense::Util::aNamedArg(["file"], @_); |
2552
|
|
|
|
|
|
|
|
2553
|
0
|
0
|
|
|
|
0
|
$self->setFindProject(file => $file) |
2554
|
|
|
|
|
|
|
or die("Could not identify any PerlySense Project\n"); |
2555
|
|
|
|
|
|
|
|
2556
|
0
|
|
|
|
|
0
|
return $self->oProject->rhSubCovered(file => $file); |
2557
|
|
|
|
|
|
|
} |
2558
|
|
|
|
|
|
|
|
2559
|
|
|
|
|
|
|
|
2560
|
|
|
|
|
|
|
|
2561
|
|
|
|
|
|
|
|
2562
|
|
|
|
|
|
|
|
2563
|
|
|
|
|
|
|
=head2 createProject(dir => $dir) |
2564
|
|
|
|
|
|
|
|
2565
|
|
|
|
|
|
|
Create a new PerlySense Project in $dir. |
2566
|
|
|
|
|
|
|
|
2567
|
|
|
|
|
|
|
Return 1 on success, or die on errors. |
2568
|
|
|
|
|
|
|
|
2569
|
|
|
|
|
|
|
=cut |
2570
|
0
|
|
|
0
|
1
|
0
|
sub createProject { |
2571
|
0
|
|
|
|
|
0
|
my ($dir) = Devel::PerlySense::Util::aNamedArg(["dir"], @_); |
2572
|
|
|
|
|
|
|
|
2573
|
0
|
|
|
|
|
0
|
my $oConfig = Devel::PerlySense::Config::Project->new(); |
2574
|
0
|
|
|
|
|
0
|
$oConfig->createFileConfigDefault(dirRoot => $dir); |
2575
|
0
|
|
|
|
|
0
|
$oConfig->createFileCriticDefault(dirRoot => $dir); |
2576
|
|
|
|
|
|
|
|
2577
|
|
|
|
|
|
|
###TODO: assign the config to $self->oConfigProject |
2578
|
|
|
|
|
|
|
|
2579
|
0
|
|
|
|
|
0
|
return(1); |
2580
|
|
|
|
|
|
|
} |
2581
|
|
|
|
|
|
|
|
2582
|
|
|
|
|
|
|
|
2583
|
|
|
|
|
|
|
|
2584
|
|
|
|
|
|
|
|
2585
|
|
|
|
|
|
|
|
2586
|
|
|
|
|
|
|
=head2 classNameAt(file => $fileOrigin, row => $row, col => $row) |
2587
|
|
|
|
|
|
|
|
2588
|
|
|
|
|
|
|
Look in $file at location $row/$col and determine what class name that is. |
2589
|
|
|
|
|
|
|
|
2590
|
|
|
|
|
|
|
Return the class name or "" if it's package main. |
2591
|
|
|
|
|
|
|
|
2592
|
|
|
|
|
|
|
Die if $file doesn't exist, or on other errors. |
2593
|
|
|
|
|
|
|
|
2594
|
|
|
|
|
|
|
=cut |
2595
|
0
|
|
|
0
|
1
|
0
|
sub classNameAt { |
2596
|
0
|
|
|
|
|
0
|
my ($file, $row, $col) = Devel::PerlySense::Util::aNamedArg(["file", "row", "col"], @_); |
2597
|
|
|
|
|
|
|
|
2598
|
0
|
|
|
|
|
0
|
my $oDocument = $self->oDocumentParseFile($file); |
2599
|
|
|
|
|
|
|
|
2600
|
0
|
|
|
|
|
0
|
my $package = $oDocument->packageAt(row => $row); |
2601
|
|
|
|
|
|
|
|
2602
|
0
|
0
|
|
|
|
0
|
$package eq "main" and return ""; |
2603
|
0
|
|
|
|
|
0
|
return($package); |
2604
|
|
|
|
|
|
|
} |
2605
|
|
|
|
|
|
|
|
2606
|
|
|
|
|
|
|
|
2607
|
|
|
|
|
|
|
|
2608
|
|
|
|
|
|
|
|
2609
|
|
|
|
|
|
|
|
2610
|
|
|
|
|
|
|
=head2 classAt(file => $fileOrigin, row => $row, col => $row) |
2611
|
|
|
|
|
|
|
|
2612
|
|
|
|
|
|
|
Look in $file at location $row/$col and determine what |
2613
|
|
|
|
|
|
|
PerlySelse::Class that is. |
2614
|
|
|
|
|
|
|
|
2615
|
|
|
|
|
|
|
Return the Class object or undef if it's package main. |
2616
|
|
|
|
|
|
|
|
2617
|
|
|
|
|
|
|
Die if $file doesn't exist, or on other errors. |
2618
|
|
|
|
|
|
|
|
2619
|
|
|
|
|
|
|
=cut |
2620
|
0
|
|
|
0
|
1
|
0
|
sub classAt { |
2621
|
0
|
|
|
|
|
0
|
my ($file, $row, $col) = Devel::PerlySense::Util::aNamedArg(["file", "row", "col"], @_); |
2622
|
|
|
|
|
|
|
|
2623
|
0
|
|
|
|
|
0
|
return(Devel::PerlySense::Class->newFromFileAt( |
2624
|
|
|
|
|
|
|
oPerlySense => $self, |
2625
|
|
|
|
|
|
|
file => $file, |
2626
|
|
|
|
|
|
|
row => $row, |
2627
|
|
|
|
|
|
|
col => $col, |
2628
|
|
|
|
|
|
|
)); |
2629
|
|
|
|
|
|
|
} |
2630
|
|
|
|
|
|
|
|
2631
|
|
|
|
|
|
|
|
2632
|
|
|
|
|
|
|
|
2633
|
|
|
|
|
|
|
|
2634
|
|
|
|
|
|
|
|
2635
|
|
|
|
|
|
|
=head2 classByName(name => $name, dirOrigin => $dirOrigin) |
2636
|
|
|
|
|
|
|
|
2637
|
|
|
|
|
|
|
Find the file that contains the Class $name, starting at $dirOrigin. |
2638
|
|
|
|
|
|
|
|
2639
|
|
|
|
|
|
|
Return the Class object or undef if it couldn't be found. |
2640
|
|
|
|
|
|
|
|
2641
|
|
|
|
|
|
|
Die on errors. |
2642
|
|
|
|
|
|
|
|
2643
|
|
|
|
|
|
|
=cut |
2644
|
0
|
|
|
0
|
1
|
0
|
sub classByName { |
2645
|
0
|
|
|
|
|
0
|
my ($name, $dirOrigin) = Devel::PerlySense::Util::aNamedArg(["name", "dirOrigin"], @_); |
2646
|
|
|
|
|
|
|
|
2647
|
0
|
0
|
|
|
|
0
|
my $oDocument = $self->oDocumentFindModule( |
2648
|
|
|
|
|
|
|
nameModule => $name, |
2649
|
|
|
|
|
|
|
dirOrigin => $dirOrigin, |
2650
|
|
|
|
|
|
|
) or return undef; |
2651
|
|
|
|
|
|
|
|
2652
|
0
|
|
|
|
|
0
|
return( Devel::PerlySense::Class->new( |
2653
|
|
|
|
|
|
|
oPerlySense => $self, |
2654
|
|
|
|
|
|
|
name => $name, |
2655
|
|
|
|
|
|
|
raDocument => [ $oDocument ], |
2656
|
|
|
|
|
|
|
) ); |
2657
|
|
|
|
|
|
|
} |
2658
|
|
|
|
|
|
|
|
2659
|
|
|
|
|
|
|
|
2660
|
|
|
|
|
|
|
|
2661
|
|
|
|
|
|
|
|
2662
|
|
|
|
|
|
|
|
2663
|
|
|
|
|
|
|
=head2 fileFindModule(nameModule => $nameModule, dirOrigin => $dirOrigin) |
2664
|
|
|
|
|
|
|
|
2665
|
|
|
|
|
|
|
Find the file containing the $nameModule given the $dirOrigin. |
2666
|
|
|
|
|
|
|
|
2667
|
|
|
|
|
|
|
Return the absolute file name, or undef if none could be found. Die on |
2668
|
|
|
|
|
|
|
errors. |
2669
|
|
|
|
|
|
|
|
2670
|
|
|
|
|
|
|
=cut |
2671
|
716
|
|
|
716
|
1
|
29009
|
sub fileFindModule { |
2672
|
716
|
|
|
|
|
2245
|
my ($nameModule, $dirOrigin) = Devel::PerlySense::Util::aNamedArg(["nameModule", "dirOrigin"], @_); |
2673
|
|
|
|
|
|
|
|
2674
|
|
|
|
|
|
|
# TODO: Move this into fileFindLookingInInc and pass in the dir |
2675
|
715
|
|
|
|
|
2562
|
$self->setFindProject(dir => $dirOrigin); |
2676
|
|
|
|
|
|
|
|
2677
|
|
|
|
|
|
|
#my $tt = Devel::TimeThis->new("fileFindModule"); |
2678
|
715
|
|
|
|
|
2088
|
my $fileModuleBase = $self->fileFromModule($nameModule); |
2679
|
715
|
|
|
|
|
47430
|
$dirOrigin = dir($dirOrigin)->absolute; |
2680
|
|
|
|
|
|
|
|
2681
|
|
|
|
|
|
|
return( |
2682
|
715
|
|
100
|
|
|
64089
|
$self->fileFindLookingAround($fileModuleBase, $dirOrigin, $nameModule) || |
2683
|
|
|
|
|
|
|
$self->fileFindLookingInInc($fileModuleBase) || |
2684
|
|
|
|
|
|
|
undef |
2685
|
|
|
|
|
|
|
); |
2686
|
|
|
|
|
|
|
} |
2687
|
|
|
|
|
|
|
|
2688
|
|
|
|
|
|
|
|
2689
|
|
|
|
|
|
|
|
2690
|
|
|
|
|
|
|
|
2691
|
|
|
|
|
|
|
|
2692
|
|
|
|
|
|
|
=head2 oDocumentFindModule(nameModule => $nameModule, dirOrigin => $dirOrigin) |
2693
|
|
|
|
|
|
|
|
2694
|
|
|
|
|
|
|
Find the file containing the $nameModule given the $dirOrigin. |
2695
|
|
|
|
|
|
|
|
2696
|
|
|
|
|
|
|
Return a parsed PerlySense::Document, or undef if none could be |
2697
|
|
|
|
|
|
|
found. Die on errors. |
2698
|
|
|
|
|
|
|
|
2699
|
|
|
|
|
|
|
=cut |
2700
|
177
|
|
|
177
|
1
|
7965
|
sub oDocumentFindModule { |
2701
|
177
|
|
|
|
|
702
|
my ($nameModule, $dirOrigin) = Devel::PerlySense::Util::aNamedArg(["nameModule", "dirOrigin"], @_); |
2702
|
|
|
|
|
|
|
|
2703
|
177
|
100
|
|
|
|
653
|
my $fileModule = $self->fileFindModule( |
2704
|
|
|
|
|
|
|
nameModule => $nameModule, |
2705
|
|
|
|
|
|
|
dirOrigin => $dirOrigin, |
2706
|
|
|
|
|
|
|
) or return(undef); |
2707
|
|
|
|
|
|
|
|
2708
|
175
|
50
|
|
|
|
20473
|
my $oDocument = $self->oDocumentParseFile($fileModule) or return(undef); |
2709
|
|
|
|
|
|
|
|
2710
|
175
|
|
|
|
|
882
|
return($oDocument); |
2711
|
|
|
|
|
|
|
} |
2712
|
|
|
|
|
|
|
|
2713
|
|
|
|
|
|
|
|
2714
|
|
|
|
|
|
|
|
2715
|
|
|
|
|
|
|
|
2716
|
|
|
|
|
|
|
|
2717
|
|
|
|
|
|
|
=head2 isFileInProject(file => $fileSource, fileProjectOf => $fileProjectOf) |
2718
|
|
|
|
|
|
|
|
2719
|
|
|
|
|
|
|
Determine whether $fileSource is located within the current Project. |
2720
|
|
|
|
|
|
|
|
2721
|
|
|
|
|
|
|
If there is no current Project, figure it out using $fileProjectOf |
2722
|
|
|
|
|
|
|
(that file should be located in the current project). |
2723
|
|
|
|
|
|
|
|
2724
|
|
|
|
|
|
|
Return true if $fileSource is in the project, else false. Die on |
2725
|
|
|
|
|
|
|
errors. |
2726
|
|
|
|
|
|
|
|
2727
|
|
|
|
|
|
|
=cut |
2728
|
2
|
|
|
2
|
1
|
3
|
sub isFileInProject { |
2729
|
2
|
|
|
|
|
6
|
my ($file, $fileProjectOf) = Devel::PerlySense::Util::aNamedArg(["file", "fileProjectOf"], @_); |
2730
|
|
|
|
|
|
|
|
2731
|
2
|
50
|
|
|
|
7
|
$self->setFindProject(file => $fileProjectOf) |
2732
|
|
|
|
|
|
|
or die("Could not identify any PerlySense Project\n"); |
2733
|
|
|
|
|
|
|
|
2734
|
2
|
|
|
|
|
26
|
return $self->oProject->isFileInProject(file => $file); |
2735
|
|
|
|
|
|
|
} |
2736
|
|
|
|
|
|
|
|
2737
|
|
|
|
|
|
|
|
2738
|
|
|
|
|
|
|
|
2739
|
|
|
|
|
|
|
|
2740
|
|
|
|
|
|
|
|
2741
|
|
|
|
|
|
|
=head2 raCallSiteForMethod(method => $nameMethod, dirOrigin => $dirOrigin) |
2742
|
|
|
|
|
|
|
|
2743
|
|
|
|
|
|
|
Find callers of $nameMethod in $dirOrigin. |
2744
|
|
|
|
|
|
|
|
2745
|
|
|
|
|
|
|
Return array ref of call sites. |
2746
|
|
|
|
|
|
|
|
2747
|
|
|
|
|
|
|
=cut |
2748
|
0
|
|
|
0
|
1
|
0
|
sub raCallSiteForMethod { |
2749
|
0
|
|
|
|
|
0
|
my ($nameMethod, $dirOrigin) = Devel::PerlySense::Util::aNamedArg(["nameMethod", "dirOrigin"], @_); |
2750
|
|
|
|
|
|
|
|
2751
|
0
|
|
|
|
|
0
|
$self->setFindProject(dir => $dirOrigin); |
2752
|
|
|
|
|
|
|
|
2753
|
0
|
|
|
|
|
0
|
my @aMatch; |
2754
|
|
|
|
|
|
|
my %hSeen; |
2755
|
0
|
|
|
|
|
0
|
for my $file ( $self->oProject->aFileSourceCode ) { |
2756
|
0
|
|
|
|
|
0
|
my $source = slurp($file); |
2757
|
|
|
|
|
|
|
|
2758
|
0
|
|
|
|
|
0
|
my $row = 0; |
2759
|
0
|
|
|
|
|
0
|
my $oDocument; |
2760
|
0
|
|
|
|
|
0
|
for my $line (split("\n", $source)) { |
2761
|
0
|
|
|
|
|
0
|
$row++; |
2762
|
0
|
0
|
|
|
|
0
|
$line =~ m/ -> \s* $nameMethod \b /x or next; |
2763
|
0
|
0
|
|
|
|
0
|
$line =~ m/ ^ \s* \# /x and next; # No comments |
2764
|
0
|
0
|
0
|
|
|
0
|
$oDocument ||= $self->oDocumentParseFile($file) or last; |
2765
|
|
|
|
|
|
|
|
2766
|
0
|
0
|
|
|
|
0
|
my $oLocationSub = $oDocument->oLocationSubAt( |
2767
|
|
|
|
|
|
|
row => $row, |
2768
|
|
|
|
|
|
|
col => 1, |
2769
|
|
|
|
|
|
|
) or next; |
2770
|
|
|
|
|
|
|
|
2771
|
0
|
|
|
|
|
0
|
my $rhPropertySub = $oLocationSub->rhProperty; |
2772
|
0
|
|
|
|
|
0
|
my $namePackage = $rhPropertySub->{namePackage}; |
2773
|
0
|
|
|
|
|
0
|
my $nameSub = $rhPropertySub->{nameSub}; |
2774
|
|
|
|
|
|
|
|
2775
|
0
|
0
|
|
|
|
0
|
$hSeen{ "$namePackage->$nameSub" }++ and next; |
2776
|
|
|
|
|
|
|
|
2777
|
0
|
|
|
|
|
0
|
push( |
2778
|
|
|
|
|
|
|
@aMatch, |
2779
|
|
|
|
|
|
|
{ |
2780
|
|
|
|
|
|
|
file => $file, |
2781
|
|
|
|
|
|
|
package => $namePackage, |
2782
|
|
|
|
|
|
|
method => $nameSub, |
2783
|
|
|
|
|
|
|
}, |
2784
|
|
|
|
|
|
|
); |
2785
|
|
|
|
|
|
|
} |
2786
|
|
|
|
|
|
|
} |
2787
|
|
|
|
|
|
|
|
2788
|
|
|
|
|
|
|
###JPL: make any file names relative to project |
2789
|
|
|
|
|
|
|
|
2790
|
0
|
|
|
|
|
0
|
return \@aMatch; |
2791
|
|
|
|
|
|
|
} |
2792
|
|
|
|
|
|
|
|
2793
|
|
|
|
|
|
|
|
2794
|
|
|
|
|
|
|
|
2795
|
|
|
|
|
|
|
|
2796
|
|
|
|
|
|
|
|
2797
|
|
|
|
|
|
|
=head2 rhFileCallerVisualized(source => $source) |
2798
|
|
|
|
|
|
|
|
2799
|
|
|
|
|
|
|
Extract call tree from $source and render it into a .dot and .png |
2800
|
|
|
|
|
|
|
file. |
2801
|
|
|
|
|
|
|
|
2802
|
|
|
|
|
|
|
Return hash ref with (keys: "dot", "image"; values: file names). |
2803
|
|
|
|
|
|
|
|
2804
|
|
|
|
|
|
|
Die if there is no "dot" binary to run. |
2805
|
|
|
|
|
|
|
|
2806
|
|
|
|
|
|
|
=cut |
2807
|
0
|
|
|
0
|
1
|
0
|
sub rhFileCallerVisualized { |
2808
|
0
|
|
|
|
|
0
|
my ($source) = Devel::PerlySense::Util::aNamedArg(["source"], @_); |
2809
|
|
|
|
|
|
|
|
2810
|
|
|
|
|
|
|
# TODO: extract |
2811
|
0
|
|
|
|
|
0
|
my $dirTemp = path("~/.PerlySense/temp"); |
2812
|
0
|
|
|
|
|
0
|
my $dirTempCallTree = path($dirTemp, "call_tree"); |
2813
|
|
|
|
|
|
|
|
2814
|
0
|
|
|
|
|
0
|
my $treeCallers = Devel::PerlySense::CallTree->new(source => $source); |
2815
|
0
|
|
|
|
|
0
|
my $graph = Devel::PerlySense::CallTree::Graph->new({ |
2816
|
|
|
|
|
|
|
call_tree => $treeCallers, |
2817
|
|
|
|
|
|
|
output_dir => $dirTempCallTree, |
2818
|
|
|
|
|
|
|
}); |
2819
|
0
|
|
|
|
|
0
|
$graph->create_graph(); |
2820
|
|
|
|
|
|
|
|
2821
|
|
|
|
|
|
|
return { |
2822
|
0
|
|
|
|
|
0
|
dot => $graph->dot_file . "", |
2823
|
|
|
|
|
|
|
image => $graph->output_file . "", |
2824
|
|
|
|
|
|
|
}; |
2825
|
|
|
|
|
|
|
} |
2826
|
|
|
|
|
|
|
|
2827
|
|
|
|
|
|
|
|
2828
|
|
|
|
|
|
|
|
2829
|
|
|
|
|
|
|
|
2830
|
|
|
|
|
|
|
=head1 IMPLEMENTATION METHODS |
2831
|
|
|
|
|
|
|
|
2832
|
|
|
|
|
|
|
=head2 fileFindLookingAround($fileModuleBase, $dirOrigin, $nameModule?) |
2833
|
|
|
|
|
|
|
|
2834
|
|
|
|
|
|
|
Find the file containing the $fileModuleBase given the $dirOrigin. If |
2835
|
|
|
|
|
|
|
$nameModule is specified, the file must either be in the inc_dir, or |
2836
|
|
|
|
|
|
|
contain a package declaration for $nameModule. |
2837
|
|
|
|
|
|
|
|
2838
|
|
|
|
|
|
|
Return the file name relative to $dirOrigin, or undef if none could be |
2839
|
|
|
|
|
|
|
found. Die on errors. |
2840
|
|
|
|
|
|
|
|
2841
|
|
|
|
|
|
|
=cut |
2842
|
766
|
|
|
766
|
1
|
1142
|
sub fileFindLookingAround { |
2843
|
766
|
|
|
|
|
1087
|
my ($fileModuleBase, $dirOrigin, $nameModule) = @_; |
2844
|
|
|
|
|
|
|
|
2845
|
766
|
|
|
|
|
13695
|
my @aDirIncProject = map { dir($_)->absolute . "" } |
|
1532
|
|
|
|
|
67466
|
|
2846
|
|
|
|
|
|
|
$self->oProject->aDirIncProject( |
2847
|
|
|
|
|
|
|
dirRelativeTo => $self->oProject->dirProject, |
2848
|
|
|
|
|
|
|
); |
2849
|
|
|
|
|
|
|
|
2850
|
766
|
|
|
|
|
60985
|
my $dir = dir($dirOrigin); |
2851
|
766
|
|
|
|
|
14700
|
while(1) { |
2852
|
5495
|
|
|
|
|
57173
|
for my $dirCur (map { dir($dir, $_) } qw/. bin lib/) { |
|
16485
|
|
|
|
|
230195
|
|
2853
|
16041
|
100
|
|
|
|
123486
|
if(my $fileFound = $self->fileFoundInDir($dirCur, $fileModuleBase)) { |
2854
|
|
|
|
|
|
|
# is it in a local inc_dir? |
2855
|
224
|
50
|
|
448
|
|
20019
|
if( first { $_ eq $dir } @aDirIncProject) { |
|
448
|
|
|
|
|
3105
|
|
2856
|
0
|
|
|
|
|
0
|
return(file($fileFound)->absolute . ""); |
2857
|
|
|
|
|
|
|
} |
2858
|
|
|
|
|
|
|
|
2859
|
|
|
|
|
|
|
# Are we expecting a module name? If not, it's a match. |
2860
|
224
|
100
|
|
|
|
3095
|
$nameModule or return(file($fileFound)->absolute . ""); |
2861
|
|
|
|
|
|
|
|
2862
|
|
|
|
|
|
|
|
2863
|
|
|
|
|
|
|
# Check for the dir above the file, is there a package |
2864
|
|
|
|
|
|
|
# name like that in the file? If so, this one isn't |
2865
|
|
|
|
|
|
|
# it. |
2866
|
|
|
|
|
|
|
# If I do this, the next one might not even be needed |
2867
|
|
|
|
|
|
|
|
2868
|
|
|
|
|
|
|
|
2869
|
|
|
|
|
|
|
# Does the file contain a Package declaration for the |
2870
|
|
|
|
|
|
|
# module name? This is a manual and cheap workaround |
2871
|
|
|
|
|
|
|
# to avoid recursive and slow parse |
2872
|
173
|
|
|
|
|
481
|
my $textFile = file($fileFound)->slurp(); |
2873
|
173
|
100
|
|
|
|
55140
|
if($textFile =~ m| |
2874
|
|
|
|
|
|
|
package # package declaration |
2875
|
|
|
|
|
|
|
\s+ |
2876
|
|
|
|
|
|
|
[^;]*? # up until until the next |
2877
|
|
|
|
|
|
|
# statement separator (fragile, |
2878
|
|
|
|
|
|
|
# could well be in comments or a |
2879
|
|
|
|
|
|
|
# block) |
2880
|
|
|
|
|
|
|
(?<!::) # Not preceeded by a module |
2881
|
|
|
|
|
|
|
# separator, i.e. it's not a |
2882
|
|
|
|
|
|
|
# module shadowing the shorter |
2883
|
|
|
|
|
|
|
# name |
2884
|
|
|
|
|
|
|
$nameModule |
2885
|
|
|
|
|
|
|
\b |
2886
|
|
|
|
|
|
|
(?!::) # Not followed by a module |
2887
|
|
|
|
|
|
|
# separator, i.e. it's not a |
2888
|
|
|
|
|
|
|
# longer, other module |
2889
|
|
|
|
|
|
|
|xsm) { |
2890
|
|
|
|
|
|
|
###TODO: possibly check using parse here, now that |
2891
|
|
|
|
|
|
|
###we know the package name is in there. |
2892
|
172
|
|
|
|
|
690
|
return(file($fileFound)->absolute . ""); |
2893
|
|
|
|
|
|
|
} |
2894
|
|
|
|
|
|
|
} |
2895
|
|
|
|
|
|
|
} |
2896
|
|
|
|
|
|
|
|
2897
|
5272
|
|
|
|
|
21303
|
$dir = $dir->parent; |
2898
|
5272
|
100
|
|
|
|
226815
|
$dir =~ m{^( / | \\ | \w: \\ )$}x and last; #At the root? Unix/Win32. What filesystems are missing? |
2899
|
|
|
|
|
|
|
} |
2900
|
|
|
|
|
|
|
|
2901
|
543
|
|
|
|
|
8987
|
return(undef); |
2902
|
|
|
|
|
|
|
} |
2903
|
|
|
|
|
|
|
|
2904
|
|
|
|
|
|
|
|
2905
|
|
|
|
|
|
|
|
2906
|
|
|
|
|
|
|
|
2907
|
|
|
|
|
|
|
|
2908
|
|
|
|
|
|
|
=head2 dirFindLookingAround($fileModuleBase, $dirOrigin, [$raDirSub = [".", "lib", "bin"]]) |
2909
|
|
|
|
|
|
|
|
2910
|
|
|
|
|
|
|
Find the dir containing the $fileModuleBase (relative file path) given |
2911
|
|
|
|
|
|
|
the $dirOrigin. For all directories, also look in subdirectories in |
2912
|
|
|
|
|
|
|
$raDirSub. |
2913
|
|
|
|
|
|
|
|
2914
|
|
|
|
|
|
|
Return the absolute dir name, or undef if none could be found. Die on |
2915
|
|
|
|
|
|
|
errors. |
2916
|
|
|
|
|
|
|
|
2917
|
|
|
|
|
|
|
=cut |
2918
|
|
|
|
|
|
|
###TODO: remove duplication |
2919
|
59
|
|
|
59
|
1
|
144
|
sub dirFindLookingAround { |
2920
|
59
|
|
|
|
|
294
|
my ($fileModuleBase, $dirOrigin, $raDirSub) = @_; |
2921
|
59
|
|
100
|
|
|
182
|
$raDirSub ||= [".", "lib", "bin"]; |
2922
|
|
|
|
|
|
|
|
2923
|
59
|
|
|
|
|
192
|
my $dir = dir($dirOrigin); |
2924
|
59
|
|
|
|
|
1683
|
while(1) { |
2925
|
457
|
|
|
|
|
5012
|
for my $dirCur (map { dir($dir, $_) } @$raDirSub) { |
|
465
|
|
|
|
|
1057
|
|
2926
|
461
|
100
|
|
|
|
10663
|
if($self->fileFoundInDir($dirCur, $fileModuleBase)) { |
2927
|
10
|
|
|
|
|
849
|
return($dirCur->absolute . ""); |
2928
|
|
|
|
|
|
|
} |
2929
|
|
|
|
|
|
|
} |
2930
|
|
|
|
|
|
|
|
2931
|
447
|
|
|
|
|
1274
|
$dir = $dir->parent; |
2932
|
|
|
|
|
|
|
|
2933
|
|
|
|
|
|
|
#At the root? Unix/Win32. What filesystems are missing? |
2934
|
447
|
100
|
|
|
|
20414
|
$dir =~ m{^( / | \\ | \w: \\ )$}x and last; |
2935
|
|
|
|
|
|
|
} |
2936
|
|
|
|
|
|
|
|
2937
|
49
|
|
|
|
|
757
|
return(undef); |
2938
|
|
|
|
|
|
|
} |
2939
|
|
|
|
|
|
|
|
2940
|
|
|
|
|
|
|
|
2941
|
|
|
|
|
|
|
|
2942
|
|
|
|
|
|
|
|
2943
|
|
|
|
|
|
|
|
2944
|
|
|
|
|
|
|
=head2 fileFindLookingInInc($fileModuleBase) |
2945
|
|
|
|
|
|
|
|
2946
|
|
|
|
|
|
|
Find the file containing the $nameModule in config:project/extra_inc, |
2947
|
|
|
|
|
|
|
and @INC. |
2948
|
|
|
|
|
|
|
|
2949
|
|
|
|
|
|
|
Return the absolute file name, or undef if none could be found. Die on |
2950
|
|
|
|
|
|
|
errors. |
2951
|
|
|
|
|
|
|
|
2952
|
|
|
|
|
|
|
=cut |
2953
|
|
|
|
|
|
|
|
2954
|
543
|
|
|
543
|
1
|
677
|
sub fileFindLookingInInc { |
2955
|
543
|
|
|
|
|
686
|
my ($fileModuleBase) = @_; |
2956
|
|
|
|
|
|
|
|
2957
|
543
|
|
|
|
|
11464
|
my @aDirInc = uniq( $self->oProject->aDirIncAbsolute(), @INC ); |
2958
|
543
|
|
|
|
|
1738
|
for my $dirCur (@aDirInc) { |
2959
|
6375
|
100
|
|
|
|
10105
|
if(my $fileFound = $self->fileFoundInDir($dirCur, $fileModuleBase)) { |
2960
|
68
|
|
|
|
|
6343
|
return($fileFound); |
2961
|
|
|
|
|
|
|
} |
2962
|
|
|
|
|
|
|
} |
2963
|
|
|
|
|
|
|
|
2964
|
475
|
|
|
|
|
4940
|
return(undef); |
2965
|
|
|
|
|
|
|
} |
2966
|
|
|
|
|
|
|
|
2967
|
|
|
|
|
|
|
|
2968
|
|
|
|
|
|
|
|
2969
|
|
|
|
|
|
|
|
2970
|
|
|
|
|
|
|
|
2971
|
|
|
|
|
|
|
=head2 fileFromModule($nameModule) |
2972
|
|
|
|
|
|
|
|
2973
|
|
|
|
|
|
|
Return the $nameModule converted to a file name (i.e. with dirs and |
2974
|
|
|
|
|
|
|
.pm extension). |
2975
|
|
|
|
|
|
|
|
2976
|
|
|
|
|
|
|
=cut |
2977
|
720
|
|
|
720
|
1
|
2543
|
sub fileFromModule { |
2978
|
720
|
|
|
|
|
904
|
my ($nameModule) = @_; |
2979
|
720
|
|
|
|
|
4037
|
return( file( split(/::/, $nameModule) ) . ".pm" ); |
2980
|
|
|
|
|
|
|
} |
2981
|
|
|
|
|
|
|
|
2982
|
|
|
|
|
|
|
|
2983
|
|
|
|
|
|
|
|
2984
|
|
|
|
|
|
|
|
2985
|
|
|
|
|
|
|
|
2986
|
|
|
|
|
|
|
=head2 fileFoundInDir($dir, $fileModuleBase) |
2987
|
|
|
|
|
|
|
|
2988
|
|
|
|
|
|
|
Check if $fileModuleBase is located in $dir. |
2989
|
|
|
|
|
|
|
|
2990
|
|
|
|
|
|
|
Return the absolute file name, or "" if not found at $dir. |
2991
|
|
|
|
|
|
|
|
2992
|
|
|
|
|
|
|
=cut |
2993
|
22877
|
|
|
22877
|
1
|
17945
|
sub fileFoundInDir { |
2994
|
22877
|
|
|
|
|
20152
|
my ($dir, $fileModuleBase) = @_; |
2995
|
|
|
|
|
|
|
|
2996
|
22877
|
|
|
|
|
33004
|
my $file = file($dir, $fileModuleBase); |
2997
|
22877
|
100
|
|
|
|
883069
|
-e $file and return( $file->absolute . "" ); |
2998
|
|
|
|
|
|
|
|
2999
|
22575
|
|
|
|
|
690085
|
return(""); |
3000
|
|
|
|
|
|
|
} |
3001
|
|
|
|
|
|
|
|
3002
|
|
|
|
|
|
|
|
3003
|
|
|
|
|
|
|
|
3004
|
|
|
|
|
|
|
|
3005
|
|
|
|
|
|
|
|
3006
|
|
|
|
|
|
|
=head2 textFromPod($pod) |
3007
|
|
|
|
|
|
|
|
3008
|
|
|
|
|
|
|
Return $pod rendered as text, or die on errors. |
3009
|
|
|
|
|
|
|
|
3010
|
|
|
|
|
|
|
=cut |
3011
|
78
|
|
|
78
|
1
|
97
|
sub textFromPod { |
3012
|
78
|
|
|
|
|
104
|
my ($pod) = @_; |
3013
|
|
|
|
|
|
|
|
3014
|
78
|
|
|
|
|
127
|
my $text = ""; |
3015
|
78
|
|
|
|
|
711
|
my $fhIn = IO::String->new($pod); |
3016
|
78
|
|
|
|
|
4162
|
my $fhOut = IO::String->new($text); |
3017
|
78
|
|
|
|
|
1947
|
Pod::Text->new()->parse_from_filehandle($fhIn, $fhOut); |
3018
|
|
|
|
|
|
|
|
3019
|
78
|
|
|
|
|
112776
|
$text =~ s/\s+$//s; |
3020
|
|
|
|
|
|
|
|
3021
|
78
|
|
|
|
|
341
|
return($text); |
3022
|
|
|
|
|
|
|
} |
3023
|
|
|
|
|
|
|
|
3024
|
|
|
|
|
|
|
|
3025
|
|
|
|
|
|
|
|
3026
|
|
|
|
|
|
|
|
3027
|
|
|
|
|
|
|
|
3028
|
|
|
|
|
|
|
=head2 oLocationRenderPodToText($oLocation) |
3029
|
|
|
|
|
|
|
|
3030
|
|
|
|
|
|
|
Render the $oLocation->rhProperty->{pod} and put it in |
3031
|
|
|
|
|
|
|
rhProperty->{text}. |
3032
|
|
|
|
|
|
|
|
3033
|
|
|
|
|
|
|
Return the same (modified) $oLocation object, or undef if no |
3034
|
|
|
|
|
|
|
rhProperty->{pod} property ended up as text (after this operation, |
3035
|
|
|
|
|
|
|
there is content in rhProperty->{text}). |
3036
|
|
|
|
|
|
|
|
3037
|
|
|
|
|
|
|
Return undef if $oLocation is undef. |
3038
|
|
|
|
|
|
|
|
3039
|
|
|
|
|
|
|
Die on errors. |
3040
|
|
|
|
|
|
|
|
3041
|
|
|
|
|
|
|
=cut |
3042
|
84
|
|
|
84
|
1
|
138
|
sub oLocationRenderPodToText { |
3043
|
84
|
|
|
|
|
121
|
my ($oLocation) = @_; |
3044
|
84
|
100
|
|
|
|
316
|
$oLocation or return(undef); |
3045
|
|
|
|
|
|
|
|
3046
|
78
|
50
|
|
|
|
1012
|
my $pod = $oLocation->rhProperty->{pod} or return(undef); |
3047
|
78
|
50
|
|
|
|
560
|
$oLocation->rhProperty->{text} = $self->textFromPod($pod) or return(undef); |
3048
|
|
|
|
|
|
|
|
3049
|
78
|
|
|
|
|
3515
|
return($oLocation); |
3050
|
|
|
|
|
|
|
} |
3051
|
|
|
|
|
|
|
|
3052
|
|
|
|
|
|
|
|
3053
|
|
|
|
|
|
|
|
3054
|
|
|
|
|
|
|
|
3055
|
|
|
|
|
|
|
|
3056
|
|
|
|
|
|
|
=head2 aDocumentFindModuleWithInterface(raNameModule => $raNameModule, raMethodRequired => $raMethodRequired, raMethodNice => $raMethodNice, dirOrigin => $dirOrigin) |
3057
|
|
|
|
|
|
|
|
3058
|
|
|
|
|
|
|
Return a list with Devel::PerlySense::Document objects that support |
3059
|
|
|
|
|
|
|
all of the methods in $raMethodRequired and possibly the methods in |
3060
|
|
|
|
|
|
|
$raMethodNice. Look in modules in $raNameModule. |
3061
|
|
|
|
|
|
|
|
3062
|
|
|
|
|
|
|
The list is sorted with the best match first. |
3063
|
|
|
|
|
|
|
|
3064
|
|
|
|
|
|
|
If the document APIs have one or more base classes, look in the @ISA |
3065
|
|
|
|
|
|
|
(depth-first, just like Perl (see perldoc perltoot)). |
3066
|
|
|
|
|
|
|
|
3067
|
|
|
|
|
|
|
Warn on some failures to find the location. Die on errors. |
3068
|
|
|
|
|
|
|
|
3069
|
|
|
|
|
|
|
=cut |
3070
|
7
|
|
|
7
|
1
|
596
|
sub aDocumentFindModuleWithInterface { |
3071
|
7
|
|
|
|
|
33
|
my ($raNameModule, $raMethodRequired, $raMethodNice, $dirOrigin) = Devel::PerlySense::Util::aNamedArg(["raNameModule", "raMethodRequired", "raMethodNice", "dirOrigin"], @_); |
3072
|
|
|
|
|
|
|
#my $tt = Devel::TimeThis->new("aDocumentFindModuleWithInterface"); |
3073
|
|
|
|
|
|
|
|
3074
|
7
|
|
|
|
|
19
|
my @aDocument; |
3075
|
7
|
|
|
|
|
15
|
for my $nameModule (@$raNameModule) { |
3076
|
|
|
|
|
|
|
#print "module: $nameModule\n"; |
3077
|
37
|
50
|
|
|
|
287
|
my $oDocument = $self->oDocumentFindModule( |
3078
|
|
|
|
|
|
|
nameModule => $nameModule, |
3079
|
|
|
|
|
|
|
dirOrigin => $dirOrigin, |
3080
|
|
|
|
|
|
|
) or next; |
3081
|
37
|
50
|
|
|
|
144
|
$oDocument->determineLikelyApi(nameModule => $nameModule) or next; |
3082
|
37
|
100
|
|
|
|
122
|
my $score = $oDocument->scoreInterfaceMatch(nameModule => $nameModule, raMethodRequired => $raMethodRequired, raMethodNice => $raMethodNice) or next; |
3083
|
|
|
|
|
|
|
|
3084
|
8
|
|
|
|
|
31
|
push(@aDocument, { oDocument => $oDocument, score => $score }); |
3085
|
|
|
|
|
|
|
} |
3086
|
|
|
|
|
|
|
|
3087
|
|
|
|
|
|
|
my @aDocumentWithInterface = |
3088
|
8
|
|
|
|
|
28
|
map { $_->{oDocument} } |
3089
|
7
|
|
|
|
|
79
|
sort { $a->{score} <=> $b->{score} } |
|
1
|
|
|
|
|
6
|
|
3090
|
|
|
|
|
|
|
@aDocument; |
3091
|
|
|
|
|
|
|
|
3092
|
7
|
|
|
|
|
40
|
return(@aDocumentWithInterface); |
3093
|
|
|
|
|
|
|
} |
3094
|
|
|
|
|
|
|
|
3095
|
|
|
|
|
|
|
|
3096
|
|
|
|
|
|
|
|
3097
|
|
|
|
|
|
|
|
3098
|
|
|
|
|
|
|
|
3099
|
|
|
|
|
|
|
=head2 aApiOfClass(file => $fileOrigin, row => $row, col => $row) |
3100
|
|
|
|
|
|
|
|
3101
|
|
|
|
|
|
|
Look in $file at location $row/$col and determine what package is |
3102
|
|
|
|
|
|
|
there. |
3103
|
|
|
|
|
|
|
|
3104
|
|
|
|
|
|
|
Return a two item array with (Package name, |
3105
|
|
|
|
|
|
|
Devel::PerlySense::Document::Api object with the likely API of that |
3106
|
|
|
|
|
|
|
class), or () if none was found. |
3107
|
|
|
|
|
|
|
|
3108
|
|
|
|
|
|
|
Die if $file doesn't exist, or on other errors. |
3109
|
|
|
|
|
|
|
|
3110
|
|
|
|
|
|
|
=cut |
3111
|
0
|
|
|
0
|
1
|
0
|
sub aApiOfClass { |
3112
|
0
|
|
|
|
|
0
|
my ($file, $row, $col) = Devel::PerlySense::Util::aNamedArg(["file", "row", "col"], @_); |
3113
|
|
|
|
|
|
|
|
3114
|
0
|
|
|
|
|
0
|
my $oDocument = $self->oDocumentParseFile($file); |
3115
|
0
|
0
|
|
|
|
0
|
my $packageName = $oDocument->packageAt(row => $row) or return(undef); |
3116
|
|
|
|
|
|
|
|
3117
|
0
|
0
|
|
|
|
0
|
$oDocument->determineLikelyApi(nameModule => $packageName) or return(undef); |
3118
|
|
|
|
|
|
|
|
3119
|
0
|
|
|
|
|
0
|
return($packageName, $oDocument->rhPackageApiLikely->{$packageName}); |
3120
|
|
|
|
|
|
|
} |
3121
|
|
|
|
|
|
|
|
3122
|
|
|
|
|
|
|
|
3123
|
|
|
|
|
|
|
|
3124
|
|
|
|
|
|
|
|
3125
|
|
|
|
|
|
|
|
3126
|
|
|
|
|
|
|
=head2 aDocumentGrepInDir(dir => $dir, rsGrepFile => $rsGrepFile, rsGrepDocument => $rsGrepDocument) |
3127
|
|
|
|
|
|
|
|
3128
|
|
|
|
|
|
|
Return a list with Devel::PerlySense::Document objects found under the |
3129
|
|
|
|
|
|
|
$dir, and that return true for the grep sub $rsGrepFile and $rsGrepDocument. |
3130
|
|
|
|
|
|
|
|
3131
|
|
|
|
|
|
|
If any found file couldn't be parsed, skip it silently from the list. |
3132
|
|
|
|
|
|
|
|
3133
|
|
|
|
|
|
|
=cut |
3134
|
3
|
|
|
3
|
1
|
1639
|
sub aDocumentGrepInDir { |
3135
|
3
|
|
|
|
|
14
|
my ($dir, $rsGrepFile, $rsGrepDocument) = Devel::PerlySense::Util::aNamedArg(["dir", "rsGrepFile", "rsGrepDocument"], @_); |
3136
|
|
|
|
|
|
|
|
3137
|
|
|
|
|
|
|
my @aDocument = |
3138
|
|
|
|
|
|
|
map { |
3139
|
35
|
|
|
|
|
44984
|
my $oDocument = Devel::PerlySense::Document->new(oPerlySense => $self); |
3140
|
35
|
|
|
|
|
64
|
eval { $oDocument->parse(file => $_) }; |
|
35
|
|
|
|
|
116
|
|
3141
|
35
|
100
|
|
|
|
191
|
$@ ? |
|
|
50
|
|
|
|
|
|
3142
|
|
|
|
|
|
|
() : |
3143
|
|
|
|
|
|
|
$rsGrepDocument->($oDocument) ? |
3144
|
|
|
|
|
|
|
$oDocument : |
3145
|
|
|
|
|
|
|
(); |
3146
|
|
|
|
|
|
|
} |
3147
|
3
|
|
|
|
|
86
|
grep { $rsGrepFile->($_) } |
|
51
|
|
|
|
|
4960
|
|
3148
|
|
|
|
|
|
|
File::Find::Rule->file->name("*.pm")->in($dir); |
3149
|
|
|
|
|
|
|
|
3150
|
3
|
|
|
|
|
682
|
return(@aDocument); |
3151
|
|
|
|
|
|
|
} |
3152
|
|
|
|
|
|
|
|
3153
|
|
|
|
|
|
|
|
3154
|
|
|
|
|
|
|
|
3155
|
|
|
|
|
|
|
|
3156
|
|
|
|
|
|
|
|
3157
|
|
|
|
|
|
|
=head1 CACHE METHODS |
3158
|
|
|
|
|
|
|
|
3159
|
|
|
|
|
|
|
|
3160
|
|
|
|
|
|
|
=head2 cacheSet(file => $file, key => $key, value => $valuex) |
3161
|
|
|
|
|
|
|
|
3162
|
|
|
|
|
|
|
If the oCache isn't undef, store the $value in the cache under the |
3163
|
|
|
|
|
|
|
total key of ($file, $file's timestamp, $key, and the PerlySense |
3164
|
|
|
|
|
|
|
VERSION). |
3165
|
|
|
|
|
|
|
|
3166
|
|
|
|
|
|
|
$value should be a scalar or reference which can be freezed. |
3167
|
|
|
|
|
|
|
|
3168
|
|
|
|
|
|
|
$file must be an existing file. |
3169
|
|
|
|
|
|
|
|
3170
|
|
|
|
|
|
|
Return 1 if the $value was stored, else 0. Die on errors. |
3171
|
|
|
|
|
|
|
|
3172
|
|
|
|
|
|
|
=cut |
3173
|
|
|
|
|
|
|
#Move these to Devel::PerlySense::Util::Cache ? |
3174
|
732
|
|
|
732
|
1
|
8119
|
sub cacheSet { |
3175
|
732
|
|
|
|
|
3822
|
my ($file, $key, $value) = Devel::PerlySense::Util::aNamedArg(["file", "key", "value"], @_); |
3176
|
|
|
|
|
|
|
|
3177
|
732
|
100
|
|
|
|
5311
|
my $keyTotal = $self->cacheKeyTotal($file, $key) or return(0); |
3178
|
|
|
|
|
|
|
|
3179
|
26
|
50
|
|
|
|
119
|
my $data = freeze($value) or return(0); |
3180
|
26
|
|
|
|
|
35569
|
$self->oCache->set($keyTotal, $data); |
3181
|
|
|
|
|
|
|
|
3182
|
26
|
|
|
|
|
29043
|
return(1); |
3183
|
|
|
|
|
|
|
} |
3184
|
|
|
|
|
|
|
|
3185
|
|
|
|
|
|
|
|
3186
|
|
|
|
|
|
|
|
3187
|
|
|
|
|
|
|
|
3188
|
|
|
|
|
|
|
|
3189
|
|
|
|
|
|
|
=head2 cacheGet(file => $file, key => $key) |
3190
|
|
|
|
|
|
|
|
3191
|
|
|
|
|
|
|
If the oCache isn't undef, get the value in the cache under the total |
3192
|
|
|
|
|
|
|
key of ($file, $file's timestamp, $key) and return it. |
3193
|
|
|
|
|
|
|
|
3194
|
|
|
|
|
|
|
$file must be an existing file. |
3195
|
|
|
|
|
|
|
|
3196
|
|
|
|
|
|
|
Return the value, or undef if the value could not be fetched. Die on errors. |
3197
|
|
|
|
|
|
|
|
3198
|
|
|
|
|
|
|
=cut |
3199
|
760
|
|
|
760
|
1
|
1004590
|
sub cacheGet { |
3200
|
760
|
|
|
|
|
2344
|
my ($file, $key) = Devel::PerlySense::Util::aNamedArg(["file", "key"], @_); |
3201
|
|
|
|
|
|
|
|
3202
|
760
|
100
|
|
|
|
2230
|
my $keyTotal = $self->cacheKeyTotal($file, $key) or |
3203
|
|
|
|
|
|
|
# warn("Could not get key for ($file) ($key)\n"), |
3204
|
|
|
|
|
|
|
return(undef); |
3205
|
|
|
|
|
|
|
|
3206
|
52
|
100
|
|
|
|
772
|
my $data = $self->oCache->get($keyTotal) or |
3207
|
|
|
|
|
|
|
# warn("?\n"), |
3208
|
|
|
|
|
|
|
return(undef); |
3209
|
|
|
|
|
|
|
#warn("!\n"); |
3210
|
|
|
|
|
|
|
|
3211
|
26
|
50
|
|
|
|
13026
|
my $rValue = thaw($data) or warn("Could not thaw\n"), return(undef); |
3212
|
26
|
|
|
|
|
75394
|
return( $rValue ); |
3213
|
|
|
|
|
|
|
} |
3214
|
|
|
|
|
|
|
|
3215
|
|
|
|
|
|
|
|
3216
|
|
|
|
|
|
|
|
3217
|
|
|
|
|
|
|
|
3218
|
|
|
|
|
|
|
|
3219
|
|
|
|
|
|
|
=head2 cacheKeyTotal($file, $key) |
3220
|
|
|
|
|
|
|
|
3221
|
|
|
|
|
|
|
If oCache is undef, return undef. |
3222
|
|
|
|
|
|
|
|
3223
|
|
|
|
|
|
|
Otherwise, return the total key of ($file, $file's timestamp, $key, |
3224
|
|
|
|
|
|
|
and the PerlySense VERSION). |
3225
|
|
|
|
|
|
|
|
3226
|
|
|
|
|
|
|
$file must be an existing file. |
3227
|
|
|
|
|
|
|
|
3228
|
|
|
|
|
|
|
Die on errors. |
3229
|
|
|
|
|
|
|
|
3230
|
|
|
|
|
|
|
=cut |
3231
|
1492
|
|
|
1492
|
1
|
1870
|
sub cacheKeyTotal { |
3232
|
1492
|
|
|
|
|
1695
|
my ($file, $key) = @_; |
3233
|
1492
|
100
|
|
|
|
22020
|
$self->oCache or return(undef); |
3234
|
|
|
|
|
|
|
|
3235
|
80
|
100
|
|
|
|
2358
|
my $timestamp = (stat($file))[9] or die("Could not read timestamp for file ($file)\n"); |
3236
|
78
|
|
|
|
|
261
|
my $keyTotal = join("\t", $file, $timestamp, $key, $self->VERSION); |
3237
|
|
|
|
|
|
|
|
3238
|
78
|
|
|
|
|
185
|
return($keyTotal); |
3239
|
|
|
|
|
|
|
} |
3240
|
|
|
|
|
|
|
|
3241
|
|
|
|
|
|
|
1; |
3242
|
|
|
|
|
|
|
|
3243
|
|
|
|
|
|
|
|
3244
|
|
|
|
|
|
|
|
3245
|
|
|
|
|
|
|
|
3246
|
|
|
|
|
|
|
|
3247
|
|
|
|
|
|
|
__END__ |