Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature Request: Add ability to change theme/grammar content type associations at runtime #81

Open
ainslec opened this issue Mar 29, 2017 · 15 comments

Comments

@ainslec
Copy link

ainslec commented Mar 29, 2017

Hi,

My plugin used to dynamically be able to change file associations for my given editor type. Since using the TMPresentationReconciler, this ability appears to have been lost (conjecture on why later).

Here is my editor definition (NOTE : "my' is the associated file type, NOT 'pie') :

image

Here is the method in MySourceViewerConfiguration class:

image

Here is the TM4E definition (NOTE : "my' is the associated file type, NOT 'pie') :

image

Here is the code that I used to dynamically modify file associations to choose my editor (this previously worked before adding in the TM4E syntax highlighting) :

public void resetFileAssociations(java.util.Set<java.lang.String> extensionToAddSet) {
      IWorkbench workbench = PlatformUI.getWorkbench();
      final org.eclipse.ui.internal.registry.EditorRegistry editorReg = (org.eclipse.ui.internal.registry.EditorRegistry) workbench.getEditorRegistry();

      org.eclipse.ui.internal.registry.EditorDescriptor editor = (org.eclipse.ui.internal.registry.EditorDescriptor) editorReg.findEditor("com.consoli.myrion.eclipse.ui.editor.MyEditorId3");

      IFileEditorMapping[] originalMappings = editorReg.getFileEditorMappings();

      java.util.ArrayList<org.eclipse.ui.internal.registry.FileEditorMapping> newMappings = new java.util.ArrayList<org.eclipse.ui.internal.registry.FileEditorMapping>();

      boolean firstTime = false;

      if (originalMappings2 == null) {
         originalMappings2 = new java.util.HashSet<String>();
         firstTime = true;
      }

      int numOriginalMappings = originalMappings.length;

      for (int i = 0; i < numOriginalMappings; i++) {

         org.eclipse.ui.internal.registry.FileEditorMapping m = (org.eclipse.ui.internal.registry.FileEditorMapping) originalMappings[i];

         String extension = m.getExtension();

         IEditorDescriptor[] editors = m.getEditors();

         for (IEditorDescriptor currentEditor : editors) {
            boolean isMyEditor = currentEditor == editor;
            if (isMyEditor) {

               if (firstTime) {
                  originalMappings2.add(extension);
               } else {
                  if (!originalMappings2.contains(extension)) {
                     m.removeEditor(editor);
                  }
               }
            }
         }

         if (editors.length > 0) {
            newMappings.add(m);
         }
      }

      for (String extensionToAdd : extensionToAddSet) {
         if (!originalMappings2.contains(extensionToAdd)) {
            org.eclipse.ui.internal.registry.FileEditorMapping mapping = new org.eclipse.ui.internal.registry.FileEditorMapping(extensionToAdd);
            System.out.println("Associating " + extensionToAdd);
            mapping.addEditor(editor);
            mapping.setDefaultEditor(editor);
            newMappings.add(mapping);
         }
      }

      final FileEditorMapping[] fileEditorMappings = newMappings.toArray(new org.eclipse.ui.internal.registry.FileEditorMapping[newMappings.size()]);
      final Display default1 = Display.getDefault();
      final Runnable runnable = new Runnable() {
         @Override
         public void run() {
            editorReg.setFileEditorMappings(fileEditorMappings);
         }
      };
      default1.syncExec(runnable);
}

Now we wish to dynamically associate the 'pie' file type / suffix with the same editor as 'my'.

So we call the resetFileAssociations() method with set containing ["pie", "my"]. This worked perfectly prior to tm4e integration. It also works now actually. It associates the editor type with the new suffix, but TM4E then uses the content type to grammar map and finds it empty for "pie". Same with themes.

As you would expect from the previous description, here is what happens when I attempt to access a ".pie" (previously it worked because the previous reconciler did not require configuration):

g655

I believe that TM4E is using IGrammarRegistryManager to look up grammar / theme associations, and this cannot be modified at runtime (as far as I am aware) - therefore the .pie file suffix resolves to null. I would like to be able to alter grammar/theme associations on the fly same as is possible with the suffix to default editor associations.

The use case for this is that I have one file type that when saved, controls file associations for other file types. For example, lets say I have a file with suffix .mysettings, that file might contain a single line with a comma separated list of file suffixes such as "one, two, three". Now the builder sees when that file changes and calls the method detailed earlier. It reads the content of the file, and now dynamically associates the files with the editor (described earlier). That editor is associated with the TMPresentationReconciler. BUT, the presentation reconciler will not allow files without statically registered suffixes and themes to render. Eclipse has the functionality to change file associations at runtime, but TM4E does not (yet) (as far as I know) have the ability to render for a file suffix that has not been statically registered via the plugin.xml.

@angelozerr
Copy link
Contributor

@Ainsley please let me time to study your usecase.

@mickaelistria
Copy link
Contributor

FYI, in LSP4E, we have the registry that uses 2 strategies simultaneously:

  • First is to resolve content-type -> LS association with the extensions defined in plugin.xml
  • Another is to read a preference that also stores content-type -> LS associations. A preference page allows to dynamically add associations by changing the value of that preference.
    I believe a similar pattern could work for TM4E in Eclipse IDE.

@ainslec
Copy link
Author

ainslec commented Apr 7, 2017

Hello, just checking in on this. This is a blocking issue for me at the moment.

@angelozerr
Copy link
Contributor

@Ainsley very busy for the moment with typescript.java release, i must find time

I believe a similar pattern could work for TM4E in Eclipse IDE.

@Ainsley is suggestion (manage link between grammar and content types with preferences) of @mickaelistria will work for you?

@ainslec
Copy link
Author

ainslec commented Apr 9, 2017

@Ainsley is suggestion (manage link between grammar and content types with preferences) of @mickaelistria will work for you?

I think decoupling the grammar definition from the content type as described would work. Although there would not necessarily need to be a preferences page for my particular use case as I'd be adjusting the associations dynamically.

However I also have another use-case where I users may wish to change the associations themselves.

Maybe the associations as described in plugin.xml should be locked from modification in the preferences page, but other associations should be able to be created, edited and deleted? Maybe locking the associations should be configurable in plugin.xml itself (lock, lock_advise, open)?

@angelozerr
Copy link
Contributor

And TMPresentationReconciler#setGrammar cannot help you to set grammar as you wish?

@ainslec
Copy link
Author

ainslec commented Apr 9, 2017

OK, so I understand that I can disable the content type to grammar type mapping by explicitly setting the grammar type for the TMPresentationReconciler within my editor configuration class (class that extends TextSourceViewerConfiguration).

I have used the following workaround logic and it seems to work:

   @Override
   public IPresentationReconciler getPresentationReconciler(ISourceViewer viewer) {
      // Defines a TextMate Presentation reconcilier (TM4E)
      final TMPresentationReconciler tmPresentationReconciler = new TMPresentationReconciler();
      
      // WORKAROUND - START
      
      // We are forcing the TM Presentation reconciler for this editor type to ONLY be ever of the ".my" grammar variety
      {
         IContentTypeManager contentTypeManager         = Platform.getContentTypeManager();
         IContentType        myContentType              = contentTypeManager.findContentTypeFor("anything" + MY_FILE_SUFFIX);
         IContentType[]      contentType                = new IContentType[]{ myContentType};
         
         IGrammarRegistryManager grammarRegistryManager = TMEclipseRegistryPlugin.getGrammarRegistryManager();
         IGrammar                tmGrammar              = grammarRegistryManager.getGrammarFor(contentType);
         
         // This should always be non-null
         if (tmGrammar != null) {
            tmPresentationReconciler.setGrammar(tmGrammar);
         }
      }
      
      // WORKAROUND - END
      
      return tmPresentationReconciler;
   }

I think the validation code could be stronger in this case, as it assumes that there will only ever be one content type (the one associated with the TM grammar definition) in the content types registry.

But the workaround is good enough for my use case (as I have a single grammar type). It may be worth thinking through a solution where content type to TM grammar associations can be reconfigured, but I can confirm this is no longer a block for me.

@angelozerr
Copy link
Contributor

@Ainsley I have refactored the TMPresentationReconciler to support preferences and in this case my Document has none content type. So I'm working only with scopeName (grammar) and theme.

So now you have TMPresentationReconciler#setGrammar and TMPresentationReconciler#setThemeId which recolorizes if the grammar or theme changed. You don't need in this case content type. Content type is helpful if you don't set grammar.

See https://github.com/eclipse/tm4e/blob/master/org.eclipse.tm4e.ui/src/main/java/org/eclipse/tm4e/ui/internal/preferences/TMViewer.java that I'm using for preview inside preferences:

If it works for you, please close this issue. Thanks!

@mickaelistria
Copy link
Contributor

With the current version of TM4E, I cannot set a content-type for a grammar added manually.
I've added a grammar ( graphQL.tmLanguage ) and I would like to associate it with a local content-type. I do not see how I can do it from UI.

@angelozerr
Copy link
Contributor

I've added a grammar ( graphQL.tmLanguage ) and I would like to associate it with a local content-type. I do not see how I can do it from UI.

It's not possible today to use content type, you must use file extension. I had done this choice because with Oxygen/Neon you was not able to create custom content type, that's why I have supported file extension.

It seems @mickaelistria that you are working on create custom content type. In this case I think it could be interested to do that.

@mickaelistria
Copy link
Contributor

It's not possible today to use content type, you must use file extension. I had done this choice because with Oxygen/Neon you was not able to create custom content type, that's why I have supported file extension.

That didn't work for my use-case. Opening a .graphql file with generic editor and graphql grammar available doesn't highlight the code.

It seems @mickaelistria that you are working on create custom content type. In this case I think it could be interested to do that.

This has been available since Oxygen M4 ( https://bugs.eclipse.org/bugs/show_bug.cgi?id=98230 ).

@angelozerr
Copy link
Contributor

That didn't work for my use-case. Opening a .graphql file with generic editor and graphql grammar available doesn't highlight the code.

It's because you cannot use Generic Editor, you must use the TextMate Editor for the moment. We must implement #17 I have tried to do that quicly but I must fix problem when grammar is not found (in the case of generic editor, you must not see errors)

@mickaelistria
Copy link
Contributor

Issue #17 would become less important once this one is fixed. In the Eclipse IDE, dealing with content-type scales better and is less error prone.

@angelozerr
Copy link
Contributor

angelozerr commented Aug 7, 2017

To fix this issue #17 , you must add add in the org.eclipse.tm4e.ui/plugin.xml

<extension
         point="org.eclipse.ui.genericeditor.presentationReconcilers">
      <presentationReconciler
            class="org.eclipse.tm4e.ui.text.TMPresentationReconciler"
            contentType="org.eclipse.core.runtime.text">
      </presentationReconciler>
   </extension>

To use TMPresentationReconciler with Generic Editor.

It should work with your use case, no?

Issue #17 would become less important

I don't understand? You mean that you can manage org.eclipse.ui.genericeditor.presentationReconcilers with Java API?

@mickaelistria
Copy link
Contributor

You're right, it would still require ability to map dynamically a presentationReconciler to a content-type.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants