Perl5 plugin for Intellij IDEA

Hi everyone.

Recently i've decided to try to make perl5 plugin for InterlliJ IDEA. I've seen feew attempts to start, but they've failed after creating four base classes :)

The problem with perl is his too free syntax, which requires a very custom lexer and parser. I've tried to port perl's original lexer, but it's too big, got lot of legacy stuff, so i've droped the idea.

Currently, i'm making lexer with JFlex and it works, but need tunings for different perlish situations. Anyway, some syntax is already highighted:
46fdd3eb65.jpg
The second problem is that language plugins development documentation looks outdated (of course, not so many readers), but examples helps in such cases. Sometimes...

But is there some gurus in language plugins who could help with tricky questions or just to save some time in useless tries?

Also, if you would like to participate in perl5 plugin development, you are welcome: https://github.com/hurricup/Perl5-IDEA

1
236 comments

PsiReference.bindToElement should be invoked on target move.

0
Avatar
Alexandr Evstigneev

Is it possible an how to have different name validators (or get name context) for name validation. In Perl rules should be different for different entities.

0
Please try implementing RenameInputValidator 
0
Avatar
Alexandr Evstigneev

FileReference problem:

When I'm throwing IncorrectOperationException from handleElementRename - works fine, i'm getting error message popup and operation being cancelled.

When i'm throwing it from bindToElement it's not being catched:

 com.intellij.util.IncorrectOperationException: Failed attempt to move package file outside of the one of the source roots:       at com.perl5.lang.perl.psi.references.PerlNamespaceFileReference.bindToElement(PerlNamespaceFileReference.java:132)      at com.intellij.refactoring.move.moveFilesOrDirectories.MoveFilesOrDirectoriesProcessor.retargetUsages(MoveFilesOrDirectoriesProcessor.java:279)      at com.intellij.refactoring.move.moveFilesOrDirectories.MoveFilesOrDirectoriesProcessor.performRefactoring(MoveFilesOrDirectoriesProcessor.java:192)      at com.intellij.refactoring.BaseRefactoringProcessor$7.run(BaseRefactoringProcessor.java:475)      at com.intellij.openapi.application.impl.ApplicationImpl.runWriteAction(ApplicationImpl.java:931) 
0
Avatar
Alexandr Evstigneev

Again, about file reference:

If i move/rename file - bindToElement works fine.
But, if i change containing directory name, for example, nothing being invoked and reference is not being used. How to handle this?

0
Avatar
Alexandr Evstigneev

It looks like i need to implement PsiCheckedRenameElement here

0
Avatar
Alexandr Evstigneev

Chained elements rename:

I've got:

PsiElementA references to PsiElementB references to PsiFile

If i'm trying to rename elementB, psiFile renaming starts and b->file reference's handleElementRename being invoked, but nothing going on with elementA
If i'm trying to rename elementA, A and B renameing starts and nothgin going on with a PsiFile.

Question:
How to make chained renaming work? Or all elements should be referenced directly?
And how PolyVariantReferenced PsiElements works? Tried to make A references B and File but now on renaming A not B nor File being renamed.

0

Btw, the method is documented, do you find the documentation inadequate ?

0
Avatar
Alexandr Evstigneev

* Returns the start offset of the code construct which owns the opening structural brace at the specified offset. For example,

* if the opening brace belongs to an 'if' statement, returns the start offset of the 'if' statement.
Not clear when it's being invoked and what is for.
Also, sorry, i'm getting so huge amount of new information, so can miss something sometimes. Sorry about that :(
0
Avatar
Alexandr Evstigneev

Is it possible to merge or bundle RenameRefactoring objects?
On directory name change i need to refactor several PsiElements in several files they are referenced from different other files.
Currently i'm creating RenameRefactoring object for each PsiElement and starting them one by one, but after i click refactor on one of them, i'm getting error on the next one that code been changed and i need to run search again.

Or should i just use addElement ?

0
Avatar
Alexandr Evstigneev

Got some troubles here.

There are RenameInputValidator, RenamePsiElementProcessor and NamesValidator

When i'm finishing element rename, first NamesValidator.isIdentifier being invoked. But, there is no context and and i can't say if it's valid.
Than, if isIdentifier returned true, RenamePsiElementProcessor.renameElement being invoked with element and newName.

Question: what is RenameInputValidator for and why it's never been invoked?

0

The ultimate answers are in open source of IntelliJ IDEA community edition.

0
Avatar
Alexandr Evstigneev

I have it and checked it, and not've asked if could find an answer, thank you.

And btw, link to appropriate place would much more useful comment, than just "rtfm". Not everyone knows sources as good as You are.

0
Avatar
Alexandr Evstigneev

Complicated parsing situation:

Got files A and B.

Some complex element parsing in file B depends on PSI tree of file A and (probably) file B. It can be parsed two ways (both are valid from grammar's pov), and hint, how to do this right way is in current or other file.

I guess it's not a problem to get psi tree of file A, but seems I can't use parsing results of file B before parsing completes. Or is it possible to re-parse some branches after it? And what is the best way to handle such situation?

0
Avatar
Alexandr Evstigneev

VirtualFileListener solved the problem    

0
Avatar
Alexandr Evstigneev

Couldn't solve this. Broke reference between B and File and manually run RenameRefactoring if filename or path is changed. Direct references to a file works fine.

0
Avatar
Alexandr Evstigneev

Yes, stupid question. RenameRefactoring can handle multiple elements with addElement

0
Avatar
Alexandr Evstigneev

Rename refactoring for poly-referenced elements.

Got multiple declarations: A1, A2, A3

Got usage A which resolves to A1, A2, A3

When i'm renaming A, declarations are not being renamed, because (i suppose) IDEA mechanisms using resolve() which, by default, returns empty result if there is more than one entity in multiResolve() result array.

So, is there standard solution for such situation in IDEA, or should I handle such situations myself?

0
Avatar
Alexandr Evstigneev

How to help IntelliLang to detect proper language for injection?

0
Avatar
Alexandr Evstigneev

First, your PSI element, which content should be treated as another language must implement PsiLanguageInjectionHost interface (PsiComment implements it by default)
Second, you should implement class which implements LanguageInjector interface and register it in languageInjector extension point. This class contains logic - which language to inject in particular element. Available languages may be obtained from Language.getRegisteredLanguages()

1
Avatar
Alexandr Evstigneev

Got problem with languageInjections. Injection itself works fine, but, if I'm editing text which been injected with other language and click enter, cusor moves in the beginning of the element. First time encountered this behavior in jflex files, now got it myself.

It's not really convenient to edit such blocks in subwindow. Is it possible to do something with this?

0
Avatar
Alexandr Evstigneev

Question about live templates. Implemented them and they are works fine, but need to do a tricky thing and don't know how. Checked different template processors and couldn't find what i need.

Got a template like:

my $var = <<'MARKER';

MARKER

This is a perl's heredoc. The tricky part is that last (closing) marker MUST be in the begining of the line. But, if i'm inserting live template from indented position (inside block for example), template being indented too. Both - first and last line. How to force last line to start from beginning of the line? Seems that formatting being done after template processing.

0
Avatar
Alexandr Evstigneev
  1. To handle such situation reference's resolve method should be overrided and return result even if there are more than one declaration (first of it or any preferrable).
  2. All declarations should be cross-referenced.
  3. To override possible side effect in navigation because of declaration cross-referencing it may be necessary to implement (or modify) GotoDeclarationHandler.


With such implementation when you are trying to rename A, reference leads IDEA to A1 for example. Than, idea searches for all elements that refers to the A1 and will find all declarations and all usages (if there are more than one) and you'll get consistent renaming.

0
Avatar
Alexandr Evstigneev

As I wrote before, i need data from other files to properly parse file. And seems not only to parse, but to lex too. And here is my problem. To access indexes, you need to know a project. And it's ok with when Lexer being created via ParserDefinition or HghlighterFactory. But, words scanner creates lexers before opening any projects and these lexers don't know project, can't properly lex files and, I assume, can't make proper words index. Is there any solution?

0

Have you tried TemplatePreprocessor?

0

Word scanner is needed to index the identifiers in your files for Find Usages to find them later. If you index some other words like identifiers, it probably won't do harm. So I'd suggest to write the scanner in a project-independent way and index everything there that could possibly be an identifier.

0
Avatar
Permanently deleted user

could you please submit an issue with samples to reproduce to the https://youtrack.jetbrains.com ?
--
regards,
Alexey Kudravtsev
Intellij IDEA developer
JetBrains, Inc
http://www.jetbrains.com
"Develop with  pleasure!"

0
Avatar
Alexandr Evstigneev

Not sure I understand it right. In the crazy Perl world same thing may mean different entities.
For example:

Foo::Bar

May be a package (or class) Foo::Bar if it was defined.
It may be Bar method of Foo class if it was defined.

And you can't say what is it from just a context.

So as far as I understand I should index it several times as Foo::Bar class, Foo::Bar method, Foo class and Bar method.
And, to make it more interesting, some legacy (but still supported) from of syntax is Foo'Bar :)

Do I understand it right, that I need to inherit from DefaultWordsScanner (even duplicate it, because of static stripWords method) and do additional procesings for such ambiguous constructions and index Foo::Bar as Foo::Bar, Foo'Bar, Foo and Bar ?

0

To me, it looks like just two identifiers: Foo and Bar. Find Usages will split the name of searched entity anyway using StringUtil#getWordsIn, so you will need to have indexed the same words.

0

Please sign in to leave a comment.