Coverage Summary

# File Coverage

lib/Devel/PerlySense.pm
Criterion | Covered | Total | %
--- | --- | --- | ---
statement | 308 | 359 | 85.7
branch | 80 | 124 | 64.5
condition | 18 | 27 | 66.6
subroutine | 57 | 68 | 83.8
pod | 38 | 38 | 100.0
total | 501 | 616 | 81.3

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__