diff src/nabble/view/web/template/MacroSourceNamespace.java @ 0:7ecd1a4ef557

add content
author Franklin Schmidt <fschmidt@gmail.com>
date Thu, 21 Mar 2019 19:15:52 -0600
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/nabble/view/web/template/MacroSourceNamespace.java	Thu Mar 21 19:15:52 2019 -0600
@@ -0,0 +1,703 @@
+package nabble.view.web.template;
+
+import nabble.model.Site;
+import nabble.modules.ModuleManager;
+import nabble.naml.compiler.Command;
+import nabble.naml.compiler.CommandSpec;
+import nabble.naml.compiler.CompileException;
+import nabble.naml.compiler.IPrintWriter;
+import nabble.naml.compiler.Interpreter;
+import nabble.naml.compiler.JavaCommand;
+import nabble.naml.compiler.Macro;
+import nabble.naml.compiler.Meaning;
+import nabble.naml.compiler.Namespace;
+import nabble.naml.compiler.ParamMeaning;
+import nabble.naml.compiler.Program;
+import nabble.naml.compiler.ScopedInterpreter;
+import nabble.naml.compiler.Source;
+import nabble.naml.compiler.Template;
+import nabble.naml.compiler.Usage;
+import nabble.naml.dom.Attribute;
+import nabble.naml.dom.Container;
+import nabble.naml.dom.Element;
+import nabble.naml.dom.ElementName;
+import nabble.naml.dom.EmptyElement;
+import nabble.naml.dom.Naml;
+import nabble.naml.namespaces.BasicNamespace;
+import nabble.naml.namespaces.CommandDoc;
+import nabble.naml.namespaces.ListSequence;
+import nabble.naml.namespaces.StringList;
+
+import javax.servlet.ServletException;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+
+@Namespace(
+	name = "macro_source",
+	global = true
+)
+public class MacroSourceNamespace extends ServletNamespaceUtils {
+
+	private Site site;
+	private String[] baseClassArray;
+	private String baseClasses;
+	private String nextBreadcrumbs;
+	private String[] navigationBreadcrumbs;
+	private Meaning meaning;
+	private String macroOverriddenId;
+	private String macroWhichOverridesId;
+
+	MacroSourceNamespace(Site site, String id, String base, String breadcrumbs) {
+		this.site = site;
+		Program program = site.getProgram();
+		meaning = program.getMeaning(id);
+		if( meaning == null )
+			throw new NullPointerException("meaning is null for: "+id);
+
+		this.baseClasses = base;
+		this.baseClassArray = buildBaseClassArray(base);
+		this.nextBreadcrumbs = buildNextBreadcrumbs(id, breadcrumbs);
+
+		boolean hasBaseClasses = base != null && base.length() > 0;
+		if (hasBaseClasses) {
+			try {
+				Meaning root = program.getMeaning(navigationBreadcrumbs[0]);
+				program.getTemplate(root.getName(), this.baseClassArray);
+			} catch (CompileException e) {
+				throw new RuntimeException(e);
+			}
+		}
+
+		if (meaning instanceof Macro) {
+			Macro macro = (Macro) meaning;
+			Macro overridden = program.getMacroOverriddenBy(macro);
+			this.macroOverriddenId = overridden != null? overridden.getId() : null;
+			Macro overrides = program.getMacroWhichOverrides(macro);
+			this.macroWhichOverridesId = overrides != null? overrides.getId() : null;
+		}
+	}
+
+	static String[] buildBaseClassArray(String base) {
+		boolean hasBaseClasses = base != null && base.length() > 0;
+		String[] baseArray = hasBaseClasses? base.split("-") : new String[0];
+
+		String basicNamespace = nabble.naml.namespaces.BasicNamespace.class.getName();
+		String nabbleNamespace = nabble.view.web.template.NabbleNamespace.class.getName();
+
+		if (baseArray.length >=2 && baseArray[0].equals(basicNamespace) && baseArray[1].equals(nabbleNamespace)) {
+			return baseArray;
+		} else {
+			String[] array = new String[baseArray.length+2];
+			array[0] = basicNamespace;
+			array[1] = nabbleNamespace;
+
+			int i = 2;
+			for (String className : baseArray) {
+				array[i++] = className;
+			}
+			return array;
+		}
+	}
+
+	private String buildNextBreadcrumbs(String id, String breadcrumbs) {
+		if (breadcrumbs == null) {
+			navigationBreadcrumbs = new String[] { id };
+			return id;
+		} else {
+			String[] breadcrumbParts = breadcrumbs.split("-");
+			navigationBreadcrumbs = new String[breadcrumbParts.length+1];
+			int i = 0;
+			for (String s : breadcrumbParts) {
+				navigationBreadcrumbs[i++] = s;
+			}
+			navigationBreadcrumbs[i] = meaning.getId();
+			return breadcrumbs + '-' + navigationBreadcrumbs[i];
+		}
+	}
+
+	@Command public void id(IPrintWriter out,Interpreter interp) {
+		out.print(meaning.getId());
+	}
+
+	@Command public void name(IPrintWriter out,Interpreter interp) {
+		out.print(meaning.getName());
+	}
+
+	@Command public void macro_overridden_id(IPrintWriter out,Interpreter interp) {
+		out.print(macroOverriddenId);
+	}
+
+	@Command public void macro_which_overrides_id(IPrintWriter out,Interpreter interp) {
+		out.print(macroWhichOverridesId);
+	}
+
+	@Command public void is_compiled(IPrintWriter out,Interpreter interp) {
+		out.print(meaning instanceof Macro && site.getProgram().isCompiled(meaning));
+	}
+
+	public static final CommandSpec documentation = requiresServletNamespace;
+
+	@Command public void documentation(IPrintWriter out,Interpreter interp)
+		throws ServletException
+	{
+		String name = meaning.getName();
+		Collection<String> namespaces = meaning.getRequiredNamespaces();
+		Site site = NabbleNamespace.current().site();
+		Template template = site.getTemplate(DocNamespace.getDocName(name,namespaces), BasicNamespace.class, NabbleNamespace.class, DocNamespace.class);
+		if( template == null && !namespaces.isEmpty() )
+			template = site.getTemplate(DocNamespace.getDocName(name,Collections.<String>emptySet()), BasicNamespace.class, NabbleNamespace.class, DocNamespace.class);
+		if( template == null ) {
+			if( meaning instanceof JavaCommand ) {
+				JavaCommand jc = (JavaCommand)meaning;
+				CommandDoc doc = jc.getMethod().getAnnotation(CommandDoc.class);
+
+				String value = doc == null? "" : doc.value();
+				String[] params = doc == null? new String[0] : doc.params();
+				String[] seeAlso = doc == null? new String[0] : doc.seeAlso();
+
+				template = site.getTemplate("binary doc", BasicNamespace.class, NabbleNamespace.class, DocNamespace.BinaryDocNamespace.class);
+				if( template != null ) {
+					DocNamespace.BinaryDocNamespace ns = new DocNamespace.BinaryDocNamespace(site, jc, value, params, seeAlso);
+					template.run(out,Collections.<String,Object>emptyMap(),new BasicNamespace(template), new NabbleNamespace(site), ns);
+					return;
+				}
+			}
+			template = site.getTemplate("doc not found",BasicNamespace.class,NabbleNamespace.class,DocNamespace.class);
+		}
+		DocNamespace ns = new DocNamespace();
+		template.run(out,Collections.<String,Object>emptyMap(), new BasicNamespace(template), new NabbleNamespace(site), ns);
+	}
+
+	public static final CommandSpec navigation_breadcrumbs = CommandSpec.DO;
+
+	@Command public void navigation_breadcrumbs(IPrintWriter out, ScopedInterpreter<Parts> interp) {
+		List<CommandInfo> infos = new ArrayList<CommandInfo>();
+		Program program = site.getProgram();
+		StringBuilder breadCrumbs = new StringBuilder();
+		for (String id : navigationBreadcrumbs) {
+			Meaning m = program.getMeaning(id);
+			// Skip broken meanings in the breadcrumbs
+			if (m != null)
+				infos.add(new CommandInfo(m,baseClasses,breadCrumbs.length() == 0? null : breadCrumbs.toString()));
+			if( breadCrumbs.length() > 0 )
+				breadCrumbs.append('-');
+			breadCrumbs.append(id);
+		}
+		out.print(interp.getArg(new Parts(infos),"do"));
+	}
+
+	@Command public void source(IPrintWriter out, Interpreter interp) {
+		out.print(getSource(meaning));
+	}
+
+	@Command public void source_path(IPrintWriter out, Interpreter interp) {
+		String path = null;
+		if (meaning instanceof Macro) {
+			path = "/template/NamlEditor$ViewFile.jtp?file="+ ((Macro) meaning).source.toString();
+		}
+		out.print(path);
+	}
+
+	private static String getSource(Meaning m) {
+		if (m instanceof Macro) {
+			String source = ((Macro) m).source.toString();
+			return source.endsWith(".naml")? source : source + ".naml";
+		}
+		return null;
+	}
+
+	@Command public void has_macro_overridden(IPrintWriter out,Interpreter interp) {
+		out.print(macroOverriddenId != null);
+	}
+
+	@Command public void has_macro_which_overrides(IPrintWriter out,Interpreter interp) {
+		out.print(macroWhichOverridesId != null);
+	}
+
+	@Command public void is_override(IPrintWriter out,Interpreter interp) {
+		out.print(isOverride(meaning));
+	}
+
+	private static boolean isOverride(Meaning meaning) {
+		if( !(meaning instanceof Macro) )
+			return false;
+		return ((Macro) meaning).isOverride();
+	}
+
+	@Command public void is_custom_tweak(IPrintWriter out,Interpreter interp) {
+		out.print(isCustomTweak(meaning));
+	}
+
+	@Command public void is_configuration_tweak(IPrintWriter out,Interpreter interp) {
+		out.print(isConfigurationTweak(meaning));
+	}
+
+	private static boolean isCustomTweak(Meaning meaning) {
+		if( !(meaning instanceof Macro) )
+			return false;
+		Source source = ((Macro) meaning).source;
+		return ModuleManager.isCustomTweak(source);
+	}
+
+	private static boolean isConfigurationTweak(Meaning meaning) {
+		if( !(meaning instanceof Macro) )
+			return false;
+		Source source = ((Macro) meaning).source;
+		return ModuleManager.isConfigurationTweak(source);
+	}
+
+	@Command public void is_binary(IPrintWriter out,Interpreter interp) {
+		out.print(!(meaning instanceof Macro));
+	}
+
+	@Command public void tweak_file_contents(IPrintWriter out,Interpreter interp) {
+		String file = null;
+		if (meaning instanceof Macro) {
+			Macro macro = (Macro) meaning;
+			if (isCustomTweak(meaning)) {
+				file = macro.source.content;
+			} else {
+				file = macro.element.toString();
+				file = file.replace("<macro ", "<override_macro ");
+				file = file.replace("</macro", "</override_macro");
+				file = file.replace("<subroutine ", "<override_subroutine ");
+				file = file.replace("</subroutine", "</override_subroutine");
+				file = file.replace("<translation ", "<override_translation ");
+				file = file.replace("</translation", "</override_translation");
+			}
+		}
+		out.print(file);
+	}
+
+	@Command public void macro_opening_tag(IPrintWriter out,Interpreter interp) {
+		String tag = null;
+		if (meaning instanceof Macro) {
+			Macro macro = (Macro) meaning;
+			tag = macro.element.openingTag();
+			if (!isCustomTweak(meaning)) {
+				tag = tag.replace("<macro ", "<override_macro ");
+				tag = tag.replace("<subroutine ", "<override_subroutine ");
+				tag = tag.replace("<translation ", "<override_translation ");
+			}
+		}
+		out.print(tag);
+	}
+
+	public static final CommandSpec rows = CommandSpec.DO;
+
+	@Command public void rows(IPrintWriter out,ScopedInterpreter<Rows> interp)
+		throws IOException, ServletException
+	{
+		if (meaning instanceof Macro) {
+			Macro m = (Macro) meaning;
+			List<CommandInfo> infos = new ArrayList<CommandInfo>();
+			List<Parts> rows = new ArrayList<Parts>();
+			buildElement(m.element, infos, rows, false);
+			if (infos.size() > 0)
+				rows.add(new Parts(infos));
+
+			Object block = interp.getArg(new Rows(rows, m),"do");
+			out.print(block);
+		}
+	}
+
+	private void traverse(Naml naml, List<CommandInfo> parts, List<Parts> rows, boolean isAttribute) {
+		for (Object o : naml) {
+			if (o instanceof EmptyElement) {
+				buildEmptyElement((EmptyElement) o, parts, rows, isAttribute);
+			} else if (o instanceof Container) {
+				buildElement((Container) o, parts, rows, isAttribute);
+			} else {
+				processString(o.toString(), parts, rows);
+			}
+		}
+	}
+
+	private void buildEmptyElement(EmptyElement e, List<CommandInfo> parts, List<Parts> rows, boolean isAttribute) {
+		startTag(parts, isAttribute);
+		extractParts(e, parts);
+		extractAttributes(e, parts, rows);
+		finishTag(e, parts, rows, isAttribute);
+	}
+
+	private void buildElement(Element e, List<CommandInfo> parts, List<Parts> rows, boolean isAttribute) {
+		startTag(parts, isAttribute);
+		extractParts(e, parts);
+
+		if (e.name().endsWithDot())
+			parts.add(new CommandInfo("."));
+
+		extractAttributes(e, parts, rows);
+		finishTag(e, parts, rows, isAttribute);
+		if (e instanceof Container) {
+			Container container = (Container) e;
+			traverse(container.contents(), parts, rows, isAttribute);
+			closingTag(container, isAttribute, parts);
+		}
+	}
+
+	private void closingTag(Container e, boolean isAttribute, List<CommandInfo> parts) {
+		String closingTag = e
+			.closingTag()
+			.replace("<",isAttribute?"[":"&lt;")
+			.replace(">",isAttribute?"]":"&gt;");
+		parts.add(new CommandInfo(closingTag));
+	}
+
+	private void finishTag(Element e, List<CommandInfo> infos, List<Parts> rows, boolean isAttribute) {
+		processString(e.spaceAtEndOfOpeningTag(), infos, rows);
+		infos.add(new CommandInfo(
+			(e instanceof EmptyElement? '/': "") +
+	        (isAttribute? "]":"&gt;")
+		));
+	}
+
+	private void startTag(List<CommandInfo> parts, boolean isAttribute) {
+		parts.add(new CommandInfo(isAttribute?"[":"&lt;"));
+	}
+
+	private void extractAttributes(Element e, List<CommandInfo> parts, List<Parts> rows) {
+		for (Attribute attribute : e.attributes()) {
+			processString(attribute.spaceBeforeName(), parts, rows);
+			parts.add(new CommandInfo(
+				attribute.name() +
+				attribute.spaceAfterName() +
+				'=' +
+				attribute.quote())
+			);
+			traverse(attribute.value(), parts, rows, true);
+			parts.add(new CommandInfo(String.valueOf(attribute.quote())));
+		}
+	}
+
+	private void extractParts(Element e, List<CommandInfo> parts) {
+		int i = 0;
+		List<Macro> currentMacroPath = getCurrentMacroPath();
+		Usage currentUsage = site.getProgram().getUsage(baseClassArray, currentMacroPath);
+		for (ElementName.Part p : e.name().parts()) {
+			if (i++ > 0)
+				parts.add(new CommandInfo("."));
+			Meaning meaning = site.getProgram().getMeaning(p, currentUsage);
+			if (meaning != null) {
+				boolean isArgument = meaning instanceof ParamMeaning;
+				boolean isOverridden = "overridden".equals(p.text());
+				if (isArgument || isOverridden) {
+					parts.add(new CommandInfo(p.text()));
+				} else {
+					parts.add(new CommandInfo(meaning, baseClasses, nextBreadcrumbs));
+				}
+			} else
+				parts.add(new CommandInfo(p.text()));
+		}
+	}
+
+	private List<Macro> getCurrentMacroPath() {
+		List<Macro> macroPath = new ArrayList<Macro>();
+		Program program = site.getProgram();
+		for (String id : navigationBreadcrumbs) {
+			macroPath.add((Macro) program.getMeaning(id));
+		}
+		return macroPath;
+	}
+
+	private void processString(String e, List<CommandInfo> infos, List<Parts> rows) {
+		StringBuilder builder = new StringBuilder();
+		int i = 0;
+		while (i < e.length()) {
+			char c = e.charAt(i);
+			if (c == ' ')
+				builder.append("&ensp;");
+			else if (c == '\t')
+				builder.append("&nbsp;&nbsp;&nbsp;&nbsp;");
+			else if ((c == '\r' && e.charAt(i+1) == '\n') || c == '\n') {
+				if (builder.length() > 0) {
+					infos.add(new CommandInfo(builder.toString()));
+					builder.setLength(0);
+				}
+				rows.add(new Parts(new ArrayList<CommandInfo>(infos)));
+				infos.clear();
+				if (c == '\r')
+					i++; // CRLF
+			} else if (c == '<') {
+				builder.append("&lt;");
+			} else if (c == '>') {
+				builder.append("&gt;");
+			} else if (c == '&') {
+				builder.append("&amp;");
+			} else
+				builder.append(c);
+			i++;
+		}
+		if (builder.length() > 0)
+			infos.add(new CommandInfo(builder.toString()));
+	}
+
+	static String csv(Collection<String> strings) {
+		StringBuilder b = new StringBuilder();
+		for (String s : strings) {
+			if (b.length() > 0)
+				b.append(", ");
+			b.append(s);
+		}
+		return b.toString();
+	}
+
+	@Namespace (
+		name = "macro_row_list",
+		global = true
+	)
+	public static final class Rows extends ListSequence<Parts>
+	{
+
+		private final Macro macro;
+
+		Rows(List<Parts> rows, Macro macro) {
+			super(rows);
+			this.macro = macro;
+		}
+
+		@Command public void line_number(IPrintWriter out,Interpreter interp) {
+			out.print(index + 1);
+		}
+
+		@Command public void file_line_number(IPrintWriter out,Interpreter interp) {
+			out.print(macro.element.lineNumber() + index + 1);
+		}
+
+		public static final CommandSpec current_row = CommandSpec.DO;
+
+		@Command public void current_row(IPrintWriter out,ScopedInterpreter<Parts> interp) {
+			out.print(interp.getArg(get(),"do"));
+		}
+
+		@Command public void next_row(IPrintWriter out,Interpreter interp) {
+			next_element(out,interp);
+		}
+	}
+
+	@Namespace (
+		name = "macro_parts_list",
+		global = true
+	)
+	public static final class Parts extends ListSequence<CommandInfo> {
+
+		Parts(List<CommandInfo> infos) {
+			super(infos);
+		}
+
+		public static final CommandSpec parts = CommandSpec.DO;
+
+		@Command public void parts(IPrintWriter out,ScopedInterpreter<CommandInfo> interp) {
+			out.print( interp.getArg(get(),"do") );
+		}
+
+		@Command public void is_blank(IPrintWriter out, Interpreter interp) {
+			out.print(elements.size() == 0);
+		}
+	}
+
+	public static final CommandSpec macro_usages = CommandSpec.DO;
+
+	@Command public void macro_usages(IPrintWriter out,ScopedInterpreter<MacroUsages> interp)
+		throws IOException, ServletException, CompileException
+	{
+		List<Commands> macros = new ArrayList<Commands>();
+		Set<Usage> usages = site.getProgram().getUsages(meaning);
+		if (usages == null)
+			usages = new HashSet<Usage>();
+		for (Usage u : usages) {
+			List<CommandInfo> infos = new ArrayList<CommandInfo>();
+			StringBuilder breadcrumbs = new StringBuilder();
+			String usageBase = asBaseParam(u.baseIds());
+			for (Macro m : u.macroPath()) {
+				infos.add(new CommandInfo(m, usageBase, breadcrumbs.length() == 0? null : breadcrumbs.toString()));
+
+				if (breadcrumbs.length() > 0)
+					breadcrumbs.append('-');
+				breadcrumbs.append(m.getId());
+			}
+			infos.add(new CommandInfo(meaning, usageBase, breadcrumbs.toString()));
+			macros.add(new Commands(infos));
+		}
+		Object block = interp.getArg(new MacroUsages(macros),"do");
+		out.print(block);
+	}
+
+	static String asBaseParam(String[] base) {
+		StringBuilder b = new StringBuilder();
+		for (String c : base) {
+			if (b.length() > 0)
+				b.append('-');
+			b.append(c);
+		}
+		return b.toString();
+	}
+
+	static String asBreadcrumbsParam(List<Macro> macroPath) {
+		StringBuilder breadcrumbs = new StringBuilder();
+		for (Macro p : macroPath) {
+			if (breadcrumbs.length() > 0)
+				breadcrumbs.append('-');
+			breadcrumbs.append(p.getId());
+		}
+		return breadcrumbs.toString();
+	}
+
+	@Namespace (
+		name = "macro_usages",
+		global = true
+	)
+	public static final class MacroUsages extends ListSequence<Commands>
+	{
+		public MacroUsages(List<Commands> elements) {
+			super(elements);
+		}
+
+		public static final CommandSpec current_usage = CommandSpec.DO;
+
+		@Command public void current_usage(IPrintWriter out,ScopedInterpreter<Commands> interp) {
+			out.print(interp.getArg(get(),"do"));
+		}
+	}
+
+	@Namespace (
+		name = "command_list",
+		global = true
+	)
+	public static final class Commands extends ListSequence<CommandInfo>
+	{
+		public Commands(List<CommandInfo> elements) {
+			super(elements);
+		}
+
+		public static final CommandSpec current_command = CommandSpec.DO;
+
+		@Command public void current_command(IPrintWriter out,ScopedInterpreter<CommandInfo> interp) {
+			out.print(interp.getArg(get(),"do"));
+		}
+	}
+
+	@Namespace (
+		name = "command_info",
+		global = false
+	)
+	public static final class CommandInfo {
+		private final String id;
+		private final String name;
+		private final String tag;
+		private final String source;
+		private final int fileLineNumber;
+		private final String baseClasses;
+		private final String breadcrumbs;
+		private final String requiredNamespaces;
+		private final boolean isCustomTweak;
+		private final boolean isMacro;
+		private final boolean isBinary;
+		private final String namespaceClass;
+		private final String[] parameterNames;
+
+		static final Comparator<? super CommandInfo> MACRO_NAME_COMPARATOR = new Comparator<MacroSourceNamespace.CommandInfo>() {
+			public int compare(MacroSourceNamespace.CommandInfo o1, MacroSourceNamespace.CommandInfo o2) {
+				return o1.name.compareTo(o2.name);
+			}
+		};
+
+		public CommandInfo(String name) {
+			this.id = null;
+			this.tag = name;
+			this.name = name;
+			this.isMacro = false;
+			this.isBinary = false;
+			this.source = null;
+			this.fileLineNumber = 0;
+			this.baseClasses = null;
+			this.breadcrumbs = null;
+			this.requiredNamespaces = null;
+			this.isCustomTweak = false;
+			this.namespaceClass = null;
+			this.parameterNames = null;
+		}
+
+		public CommandInfo(Meaning meaning, String baseClasses, String breadcrumbs) {
+			this.id = meaning.getId();
+			this.name = meaning.getName();
+			this.tag = this.name.startsWith("translation:")? "t" : this.name;
+			this.isMacro = meaning instanceof Macro;
+			this.isBinary = meaning instanceof JavaCommand;
+			this.source = getSource(meaning);
+			this.fileLineNumber = isMacro? ((Macro) meaning).element.lineNumber() + 1 : 0;
+			this.baseClasses = baseClasses;
+			this.breadcrumbs = breadcrumbs;
+			this.requiredNamespaces = csv(meaning.getRequiredNamespaces());
+			this.isCustomTweak = isCustomTweak(meaning);
+			this.namespaceClass = meaning instanceof JavaCommand? ((JavaCommand) meaning).getMethod().getDeclaringClass().getSimpleName() : null;
+			this.parameterNames = isMacro? ((Macro) meaning).getParameterNames() : meaning instanceof JavaCommand? ((JavaCommand) meaning).getParameterNames() : null;
+		}
+
+		@Command("id") public void _id(IPrintWriter out,Interpreter interp) {
+			out.print(id);
+		}
+
+		@Command("name") public void _name(IPrintWriter out,Interpreter interp) {
+			out.print(name);
+		}
+
+		@Command("tag") public void _tag(IPrintWriter out,Interpreter interp) {
+			out.print(tag);
+		}
+
+		@Command("source") public void _source(IPrintWriter out,Interpreter interp) {
+			out.print(source);
+		}
+
+		@Command public void naml_breadcrumbs(IPrintWriter out,Interpreter interp) {
+			out.print(breadcrumbs);
+		}
+
+		@Command public void base(IPrintWriter out,Interpreter interp) {
+			out.print(baseClasses);
+		}
+
+		@Command public void file_line_number(IPrintWriter out,Interpreter interp) {
+			out.print(fileLineNumber);
+		}
+
+		@Command public void required_namespaces(IPrintWriter out,Interpreter interp) {
+			out.print(requiredNamespaces);
+		}
+
+		@Command public void is_custom_tweak(IPrintWriter out,Interpreter interp) {
+			out.print(isCustomTweak);
+		}
+
+		@Command public void is_macro(IPrintWriter out,Interpreter interp) {
+			out.print(isMacro);
+		}
+
+		@Command public void namespace_class(IPrintWriter out,Interpreter interp) {
+			out.print(namespaceClass);
+		}
+
+		@Command public void is_binary(IPrintWriter out,Interpreter interp) {
+			out.print(isBinary);
+		}
+
+		@Command public void has_parameters(IPrintWriter out,Interpreter interp) {
+			out.print(parameterNames != null && parameterNames.length > 0);
+		}
+
+		public static final CommandSpec parameter_names = CommandSpec.DO;
+
+		@Command public void parameter_names(IPrintWriter out, ScopedInterpreter<StringList> interp) {
+			out.print(interp.getArg(new StringList(Arrays.asList(parameterNames)),"do"));
+		}
+	}
+}