-
Notifications
You must be signed in to change notification settings - Fork 463
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Create service to map file types to supported languages (stylesheets/…
…regex-mapping) Consumers can add their own extensions as well. Users can change file type associations to alternative languages if they want, or create entries themselves to point to a supported language.
- Loading branch information
Showing
13 changed files
with
419 additions
and
36 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
92 changes: 92 additions & 0 deletions
92
recaf-ui/src/main/java/software/coley/recaf/services/text/FileTypeAssociationService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
package software.coley.recaf.services.text; | ||
|
||
import jakarta.annotation.Nonnull; | ||
import jakarta.enterprise.context.ApplicationScoped; | ||
import jakarta.inject.Inject; | ||
import javafx.collections.ObservableList; | ||
import software.coley.recaf.info.BinaryXmlFileInfo; | ||
import software.coley.recaf.info.FileInfo; | ||
import software.coley.recaf.info.Info; | ||
import software.coley.recaf.info.JvmClassInfo; | ||
import software.coley.recaf.services.Service; | ||
import software.coley.recaf.ui.LanguageStylesheets; | ||
import software.coley.recaf.ui.control.richtext.Editor; | ||
import software.coley.recaf.ui.control.richtext.syntax.RegexLanguages; | ||
import software.coley.recaf.ui.control.richtext.syntax.RegexRule; | ||
import software.coley.recaf.ui.control.richtext.syntax.RegexSyntaxHighlighter; | ||
import software.coley.recaf.util.StringUtil; | ||
|
||
import static software.coley.recaf.util.StringUtil.*; | ||
|
||
/** | ||
* Provides mapping of {@link FileInfo#getName() file extensions} to {@link RegexRule language patterns} | ||
* for use by {@link RegexSyntaxHighlighter}. | ||
* <p> | ||
* Users can change which highlighter is used for different file extensions by updating the | ||
* {@link FileTypeAssociationServiceConfig#getExtensionsToLangKeys() extensions map config}. | ||
* | ||
* @author Matt Coley | ||
*/ | ||
@ApplicationScoped | ||
public class FileTypeAssociationService implements Service { | ||
public static final String SERVICE_ID = "file-type-association"; | ||
private final FileTypeAssociationServiceConfig config; | ||
|
||
@Inject | ||
public FileTypeAssociationService(@Nonnull FileTypeAssociationServiceConfig config) { | ||
this.config = config; | ||
} | ||
|
||
/** | ||
* Updates the syntax highlighter and stylesheet of the given editor to match the file contents of the given info object. | ||
* <br> | ||
* {@link JvmClassInfo} will use {@code java} syntax highlighting. {@link FileInfo} will use the file extension in their names | ||
* to determine which syntax to use. | ||
* | ||
* @param info | ||
* Info to target. | ||
* @param editor | ||
* Editor to update. | ||
*/ | ||
public void configureEditorSyntax(@Nonnull Info info, @Nonnull Editor editor) { | ||
String fileExtension; | ||
if (info.isClass()) fileExtension = "java"; | ||
else if (info instanceof BinaryXmlFileInfo) fileExtension = "xml"; | ||
else fileExtension = getAfter(info.getName(), "."); | ||
configureEditorSyntax(fileExtension, editor); | ||
} | ||
|
||
/** | ||
* Updates the syntax highlighter and stylesheet of the given editor to match the file contents of the given file type. | ||
* | ||
* @param fileExtension | ||
* File extension to target. | ||
* @param editor | ||
* Editor to update. | ||
*/ | ||
public void configureEditorSyntax(@Nonnull String fileExtension, @Nonnull Editor editor) { | ||
String lowerExtension = fileExtension.toLowerCase(); | ||
String mappedExtension = config.getExtensionsToLangKeys().getOrDefault(lowerExtension, lowerExtension); | ||
|
||
String sheet = LanguageStylesheets.getLanguageStylesheet(mappedExtension); | ||
RegexRule language = RegexLanguages.getLanguage(mappedExtension); | ||
|
||
ObservableList<String> stylesheets = editor.getStylesheets(); | ||
if (sheet != null && !stylesheets.contains(sheet)) | ||
stylesheets.add(sheet); | ||
if (language != null) | ||
editor.setSyntaxHighlighter(new RegexSyntaxHighlighter(language)); | ||
} | ||
|
||
@Nonnull | ||
@Override | ||
public String getServiceId() { | ||
return SERVICE_ID; | ||
} | ||
|
||
@Nonnull | ||
@Override | ||
public FileTypeAssociationServiceConfig getServiceConfig() { | ||
return config; | ||
} | ||
} |
70 changes: 70 additions & 0 deletions
70
...ui/src/main/java/software/coley/recaf/services/text/FileTypeAssociationServiceConfig.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
package software.coley.recaf.services.text; | ||
|
||
import jakarta.annotation.Nonnull; | ||
import jakarta.enterprise.context.ApplicationScoped; | ||
import jakarta.inject.Inject; | ||
import software.coley.collections.tuple.Pair; | ||
import software.coley.observables.ObservableMap; | ||
import software.coley.recaf.config.BasicConfigContainer; | ||
import software.coley.recaf.config.BasicMapConfigValue; | ||
import software.coley.recaf.config.ConfigGroups; | ||
import software.coley.recaf.services.ServiceConfig; | ||
import software.coley.recaf.ui.LanguageStylesheets; | ||
import software.coley.recaf.ui.control.richtext.syntax.RegexLanguages; | ||
|
||
import java.util.HashMap; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.stream.Collectors; | ||
|
||
/** | ||
* Config for {@link FileTypeAssociationService}. | ||
* <p> | ||
* Retains mapping of file extensions to language keys used by: | ||
* <ul> | ||
* <li>{@link RegexLanguages#getLanguage(String)}</li> | ||
* <li>{@link LanguageStylesheets#getLanguageStylesheet(String)}</li> | ||
* </ul> | ||
* | ||
* @author Matt Coley | ||
*/ | ||
@ApplicationScoped | ||
public class FileTypeAssociationServiceConfig extends BasicConfigContainer implements ServiceConfig { | ||
private final ExtensionMapping extensionsToLangKeys; | ||
|
||
@Inject | ||
public FileTypeAssociationServiceConfig() { | ||
super(ConfigGroups.SERVICE_UI, FileTypeAssociationService.SERVICE_ID + CONFIG_SUFFIX); | ||
|
||
extensionsToLangKeys = new ExtensionMapping(List.of( | ||
new Pair<>("java", "java"), | ||
new Pair<>("xml", "xml"), | ||
new Pair<>("html", "xml"), | ||
new Pair<>("enigma", "enigma") | ||
)); | ||
addValue(new BasicMapConfigValue<>("extensions-to-langs", Map.class, String.class, String.class, extensionsToLangKeys)); | ||
} | ||
|
||
/** | ||
* @return Map of file extensions to {@link RegexLanguages} name keys. | ||
* | ||
* @see RegexLanguages#getLanguage(String) | ||
*/ | ||
@Nonnull | ||
public ExtensionMapping getExtensionsToLangKeys() { | ||
return extensionsToLangKeys; | ||
} | ||
|
||
/** | ||
* Maps file extensions to {@link RegexLanguages} entries. | ||
*/ | ||
public static class ExtensionMapping extends ObservableMap<String, String, Map<String, String>> { | ||
public ExtensionMapping(@Nonnull List<Pair<String, String>> extensions) { | ||
this(extensions.stream().collect(Collectors.toMap(Pair::getLeft, Pair::getRight))); | ||
} | ||
|
||
public ExtensionMapping(@Nonnull Map<String, String> extensions) { | ||
super(extensions, HashMap::new); | ||
} | ||
} | ||
} |
117 changes: 117 additions & 0 deletions
117
recaf-ui/src/main/java/software/coley/recaf/ui/LanguageStylesheets.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
package software.coley.recaf.ui; | ||
|
||
import jakarta.annotation.Nonnull; | ||
import jakarta.annotation.Nullable; | ||
import software.coley.recaf.ui.control.richtext.syntax.RegexLanguages; | ||
|
||
import java.io.InputStream; | ||
import java.util.HashMap; | ||
import java.util.Map; | ||
|
||
import static software.coley.recaf.util.StringUtil.cutOffAtFirst; | ||
import static software.coley.recaf.util.StringUtil.shortenPath; | ||
|
||
/** | ||
* Languages stylesheets for syntax highlighting. | ||
* | ||
* @author Matt Coley | ||
* @see RegexLanguages For retrieving matchers for the supported languages. | ||
*/ | ||
public class LanguageStylesheets { | ||
private static final Map<String, String> NAME_TO_PATH = new HashMap<>(); | ||
private static final String SHEET_JAVA; | ||
private static final String SHEET_XML; | ||
private static final String SHEET_ENIGMA; | ||
|
||
// Prevent construction | ||
private LanguageStylesheets() { | ||
} | ||
|
||
static { | ||
SHEET_JAVA = addLanguage("/syntax/java.css"); | ||
SHEET_XML = addLanguage("/syntax/xml.css"); | ||
SHEET_ENIGMA = addLanguage("/syntax/enigma.css"); | ||
} | ||
|
||
/** | ||
* Adds support for the language outlined by the contents of the given file. | ||
* | ||
* @param path | ||
* Full path to stylesheet file. The language name is the file name, without the extension. | ||
* | ||
* @return Full path to stylesheet file. | ||
*/ | ||
@Nonnull | ||
public static String addLanguage(@Nonnull String path) { | ||
String name = cutOffAtFirst(shortenPath(path), "."); | ||
return addLanguage(name, path); | ||
} | ||
|
||
/** | ||
* @param name | ||
* Language name to use as a key. Will be used in {@link #getLanguages()} and {@link #getLanguageStylesheet(String)}. | ||
* @param path | ||
* Full path to stylesheet file. | ||
* | ||
* @return Full path to stylesheet file. | ||
* | ||
* @see RegexLanguages#addLanguage(String, InputStream) You should assign a regex language model by the same language name. | ||
*/ | ||
@Nonnull | ||
public static String addLanguage(@Nonnull String name, @Nonnull String path) { | ||
NAME_TO_PATH.put(name, path); | ||
return path; | ||
} | ||
|
||
/** | ||
* @param name | ||
* Name of the language, matching the file path inside Recaf's {@code /syntax/} directory, | ||
* without the {@code .css} extension. To get {@link #getJavaStylesheet()} use {@code java}. | ||
* | ||
* @return Language stylesheet path, or {@code null} if unknown language. | ||
* | ||
* @see RegexLanguages#getLanguage(String) Language to match with. | ||
*/ | ||
@Nullable | ||
public static String getLanguageStylesheet(String name) { | ||
return NAME_TO_PATH.get(name); | ||
} | ||
|
||
/** | ||
* @return Map of language names to stylesheet paths. | ||
*/ | ||
@Nonnull | ||
public static Map<String, String> getLanguages() { | ||
return NAME_TO_PATH; | ||
} | ||
|
||
/** | ||
* @return Stylesheet for Java. | ||
* | ||
* @see RegexLanguages#getJavaLanguage() | ||
*/ | ||
@Nonnull | ||
public static String getJavaStylesheet() { | ||
return SHEET_JAVA; | ||
} | ||
|
||
/** | ||
* @return Stylesheet for XML. | ||
* | ||
* @see RegexLanguages#getXmlLanguage() | ||
*/ | ||
@Nonnull | ||
public static String getXmlStylesheet() { | ||
return SHEET_XML; | ||
} | ||
|
||
/** | ||
* @return Stylesheet for Engima. | ||
* | ||
* @see RegexLanguages#getLangEngimaMap() | ||
*/ | ||
@Nonnull | ||
public static String getEnigmaStylesheet() { | ||
return SHEET_ENIGMA; | ||
} | ||
} |
Oops, something went wrong.