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