The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.

NAME

Devel::PerlySense - IntelliSense for Perl

DESCRIPTION

PerlySense is an IntelliSense style utility for editors.

Conveniently navigate and browse the code and documentation of your project and Perl installation.

SYNOPSIS

From Emacs

C-p C-c -- Class Overview -- Show information about the Class at point or the current Class.

C-p C-d -- Smart docs -- Show docs (POD/signature/etc) for the symbol (module/method/sub) at point. A doc hint is displayed in the message area (for methods and subs), or a new POD buffer is created (for modules).

C-p C-g -- Smart go to -- Open file at proper location for module, method/sub declaration for the symbol (module/method/sub) at point. If no sub declaration is available (like for generated getters/setters), any appropriate POD is used instead.

C-p m f -- Perl Module open File -- Open the source file of the module at point.

As you can see, PerlySense duplicates some of the functionality in cperl-mode, and does more in certain areas.

From the command line

This is not very convenient unless you let your editor do it. See the "perly_sense" in bin script on how to do this.

There is one useful command though; cache all the modules in @INC:

    perly_sense process_inc

From other editors

Any editor that is programmable and that can call a shell script could take advantage of PerlySense to implement something similar to the Emacs functionality. And most editors are programmable by the authors, if not by the users.

From Perl

See the source of the "perly_sense" in bin script, or the t directory.

INSTALLATION

Install required modules from CPAN.

Emacs installation

Copy the file editors/emacs/perly-sense.el to your Emacs script dir (which should be in the load-path), and add this to the end of your .emacs config file:

  ; PerlySense
  (load "perly-sense")
  (global-unset-key "\C-p")
  (global-set-key (kbd "\C-p \C-d") 'perly-sense-smart-docs-at-point)
  (global-set-key (kbd "\C-p \C-g") 'perly-sense-smart-go-to-at-point)

Adjust according to preference: the key mappings will replace the C-p command which obviously be fantastically annoying for you if you don't use the arrow keys to move around. This is just how I use Emacs, and I expect this will make you go *&&*%!

Calm down. The default key bindings will be changed before the first real release. Suggestions welcome.

GETTING STARTED WITH EMACS

Open one of your Perl source files.

Class Overview

Pressing C-p C-c will bring up the Class Overview of the Class name at point, or otherwise the current Class (the active Package).

When in the Class Overview buffer:

g -- Go to the thing at point. RET does the same.

d -- Documentation for the thing at point.

c -- Class Overview for the thing at point.

H -- Move point to the Hierarchy heading in the buffer.

I -- Move point to the Interface heading in the buffer.

N -- Move point to the 'new' method in the buffer (if any).

q -- Quit the Class Overview buffer.

Smart docs

C-p C-d is the "Smart docs" command. It brings up documentation for what's at point.

Put the cursor on the "method" word of a $self->method call and press C-p C-d and wait until a documentation hint for the method call is displayed briefly in the message buffer. PerlySense will look in base classes if the method can't be found in the current class.

Put the cursor on the "method" word of an $object->method call and press C-p C-d to see the docs hint. PerlySense will look through all your "use" modules (and their base classes) for the method call and try to identify the best match.

Note! The first time each module is parsed this will take a second or two, and the very first time you run the command with lots of "use" modules it's bound to take longer than that.

Put the cursor on a module name and press C-p C-d to bring up a new buffer with the POD for that module (this is similar to the cperl-mode feature, only a) not as good, but b) it works on Windows).

Press C-p C-d with nothing under the cursor brings up a POD buffer for the current file.

Smart go to

C-p C-g is the "Smart go to" command. It's similar to Smart Docs, but instead of bringing the docs to you, it brings you to the definition of what's at point.

The definition can be either the sub declaration, or if the declaration can't be found (like for auto-generated getters/setters, autoloaded subs etc), the POD documentation for the sub.

Before you go anywhere the mark is set. Go back to earlier marks globally with C-x C-SPC, or locally with C-u C-SPC.

ON PARSING PERL

Oh, that old topic again...

Well, since Perl is so dynamic, a perfect static analysis of the source is impossible. But not unusably so. Well, hopefully.

Because of this PerlySense is not about exact rules, but about heuristics and a 90% solution that isn't perfect, but good-enough. Sometimes when PerlySense can't make a decision, you're expected to chip in and tell it what you meant.

If it works for you, brilliant, use it to be more productive.

PerlySense tries to take advantage of the fact that Perl code is more than the plain source. The source lives in a context of POD and a directory structure and... well, oher source code.

SEE ALSO

sepia - similar effort

PPI - excellent for parsing Perl

CPANXR - also uses PPI for cross referencing the CPAN

http://www.DarSerMan.com/Perl/Oasis/ - Win32 class browser/IDE. Earlier (a lot) work by me.

AUTHOR

Johan Lindström, <johanl[ÄT]DarSerMan.com>

BUGS AND CAVEATS

BUG REPORTS

Please report any bugs or feature requests to bug-devel-perlysense@rt.cpan.org, or through the web interface at http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Devel-PerlySense. I will be notified, and then you'll automatically be notified of progress on your bug as I make changes.

CAVEATS

Tab/space isn't supported by PPI yet, but it's supposed to be. So using Tab instead of spaces won't work properly.

KNOWN BUGS

PPI is kinda slow for large documents. Lots of objects being created etc.

There are certainly edge cases. Bug reports with failing tests appreciated :)

ACKNOWLEDGEMENTS

Peter Liljenberg for his elisp fu.

COPYRIGHT & LICENSE

Copyright 2007 Johan Lindström, All Rights Reserved.

This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.

*** THE FOLLOWING IS DEVELOPER DOCUMENTATION ***

PROPERTIES

oCache

Cache::Cache object, or undef if no cache is active.

Default: undef

API METHODS

new()

Create new PerlySense object.

oDocumentParseFile($file)

Parse $file into a new PerlySense::Document object.

Return the new object.

Die on errors (like if the file wasn't found).

podFromFile(file => $file)

Return the pod in $file as text, or die on errors.

Die if $file doesn't exist.

oLocationSmartGoTo(file => $fileOrigin, row => $row, col => $row)

Look in $file at location $row/$col and determine what is there. Depending on what's there, find the source declaration/whatever, find it and return an Devel::PerlySense::Document::Location object.

Currently supported:

  $self->method, look in current file and base classes. If no sub can
  be found, look for POD.

  $object->method, look in current file and used modules. If no sub
  can be found, look for POD.

  Module::Name (bareword)

  Module::Name (as the only contents of a string literal)

If there's nothing at $row/col, or if the source can't be found, return undef.

Die if $file doesn't exist, or on other errors.

oLocationSmartDoc(file => $fileOrigin, row => $row, col => $row)

Look in $file at location $row/$col and determine what is there. Depending on what's there, find the documentation for it and return a Document::Location object with the following rhProperty keys set:

  text - the docs text
  found - "method" | "module"
  docType - "hint" | "document"
  name - the name of the thing found

Currently supported:

  Same as for oLocationSmartGoTo

If there's nothing at $row/col, use the current document.

Die if $file doesn't exist, or on other errors.

classNameAt(file => $fileOrigin, row => $row, col => $row)

Look in $file at location $row/$col and determine what class name that is.

Return the class name or "" if it's package main.

Die if $file doesn't exist, or on other errors.

classAt(file => $fileOrigin, row => $row, col => $row)

Look in $file at location $row/$col and determine what PerlySelse::Class that is.

Return the Class object or undef if it's package main.

Die if $file doesn't exist, or on other errors.

fileFindModule(nameModule => $nameModule, dirOrigin => $dirOrigin)

Find the file containing the $nameModule given the $dirOrigin.

Return the absolute file name, or undef if none could be found. Die on errors.

oDocumentFindModule(nameModule => $nameModule, dirOrigin => $dirOrigin)

Find the file containing the $nameModule given the $dirOrigin.

Return a parsed PerlySense::Document, or undef if none could be found. Die on errors.

IMPLEMENTATION METHODS

fileFindLookingAround($fileModuleBase, $dirOrigin)

Find the file containing the $nameModule given the $dirOrigin.

Return the file name relative to $dirOrigin, or undef if none could be found. Die on errors.

fileFindLookingInInc($fileModuleBase)

Find the file containing the $nameModule in @INC.

Return the absolute file name, or undef if none could be found. Die on errors.

fileFromModule($nameModule)

Return the $nameModule converted to a file name (i.e. with dirs and .pm extension).

fileFoundInDir($dir, $fileModuleBase)

Look if $fileModuleBase is located in $dir. If it is, return the file name relative to $dirOrigin.

Return the absolute file name, or "" if not found at $dir.

textFromPod($pod)

Return $pod rendered as text, or die on errors.

oLocationRenderPodToText($oLocation)

Render the $oLocation->rhProperty->{pod} and put it in rhProperty->{text}.

Return the same (modified) $oLocation object, or undef if no rhProperty->{pod} property ended up as text (after this operation, there is content in rhProperty->{text}).

Return undef if $oLocation is undef.

Die on errors.

aDocumentFindModuleWithInterface(raNameModule => $raNameModule, raMethodRequired => $raMethodRequired, raMethodNice => $raMethodNice, dirOrigin => $dirOrigin)

Return a list with Devel::PerlySense::Document objects that support all of the methods in $raMethodRequired and possibly the methods in $raMethodNice. Look in modules in $raNameModule.

The list is sorted with the best match first.

If the document APIs have one or more base classes, look in the @ISA (depth-first, just like Perl (see perldoc perltoot)).

Warn on some failures to find the location. Die on errors.

aApiOfClass(file => $fileOrigin, row => $row, col => $row)

Look in $file at location $row/$col and determine what package is there.

Return a two item array with (Package name, Devel::PerlySense::Document::Api object with the likely API of that class), or () if none was found.

Die if $file doesn't exist, or on other errors.

CACHE METHODS

cacheSet(file => $file, key => $key, value => $valuex)

If the oCache isn't undef, store the $value in the cache under the total key of ($file, $file's timestamp, $key, and the PerlySense VERSION).

$value should be a scalar or reference which can be freezed.

$file must be an existing file.

Return 1 if the $value was stored, else 0. Die on errors.

cacheGet(file => $file, key => $key)

If the oCache isn't undef, get the value in the cache under the total key of ($file, $file's timestamp, $key) and return it.

$file must be an existing file.

Return the value, or undef if the value could not be fetched. Die on errors.

cacheKeyTotal($file, $key)

If oCache is undef, return undef.

Otherwise, return the total key of ($file, $file's timestamp, $key, and the PerlySense VERSION).

$file must be an existing file.

Die on errors.

1 POD Error

The following errors were encountered while parsing the POD:

Around line 197:

Non-ASCII character seen before =encoding in 'Lindström,'. Assuming CP1252