Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Question: Multiple styles in code area #62

Closed
ghost opened this issue Jun 10, 2014 · 19 comments
Closed

Question: Multiple styles in code area #62

ghost opened this issue Jun 10, 2014 · 19 comments

Comments

@ghost
Copy link

ghost commented Jun 10, 2014

Hi Tomas, I was looking at the code highlighting example. I have a question.
How do get multiple colors per keyword.
I did this:

private static final String[] GOLD = new String[]{
    "abstract", "assert", "boolean", "break", "byte",
    "case", "catch", "char", "class", "const",
    "continue", "default", "double", "else",
    "enum", "extends", "final", "finally", "float",
    "goto", "if", "implements", "import",
    "instanceof", "int", "interface", "long", "native",
    "new", "package", "private", "protected", "public",
    "return", "short", "static", "strictfp", "super",
    "switch", "synchronized", "this", "throw", "throws",
    "transient", "try", "void", "volatile",};

private static final String[] GREEN = new String[]{
    "for", "while", "do"
};

private static final Pattern GOlD_PATTERN = Pattern.compile("\\b(" + String.join("|", GOLD) + ")\\b");
private static final Pattern GREEN_PATTERN = Pattern.compile("\\b(" + String.join("|", GREEN) + ")\\b");

private static final String sampleCode = String.join("\n", new String[]{
    "package com.example;",
    "",
    "import java.util.*;",
    "",
    "public class Foo extends Bar implements Baz {",
    "",
    "   public static void main(String[] args) {",
    "       for(String arg: args) {",
    "           if(arg.length() != 0)",
    "               System.out.println(arg);",
    "           else",
    "               System.err.println(\"Warning: empty string as argument\");",
    "       }",
    "   }",
    "",
    "}"
});

public static void main(String[] args) {
    launch(args);
}

@Override
public void start(Stage primaryStage) {
    CodeArea codeArea = new CodeArea();
    codeArea.textProperty().addListener((obs, oldText, newText) -> {
        codeArea.setStyleSpans(0, computeHighlighting(newText));
    });
    codeArea.replaceText(0, 0, sampleCode);

    Scene scene = new Scene(new StackPane(codeArea), 600, 400);
    scene.getStylesheets().add(Keywords.class.getResource("java-keywords.css").toExternalForm());
    primaryStage.setScene(scene);
    primaryStage.show();
}

private static StyleSpans<Collection<String>> computeHighlighting(String text) {
    Matcher goldMatcher = GOlD_PATTERN.matcher(text);
    Matcher greenMatcher = GREEN_PATTERN.matcher(text);

    int lastKwEnd = 0;
    StyleSpansBuilder<Collection<String>> spansBuilder = new StyleSpansBuilder<>();
    while (goldMatcher.find()) {
        spansBuilder.add(Collections.emptyList(), goldMatcher.start() - lastKwEnd);
        spansBuilder.add(Collections.singleton("gold"), goldMatcher.end() - goldMatcher.start());
        lastKwEnd = goldMatcher.end();
    }

    while (greenMatcher.find()) {
        spansBuilder.add(Collections.emptyList(), greenMatcher.start() - lastKwEnd);
        spansBuilder.add(Collections.singleton("green"), greenMatcher.end() - greenMatcher.start());
        lastKwEnd = greenMatcher.end();
    }
    spansBuilder.add(Collections.emptyList(), text.length() - lastKwEnd);
    return spansBuilder.create();
}

but this of course is giving me an error at the copmuteHighlighting method. My understanding is that you build a collection of styleSpans based on the matches founds. so I just went and made another loop to look for words that match another pattern. What is the right way to get it to work? I must be extremely wrong somewhere :) Thanks!

@ghost
Copy link
Author

ghost commented Jun 10, 2014

this is the css
.green {
-fx-fill: green;
-fx-font-weight: bold;
}

.gold {
-fx-fill: gold;
-fx-font-weight: bold;
}

@TomasMikula
Copy link
Member

You are probably getting an error because you didn't reset lastKwEnd to 0 before the second loop. But anyway, even after that you would get an error at setStyleSpans, because you are attempting to build StyleSpans that are (roughly) twice as long as the text in the area.

You need a little work with regular expressions:

private static final String[] GOLD = new String[] {
    "abstract", "assert", "boolean", "break", "byte",
    "case", "catch", "char", "class", "const",
    "continue", "default", "double", "else",
    "enum", "extends", "final", "finally", "float",
    "goto", "if", "implements", "import",
    "instanceof", "int", "interface", "long", "native",
    "new", "package", "private", "protected", "public",
    "return", "short", "static", "strictfp", "super",
    "switch", "synchronized", "this", "throw", "throws",
    "transient", "try", "void", "volatile",};

private static final String[] GREEN = new String[] {
    "for", "while", "do"
};

private static final String GOLD_PATTERN = "\\b(" + String.join("|", GOLD) + ")\\b";
private static final String GREEN_PATTERN = "\\b(" + String.join("|", GREEN) + ")\\b";

private static final Pattern PATTERN = Pattern.compile(
        "(?<GOLD>" + GOLD_PATTERN + ")" +
        "|" +
        "(?<GREEN>" + GREEN_PATTERN + ")");



private static StyleSpans<Collection<String>> computeHighlighting(String text) {
    Matcher matcher = PATTERN.matcher(text);
    int lastKwEnd = 0;
    StyleSpansBuilder<Collection<String>> spansBuilder
            = new StyleSpansBuilder<>();
    while(matcher.find()) {
        String styleClass = matcher.group("GOLD") != null ? "gold" : "green";
        spansBuilder.add(Collections.emptyList(), matcher.start() - lastKwEnd);
        spansBuilder.add(Collections.singleton(styleClass), matcher.end() - matcher.start());
        lastKwEnd = matcher.end();
    }
    spansBuilder.add(Collections.emptyList(), text.length() - lastKwEnd);
    return spansBuilder.create();
}

@ghost
Copy link
Author

ghost commented Jun 10, 2014

Thanks! that did it. I will take it from here.

@ghost
Copy link
Author

ghost commented Jun 12, 2014

Tomas, I am really sorry my questions might be really simple, but I can't figure out how to set multiple colors. How do you do that?

how do I make this function sort multiple colors?

while(matcher.find()) {
    String styleClass = matcher.group("GOLD") != null ? "gold" : "green";
    spansBuilder.add(Collections.emptyList(), matcher.start() - lastKwEnd);
    spansBuilder.add(Collections.singleton(styleClass), matcher.end() - matcher.start());
    lastKwEnd = matcher.end();
}

@TomasMikula
Copy link
Member

You mean more than two colors? Any number of colors?

@ghost
Copy link
Author

ghost commented Jun 13, 2014

I mean a finite set if colours. In my case it's probably 5 or 6.
On Jun 13, 2014 8:07 AM, "TomasMikula" notifications@github.com wrote:

You mean more than two colors? Any number of colors?


Reply to this email directly or view it on GitHub
#62 (comment)
.

@TomasMikula
Copy link
Member

If you have a fixed set of colors, then extending my sample is really simple.

    String styleClass =
            matcher.group("GOLD") != null ? "gold" :
            matcher.group("GREEN") != null ? "green" :
            matcher.group("BLUE") != null ? "brown" :
            matcher.group("RED") != null ? "red" :
            "purple";

@ghost
Copy link
Author

ghost commented Jun 13, 2014

The last colour, purple, is basically the colour that gets applied when no
match have is found correct?
On Jun 13, 2014 8:22 AM, "TomasMikula" notifications@github.com wrote:

If you have a fixed set of colors, then extending my sample is really
simple.

String styleClass =
        matcher.group("GOLD") != null ? "gold" :
        matcher.group("GREEN") != null ? "green" :
        matcher.group("BLUE") != null ? "brown" :
        matcher.group("RED") != null ? "red" :
        "purple";


Reply to this email directly or view it on GitHub
#62 (comment)
.

@TomasMikula
Copy link
Member

No, everything inside the while(matcher.find()) loop is applied when a match is found. As the match is either of "GOLD", "GREEN", "BLUE", "RED" and "PURPLE", once you rule out "GOLD", "GREEN", "BLUE", "RED", the only possibility is it being "PURPLE", so you don't test it. If you want, you can rewrite the above as

String styleClass;
if(matcher.group("GOLD") != null) {
    styleClass = "gold";
} else if(matcher.group("GREEN") != null) {
    styleClass = "green";
} else if(matcher.group("BLUE") != null) {
    styleClass = "blue";
} else if(matcher.group("RED") != null) {
    styleClass = "red";
} else if(matcher.group("PURPLE") != null) {
    styleClass = "purple";
} else {
    throw new AssertionError("Unreachable code");
}

@ghost
Copy link
Author

ghost commented Jun 13, 2014

Thanks! I am still in the verbose code writing skill level. :)
On Jun 13, 2014 8:32 AM, "TomasMikula" notifications@github.com wrote:

No, everything inside the while(matcher.find()) loop is applied when a
match is found. As the match is either of "GOLD", "GREEN", "BLUE",
"RED" and "PURPLE", once you rule out "GOLD", "GREEN", "BLUE", "RED", the
only possibility is it being "PURPLE", so you don't test it. If you want,
you can rewrite the above as

String styleClass;if(matcher.group("GOLD") != null) {
styleClass = "gold";} else if(matcher.group("GREEN") != null) {
styleClass = "green";} else if(matcher.group("BLUE") != null) {
styleClass = "blue";} else if(matcher.group("RED") != null) {
styleClass = "red";} else if(matcher.group("PURPLE") != null) {
styleClass = "purple";} else {
throw new AssertionError("Unreachable code");}


Reply to this email directly or view it on GitHub
#62 (comment)
.

@TomasMikula TomasMikula changed the title Question: code area Question: Multiple styles in code area Oct 30, 2014
@deepsidhu1313
Copy link

From above example i tried modifying my CodeArea code, but it didn't worked for me. Here is the url to my code https://github.com/deepsidhu1313/java-example/ . Am i doing something wrong ??

@TomasMikula
Copy link
Member

Brackets, semicolons, etc. are not words for the purposes of regular expressions, so you shouldn't delimit them by word boundaries (\\b). Change the corresponding lines to

private static final Pattern SEMICOL_PATTERN = Pattern.compile("(" + String.join("|", SEMICOL) + ")");

i.e. omitting \\b, and let me know if it resolved your problem.

@TomasMikula
Copy link
Member

Also, I noticed you use Pattern instead of String for the individual patterns. I'm not sure whether compiling the individual patterns beforehand and then converting them back to strings before compiling the resulting pattern PATTERN doesn't mess up the original string representation. Thus, if my above comment didn't help, try going back to

private static final String SEMICOL_PATTERN = "(" + String.join("|", SEMICOL) + ")";

@deepsidhu1313
Copy link

I made few changes in code, now it doesn't return any error but color of other elements except keyword is not changing.

updated version of my code is here https://github.com/deepsidhu1313/java-example/

@TomasMikula
Copy link
Member

Oh well, (), {}, [] have special meaning in regular expressions, so you need to escape them. Not sure about ;, but it shouldn't hurt to escape it as well. Change your code to contain

private static final String[] SEMICOL = new String[]{"\\;"};
private static final String[] RBRAC = new String[]{"\\(", "\\)"};
private static final String[] SBRAC = new String[]{"\\[", "\\]"};
private static final String[] OBRAC = new String[]{"\\{", "\\}"};

and report back whether it resolved your issue.

@deepsidhu1313
Copy link

Thanks a lot that solved the problem . :) Everything is working fine now. :)
Can you share this code in your documentation so that others can benefit from this.

@TomasMikula
Copy link
Member

Good idea. I updated the JavaKeywords example to use multiple colors. I also added highlighting of string literals.

@deepsidhu1313
Copy link

Did you tested your latest example ?? i am getting stackoverflow error by implementing your new code given in JavaKeywords.

@TomasMikula
Copy link
Member

Yes, I can run the JavaKeywords demo without any problem. What is the exact error? Do you get an error when running the JavaKeywords demo, or after incorporating the code into your program?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants