package com.laytonsmith.tools; import com.laytonsmith.PureUtilities.ClassLoading.ClassDiscovery; import com.laytonsmith.PureUtilities.Common.StreamUtils; import com.laytonsmith.abstraction.Implementation; import com.laytonsmith.abstraction.enums.MCChatColor; import com.laytonsmith.annotations.MEnum; import com.laytonsmith.annotations.api; import com.laytonsmith.annotations.typeof; import com.laytonsmith.core.Documentation; import com.laytonsmith.core.Static; import com.laytonsmith.core.constructs.NativeTypeList; import com.laytonsmith.core.events.Event; import com.laytonsmith.core.exceptions.CRE.CREThrowable; import com.laytonsmith.core.functions.Function; import java.lang.annotation.Annotation; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * * */ public class SyntaxHighlighters { public static String generate(String type, String theme) { Implementation.forceServerType(Implementation.Type.BUKKIT); if ("npp".equals(type) || "notepad++".equals(type)) { if ("default".equals(theme)) { return template("/syntax-templates/notepad++/default.xml"); } if ("obsidian".equals(theme)) { return template("/syntax-templates/notepad++/obsidian.xml"); } if ("solarized-dark".equals(theme)) { return template("/syntax-templates/notepad++/solarized_dark.xml"); } if ("solarized-light".equals(theme)) { return template("/syntax-templates/notepad++/solarized_light.xml"); } return "Available themes for Notepad++: default, obsidian, solarized-dark, solarized-light"; } if ("textwrangler".equals(type)) { return template("/syntax-templates/text-wrangler/default.plist"); } if ("geshi".equals(type)) { return template("/syntax-templates/geshi/default.php"); } if ("vim".equals(type)) { return template("/syntax-templates/vim/default.vim"); } if ("nano".equals(type)) { return template("/syntax-templates/nano/default.txt"); } if ("atom".equals(type)) { return template("/syntax-templates/atom/default.cson"); } if ("sublime".equals(type)) { return template("/syntax-templates/sublime/default.xml"); } return "File for the following syntax highlighters are currently available:\n" + "\tNotepad++ - Use type \"npp\". You may also select a theme, either \"default\" or \"obsidian\"\n" + "\tTextWrangler - Use type \"textwrangler\". Only the default theme is available.\n" + "\t\tTo install: put the generated file in ~/Library/Application Support/TextWrangler/Language Modules/\n" + "\t\tNote that this output file can also be used for BBEdit.\n" + "\tGeSHi - Use type \"geshi\". Only the default theme is available.\n" + "\tViM - Use type \"vim\". Only the default theme is available.\n" + "\t\tTo install: put in ~/.vim/syntax/commandhelper.vim then edit\n" + "\t\t~/.vim/ftdetect/commandhelper.vim and add the line \n" + "\t\tau BufRead,BufNewFile *.ms set filetype=commandhelper\n" + "\t\tThen, if you're on linux and use cmdline mode, in ~/.vim/scripts.vim, add the following lines:\n" + "\t\t\tif did_filetype()\n" + "\t\t\t\tfinish\n" + "\t\t\tendif\n" + "\t\t\tif getline(1) =~# '^#!.*\\(/bin/env\\s\\+mscript\\|/bin/mscript\\)\\>'\n" + "\t\t\t\tsetfiletype commandhelper\n" + "\t\t\tendif" + "\t\t(Create directories and files as needed)\n" + "\tnano - Use type\"nano\". Only the default theme is available.\n" + "\tSublime Text - Use type \"sublime\". Only the default theme is available.\n" + "\t\tTo install: Place in Sublime Text's ./SublimeText/data/Packages/User folder.\n" + "\tAtom - Use type \"atom\". Only the default theme is available.\n" + "\t\tTo install: Install package language-mscript from the Atom package manager." + "\n\n" + "Know how to write a syntax highlighter file for your favorite text editor? Let me know, and we\n" + "can work to get it included in CommandHelper!"; } /** * Available macros are listed in the code below. * * @param location * @return */ private static String template(String location) { String template = Static.GetStringResource(location); //Replace all instances of ///! with nothing. template = template.replace("///!", ""); Pattern p = Pattern.compile("%%(.*?)%%"); Matcher m = p.matcher(template); while (m.find()) { template = template.replaceAll("%%" + m.group(1) + "%%", macro(m.group(1))); } return template; } private static String macro(String macroName) { String[] split = macroName.split(":"); String type = split[0]; String datalist = split[1]; List<String> params = new ArrayList<>(); for (int i = 2; i < split.length; i++) { params.add(split[i].toLowerCase()); } List<String> base = new ArrayList<>(); if (datalist.equalsIgnoreCase("colors")) { for (MCChatColor c : MCChatColor.values()) { base.add(c.name()); } } else if (datalist.equalsIgnoreCase("keywords")) { for (String keyword : SimpleSyntaxHighlighter.KEYWORDS) { base.add(keyword); } } else if (datalist.equalsIgnoreCase("functions")) { for (Function f : GetFunctions()) { if (SimpleSyntaxHighlighter.KEYWORDS.contains(f.getName())) { // Keywords override functions continue; } if (!f.appearInDocumentation()) { continue; } if (params.contains("restricted") || params.contains("unrestricted")) { if (params.contains("restricted") && f.isRestricted()) { base.add(f.getName()); } else if (params.contains("unrestricted") && !f.isRestricted()) { base.add(f.getName()); } } else { base.add(f.getName()); } } } else if (datalist.equalsIgnoreCase("events")) { for (Documentation d : GetEvents()) { base.add(d.getName()); } } else if (datalist.equalsIgnoreCase("exceptions")) { for (Class<? extends CREThrowable> c : ClassDiscovery.getDefaultInstance().loadClassesWithAnnotationThatExtend(typeof.class, CREThrowable.class)) { base.add(c.getAnnotation(typeof.class).value()); } } else if (datalist.equalsIgnoreCase("types")) { base.addAll(NativeTypeList.getNativeTypeList()); base.remove("null"); // Null is technically in the list, but it shouldn't be added. } else if (datalist.equalsIgnoreCase("enums")) { Set<String> set = new HashSet<>(); Set<Class<Enum>> enums = ClassDiscovery.getDefaultInstance().loadClassesWithAnnotationThatExtend(MEnum.class, Enum.class); for(Class<Enum> e : enums) { Enum[] es = e.getEnumConstants(); for(Enum ee : es) { set.add(ee.name()); } } base.addAll(set); } String header = ""; String spliter = "IMPROPER FORMATTING"; String footer = ""; if (type.equalsIgnoreCase("space")) { if (params.contains("quoted")) { header = "'"; spliter = "' '"; footer = "'"; } else { spliter = " "; } } else if (type.equalsIgnoreCase("comma")) { if (params.contains("quoted")) { header = "'"; spliter = "', '"; footer = "'"; } else { spliter = ", "; } } else if (type.equalsIgnoreCase("pipe")) { if (params.contains("quoted")) { header = "'"; spliter = "|"; footer = "'"; } else { spliter = "|"; } } else if (type.equalsIgnoreCase("xml")) { String tag = "PLEASE INCLUDE THE TAG NAME USING tag=tagname AS A PARAMETER"; for (String param : params) { //Find the tag name if (param.matches("tag=.*")) { tag = param.substring(4); break; } } if (params.contains("quoted")) { header = "<" + tag + ">'"; spliter = "'</" + tag + "><" + tag + ">'"; footer = "'</" + tag + ">"; } else { header = "<" + tag + ">"; spliter = "</" + tag + "><" + tag + ">"; footer = "</" + tag + ">"; } } return header + Join(base, spliter) + footer; } private static List<Documentation> GetEvents() { List<Documentation> l = new ArrayList<>(); Set<Class<?>> classes = ClassDiscovery.getDefaultInstance().loadClassesWithAnnotation(api.class); for (Class<?> c : classes) { if (Event.class.isAssignableFrom(c) && Documentation.class.isAssignableFrom(c)) { try { Constructor<?> m = c.getConstructor(); Documentation e = (Documentation) m.newInstance(); l.add(e); } catch (NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) { StreamUtils.GetSystemErr().println(ex.getMessage()); } } } return l; } private static List<Function> GetFunctions() { List<Function> fl = new ArrayList<>(); Set<Class<?>> functions = ClassDiscovery.getDefaultInstance().loadClassesWithAnnotation(api.class); for (Class<?> c : functions) { if (Function.class.isAssignableFrom(c)) { try { fl.add((Function) c.newInstance()); } catch (InstantiationException | IllegalAccessException ex) { Logger.getLogger(SyntaxHighlighters.class.getName()).log(Level.SEVERE, null, ex); } catch (NoClassDefFoundError e) { //Hmm. No real good way to handle this... echo out to stderr, I guess. StreamUtils.GetSystemErr().println(e.getMessage()); } } } return fl; } private static String Join(List<?> l, String joiner) { StringBuilder b = new StringBuilder(); for (int i = 0; i < l.size(); i++) { if (i == 0) { b.append(l.get(i).toString()); } else { b.append(joiner).append(l.get(i).toString()); } } return b.toString(); } }