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__