Skip to content

Commit

Permalink
Merge pull request #1382 from jediwhale/master
Browse files Browse the repository at this point in the history
Markup system plugins
  • Loading branch information
Mike Stockdale authored Aug 18, 2022
2 parents 5cde620 + 57d4a24 commit da7e8d5
Show file tree
Hide file tree
Showing 17 changed files with 191 additions and 23 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
A markup system plugin allows the use of markup syntax different from the standard !-FitNesse-! wiki markup.

The plugin registers itself with a name and a factory function:
{{{public class PluginFeatureFactory extends PluginFeatureFactoryBase {

@Override
public void registerMarkupSystems(MarkUpSystems systems) {
systems.register(MyMarkup.NAME, MyMarkup::new);
System.out.println(MyMarkup.NAME + " system registered");
... any initialization required ...
}
}
}}}
The markup system class must implement the !-MarkUpSystem-! interface:
{{{public class MyMarkup implements MarkUpSystem {
public static final String NAME = "mymarkup";
... implemented methods...
}
}}}
To use the new markup syntax, include a comment with the markup system name on the first line of the page:
{{{#lang mymarkup
... page content ...
}}}
Other pages in the wiki will continue to use the standard wiki syntax.
Original file line number Diff line number Diff line change
Expand Up @@ -12,29 +12,38 @@ The interface looks like this:

{{{public interface PluginFeatureFactory {

Authenticator getAuthenticator();
default Authenticator getAuthenticator() { return null; }

ContentFilter getContentFilter();
default ContentFilter getContentFilter() { return null; }

void registerResponders(ResponderFactory responderFactory) throws PluginException;
default String getDefaultTheme() { return null; }

void registerSymbolTypes(SymbolProvider symbolProvider) throws PluginException;
default void registerResponders(ResponderFactory responderFactory) throws PluginException { }

void registerWikiPageFactories(WikiPageFactoryRegistry wikiPageFactoryRegistry) throws PluginException;
default void registerSymbolTypes(SymbolProvider symbolProvider) throws PluginException { }

void registerTestSystemFactories(TestSystemFactoryRegistry testSystemFactoryRegistry) throws PluginException;
default void registerWikiPageFactories(WikiPageFactoryRegistry wikiPageFactoryRegistry) throws PluginException { }

void registerSlimTables(SlimTableFactory slimTableFactory) throws PluginException;
default void registerFormatters(FormatterRegistry registrar) throws PluginException { }

void registerCustomComparators(CustomComparatorRegistry customComparatorRegistry) throws PluginException;
default void registerTestSystemFactories(TestSystemFactoryRegistry testSystemFactoryRegistry) throws PluginException { }

default void registerSlimTables(SlimTableFactory slimTableFactory) throws PluginException { }

default void registerCustomComparators(CustomComparatorRegistry customComparatorRegistry) throws PluginException { }

default void registerTestRunFactories(TestRunFactoryRegistry runFactoryRegistry) throws PluginException { }

default void registerMarkupSystems(MarkUpSystems systems) {}
}
}}}
The methods ''getAuthenticator()'' should return one instance. If multiple plugins provide an authenticator, the first authenticator found will be used. The setting can be overridden by explicitly defining an authenticator in the configuration file.

The same rules are applicable for ''getContentFilter()''.

The ''registerXxx'' methods are invoked with the appropriate provider or registry. It is the responsibility of the plugin to register it's plugins with those providers.
The ''registerXxx'' methods are invoked with the appropriate provider or registry. It is the responsibility of the plugin to register its plugins with those providers.

>MarkupSystemPlugIns

!2 Documentation
Every plugin should provide documentation which should be placed in the .PlugIns folder.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<properties>
<Files/>
<RecentChanges/>
<Search/>
<Static/>
<WhereUsed/>
<Files/>
<RecentChanges/>
<Search/>
<Static/>
<WhereUsed/>
</properties>
2 changes: 2 additions & 0 deletions src/fitnesse/ContextConfigurator.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import fitnesse.wiki.fs.FileSystemPageFactory;
import fitnesse.wiki.fs.VersionsController;
import fitnesse.wiki.fs.ZipFileVersionsController;
import fitnesse.wikitext.MarkUpSystems;
import fitnesse.wikitext.parser.SymbolProvider;
import fitnesse.wikitext.parser.decorator.SlimTableDefaultColoring;

Expand Down Expand Up @@ -190,6 +191,7 @@ public FitNesseContext makeFitNesseContext() throws IOException, PluginException
pluginsLoader.loadSlimTables(slimTableFactory);
pluginsLoader.loadCustomComparators(customComparatorRegistry);
pluginsLoader.loadTestRunFactories(context.testRunFactoryRegistry);
pluginsLoader.loadMarkupSystems(MarkUpSystems.STORE);

ContentFilter contentFilter = pluginsLoader.loadContentFilter();

Expand Down
2 changes: 2 additions & 0 deletions src/fitnesse/plugins/PluginFeatureFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import fitnesse.testsystems.slim.CustomComparatorRegistry;
import fitnesse.testsystems.slim.tables.SlimTableFactory;
import fitnesse.wiki.WikiPageFactoryRegistry;
import fitnesse.wikitext.MarkUpSystems;
import fitnesse.wikitext.parser.SymbolProvider;

public interface PluginFeatureFactory {
Expand Down Expand Up @@ -49,4 +50,5 @@ default void registerCustomComparators(CustomComparatorRegistry customComparator
default void registerTestRunFactories(TestRunFactoryRegistry runFactoryRegistry) throws PluginException {
}

default void registerMarkupSystems(MarkUpSystems systems) {}
}
10 changes: 8 additions & 2 deletions src/fitnesse/plugins/PluginsLoader.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import fitnesse.testsystems.slim.CustomComparatorRegistry;
import fitnesse.testsystems.slim.tables.SlimTableFactory;
import fitnesse.wiki.WikiPageFactoryRegistry;
import fitnesse.wikitext.MarkUpSystems;
import fitnesse.wikitext.parser.SymbolProvider;

import java.io.File;
Expand All @@ -37,8 +38,7 @@ public PluginsLoader(ComponentFactory componentFactory, ClassLoader classLoader)
}

private Collection<PluginFeatureFactory> findPluginFeatureFactories() throws PluginException {
List<PluginFeatureFactory> factories = new ArrayList<>();
factories.addAll(PropertyBasedPluginFeatureFactory.loadFromProperties(componentFactory));
List<PluginFeatureFactory> factories = new ArrayList<>(PropertyBasedPluginFeatureFactory.loadFromProperties(componentFactory));

for (PluginFeatureFactory factory : ServiceLoader.load(PluginFeatureFactory.class, classLoader)) {
factories.add(factory);
Expand Down Expand Up @@ -151,4 +151,10 @@ public void loadTestRunFactories(final TestRunFactoryRegistry registry) throws P
pff.registerTestRunFactories(registry);
}
}

public void loadMarkupSystems(MarkUpSystems systems) {
for (PluginFeatureFactory pff : pluginFeatureFactories) {
pff.registerMarkupSystems(systems);
}
}
}
2 changes: 1 addition & 1 deletion src/fitnesse/testrunner/WikiTestPage.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public String getHtml() {
// -AJM- Okay, this is not as clean as I'd like it to be, but for now it does the trick
if (containsWikitext()) {
String content = getDecoratedContent();
return MarkUpSystem.make().parse(BaseWikitextPage.makeParsingPage((BaseWikitextPage) sourcePage), content).translateToHtml();
return MarkUpSystem.make(content).parse(BaseWikitextPage.makeParsingPage((BaseWikitextPage) sourcePage), content).translateToHtml();
} else {
return sourcePage.getHtml();
}
Expand Down
3 changes: 2 additions & 1 deletion src/fitnesse/wiki/BaseWikitextPage.java
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ public SyntaxTree getSyntaxTree() {
private void parse() {
if (syntaxTree == null) {
parsingPage = makeParsingPage(this);
syntaxTree = MarkUpSystem.make().parse(parsingPage, getData().getContent());
String content = getData().getContent();
syntaxTree = MarkUpSystem.make(content).parse(parsingPage, content);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ private boolean updatePageContent(WikiPage currentPage, PageData data) {
}

private String getUpdatedPageContent(WikiPage currentPage) {
return MarkUpSystem.make().changeReferences(new WikiSourcePage(currentPage), reference -> changeReference.changeReference(currentPage, reference));
return MarkUpSystem.make(currentPage.getData().getContent())
.changeReferences(new WikiSourcePage(currentPage), reference -> changeReference.changeReference(currentPage, reference));
}
}
2 changes: 1 addition & 1 deletion src/fitnesse/wiki/search/WhereUsedPageFinder.java
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ private void checkSymbolicLinks() {
}

private void checkContent() {
MarkUpSystem.make().findWhereUsed(new WikiSourcePage(currentPage), name -> {
MarkUpSystem.make(currentPage.getData().getContent()).findWhereUsed(new WikiSourcePage(currentPage), name -> {
WikiPage referencedPage = new WikiWordReference(currentPage, name).getReferencedPage();
if (referencedPage != null && referencedPage.equals(subjectPage)) {
addHit();
Expand Down
6 changes: 4 additions & 2 deletions src/fitnesse/wikitext/MarkUpSystem.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,13 @@ public interface MarkUpSystem {
void findWhereUsed(SourcePage page, Consumer<String> takeWhereUsed);
String changeReferences(SourcePage page, Function<String, Optional<String>> changeReference);

static MarkUpSystem make() { return new MarkUpSystemV2(); } //todo: eventually can make different kinds of markup systems
static MarkUpSystem make() { return new MarkUpSystemV2(); }
static MarkUpSystem make(String content) { return MarkUpSystems.STORE.make(content); }

static List<String> listVariables(WikiPage page) {
ParsingPage parsingPage = new ParsingPage(new WikiSourcePage(page));
MarkUpSystem.make().parse(parsingPage, page.getData().getContent());
String content = page.getData().getContent();
MarkUpSystem.make(content).parse(parsingPage, content);
return parsingPage.listVariables();
}
}
52 changes: 52 additions & 0 deletions src/fitnesse/wikitext/MarkUpSystems.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package fitnesse.wikitext;

import fitnesse.wikitext.parser.MarkUpSystemV2;
import fitnesse.wikitext.parser.MatchResult;
import fitnesse.wikitext.parser.Matcher;
import fitnesse.wikitext.parser.ScanString;

import java.util.HashMap;
import java.util.Map;
import java.util.function.Supplier;

public class MarkUpSystems {
public static MarkUpSystems STORE = new MarkUpSystems();

public MarkUpSystem make(String content) {
String key = findName(content).toLowerCase();
return key.length() > 0 && systems.containsKey(key)
? systems.get(key).get()
: new MarkUpSystemV2();
}

public void register(String name, Supplier<MarkUpSystem> supplier) {
systems.put(name.toLowerCase(), supplier);
}

public static String findName(String content) {
ScanString input = new ScanString(content, 0);

while (true) {
MatchResult leadingMatch = LEADING_MATCHER.makeMatch(input);
if (!leadingMatch.isMatched()) break;
leadingMatch.advance(input);
}

MatchResult langMatch = LANG_MATCHER.makeMatch(input);
if (langMatch.isMatched()) {
langMatch.advance(input);
int startName = input.getOffset();
MatchResult newLineMatch = NEWLINE_MATCHER.findMatch(input);
if (newLineMatch.isMatched()) {
return input.rawSubstring(startName, input.getOffset());
}
}
return "";
}

private static final Map<String, Supplier<MarkUpSystem>> systems = new HashMap<>();

private static final Matcher LANG_MATCHER = new Matcher().string("#lang").whitespace();
private static final Matcher NEWLINE_MATCHER = new Matcher().newLine();
private static final Matcher LEADING_MATCHER = new Matcher().ignoreWhitespace().newLine();
}
4 changes: 4 additions & 0 deletions src/fitnesse/wikitext/parser/MatchResult.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ public void checkLength(int check) {
if (check > 0) addLength(check); else noMatch();
}

public void advance(ScanString scan) {
scan.moveNext(length);
}

private int length;
private final ArrayList<String> options = new ArrayList<>(1);
private boolean matched;
Expand Down
30 changes: 30 additions & 0 deletions src/fitnesse/wikitext/parser/Matcher.java
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,28 @@ public Matcher optional(String... options) {
return this;
}

public Matcher newLine() {
if (firsts == null) {
firsts = new ArrayList<>();
firsts.add('\r');
firsts.add('\n');
}
matches.add((input, symbols, match) -> {
if (input.matches("\r\n", match.getLength())) {
match.addLength(2);
}
else if (input.matches("\n", match.getLength())) {
match.addLength(1);
}
else match.noMatch();
});
return this;
}

public MatchResult makeMatch(ScanString input) {
return makeMatch(input, new SymbolStream());
}

public MatchResult makeMatch(ScanString input, SymbolStream symbols) {
MatchResult result = new MatchResult();
for (ScanMatch match: matches) {
Expand All @@ -142,4 +164,12 @@ public MatchResult makeMatch(ScanString input, SymbolStream symbols) {
}
return result;
}

public MatchResult findMatch(ScanString input) {
while (true) {
MatchResult match = makeMatch(input);
if (match.isMatched() || input.isEnd()) return match;
input.moveNext();
}
}
}
2 changes: 2 additions & 0 deletions src/fitnesse/wikitext/parser/ScanString.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ public ScanString(ScanString other) {
public void setOffset(int offset) { this.offset = offset; }
public int getOffset() { return offset; }
public void moveNext() { offset++; }
public void moveNext(int length) { offset += length; }
public boolean isEnd() { return isEnd(0); }
public boolean isEnd(int startAt) { return offset + startAt >= input.length(); }
public void markStart(int markStartOffset) { this.markStartOffset = markStartOffset; }
Expand Down Expand Up @@ -95,4 +96,5 @@ public static boolean isVariableName(String content) {
}
return true;
}

}
18 changes: 18 additions & 0 deletions test/fitnesse/wikitext/MarkupSystemsTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package fitnesse.wikitext;

import org.junit.Assert;
import org.junit.Test;

public class MarkupSystemsTest {
@Test
public void findsName() {
assertName("lang on first line", "name", "#lang name\nstuff");
assertName("skips leading blankspace", "name", "\n \n#lang name\nstuff");
assertName("missing lang", "", "#langname\nstuff");
assertName("alternate newline", "name", "#lang name\r\nstuff");
}

private void assertName(String message, String expected, String content) {
Assert.assertEquals(message, expected, MarkUpSystems.findName(content));
}
}
17 changes: 16 additions & 1 deletion test/fitnesse/wikitext/parser/MatcherTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,24 @@ public class MatcherTest {
assertOptionalMatch(0, "", "x");
}

@Test public void newLine() {
Matcher matcher = new Matcher().newLine();
Assert.assertEquals(2, matcher.makeMatch(new ScanString("\r\n", 0)).getLength());
Assert.assertEquals(1, matcher.makeMatch(new ScanString("\n", 0)).getLength());
Assert.assertFalse(matcher.makeMatch(new ScanString("\r", 0)).isMatched());
}

@Test public void findMatch() {
Matcher matcher = new Matcher().string("stuff");
ScanString input = new ScanString("somestuff", 0);
Assert.assertEquals(5, matcher.findMatch(input).getLength());
Assert.assertEquals(4, input.getOffset());
Assert.assertFalse(matcher.findMatch(new ScanString("stuff not here", 5)).isMatched());
}

private void assertOptionalMatch(int length, String options, String input) {
Matcher matcher = new Matcher().optional("<", ">");
MatchResult result = matcher.makeMatch(new ScanString(input, 0), new SymbolStream());
MatchResult result = matcher.makeMatch(new ScanString(input, 0));
Assert.assertEquals(length, result.getLength());
Assert.assertEquals(options, String.join("", result.getOptions()));
}
Expand Down

0 comments on commit da7e8d5

Please sign in to comment.