Rework themes support
- Add theme settings for figures. - Settings for styles are now in it own class and ThemeSettings are wrapping those. - Prefixed formatting string to have `style-` prefix. - Templates can now use added `figures` dictionary which is resolved per activated theme. - Create `dump` theme. - New `ThemeActive` interface where default impl uses dump theme if `CI` or `NO_COLOR` env is set. - Rework existing templates. - Fixes #442
This commit is contained in:
@@ -174,7 +174,7 @@ public class SpringShellProperties {
|
||||
|
||||
public static class Theme {
|
||||
|
||||
private String name = "default";
|
||||
private String name;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
|
||||
@@ -16,35 +16,57 @@
|
||||
package org.springframework.shell.boot;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.shell.style.TemplateExecutor;
|
||||
import org.springframework.shell.style.Theme;
|
||||
import org.springframework.shell.style.ThemeActive;
|
||||
import org.springframework.shell.style.ThemeRegistry;
|
||||
import org.springframework.shell.style.ThemeResolver;
|
||||
import org.springframework.shell.style.ThemeSettings;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableConfigurationProperties(SpringShellProperties.class)
|
||||
public class ThemingAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public ThemeActive themeActive() {
|
||||
return () -> {
|
||||
if (System.getenv("CI") != null || System.getenv("NO_COLOR") != null) {
|
||||
return "dump";
|
||||
}
|
||||
return "default";
|
||||
};
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ThemeRegistry themeRegistry(ObjectProvider<Theme> themes) {
|
||||
ThemeRegistry registry = new ThemeRegistry();
|
||||
registry.register(Theme.of("default", ThemeSettings.themeSettings()));
|
||||
registry.register(Theme.of("default", ThemeSettings.defaults()));
|
||||
registry.register(Theme.of("dump", ThemeSettings.dump()));
|
||||
themes.orderedStream().forEachOrdered(registry::register);
|
||||
return registry;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ThemeResolver shellThemeResolver(ThemeRegistry themeRegistry, SpringShellProperties properties) {
|
||||
return new ThemeResolver(themeRegistry, properties.getTheme().getName());
|
||||
public ThemeResolver shellThemeResolver(ThemeRegistry themeRegistry, SpringShellProperties properties,
|
||||
ThemeActive themeActive) {
|
||||
String themeName = properties.getTheme().getName();
|
||||
if (!StringUtils.hasText(themeName)) {
|
||||
themeName = themeActive.get();
|
||||
}
|
||||
if (!StringUtils.hasText(themeName)) {
|
||||
themeName = "default";
|
||||
}
|
||||
return new ThemeResolver(themeRegistry, themeName);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public TemplateExecutor templateExecutor(ThemeResolver themeResolver) {
|
||||
return new TemplateExecutor(themeResolver);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ public class SpringShellPropertiesTests {
|
||||
assertThat(properties.getScript().isEnabled()).isTrue();
|
||||
assertThat(properties.getInteractive().isEnabled()).isTrue();
|
||||
assertThat(properties.getNoninteractive().isEnabled()).isTrue();
|
||||
assertThat(properties.getTheme().getName()).isEqualTo("default");
|
||||
assertThat(properties.getTheme().getName()).isNull();
|
||||
assertThat(properties.getCommand().getClear().isEnabled()).isTrue();
|
||||
assertThat(properties.getCommand().getHelp().isEnabled()).isTrue();
|
||||
assertThat(properties.getCommand().getHelp().getGroupingMode()).isEqualTo(GroupingMode.GROUP);
|
||||
|
||||
@@ -15,17 +15,24 @@
|
||||
*/
|
||||
package org.springframework.shell.boot;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.assertj.core.api.AbstractAssert;
|
||||
import org.assertj.core.api.InstanceOfAssertFactory;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.shell.style.FigureSettings;
|
||||
import org.springframework.shell.style.StyleSettings;
|
||||
import org.springframework.shell.style.TemplateExecutor;
|
||||
import org.springframework.shell.style.Theme;
|
||||
import org.springframework.shell.style.ThemeRegistry;
|
||||
import org.springframework.shell.style.ThemeResolver;
|
||||
import org.springframework.shell.style.ThemeSettings;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@@ -35,19 +42,22 @@ public class ThemingAutoConfigurationTests {
|
||||
.withConfiguration(AutoConfigurations.of(ThemingAutoConfiguration.class));
|
||||
|
||||
@Test
|
||||
public void testDefaults() {
|
||||
void createsDefaultBeans() {
|
||||
this.contextRunner
|
||||
.run(context -> {
|
||||
assertThat(context).hasSingleBean(TemplateExecutor.class);
|
||||
assertThat(context).hasSingleBean(ThemeRegistry.class);
|
||||
ThemeRegistry registry = context.getBean(ThemeRegistry.class);
|
||||
assertThat(registry.get("default")).isNotNull();
|
||||
assertThat(registry.get("dump")).isNotNull();
|
||||
assertThat(context).hasSingleBean(ThemeResolver.class);
|
||||
ThemeResolver resolver = context.getBean(ThemeResolver.class);
|
||||
assertThat(resolver).extracting("theme").asInstanceOf(THEME).hasName("default", "dump");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRegistersCustomTheme() {
|
||||
public void canRegisterCustomTheme() {
|
||||
this.contextRunner
|
||||
.withUserConfiguration(CustomThemeConfig.class)
|
||||
.run(context -> {
|
||||
@@ -79,5 +89,36 @@ public class ThemingAutoConfigurationTests {
|
||||
}
|
||||
|
||||
static class MyThemeSettings extends ThemeSettings {
|
||||
MyThemeSettings() {
|
||||
super(StyleSettings.defaults(), FigureSettings.defaults());
|
||||
}
|
||||
}
|
||||
|
||||
InstanceOfAssertFactory<Theme, ThemeAssert> THEME = new InstanceOfAssertFactory<>(Theme.class,
|
||||
ThemeAssertions::assertThat);
|
||||
|
||||
static class ThemeAssertions {
|
||||
|
||||
public static ThemeAssert assertThat(Theme actual) {
|
||||
return new ThemeAssert(actual);
|
||||
}
|
||||
}
|
||||
|
||||
static class ThemeAssert extends AbstractAssert<ThemeAssert, Theme> {
|
||||
|
||||
public ThemeAssert(Theme actual) {
|
||||
super(actual, ThemeAssert.class);
|
||||
}
|
||||
|
||||
public ThemeAssert hasName(String... names) {
|
||||
isNotNull();
|
||||
boolean match = Stream.of(names).filter(n -> actual.getName().equals(n)).findFirst().isPresent();
|
||||
if (!match) {
|
||||
failWithMessage("Expected theme to have names %s but was %s",
|
||||
StringUtils.arrayToCommaDelimitedString(names), actual.getName());
|
||||
}
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,257 @@
|
||||
/*
|
||||
* 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.style;
|
||||
|
||||
/**
|
||||
* Base class defining a settings for figures.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*/
|
||||
public abstract class FigureSettings {
|
||||
|
||||
/**
|
||||
* Figure meant to be used as ok like a checkmark.
|
||||
*/
|
||||
public final static String TAG_TICK = "tick";
|
||||
|
||||
/**
|
||||
* Figure indicating something about on a level of info.
|
||||
*/
|
||||
public final static String TAG_INFO = "info";
|
||||
|
||||
/**
|
||||
* Figure indicating something about on a level of warning.
|
||||
*/
|
||||
public final static String TAG_WARNING = "warning";
|
||||
|
||||
/**
|
||||
* Figure indicating something about on a level of error.
|
||||
*/
|
||||
public final static String TAG_ERROR = "error";
|
||||
|
||||
/**
|
||||
* Figure used as a checkbox for off position.
|
||||
*/
|
||||
public final static String TAG_CHECKBOX_OFF = "checkboxOff";
|
||||
|
||||
/**
|
||||
* Figure used as a checkbox for on position.
|
||||
*/
|
||||
public final static String TAG_CHECKBOX_ON = "checkboxOn";
|
||||
|
||||
/**
|
||||
* Figure for left arrow.
|
||||
*/
|
||||
public final static String TAG_LEFTWARDS_ARROR = "leftwardsArrow";
|
||||
|
||||
/**
|
||||
* Figure for up arrow.
|
||||
*/
|
||||
public final static String TAG_UPWARDS_ARROR = "upwardsArrow";
|
||||
|
||||
/**
|
||||
* Figure for right arrow.
|
||||
*/
|
||||
public final static String TAG_RIGHTWARDS_ARROR = "righwardsArror";
|
||||
|
||||
/**
|
||||
* Figure for down arrow.
|
||||
*/
|
||||
public final static String TAG_DOWNWARDS_ARROR = "downwardsArror";
|
||||
|
||||
/**
|
||||
* Figure used as indicator pointing left.
|
||||
*/
|
||||
public final static String TAG_LEFT_POINTING_QUOTATION = "leftPointingQuotation";
|
||||
|
||||
/**
|
||||
* Figure used as indicator pointing left.
|
||||
*/
|
||||
public final static String TAG_RIGHT_POINTING_QUOTATION = "rightPointingQuotation";
|
||||
|
||||
/**
|
||||
* Figure meant for question.
|
||||
*/
|
||||
public final static String TAG_QUESTION_MARK = "questionMark";
|
||||
|
||||
public String tick() {
|
||||
return "✔"; // U+2714 Heavy Check Mark Emoji, tick, checkmark
|
||||
}
|
||||
|
||||
public String info() {
|
||||
return "ℹ"; // U+2139 Information Source Emoji
|
||||
}
|
||||
|
||||
public String warning() {
|
||||
return "⚠"; // U+26A0 Warning Sign Emoji, danger, аlert
|
||||
}
|
||||
|
||||
public String error() {
|
||||
return "✖"; // U+2716 Heavy Multiplication X Emoji, cross
|
||||
}
|
||||
|
||||
public String checkboxOff() {
|
||||
return "☐"; // U+2610 Ballot Box
|
||||
}
|
||||
|
||||
public String checkboxOn() {
|
||||
return "☒"; // U+2612 Ballot Box with X
|
||||
}
|
||||
|
||||
public String leftwardsArrow() {
|
||||
return "←"; // U+2190 Leftwards Arrow
|
||||
}
|
||||
|
||||
public String upwardsArrow() {
|
||||
return "↑"; // U+2191 Upwards Arrow
|
||||
}
|
||||
|
||||
public String righwardsArror() {
|
||||
return "→"; // U+2192 Rightwards Arrow
|
||||
}
|
||||
|
||||
public String downwardsArror() {
|
||||
return "↓"; // U+2193 Downwards Arrow
|
||||
}
|
||||
|
||||
public String leftPointingQuotation() {
|
||||
return "❮"; // U+276E Heavy Left-Pointing Angle Quotation Mark Ornament
|
||||
}
|
||||
|
||||
public String rightPointingQuotation() {
|
||||
return "❯"; // U+276F Heavy Right-Pointing Angle Quotation Mark Ornament
|
||||
}
|
||||
|
||||
public String questionMark() {
|
||||
return "?"; // U+003F Question Mark
|
||||
}
|
||||
|
||||
public String resolveTag(String tag) {
|
||||
switch (tag) {
|
||||
case TAG_TICK:
|
||||
return tick();
|
||||
case TAG_INFO:
|
||||
return info();
|
||||
case TAG_WARNING:
|
||||
return warning();
|
||||
case TAG_ERROR:
|
||||
return error();
|
||||
case TAG_CHECKBOX_OFF:
|
||||
return checkboxOff();
|
||||
case TAG_CHECKBOX_ON:
|
||||
return checkboxOn();
|
||||
case TAG_LEFTWARDS_ARROR:
|
||||
return leftwardsArrow();
|
||||
case TAG_UPWARDS_ARROR:
|
||||
return upwardsArrow();
|
||||
case TAG_RIGHTWARDS_ARROR:
|
||||
return righwardsArror();
|
||||
case TAG_DOWNWARDS_ARROR:
|
||||
return downwardsArror();
|
||||
case TAG_LEFT_POINTING_QUOTATION:
|
||||
return leftPointingQuotation();
|
||||
case TAG_RIGHT_POINTING_QUOTATION:
|
||||
return rightPointingQuotation();
|
||||
case TAG_QUESTION_MARK:
|
||||
return questionMark();
|
||||
}
|
||||
throw new IllegalArgumentException(String.format("Unknown tag '%s'", tag));
|
||||
}
|
||||
|
||||
public static FigureSettings defaults() {
|
||||
return new DefaultFigureSettings();
|
||||
}
|
||||
|
||||
public static FigureSettings dump() {
|
||||
return new DumpFigureSettings();
|
||||
}
|
||||
|
||||
public static String[] tags() {
|
||||
return new String[] {
|
||||
TAG_TICK,
|
||||
TAG_INFO,
|
||||
TAG_WARNING,
|
||||
TAG_ERROR,
|
||||
TAG_CHECKBOX_OFF,
|
||||
TAG_CHECKBOX_ON,
|
||||
TAG_LEFTWARDS_ARROR,
|
||||
TAG_UPWARDS_ARROR,
|
||||
TAG_RIGHTWARDS_ARROR,
|
||||
TAG_DOWNWARDS_ARROR,
|
||||
TAG_LEFT_POINTING_QUOTATION,
|
||||
TAG_RIGHT_POINTING_QUOTATION,
|
||||
TAG_QUESTION_MARK
|
||||
};
|
||||
}
|
||||
|
||||
private static class DefaultFigureSettings extends FigureSettings {
|
||||
}
|
||||
|
||||
private static class DumpFigureSettings extends FigureSettings {
|
||||
|
||||
@Override
|
||||
public String tick() {
|
||||
return "v";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String info() {
|
||||
return "i";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String warning() {
|
||||
return "!";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String error() {
|
||||
return "x";
|
||||
}
|
||||
|
||||
public String checkboxOff() {
|
||||
return "[ ]";
|
||||
}
|
||||
|
||||
public String checkboxOn() {
|
||||
return "[x]";
|
||||
}
|
||||
|
||||
public String leftwardsArrow() {
|
||||
return "<";
|
||||
}
|
||||
|
||||
public String upwardsArrow() {
|
||||
return "^";
|
||||
}
|
||||
|
||||
public String righwardsArror() {
|
||||
return ">";
|
||||
}
|
||||
|
||||
public String downwardsArror() {
|
||||
return "v";
|
||||
}
|
||||
|
||||
public String leftPointingQuotation() {
|
||||
return "<";
|
||||
}
|
||||
|
||||
public String rightPointingQuotation() {
|
||||
return ">";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -45,7 +45,7 @@ public class StringToStyleExpressionRenderer implements AttributeRenderer<String
|
||||
return value;
|
||||
}
|
||||
else {
|
||||
return String.format("@{%s %s}", themeResolver.resolveTag(formatString), value);
|
||||
return String.format("@{%s %s}", themeResolver.resolveStyleTag(formatString), value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,290 @@
|
||||
/*
|
||||
* 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.style;
|
||||
|
||||
/**
|
||||
* Base class defining a settings for styles.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*/
|
||||
public abstract class StyleSettings {
|
||||
|
||||
/**
|
||||
* Represents some arbitrary {@code title}.
|
||||
*/
|
||||
public final static String TAG_TITLE = "style-title";
|
||||
|
||||
/**
|
||||
* Represents some arbitrary {@code value}.
|
||||
*/
|
||||
public final static String TAG_VALUE = "style-value";
|
||||
|
||||
/**
|
||||
* Styling for keys or names in a lists:
|
||||
* <list key1> : list value1
|
||||
* <list key2> : list value2
|
||||
*/
|
||||
public final static String TAG_LIST_KEY = "style-list-key";
|
||||
|
||||
/**
|
||||
* Styling for keys or names in a lists:
|
||||
* list key1 : <list value1>
|
||||
* list key2 : <list value2>
|
||||
*/
|
||||
public final static String TAG_LIST_VALUE = "style-list-value";
|
||||
|
||||
/**
|
||||
* Styling for some arbitrary content indicating {@code INFO} level.
|
||||
*/
|
||||
public final static String TAG_LEVEL_INFO = "style-level-info";
|
||||
|
||||
/**
|
||||
* Styling for some arbitrary content indicating {@code WARN} level.
|
||||
*/
|
||||
public final static String TAG_LEVEL_WARN = "style-level-warn";
|
||||
|
||||
/**
|
||||
* Styling for some arbitrary content indicating {@code ERROR} level.
|
||||
*/
|
||||
public final static String TAG_LEVEL_ERROR = "style-level-error";
|
||||
|
||||
/**
|
||||
* Styling for something i.e. in selectors when item is selectable.
|
||||
*/
|
||||
public final static String TAG_ITEM_ENABLED = "style-item-enabled";
|
||||
|
||||
/**
|
||||
* Styling for something i.e. in selectors when item can't be selected.
|
||||
*/
|
||||
public final static String TAG_ITEM_DISABLED = "style-item-disabled";
|
||||
|
||||
/**
|
||||
* Styling for something i.e. in selectors when item is selected.
|
||||
*/
|
||||
public final static String TAG_ITEM_SELECTED = "style-item-selected";
|
||||
|
||||
/**
|
||||
* Styling for something i.e. in selectors when item is not selected.
|
||||
*/
|
||||
public final static String TAG_ITEM_UNSELECTED = "style-item-unselected";
|
||||
|
||||
/**
|
||||
* Styling for selector i.e. arrow in selectors.
|
||||
*/
|
||||
public final static String TAG_ITEM_SELECTOR = "style-item-selector";
|
||||
|
||||
/**
|
||||
* Styling for something which is highlighted.
|
||||
*/
|
||||
public final static String TAG_HIGHLIGHT = "style-highlight";
|
||||
|
||||
public String title() {
|
||||
return "bold,fg:bright-white";
|
||||
}
|
||||
|
||||
public String value() {
|
||||
return "fg:blue";
|
||||
}
|
||||
|
||||
public String listKey() {
|
||||
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";
|
||||
}
|
||||
|
||||
public String highlight() {
|
||||
return "bold";
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve a theme setting from a given tag.
|
||||
*
|
||||
* @param tag the tag
|
||||
* @return a theme setting
|
||||
*/
|
||||
public String resolveTag(String tag) {
|
||||
switch (tag) {
|
||||
case TAG_TITLE:
|
||||
return title();
|
||||
case TAG_VALUE:
|
||||
return value();
|
||||
case TAG_LIST_KEY:
|
||||
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();
|
||||
case TAG_HIGHLIGHT:
|
||||
return highlight();
|
||||
}
|
||||
throw new IllegalArgumentException(String.format("Unknown tag '%s'", tag));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance of a default settings.
|
||||
*
|
||||
* @return a default theme settings
|
||||
*/
|
||||
public static StyleSettings defaults() {
|
||||
return new DefaultStyleSettings();
|
||||
}
|
||||
|
||||
public static StyleSettings dump() {
|
||||
return new DumpStyleSettings();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all tags.
|
||||
*
|
||||
* @return array of all tags
|
||||
*/
|
||||
public static String[] tags() {
|
||||
return new String[] {
|
||||
TAG_TITLE,
|
||||
TAG_VALUE,
|
||||
TAG_LIST_KEY,
|
||||
TAG_LIST_VALUE,
|
||||
TAG_LEVEL_INFO,
|
||||
TAG_LEVEL_WARN,
|
||||
TAG_LEVEL_ERROR,
|
||||
TAG_ITEM_ENABLED,
|
||||
TAG_ITEM_DISABLED,
|
||||
TAG_ITEM_SELECTED,
|
||||
TAG_ITEM_UNSELECTED,
|
||||
TAG_ITEM_SELECTOR,
|
||||
TAG_HIGHLIGHT
|
||||
};
|
||||
}
|
||||
|
||||
private static class DefaultStyleSettings extends StyleSettings {
|
||||
}
|
||||
|
||||
private static class DumpStyleSettings extends StyleSettings {
|
||||
|
||||
@Override
|
||||
public String title() {
|
||||
return "default";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String value() {
|
||||
return "default";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String listKey() {
|
||||
return "default";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String listValue() {
|
||||
return "default";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String listLevelInfo() {
|
||||
return "default";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String listLevelWarn() {
|
||||
return "default";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String listLevelError() {
|
||||
return "default";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String itemEnabled() {
|
||||
return "default";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String itemDisabled() {
|
||||
return "default";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String itemSelected() {
|
||||
return "default";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String itemUnselected() {
|
||||
return "default";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String itemSelector() {
|
||||
return "default";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String highlight() {
|
||||
return "default";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,8 @@
|
||||
package org.springframework.shell.style;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.jline.utils.AttributedString;
|
||||
import org.slf4j.Logger;
|
||||
@@ -76,6 +78,11 @@ public class TemplateExecutor {
|
||||
group.setListener(ERROR_LISTENER);
|
||||
group.registerRenderer(String.class, renderer);
|
||||
|
||||
// define styled figures as dictionary
|
||||
Map<String, Object> figureDict = Stream.of(FigureSettings.tags())
|
||||
.collect(Collectors.toMap(tag -> tag, tag -> this.themeResolver.resolveFigureTag(tag)));
|
||||
group.defineDictionary("figures", figureDict);
|
||||
|
||||
ST st = group.getInstanceOf("main");
|
||||
if (st == null) {
|
||||
throw new IllegalArgumentException("template instance 'main' not found from a group");
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* 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.style;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public interface ThemeActive extends Supplier<String> {
|
||||
}
|
||||
@@ -54,8 +54,18 @@ public class ThemeResolver {
|
||||
* @param tag the tag
|
||||
* @return a style
|
||||
*/
|
||||
public String resolveTag(String tag) {
|
||||
return theme.getSettings().resolveTag(tag);
|
||||
public String resolveStyleTag(String tag) {
|
||||
return theme.getSettings().styles().resolveTag(tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve figure from a tag with activated theme.
|
||||
*
|
||||
* @param tag the tag
|
||||
* @return a style
|
||||
*/
|
||||
public String resolveFigureTag(String tag) {
|
||||
return theme.getSettings().figures().resolveTag(tag);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -16,206 +16,77 @@
|
||||
package org.springframework.shell.style;
|
||||
|
||||
/**
|
||||
* {@link ThemeSettings} is storing {@link Theme} related settings together and
|
||||
* is base class for further customizations.
|
||||
*
|
||||
* Settings in this base class are default styles.
|
||||
* Base class defining a settings for themes.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*/
|
||||
public abstract class ThemeSettings {
|
||||
|
||||
/**
|
||||
* Represents some arbitrary {@code title}.
|
||||
*/
|
||||
public final static String TAG_TITLE = "title";
|
||||
private StyleSettings styleSettings;
|
||||
private FigureSettings figureSettings;
|
||||
|
||||
/**
|
||||
* Represents some arbitrary {@code value}.
|
||||
* Creates theme settings with dump styles and figures.
|
||||
*/
|
||||
public final static String TAG_VALUE = "value";
|
||||
|
||||
/**
|
||||
* Styling for keys or names in a lists:
|
||||
* <list key1> : list value1
|
||||
* <list key2> : list value2
|
||||
*/
|
||||
public final static String TAG_LIST_KEY = "list-key";
|
||||
|
||||
/**
|
||||
* Styling for keys or names in a lists:
|
||||
* list key1 : <list value1>
|
||||
* list key2 : <list value2>
|
||||
*/
|
||||
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";
|
||||
|
||||
/**
|
||||
* Styling for something which is highlighted.
|
||||
*/
|
||||
public final static String TAG_HIGHLIGHT = "highlight";
|
||||
|
||||
public String title() {
|
||||
return "bold,fg:bright-white";
|
||||
}
|
||||
|
||||
public String value() {
|
||||
return "fg:blue";
|
||||
}
|
||||
|
||||
public String listKey() {
|
||||
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";
|
||||
}
|
||||
|
||||
public String highlight() {
|
||||
return "bold";
|
||||
public ThemeSettings() {
|
||||
this(StyleSettings.dump(), FigureSettings.dump());
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve a theme setting from a given tag.
|
||||
* Creates theme settings with styles and figures.
|
||||
*
|
||||
* @param tag the tag
|
||||
* @return a theme setting
|
||||
* @param styleSettings style settings
|
||||
* @param figureSettings figure settings
|
||||
*/
|
||||
public String resolveTag(String tag) {
|
||||
switch (tag) {
|
||||
case TAG_TITLE:
|
||||
return title();
|
||||
case TAG_VALUE:
|
||||
return value();
|
||||
case TAG_LIST_KEY:
|
||||
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();
|
||||
case TAG_HIGHLIGHT:
|
||||
return highlight();
|
||||
}
|
||||
throw new IllegalArgumentException(String.format("Unknown tag '%s'", tag));
|
||||
public ThemeSettings(StyleSettings styleSettings, FigureSettings figureSettings) {
|
||||
this.styleSettings = styleSettings;
|
||||
this.figureSettings = figureSettings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance of a default settings.
|
||||
* Gets a {@link StyleSettings}.
|
||||
*
|
||||
* @return a default theme settings
|
||||
* @return style settings
|
||||
*/
|
||||
public static ThemeSettings themeSettings() {
|
||||
public StyleSettings styles() {
|
||||
return this.styleSettings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a {@link FigureSettings}.
|
||||
*
|
||||
* @return figure settings
|
||||
*/
|
||||
public FigureSettings figures() {
|
||||
return this.figureSettings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a default theme settings.
|
||||
*
|
||||
* @return default theme settings
|
||||
*/
|
||||
public static ThemeSettings defaults() {
|
||||
return new DefaultThemeSettings(StyleSettings.defaults(), FigureSettings.defaults());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a dump theme settings.
|
||||
*
|
||||
* @return dump theme settings
|
||||
*/
|
||||
public static ThemeSettings dump() {
|
||||
return new DefaultThemeSettings();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all tags.
|
||||
*
|
||||
* @return array of all tags
|
||||
*/
|
||||
public static String[] tags() {
|
||||
return new String[] {
|
||||
TAG_TITLE,
|
||||
TAG_VALUE,
|
||||
TAG_LIST_KEY,
|
||||
TAG_LIST_VALUE,
|
||||
TAG_LEVEL_INFO,
|
||||
TAG_LEVEL_WARN,
|
||||
TAG_LEVEL_ERROR,
|
||||
TAG_ITEM_ENABLED,
|
||||
TAG_ITEM_DISABLED,
|
||||
TAG_ITEM_SELECTED,
|
||||
TAG_ITEM_UNSELECTED,
|
||||
TAG_ITEM_SELECTOR,
|
||||
TAG_HIGHLIGHT
|
||||
};
|
||||
}
|
||||
|
||||
private static class DefaultThemeSettings extends ThemeSettings {
|
||||
|
||||
DefaultThemeSettings() {
|
||||
super();
|
||||
}
|
||||
|
||||
DefaultThemeSettings(StyleSettings styleSettings, FigureSettings figureSettings) {
|
||||
super(styleSettings, figureSettings);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,20 +12,20 @@ message(model) ::= <%
|
||||
// info section after '? xxx'
|
||||
info(model) ::= <%
|
||||
<if(model.defaultValue)>
|
||||
<("(Y/n)"); format="item-disabled">
|
||||
<("(Y/n)"); format="style-item-disabled">
|
||||
<else>
|
||||
<("(y/N)"); format="item-disabled">
|
||||
<("(y/N)"); format="style-item-disabled">
|
||||
<endif>
|
||||
%>
|
||||
|
||||
// start '? xxx' shows both running and result
|
||||
question_name(model) ::= <<
|
||||
<("?"); format="list-value"> <model.name; format="title">
|
||||
<({<figures.questionMark>}); format="style-list-value"> <model.name; format="style-title">
|
||||
>>
|
||||
|
||||
// component result
|
||||
result(model) ::= <<
|
||||
<question_name(model)> <model.resultValue; format="value">
|
||||
<question_name(model)> <model.resultValue; format="style-value">
|
||||
>>
|
||||
|
||||
// component is running
|
||||
|
||||
@@ -1,30 +1,30 @@
|
||||
// used to select style if item is selected/unselected
|
||||
selected_style(flag) ::= <%
|
||||
<if(flag)>item-selected<else>item-unselected<endif>
|
||||
<if(flag)>style-item-selected<else>style-item-unselected<endif>
|
||||
%>
|
||||
|
||||
// selector rows
|
||||
select_item(item) ::= <%
|
||||
<if(item.onrow)>
|
||||
<("> "); format="item-selector">
|
||||
<({<figures.rightPointingQuotation> }); format="style-item-selector">
|
||||
<else>
|
||||
<(" ")>
|
||||
<endif>
|
||||
|
||||
<if(item.enabled)>
|
||||
<if(item.selected)>
|
||||
<("[x]"); format=selected_style(item.selected)> <item.name>
|
||||
<({<figures.checkboxOn> }); format=selected_style(item.selected)> <item.name>
|
||||
<else>
|
||||
<("[ ]"); format=selected_style(item.selected)> <item.name>
|
||||
<({<figures.checkboxOff> }); format=selected_style(item.selected)> <item.name>
|
||||
<endif>
|
||||
<else>
|
||||
<("[ ]"); format="item-disabled"> <item.name; format="item-disabled">
|
||||
<({<figures.checkboxOff> }); format="style-item-disabled"> <item.name; format="style-item-disabled">
|
||||
<endif>
|
||||
%>
|
||||
|
||||
// start '? xxx' shows both running and result
|
||||
question_name(model) ::= <<
|
||||
<("?"); format="list-value"> <model.name; format="title">
|
||||
<({<figures.questionMark>}); format="style-list-value"> <model.name; format="style-title">
|
||||
>>
|
||||
|
||||
// within info section, dedicated instructions for user
|
||||
@@ -48,7 +48,7 @@ comma_delimited(values) ::= <%
|
||||
|
||||
// component result
|
||||
result(model) ::= <<
|
||||
<question_name(model)> <(comma_delimited(model.values)); format="value">
|
||||
<question_name(model)> <(comma_delimited(model.values)); format="style-value">
|
||||
>>
|
||||
|
||||
// component is running
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
// message
|
||||
message(model) ::= <%
|
||||
<if(model.message && model.hasMessageLevelError)>
|
||||
<(">>>"); format="level-error"> <model.message; format="level-error">
|
||||
<({<figures.error>}); format="style-level-error"> <model.message; format="style-level-error">
|
||||
<elseif(model.message && model.hasMessageLevelWarn)>
|
||||
<(">>"); format="level-warn"> <model.message; format="level-warn">
|
||||
<({<figures.warning>}); format="style-level-warn"> <model.message; format="style-level-warn">
|
||||
<elseif(model.message && model.hasMessageLevelInfo)>
|
||||
<(">"); format="level-info"> <model.message; format="level-info">
|
||||
<({<figures.info>}); format="style-level-info"> <model.message; format="style-level-info">
|
||||
<endif>
|
||||
%>
|
||||
|
||||
@@ -18,12 +18,12 @@ info(model) ::= <%
|
||||
|
||||
// start '? xxx' shows both running and result
|
||||
question_name(model) ::= <<
|
||||
<("?"); format="list-value"> <model.name; format="title">
|
||||
<({<figures.questionMark>}); format="style-list-value"> <model.name; format="style-title">
|
||||
>>
|
||||
|
||||
// component result
|
||||
result(model) ::= <<
|
||||
<question_name(model)> <model.resultValue; format="value">
|
||||
<question_name(model)> <model.resultValue; format="style-value">
|
||||
>>
|
||||
|
||||
// component is running
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// selector rows
|
||||
select_item(item) ::= <%
|
||||
<if(item.selected)>
|
||||
<("> "); format="item-selector"><item.name; format="item-selector">
|
||||
<({<figures.rightPointingQuotation> }); format="style-item-selector"><item.name; format="style-item-selector">
|
||||
<else>
|
||||
<(" ")><item.name>
|
||||
<endif>
|
||||
@@ -9,7 +9,7 @@ select_item(item) ::= <%
|
||||
|
||||
// start '? xxx' shows both running and result
|
||||
question_name(model) ::= <<
|
||||
<("?"); format="list-value"> <model.name; format="title">
|
||||
<({<figures.questionMark>}); format="style-list-value"> <model.name; format="style-title">
|
||||
>>
|
||||
|
||||
// within info section, dedicated instructions for user
|
||||
@@ -28,7 +28,7 @@ info(model) ::= <<
|
||||
|
||||
// component result
|
||||
result(model) ::= <<
|
||||
<question_name(model)> <model.value; format="value">
|
||||
<question_name(model)> <model.value; format="style-value">
|
||||
>>
|
||||
|
||||
// component is running
|
||||
|
||||
@@ -5,7 +5,7 @@ info(model) ::= <%
|
||||
<model.maskedInput>
|
||||
<else>
|
||||
<if(model.defaultValue)>
|
||||
<("[Default "); format="value"><model.defaultValue; format="value"><("]"); format="value">
|
||||
<("[Default "); format="style-value"><model.defaultValue; format="style-value"><("]"); format="style-value">
|
||||
<endif>
|
||||
<endif>
|
||||
<else>
|
||||
@@ -13,7 +13,7 @@ info(model) ::= <%
|
||||
<model.input>
|
||||
<else>
|
||||
<if(model.defaultValue)>
|
||||
<("[Default "); format="value"><model.defaultValue; format="value"><("]"); format="value">
|
||||
<("[Default "); format="style-value"><model.defaultValue; format="style-value"><("]"); format="style-value">
|
||||
<endif>
|
||||
<endif>
|
||||
<endif>
|
||||
@@ -21,12 +21,12 @@ info(model) ::= <%
|
||||
|
||||
// start '? xxx' shows both running and result
|
||||
question_name(model) ::= <<
|
||||
<("?"); format="list-value"> <model.name; format="title">
|
||||
<({<figures.questionMark>}); format="style-list-value"> <model.name; format="style-title">
|
||||
>>
|
||||
|
||||
// component result
|
||||
result(model) ::= <<
|
||||
<question_name(model)> <model.maskedResultValue; format="value">
|
||||
<question_name(model)> <model.maskedResultValue; format="style-value">
|
||||
>>
|
||||
|
||||
// component is running
|
||||
|
||||
@@ -64,15 +64,15 @@ public abstract class AbstractShellTests {
|
||||
themeRegistry.register(new Theme() {
|
||||
@Override
|
||||
public String getName() {
|
||||
return "default";
|
||||
return "dump";
|
||||
}
|
||||
|
||||
@Override
|
||||
public ThemeSettings getSettings() {
|
||||
return ThemeSettings.themeSettings();
|
||||
return ThemeSettings.dump();
|
||||
}
|
||||
});
|
||||
ThemeResolver themeResolver = new ThemeResolver(themeRegistry, "default");
|
||||
ThemeResolver themeResolver = new ThemeResolver(themeRegistry, "dump");
|
||||
templateExecutor = new TemplateExecutor(themeResolver);
|
||||
|
||||
resourceLoader = new DefaultResourceLoader();
|
||||
|
||||
@@ -96,7 +96,7 @@ public class MultiItemSelectorTests extends AbstractShellTests {
|
||||
public void testItemsShownWithDisabled() {
|
||||
scheduleSelect(Arrays.asList(SELECTOR_ITEM_1, SELECTOR_ITEM_7));
|
||||
await().atMost(Duration.ofSeconds(4))
|
||||
.untilAsserted(() -> assertStringOrderThat(consoleOut()).containsInOrder("[ ] simplePojo1", "[ ] simplePojo7"));
|
||||
.untilAsserted(() -> assertStringOrderThat(consoleOut()).containsInOrder("[ ] simplePojo1", "[ ] simplePojo7"));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -69,7 +69,7 @@ public abstract class AbstractShellTests {
|
||||
|
||||
@Override
|
||||
public ThemeSettings getSettings() {
|
||||
return ThemeSettings.themeSettings();
|
||||
return ThemeSettings.defaults();
|
||||
}
|
||||
});
|
||||
ThemeResolver themeResolver = new ThemeResolver(themeRegistry, "default");
|
||||
|
||||
@@ -41,7 +41,7 @@ public class TemplateExecutorTests {
|
||||
|
||||
@Override
|
||||
public ThemeSettings getSettings() {
|
||||
return ThemeSettings.themeSettings();
|
||||
return ThemeSettings.defaults();
|
||||
}
|
||||
});
|
||||
themeResolver = new ThemeResolver(themeRegistry, "default");
|
||||
@@ -70,7 +70,7 @@ public class TemplateExecutorTests {
|
||||
@Test
|
||||
public void testWithTheme() {
|
||||
TemplateExecutor executor = new TemplateExecutor(themeResolver);
|
||||
String template = "<foo; format=\"title\">";
|
||||
String template = "<foo; format=\"style-title\">";
|
||||
Map<String, Object> attributes = new HashMap<>();
|
||||
attributes.put("foo", "bar");
|
||||
AttributedString result = executor.render(template, attributes);
|
||||
@@ -84,7 +84,7 @@ public class TemplateExecutorTests {
|
||||
@Test
|
||||
public void testNullDontStyle() {
|
||||
TemplateExecutor executor = new TemplateExecutor(themeResolver);
|
||||
String template = "<foo; format=\"list-key\">";
|
||||
String template = "<foo; format=\"style-list-key\">";
|
||||
Map<String, Object> attributes = new HashMap<>();
|
||||
attributes.put("foo", "bar");
|
||||
AttributedString result = executor.render(template, attributes);
|
||||
|
||||
@@ -24,7 +24,7 @@ public class ThemeRegistryTests {
|
||||
@Test
|
||||
public void test() {
|
||||
ThemeRegistry registry = new ThemeRegistry();
|
||||
Theme theme = Theme.of("name1", ThemeSettings.themeSettings());
|
||||
Theme theme = Theme.of("name1", ThemeSettings.defaults());
|
||||
registry.register(theme);
|
||||
assertThat(registry.get("name1")).isSameAs(theme);
|
||||
assertThat(registry.get("name2")).isNull();
|
||||
|
||||
@@ -34,11 +34,11 @@ public class ThemeResolverTests {
|
||||
|
||||
@Override
|
||||
public ThemeSettings getSettings() {
|
||||
return ThemeSettings.themeSettings();
|
||||
return ThemeSettings.defaults();
|
||||
}
|
||||
});
|
||||
ThemeResolver themeResolver = new ThemeResolver(themeRegistry, "default");
|
||||
assertThat(themeResolver.resolveTag(ThemeSettings.TAG_TITLE)).isEqualTo("bold,fg:bright-white");
|
||||
assertThat(themeResolver.resolveStyleTag(StyleSettings.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));
|
||||
|
||||
@@ -29,8 +29,8 @@ public class ThemeSettingsTests {
|
||||
|
||||
@Test
|
||||
public void testGeneratedStyle() {
|
||||
ThemeSettings themeSettings = ThemeSettings.themeSettings();
|
||||
String resolveTag = themeSettings.resolveTag(ThemeSettings.TAG_TITLE);
|
||||
ThemeSettings themeSettings = ThemeSettings.defaults();
|
||||
String resolveTag = themeSettings.styles().resolveTag(StyleSettings.TAG_TITLE);
|
||||
assertThat(resolveTag).isEqualTo("bold,fg:bright-white");
|
||||
|
||||
StyleSource styleSource = new MemoryStyleSource();
|
||||
@@ -42,22 +42,22 @@ public class ThemeSettingsTests {
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = {
|
||||
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,
|
||||
ThemeSettings.TAG_HIGHLIGHT })
|
||||
StyleSettings.TAG_TITLE,
|
||||
StyleSettings.TAG_VALUE,
|
||||
StyleSettings.TAG_LIST_KEY,
|
||||
StyleSettings.TAG_LIST_VALUE,
|
||||
StyleSettings.TAG_LEVEL_INFO,
|
||||
StyleSettings.TAG_LEVEL_WARN,
|
||||
StyleSettings.TAG_LEVEL_ERROR,
|
||||
StyleSettings.TAG_ITEM_ENABLED,
|
||||
StyleSettings.TAG_ITEM_DISABLED,
|
||||
StyleSettings.TAG_ITEM_SELECTED,
|
||||
StyleSettings.TAG_ITEM_UNSELECTED,
|
||||
StyleSettings.TAG_ITEM_SELECTOR,
|
||||
StyleSettings.TAG_HIGHLIGHT })
|
||||
public void testTags(String tag) {
|
||||
ThemeSettings themeSettings = ThemeSettings.themeSettings();
|
||||
String resolveTag = themeSettings.resolveTag(ThemeSettings.TAG_TITLE);
|
||||
ThemeSettings themeSettings = ThemeSettings.defaults();
|
||||
String resolveTag = themeSettings.styles().resolveTag(StyleSettings.TAG_TITLE);
|
||||
assertThat(resolveTag).isNotNull();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ package org.springframework.shell.samples.standard;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.jline.utils.AttributedString;
|
||||
import org.jline.utils.AttributedStringBuilder;
|
||||
@@ -26,8 +27,9 @@ 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.FigureSettings;
|
||||
import org.springframework.shell.style.StyleSettings;
|
||||
import org.springframework.shell.style.ThemeResolver;
|
||||
import org.springframework.shell.style.ThemeSettings;
|
||||
|
||||
@ShellComponent
|
||||
public class StyleCommands {
|
||||
@@ -39,10 +41,10 @@ public class StyleCommands {
|
||||
List<String> 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<String> 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);
|
||||
List<String> themeTags = Arrays.asList(StyleSettings.TAG_TITLE, StyleSettings.TAG_VALUE, StyleSettings.TAG_LIST_KEY,
|
||||
StyleSettings.TAG_LIST_VALUE, StyleSettings.TAG_LEVEL_INFO, StyleSettings.TAG_LEVEL_WARN,
|
||||
StyleSettings.TAG_LEVEL_ERROR, StyleSettings.TAG_ITEM_ENABLED, StyleSettings.TAG_ITEM_DISABLED,
|
||||
StyleSettings.TAG_ITEM_SELECTED, StyleSettings.TAG_ITEM_UNSELECTED, StyleSettings.TAG_ITEM_SELECTOR);
|
||||
|
||||
@Autowired
|
||||
private ThemeResolver themeResolver;
|
||||
@@ -82,14 +84,32 @@ public class StyleCommands {
|
||||
AttributedStringBuilder builder = new AttributedStringBuilder();
|
||||
themeTags.stream()
|
||||
.forEach(tag -> {
|
||||
String resolvedStyle = themeResolver.resolveTag(tag);
|
||||
String resolvedStyle = themeResolver.resolveStyleTag(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();
|
||||
}
|
||||
|
||||
@ShellMethod(key = "style figure", value = "Showcase figures", group = "Styles")
|
||||
public AttributedString stylesFigures() {
|
||||
AttributedStringBuilder builder = new AttributedStringBuilder();
|
||||
Stream.of(FigureSettings.tags())
|
||||
.forEach(tag -> {
|
||||
builder.append(String.format("%-25s", tag));
|
||||
builder.append(" ");
|
||||
String resolveFigureTag = themeResolver.resolveFigureTag(tag);
|
||||
combinations3().stream().forEach(spec -> {
|
||||
AttributedStyle style = themeResolver.resolveStyle(spec);
|
||||
builder.append(" ");
|
||||
builder.append(new AttributedString(resolveFigureTag, style));
|
||||
});
|
||||
builder.append("\n");
|
||||
});
|
||||
return builder.toAttributedString();
|
||||
}
|
||||
|
||||
@@ -112,4 +132,16 @@ public class StyleCommands {
|
||||
});
|
||||
return styles;
|
||||
}
|
||||
|
||||
private List<String> combinations3() {
|
||||
List<String> styles = new ArrayList<>();
|
||||
Arrays.asList("fg").stream().forEach(ground -> {
|
||||
colors.stream().forEach(color -> {
|
||||
named.stream().forEach(named -> {
|
||||
styles.add(String.format("%s,%s:%s", named, ground, color));
|
||||
});
|
||||
});
|
||||
});
|
||||
return styles;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// NAME
|
||||
name(commandName, commandShortDesc) ::= <<
|
||||
<("NAME"); format="highlight">
|
||||
<("NAME"); format="style-highlight">
|
||||
<commandName> - <commandShortDesc>
|
||||
>>
|
||||
|
||||
@@ -18,8 +18,8 @@ synopsisOption(option) ::= <%
|
||||
%>
|
||||
|
||||
synopsis(commandName, options) ::= <<
|
||||
<("SYNOPSIS"); format="highlight">
|
||||
<(commandName); format="highlight"> <options: { o | <synopsisOption(o)>}; separator=" ">
|
||||
<("SYNOPSIS"); format="style-highlight">
|
||||
<(commandName); format="style-highlight"> <options: { o | <synopsisOption(o)>}; separator=" ">
|
||||
>>
|
||||
|
||||
// OPTIONS
|
||||
@@ -53,7 +53,7 @@ option(option) ::= <<
|
||||
>>
|
||||
|
||||
options(options) ::= <<
|
||||
<("OPTIONS"); format="highlight">
|
||||
<("OPTIONS"); format="style-highlight">
|
||||
<options:{ o | <option(o)>}>
|
||||
>>
|
||||
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
name() ::= <<
|
||||
<("AVAILABLE COMMANDS"); format="highlight">
|
||||
<("AVAILABLE COMMANDS"); format="style-highlight">
|
||||
|
||||
>>
|
||||
|
||||
command(command) ::= <<
|
||||
<(command.name); format="highlight"><(":"); format="highlight"> <command.description>
|
||||
<(command.name); format="style-highlight"><(":"); format="style-highlight"> <command.description>
|
||||
|
||||
>>
|
||||
|
||||
commandGroup(commandGroup) ::= <<
|
||||
<(commandGroup.group); format="highlight">
|
||||
<(commandGroup.group); format="style-highlight">
|
||||
<commandGroup.commands:{ c | <command(c)>}>
|
||||
|
||||
>>
|
||||
|
||||
@@ -228,7 +228,7 @@ public class HelpTests {
|
||||
@Bean
|
||||
public Help help() {
|
||||
ThemeRegistry registry = new ThemeRegistry();
|
||||
registry.register(Theme.of("default", ThemeSettings.themeSettings()));
|
||||
registry.register(Theme.of("default", ThemeSettings.defaults()));
|
||||
ThemeResolver resolver = new ThemeResolver(registry, "default");
|
||||
TemplateExecutor executor = new TemplateExecutor(resolver);
|
||||
Help help = new Help(executor);
|
||||
|
||||
Reference in New Issue
Block a user