Skip to content

Commit

Permalink
Merge pull request #424 from aslakknutsen/FORGE-1697
Browse files Browse the repository at this point in the history
Add support for YAML to Highlighter
  • Loading branch information
gastaldi committed Mar 23, 2014
2 parents 767d772 + 7d7cf8d commit 2f1bf1a
Show file tree
Hide file tree
Showing 15 changed files with 466 additions and 53 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,34 @@ public String peek(int length)
return sequence.peek(length);
}

/**
* Find the column number of current position.
*
* (How many chars since last \n). First column is 1.
*
* @param pos start position
* @return
*/
public int column(int pos) {
int currPrePos = 0;
while( (pos +currPrePos) > 0 && !sequence.peek(pos, currPrePos).startsWith("\n"))
{
currPrePos--;
}
return (currPrePos*-1);
}

public int index() {
return sequence.index();
}

public boolean isBeginningOfLine() {
if(sequence.index() == 0) {
return true;
}
return "\n".equals(sequence.peek(-1));
}

private static class StaticMatchResult implements MatchResult
{

Expand Down Expand Up @@ -241,7 +269,16 @@ public CharSequence subSequence(int index, int start, int end)

public String peek(int length)
{
return source.substring(this.index, this.index + length);
return peek(index, length);
}

public String peek(int pos, int length)
{
if(length < 0)
{
return source.substring(pos+length, pos);
}
return source.substring(pos, pos + length);
}

public String pop()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
import org.jboss.forge.addon.text.highlight.scanner.PlainScanner;
import org.jboss.forge.addon.text.highlight.scanner.PropertiesScanner;
import org.jboss.forge.addon.text.highlight.scanner.SQLScanner;
import org.jboss.forge.addon.text.highlight.scanner.XMLScanner;
import org.jboss.forge.addon.text.highlight.scanner.YAMLScanner;

public class Syntax
{
Expand All @@ -27,12 +29,14 @@ public static void builtIns()
Scanner.Factory.registrer(PlainScanner.class);
Scanner.Factory.registrer(JavaScanner.class);
Scanner.Factory.registrer(HTMLScanner.class);
Scanner.Factory.registrer(XMLScanner.class);
Scanner.Factory.registrer(CSSScanner.class);
Scanner.Factory.registrer(JavaScriptScanner.class);
Scanner.Factory.registrer(JSONScanner.class);
Scanner.Factory.registrer(PropertiesScanner.class);
Scanner.Factory.registrer(SQLScanner.class);
Scanner.Factory.registrer(GroovyScanner.class);
Scanner.Factory.registrer(YAMLScanner.class);


Encoder.Factory.registrer(Encoder.Type.TERMINAL.name(), TerminalEncoder.class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ public enum State

public static final String OPTION_START_STATE = "state";

public static final Type TYPE = new Type("GROOVY", "\\.(groovy|gradle)$");
public static final Type TYPE = new Type("GROOVY", "\\.(groovy|gvy|gradle)$");

@Override
public Type getType() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ public enum State
.add(EVENT_ATTRIBUTES, EmbeddedType.script)
.add(new String[] { "style" }, EmbeddedType.style);

public static final Type TYPE = new Type("HTML", "\\.(html|xhtml|xml)$");
public static final Type TYPE = new Type("HTML", "\\.(html|htm|xhtml)$");

@Override
public Type getType() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public enum State
string
}

public static final Type TYPE = new Type("JSON", "\\.(json)$");
public static final Type TYPE = new Type("JSON", "\\.(json|template)$");

@Override
public Type getType() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package org.jboss.forge.addon.text.highlight.scanner;


public class XMLScanner extends HTMLScanner {

public static final Type TYPE = new Type("XML", "\\.(xml|cfc|cfm|tmproj|xaml)$");

@Override
public Type getType() {
return TYPE;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,273 @@
package org.jboss.forge.addon.text.highlight.scanner;

import java.util.Map;
import java.util.regex.MatchResult;
import java.util.regex.Pattern;

import org.jboss.forge.addon.text.highlight.Encoder;
import org.jboss.forge.addon.text.highlight.Scanner;
import org.jboss.forge.addon.text.highlight.StringScanner;
import org.jboss.forge.addon.text.highlight.TokenType;

/*
* Based on https://github.com/rubychan/coderay/blob/master/lib/coderay/scanners/yaml.rb
* Last update sha: 603ff7d0b14521cfd0408aa68e2e1cb6ea9086bc
*
*/
public class YAMLScanner implements Scanner
{
private static final Pattern SPACE = Pattern.compile(" +[\\t ]*");
private static final Pattern SPACE_NEWLINE = Pattern.compile("\\n+");
private static final Pattern COMMENT = Pattern.compile("#.*");
private static final Pattern HEAD = Pattern.compile("---|\\.\\.\\.");
private static final Pattern DOCTYPE = Pattern.compile("%.*");
private static final Pattern STRING = Pattern.compile("(?:\"[^\"]*\")(?=: |:$)", Pattern.MULTILINE);
private static final Pattern DOUBLE_QUOTE = Pattern.compile("\"");
private static final Pattern COMMENT_ONELINE = Pattern.compile(" [^\"\\\\]* (?: \\\\. [^\"\\\\]* )* ", Pattern.DOTALL|Pattern.COMMENTS);
private static final Pattern LINE_CONTINUE = Pattern.compile("[|>][-+]?");
private static final Pattern STRING_ENDLINE = Pattern.compile("(?![!\"*&]).+?(?=$|\\s+#)", Pattern.MULTILINE);
private static final Pattern OPERATOR = Pattern.compile("[-:](?= |$)", Pattern.MULTILINE);
private static final Pattern OPERATOR_BRACKETS = Pattern.compile("[,{}\\[\\]]");
private static final Pattern KEY = Pattern.compile("[-\\w.()\\/ ]*\\S(?= *:(?: |$))", Pattern.MULTILINE);
private static final Pattern KEY_2 = Pattern.compile("(?:\"[^\"\\n]*\"|'[^'\\n]*')(?= *:(?: |$))", Pattern.MULTILINE);
private static final Pattern TYPE_EXP = Pattern.compile("(![\\w\\/]+)(:([\\w:]+))?");
private static final Pattern VARIABLE = Pattern.compile("&\\S+");
private static final Pattern GLOBAL_VARIABLE = Pattern.compile("\\*\\w+");
private static final Pattern CLASS_VARIABLE = Pattern.compile("<<");
private static final Pattern OCTAL = Pattern.compile("\\d\\d:\\d\\d:\\d\\d");
private static final Pattern OCTAL_2 = Pattern.compile("\\d\\d\\d\\d-\\d\\d-\\d\\d\\s\\d\\d:\\d\\d:\\d\\d(\\.\\d+)? [-+]\\d\\d:\\d\\d");
private static final Pattern SYMBOL = Pattern.compile(":\\w+");
private static final Pattern ERROR = Pattern.compile("[^:\\s]+(:(?! |$)[^:\\s]*)* .*");
private static final Pattern ERROR_2 = Pattern.compile("[^:\\s]+(:(?! |$)[^:\\s]*)*");

public enum State
{
initial,
value,
colon
}

public static final Type TYPE = new Type("YAML", "\\.(yml|yaml)$");

@Override
public Type getType() {
return TYPE;
}

@Override
public void scan(StringScanner source, Encoder encoder, Map<String, Object> options) {
Context contxt = new Context();
contxt.state = State.initial;
contxt.key_indent = null;

while (source.hasMore())
{
MatchResult m = null;
if(source.isBeginningOfLine())
{
contxt.key_indent = null;
}

if ((m = source.scan(SPACE)) != null)
{
encoder.textToken(m.group(), TokenType.space);
}
else if ((m = source.scan(SPACE_NEWLINE)) != null)
{
encoder.textToken(m.group(), TokenType.space);
if (m.group().indexOf("\n") != -1)
{
contxt.state = State.initial;
}
}
else if ((m = source.scan(COMMENT)) != null)
{
encoder.textToken(m.group(), TokenType.comment);
}
else if (source.isBeginningOfLine() && head_doctype(source, encoder))
{
continue;
}
else if (contxt.state == State.value && delimiter(source, encoder, contxt))
{
continue;
}
else if (value(source, encoder, contxt))
{
continue;
}
else
{
if(!source.hasMore())
{
throw new RuntimeException("unexpected end");
}
encoder.textToken(source.next(), TokenType.error);
}
}
}

private boolean head_doctype(StringScanner source, Encoder encoder) {
MatchResult m;
if ( (m = source.scan(HEAD)) != null)
{
encoder.beginGroup(TokenType.head);
encoder.textToken(m.group(), TokenType.head);
encoder.endGroup(TokenType.head);
return true;
}
else if ( (m = source.scan(DOCTYPE)) != null)
{
encoder.textToken(m.group(), TokenType.doctype);
return true;
}
return false;
}

private boolean delimiter(StringScanner source, Encoder encoder, Context context) {
MatchResult m;
int string_indent = 0;
if ( source.check(STRING) == null && (m = source.scan(DOUBLE_QUOTE)) != null)
{
encoder.beginGroup(TokenType.string);
encoder.textToken(m.group(), TokenType.delimiter);
if ( (m = source.scan(COMMENT_ONELINE)) != null && !"".equals(m.group()))
{
encoder.textToken(m.group(), TokenType.content);
}
if ( (m = source.scan(DOUBLE_QUOTE)) != null)
{
encoder.textToken(m.group(), TokenType.delimiter);
}
encoder.endGroup(TokenType.string);
return true;
}
else if ( (m = source.scan(LINE_CONTINUE)) != null)
{
encoder.beginGroup(TokenType.string);
encoder.textToken(m.group(), TokenType.delimiter);
string_indent = context.key_indent != null ? context.key_indent:source.column(source.index() - m.group().length())-1;
if ( (m = source.scan(Pattern.compile("(?:\\n+ {" + (string_indent +1) + "}.*)+"))) != null)
{
encoder.textToken(m.group(), TokenType.content);
}
encoder.endGroup(TokenType.string);
return true;
}
else if ( (m = source.scan(STRING_ENDLINE)) != null)
{
encoder.beginGroup(TokenType.string);
encoder.textToken(m.group(), TokenType.content);
string_indent = context.key_indent != null ? context.key_indent:source.column(source.index() - m.group().length())-1;
if ( (m = source.scan(Pattern.compile("(?:\\n+ {" + (string_indent +1) + "}.*)+"))) != null)
{
encoder.textToken(m.group(), TokenType.content);
}

encoder.endGroup(TokenType.string);
return true;
}
return false;
}

private boolean value(StringScanner source, Encoder encoder, Context context) {
MatchResult m;

if ( (m = source.scan(OPERATOR)) != null)
{
if (context.state == State.colon && (m.group().equals(":") | m.group().equals("-")))
{
context.state = State.value;
}
else if (context.state == State.initial && m.group().equals("-"))
{
context.state = State.value;
}
encoder.textToken(m.group(), TokenType.operator);
return true;
}
else if ( (m = source.scan(OPERATOR_BRACKETS)) != null)
{
encoder.textToken(m.group(), TokenType.operator);
return true;
}
else if ( context.state == State.initial && (m = source.scan(KEY)) != null)
{
encoder.textToken(m.group(), TokenType.key);
context.key_indent = source.column(source.index() - m.group().length()) - 1;
context.state = State.colon;
return true;
}
else if ( (m = source.scan(KEY_2)) != null)
{
encoder.beginGroup(TokenType.key);
String match = m.group();
encoder.textToken(match.substring(0, 1), TokenType.delimiter);
if (match.length() > 2)
{
encoder.textToken(match.substring(1, match.length()-1), TokenType.content);
}
encoder.textToken(match.substring(match.length()-1), TokenType.delimiter);
encoder.endGroup(TokenType.key);
context.key_indent = source.column(source.index() - match.length()) - 1;
context.state = State.colon;
return true;
}
else if ( (m = source.scan(TYPE_EXP)) != null)
{
encoder.textToken(m.group(1), TokenType.type);
if (m.group(2) != null)
{
encoder.textToken(":", TokenType.operator);
encoder.textToken(m.group(3), TokenType.class_);
}
return true;
}
else if ( (m = source.scan(VARIABLE)) != null)
{
encoder.textToken(m.group(), TokenType.variable);
return true;
}
else if ( (m = source.scan(GLOBAL_VARIABLE)) != null)
{
encoder.textToken(m.group(), TokenType.global_variable);
return true;
}
else if ( (m = source.scan(CLASS_VARIABLE)) != null)
{
encoder.textToken(m.group(), TokenType.class_variable);
return true;
}
else if ( (m = source.scan(OCTAL)) != null)
{
encoder.textToken(m.group(), TokenType.octal);
return true;
}
else if ( (m = source.scan(OCTAL_2)) != null)
{
encoder.textToken(m.group(), TokenType.octal);
return true;
}
else if ( (m = source.scan(SYMBOL)) != null)
{
encoder.textToken(m.group(), TokenType.symbol);
return true;
}
else if ( (m = source.scan(ERROR)) != null)
{
encoder.textToken(m.group(), TokenType.error);
return true;
}
else if ( (m = source.scan(ERROR_2)) != null)
{
encoder.textToken(m.group(), TokenType.error);
return true;
}
return false;
}

private static class Context {
State state;
Integer key_indent = null;
}
}

0 comments on commit 2f1bf1a

Please sign in to comment.