File Coverage

blib/lib/Devel/PerlySense.pm
Criterion Covered Total %
statement 317 396 80.0
branch 81 134 60.4
condition 18 30 60.0
subroutine 60 73 82.1
pod 40 40 100.0
total 516 673 76.6


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