From 7e2a44f1ce8aa4e0a17f79610210699b2cf3bfbc Mon Sep 17 00:00:00 2001 From: Janne Valkealahti Date: Wed, 2 Feb 2022 09:27:24 +0000 Subject: [PATCH] Render group templates with new styles - Can now render ST group with hardcoded main template name - Add additional style tags - Add sample to list various styles --- .../shell/style/TemplateExecutor.java | 27 ++++ .../shell/style/ThemeSettings.java | 92 +++++++++++++- .../shell/style/TemplateExecutorTests.java | 7 +- .../shell/style/ThemeResolverTests.java | 2 +- .../shell/style/ThemeSettingsTests.java | 5 +- .../shell/samples/standard/StyleCommands.java | 115 ++++++++++++++++++ 6 files changed, 241 insertions(+), 7 deletions(-) create mode 100644 spring-shell-samples/src/main/java/org/springframework/shell/samples/standard/StyleCommands.java diff --git a/spring-shell-core/src/main/java/org/springframework/shell/style/TemplateExecutor.java b/spring-shell-core/src/main/java/org/springframework/shell/style/TemplateExecutor.java index 40cd16fb..598e73be 100644 --- a/spring-shell-core/src/main/java/org/springframework/shell/style/TemplateExecutor.java +++ b/spring-shell-core/src/main/java/org/springframework/shell/style/TemplateExecutor.java @@ -23,6 +23,7 @@ import org.slf4j.LoggerFactory; import org.stringtemplate.v4.ST; import org.stringtemplate.v4.STErrorListener; import org.stringtemplate.v4.STGroup; +import org.stringtemplate.v4.STGroupString; import org.stringtemplate.v4.misc.STMessage; /** @@ -32,6 +33,7 @@ import org.stringtemplate.v4.misc.STMessage; */ public class TemplateExecutor { + private final static Logger log = LoggerFactory.getLogger(TemplateExecutor.class); private final static STErrorListener ERROR_LISTENER = new LoggingSTErrorListener(); private final ThemeResolver themeResolver; private StringToStyleExpressionRenderer renderer; @@ -61,6 +63,31 @@ public class TemplateExecutor { return themeResolver.evaluateExpression(templateRendered); } + /** + * Render template group with a given attributes expecting to find instance + * named {@code main}. + * + * @param template the ST template + * @param attributes the ST template attributes + * @return a rendered template + */ + public AttributedString renderGroup(String template, Map attributes) { + STGroup group = new STGroupString(template); + group.setListener(ERROR_LISTENER); + group.registerRenderer(String.class, renderer); + + ST st = group.getInstanceOf("main"); + if (st == null) { + throw new IllegalArgumentException("template instance 'main' not found from a group"); + } + if (attributes != null) { + attributes.entrySet().stream().forEach(e -> st.add(e.getKey(), e.getValue())); + } + String templateRendered = st.render(); + log.debug("Rendered template {}", templateRendered); + return themeResolver.evaluateExpression(templateRendered); + } + private static class LoggingSTErrorListener implements STErrorListener { private final static Logger log = LoggerFactory.getLogger(LoggingSTErrorListener.class); diff --git a/spring-shell-core/src/main/java/org/springframework/shell/style/ThemeSettings.java b/spring-shell-core/src/main/java/org/springframework/shell/style/ThemeSettings.java index 38a2e753..6bfe921a 100644 --- a/spring-shell-core/src/main/java/org/springframework/shell/style/ThemeSettings.java +++ b/spring-shell-core/src/main/java/org/springframework/shell/style/ThemeSettings.java @@ -49,8 +49,48 @@ public abstract class ThemeSettings { */ public final static String TAG_LIST_VALUE = "list-value"; + /** + * Styling for some arbitrary content indicating {@code INFO} level. + */ + public final static String TAG_LEVEL_INFO = "level-info"; + + /** + * Styling for some arbitrary content indicating {@code WARN} level. + */ + public final static String TAG_LEVEL_WARN = "level-warn"; + + /** + * Styling for some arbitrary content indicating {@code ERROR} level. + */ + public final static String TAG_LEVEL_ERROR = "level-error"; + + /** + * Styling for something i.e. in selectors when item is selectable. + */ + public final static String TAG_ITEM_ENABLED = "item-enabled"; + + /** + * Styling for something i.e. in selectors when item can't be selected. + */ + public final static String TAG_ITEM_DISABLED = "item-disabled"; + + /** + * Styling for something i.e. in selectors when item is selected. + */ + public final static String TAG_ITEM_SELECTED = "item-selected"; + + /** + * Styling for something i.e. in selectors when item is not selected. + */ + public final static String TAG_ITEM_UNSELECTED = "item-unselected"; + + /** + * Styling for selector i.e. arrow in selectors. + */ + public final static String TAG_ITEM_SELECTOR = "item-selector"; + public String title() { - return "bold"; + return "bold,fg:bright-white"; } public String value() { @@ -58,13 +98,45 @@ public abstract class ThemeSettings { } public String listKey() { - return null; + return "default"; } public String listValue() { return "bold,fg:green"; } + public String listLevelInfo() { + return "fg:green"; + } + + public String listLevelWarn() { + return "fg:yellow"; + } + + public String listLevelError() { + return "fg:red"; + } + + public String itemEnabled() { + return "bold"; + } + + public String itemDisabled() { + return "faint"; + } + + public String itemSelected() { + return "fg:green"; + } + + public String itemUnselected() { + return "bold"; + } + + public String itemSelector() { + return "bold,fg:bright-cyan"; + } + /** * Resolve a theme setting from a given tag. * @@ -81,6 +153,22 @@ public abstract class ThemeSettings { return listKey(); case TAG_LIST_VALUE: return listValue(); + case TAG_LEVEL_INFO: + return listLevelInfo(); + case TAG_LEVEL_WARN: + return listLevelWarn(); + case TAG_LEVEL_ERROR: + return listLevelError(); + case TAG_ITEM_ENABLED: + return itemEnabled(); + case TAG_ITEM_DISABLED: + return itemDisabled(); + case TAG_ITEM_SELECTED: + return itemSelected(); + case TAG_ITEM_UNSELECTED: + return itemUnselected(); + case TAG_ITEM_SELECTOR: + return itemSelector(); } throw new IllegalArgumentException(String.format("Unknown tag '%s'", tag)); } diff --git a/spring-shell-core/src/test/java/org/springframework/shell/style/TemplateExecutorTests.java b/spring-shell-core/src/test/java/org/springframework/shell/style/TemplateExecutorTests.java index 8587967d..fa8ccf5d 100644 --- a/spring-shell-core/src/test/java/org/springframework/shell/style/TemplateExecutorTests.java +++ b/spring-shell-core/src/test/java/org/springframework/shell/style/TemplateExecutorTests.java @@ -20,11 +20,11 @@ import java.util.Map; import org.jline.utils.AttributedString; import org.jline.utils.AttributedStringBuilder; +import org.jline.utils.AttributedStyle; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; -import static org.jline.utils.AttributedStyle.BOLD; public class TemplateExecutorTests { @@ -74,7 +74,10 @@ public class TemplateExecutorTests { Map attributes = new HashMap<>(); attributes.put("foo", "bar"); AttributedString result = executor.render(template, attributes); - AttributedString equalTo = new AttributedStringBuilder().append("bar", BOLD).toAttributedString(); + AttributedString equalTo = new AttributedStringBuilder() + .append("bar", + AttributedStyle.DEFAULT.foreground(AttributedStyle.BRIGHT + AttributedStyle.WHITE).bold()) + .toAttributedString(); assertThat(result).isEqualTo(equalTo); } diff --git a/spring-shell-core/src/test/java/org/springframework/shell/style/ThemeResolverTests.java b/spring-shell-core/src/test/java/org/springframework/shell/style/ThemeResolverTests.java index 0c02f93d..aebcec9e 100644 --- a/spring-shell-core/src/test/java/org/springframework/shell/style/ThemeResolverTests.java +++ b/spring-shell-core/src/test/java/org/springframework/shell/style/ThemeResolverTests.java @@ -38,7 +38,7 @@ public class ThemeResolverTests { } }); ThemeResolver themeResolver = new ThemeResolver(themeRegistry, "default"); - assertThat(themeResolver.resolveTag(ThemeSettings.TAG_TITLE)).isEqualTo("bold"); + assertThat(themeResolver.resolveTag(ThemeSettings.TAG_TITLE)).isEqualTo("bold,fg:bright-white"); assertThat(themeResolver.resolveStyle("bold")).isEqualTo(AttributedStyle.BOLD); assertThat(themeResolver.evaluateExpression("@{bold foo}")) .isEqualTo(new AttributedString("foo", AttributedStyle.BOLD)); diff --git a/spring-shell-core/src/test/java/org/springframework/shell/style/ThemeSettingsTests.java b/spring-shell-core/src/test/java/org/springframework/shell/style/ThemeSettingsTests.java index 3796d799..5f15b668 100644 --- a/spring-shell-core/src/test/java/org/springframework/shell/style/ThemeSettingsTests.java +++ b/spring-shell-core/src/test/java/org/springframework/shell/style/ThemeSettingsTests.java @@ -29,12 +29,13 @@ public class ThemeSettingsTests { public void test() { ThemeSettings themeSettings = ThemeSettings.themeSettings(); String resolveTag = themeSettings.resolveTag(ThemeSettings.TAG_TITLE); - assertThat(resolveTag).isEqualTo("bold"); + assertThat(resolveTag).isEqualTo("bold,fg:bright-white"); StyleSource styleSource = new MemoryStyleSource(); StyleResolver styleResolver = new StyleResolver(styleSource, "default"); AttributedStyle style = styleResolver.resolve(resolveTag); - assertThat(style).isEqualTo(AttributedStyle.BOLD); + assertThat(style) + .isEqualTo(AttributedStyle.DEFAULT.foreground(AttributedStyle.BRIGHT + AttributedStyle.WHITE).bold()); } } diff --git a/spring-shell-samples/src/main/java/org/springframework/shell/samples/standard/StyleCommands.java b/spring-shell-samples/src/main/java/org/springframework/shell/samples/standard/StyleCommands.java new file mode 100644 index 00000000..57a53a02 --- /dev/null +++ b/spring-shell-samples/src/main/java/org/springframework/shell/samples/standard/StyleCommands.java @@ -0,0 +1,115 @@ +/* + * Copyright 2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.shell.samples.standard; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.jline.utils.AttributedString; +import org.jline.utils.AttributedStringBuilder; +import org.jline.utils.AttributedStyle; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.shell.standard.ShellComponent; +import org.springframework.shell.standard.ShellMethod; +import org.springframework.shell.style.ThemeResolver; +import org.springframework.shell.style.ThemeSettings; + +@ShellComponent +public class StyleCommands { + + List colorGround = Arrays.asList("fg", "bg"); + List colors = Arrays.asList("black", "red", "green", "yellow", "blue", "magenta", "cyan", "white"); + List named = Arrays.asList("default", "bold", "faint", "italic", "underline", "blink", "inverse", + "inverseneg", "conceal", "crossedout", "hidden"); + List rgbRedHue = Arrays.asList("#ff0000", "#ff4000", "#ff8000", "#ffbf00", "#ffff00", "#bfff00", "#80ff00", + "#40ff00", "#00ff00", "#00ff40", "#00ff80", "#00ffbf", "#00ffff", "#00bfff", "#0080ff", "#0040ff", + "#0000ff", "#4000ff", "#8000ff", "#bf00ff", "#ff00ff", "#ff00bf", "#ff0080", "#ff0040", "#ff0000"); + List themeTags = Arrays.asList(ThemeSettings.TAG_TITLE, ThemeSettings.TAG_VALUE, ThemeSettings.TAG_LIST_KEY, + ThemeSettings.TAG_LIST_VALUE, ThemeSettings.TAG_LEVEL_INFO, ThemeSettings.TAG_LEVEL_WARN, + ThemeSettings.TAG_LEVEL_ERROR, ThemeSettings.TAG_ITEM_ENABLED, ThemeSettings.TAG_ITEM_DISABLED, + ThemeSettings.TAG_ITEM_SELECTED, ThemeSettings.TAG_ITEM_UNSELECTED, ThemeSettings.TAG_ITEM_SELECTOR); + + @Autowired + private ThemeResolver themeResolver; + + @ShellMethod(key = "style values", value = "Showcase colors and styles", group = "Styles") + public AttributedString stylesValues() { + AttributedStringBuilder builder = new AttributedStringBuilder(); + combinations1().stream() + .forEach(spec -> { + AttributedStyle style = themeResolver.resolveStyle(spec); + AttributedString styledStr = new AttributedString(spec, style); + builder.append(String.format("%-25s", spec)); + builder.append(" "); + builder.append(styledStr); + builder.append("\n"); + }); + return builder.toAttributedString(); + } + + @ShellMethod(key = "style rgb", value = "Showcase colors and styles", group = "Styles") + public AttributedString stylesRgb() { + AttributedStringBuilder builder = new AttributedStringBuilder(); + combinations2().stream() + .forEach(spec -> { + AttributedStyle style = themeResolver.resolveStyle(spec); + AttributedString styledStr = new AttributedString(spec, style); + builder.append(String.format("%-25s", spec)); + builder.append(" "); + builder.append(styledStr); + builder.append("\n"); + }); + return builder.toAttributedString(); + } + + @ShellMethod(key = "style theme", value = "Showcase colors and styles", group = "Styles") + public AttributedString stylesTheme() { + AttributedStringBuilder builder = new AttributedStringBuilder(); + themeTags.stream() + .forEach(tag -> { + String resolvedStyle = themeResolver.resolveTag(tag); + AttributedStyle style = themeResolver.resolveStyle(resolvedStyle); + AttributedString styledStr = new AttributedString(tag, style); + builder.append(String.format("%-25s", tag)); + builder.append(" "); + builder.append(styledStr); + builder.append("\n"); + });; + return builder.toAttributedString(); + } + + private List combinations1() { + List styles = new ArrayList<>(); + colorGround.stream().forEach(ground -> { + colors.stream().forEach(color -> { + named.stream().forEach(named -> { + styles.add(String.format("%s,%s:%s", named, ground, color)); + }); + }); + }); + return styles; + } + + private List combinations2() { + List styles = new ArrayList<>(); + rgbRedHue.stream().forEach(rgb -> { + styles.add(String.format("inverse,fg-rgb:%s", rgb)); + }); + return styles; + } +}