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