This Bugzilla instance is a read-only archive of historic NetBeans bug reports. To report a bug in NetBeans please follow the project's instructions for reporting issues.

Bug 237252

Summary: Make EmbeddingPresence configurable in LanguageHierarchy<JavaTokenId>
Product: java Reporter: thorsten_s
Component: SourceAssignee: Svata Dedic <sdedic>
Status: NEW ---    
Severity: normal    
Priority: P2    
Version: 7.4   
Hardware: All   
OS: All   
Issue Type: ENHANCEMENT Exception Reporter:
Attachments: Language provider test

Description thorsten_s 2013-10-17 13:25:09 UTC
Currently the LanguageHierarchy<JavaTokenId> implementation (anonymous class) returns the default value for LanguageHierarchy.embeddingPresence(..) from the parent LanguageHierarchy class: EmbeddingPresence.CACHED_FIRST_QUERY.
This causes problems when attempting to embed languages into java tokens where in some cases other embeddings might already be present. 

If I embed my language into a Java BlockComment then this embedding will work either for me XOR the JavaDoc embedding, depending on which is parsed first in a document. 

To change this the LanguageHierarchy<JavaTokenId> implementation should be configurable to change the default for specific tokens. 

org.netbeans.api.java.lexer.JavaTokenId:


    private static final Language<JavaTokenId> language = new LanguageHierarchy<JavaTokenId>() {

        @Override
        protected EmbeddingPresence embeddingPresence(JavaTokenId id) {
            if (id == JavaTokenId.BLOCK_COMMENT) {
                return EmbeddingPresence.ALWAYS_QUERY;
            }
            return super.embeddingPresence(id);
        }


I used the above for testing, but obviously more changes would be required to make things configurable as the Language<JavaTokenId> is not publicly accessible.
Comment 1 thorsten_s 2013-10-19 20:29:03 UTC
I took a closer look at the NetBeans sources and don't really see that my suggested change would be a solution to my problems, or even has any effect at all. In fact, I have reason to believe that my language embedding problems are at least partially related to a broken test environment. 
Installing my module in the development IDE itself (7.4) or in a fresh build from the main-silver trunk will make things work, while running my module from the project explorer's context menu and with the underlying NetBeans platform set to 'Development IDE' does cause problems in my environment. Mea culpa.
Comment 2 thorsten_s 2013-11-01 12:32:49 UTC
Created attachment 141769 [details]
Language provider test

Did some debugging and got to the conclusion that my previous comment is wrong. I got confused because of the internal caching of the token's EmbeddingPresence state. Restarting the IDE before each test gives reliable results.   

The attached file is a minimal language provider I use for testing. 

It will embed Java (again) into a Java block comment that starts with '/*xxx' and ends with '*/'.

If the opened file in the NetBeans editor contains a normal block comment before such a custom block comment, then embedding will not work. The other way around embedding will work. 

Please consider my first suggestion as a possible fix. It would be a lot easier then providing a public API for every token kind and should already be quite helpful because: 
* The normal block comment token is probably the token of choice for most third party embeddings.
* The amount of block comment tokens is probably not that large that the EmbeddingPresence.ALWAYS_QUERY setting has any negative impact on performance.
Comment 3 thorsten_s 2014-03-21 10:27:34 UTC
For the record: I managed to fix the issue using the following code snippet within a Runnable annotated with org.openide.windows.OnShowing. Had to work around non-public access through reflection, though.


// this would be nice, except access to LanguageOperation is not available for normal folks:
//        Language<JavaTokenId> language = JavaTokenId.language();
//        LexerApiPackageAccessor.get().languageOperation(language).embeddingPresence(JavaTokenId.BLOCK_COMMENT);
//        LexerApiPackageAccessor.get().languageOperation(language).setEmbeddingPresence(
//                JavaTokenId.BLOCK_COMMENT, EmbeddingPresence.ALWAYS_QUERY);
        
        
        // using reflection, then:
        
        Language<JavaTokenId> language = JavaTokenId.language();
        // get otherwise hidden field of type LanguageOperation<JavaTokenId>
        Field languageOperationField = language.getClass().getDeclaredField("languageOperation");
        languageOperationField.setAccessible(true);
        Method embeddingPresenceGet = languageOperationField.getType().getDeclaredMethod(
                "embeddingPresence",
                TokenId.class);
        // initialize LanguageOperation.embeddingPresences cache, if that did not happen already.
        embeddingPresenceGet.invoke(languageOperationField.get(language), JavaTokenId.BLOCK_COMMENT);
        // explicitly set embedding presence to desired value.
        Method embeddingPresenceSet = languageOperationField.getType().getDeclaredMethod(
                "setEmbeddingPresence",
                TokenId.class,
                EmbeddingPresence.class);
        embeddingPresenceSet.invoke(languageOperationField.get(language),
                JavaTokenId.BLOCK_COMMENT,
                EmbeddingPresence.ALWAYS_QUERY);