Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,9 @@
<awaitility.version>4.3.0</awaitility.version>
<log4j.version>2.25.2</log4j.version>

<!-- samples dependencies -->
<h2-database.version>2.4.240</h2-database.version>
<logback.version>1.5.21</logback.version>
<!-- samples dependencies -->
<h2-database.version>2.4.240</h2-database.version>
<logback.version>1.5.21</logback.version>

<!-- documentation dependencies -->
<antora-maven-plugin.version>1.0.0-alpha.5</antora-maven-plugin.version>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,9 @@
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.regex.Pattern;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jline.reader.Highlighter;
import org.jline.reader.LineReader;
import org.jline.reader.LineReaderBuilder;
import org.jline.reader.Parser;
Expand All @@ -32,7 +30,6 @@
import org.jline.terminal.TerminalBuilder;
import org.jline.terminal.TerminalBuilder.SystemOutput;
import org.jline.utils.AttributedString;
import org.jline.utils.AttributedStringBuilder;
import org.jline.utils.AttributedStyle;

import org.springframework.beans.factory.BeanCreationException;
Expand All @@ -50,6 +47,7 @@
import org.springframework.shell.core.command.CommandRegistry;
import org.springframework.shell.core.config.UserConfigPathProvider;
import org.springframework.shell.jline.CommandCompleter;
import org.springframework.shell.jline.CommandHighlighter;
import org.springframework.shell.jline.ExtendedDefaultParser;
import org.springframework.shell.jline.JLineInputProvider;
import org.springframework.shell.jline.PromptProvider;
Expand All @@ -62,6 +60,7 @@
* @author Eric Bottard
* @author Florent Biville
* @author Mahmoud Ben Hassine
* @author Piotr Olaszewski
*/
@AutoConfiguration
@EnableConfigurationProperties(SpringShellProperties.class)
Expand Down Expand Up @@ -98,36 +97,7 @@ public LineReader lineReader(Terminal terminal, Parser parser, CommandCompleter
.appName("Spring Shell")
.completer(commandCompleter)
.history(jLineHistory)
.highlighter(new Highlighter() {

@Override
public AttributedString highlight(LineReader reader, String buffer) {
int l = 0;
String best = null;
for (Command command : commandRegistry.getCommands()) {
if (buffer.startsWith(command.getName()) && command.getName().length() > l) {
l = command.getName().length();
best = command.getName();
}
}
if (best != null) {
return new AttributedStringBuilder(buffer.length()).append(best, AttributedStyle.BOLD)
.append(buffer.substring(l))
.toAttributedString();
}
else {
return new AttributedString(buffer, AttributedStyle.DEFAULT.foreground(AttributedStyle.RED));
}
}

@Override
public void setErrorPattern(Pattern errorPattern) {
}

@Override
public void setErrorIndex(int errorIndex) {
}
})
.highlighter(new CommandHighlighter(commandRegistry))
.parser(parser);

LineReader lineReader = lineReaderBuilder.build();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package org.springframework.shell.jline;

import java.util.Comparator;
import java.util.stream.Stream;

import org.jline.reader.Highlighter;
import org.jline.reader.LineReader;
import org.jline.utils.AttributedString;
import org.jline.utils.AttributedStringBuilder;
import org.jline.utils.AttributedStyle;

import org.springframework.shell.core.command.CommandRegistry;

/**
* @author Piotr Olaszewski
* @since 4.0.1
*/
public class CommandHighlighter implements Highlighter {

private final CommandRegistry commandRegistry;

public CommandHighlighter(CommandRegistry commandRegistry) {
this.commandRegistry = commandRegistry;
}

@Override
public AttributedString highlight(LineReader reader, String buffer) {
return commandRegistry.getCommands()
.stream()
.flatMap(command -> Stream.concat(Stream.of(command.getName()), command.getAliases().stream()))
.filter(buffer::startsWith)
.max(Comparator.comparingInt(String::length))
.map(bestMatch -> new AttributedStringBuilder(buffer.length()).append(bestMatch, AttributedStyle.BOLD)
.append(buffer.substring(bestMatch.length()))
.toAttributedString())
.orElseGet(() -> new AttributedString(buffer, AttributedStyle.DEFAULT.foreground(AttributedStyle.RED)));
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
@NullMarked
package org.springframework.shell.jline.command;

import org.jspecify.annotations.NullMarked;
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ else if ("last".equals(option.longName()) || 'l' == option.shortName()) {
};

@BeforeEach
public void before() {
void before() {
command = mock(Command.class);
when(command.getName()).thenReturn("hello");
when(command.getCompletionProvider()).thenReturn(completionProvider);
Expand All @@ -86,7 +86,7 @@ private List<String> toCandidateNames(List<Candidate> candidates) {

@ParameterizedTest
@MethodSource("completeData")
public void testComplete(List<String> words, List<String> expectedValues) {
void testComplete(List<String> words, List<String> expectedValues) {
// given
when(command.getName()).thenReturn("hello");
when(command.getOptions())
Expand Down Expand Up @@ -173,7 +173,7 @@ static Stream<Arguments> completeData() {

@ParameterizedTest
@MethodSource("completeCommandWithLongNamesData")
public void testCompleteCommandWithLongNames(List<String> words, List<String> expectedValues) {
void testCompleteCommandWithLongNames(List<String> words, List<String> expectedValues) {
// given
when(command.getOptions()).thenReturn(List.of(new CommandOption.Builder().longName("first").build(),
new CommandOption.Builder().longName("last").build()));
Expand Down Expand Up @@ -227,7 +227,7 @@ static Stream<Arguments> completeCommandWithLongNamesData() {

@ParameterizedTest
@MethodSource("completeCommandWithShortNamesData")
public void testCompleteCommandWithShortNames(List<String> words, List<String> expectedValues) {
void testCompleteCommandWithShortNames(List<String> words, List<String> expectedValues) {
// given
when(command.getOptions()).thenReturn(List.of(new CommandOption.Builder().shortName('f').build(),
new CommandOption.Builder().shortName('l').build()));
Expand Down Expand Up @@ -279,7 +279,7 @@ static Stream<Arguments> completeCommandWithShortNamesData() {

@ParameterizedTest
@MethodSource("completeWithSubCommandsData")
public void testCompleteWithSubCommands(List<String> words, List<String> expectedValues) {
void testCompleteWithSubCommands(List<String> words, List<String> expectedValues) {
// given
when(command.getName()).thenReturn("hello world");
when(command.getOptions())
Expand Down Expand Up @@ -328,7 +328,7 @@ static Stream<Arguments> completeWithSubCommandsData() {

@ParameterizedTest
@MethodSource("completeWithTwoOptionsWhereOneIsSubsetOfOtherData")
public void testCompleteWithTwoOptionsWhereOneIsSubsetOfOther(List<String> words, List<String> expectedValues) {
void testCompleteWithTwoOptionsWhereOneIsSubsetOfOther(List<String> words, List<String> expectedValues) {
// given
when(command.getOptions()).thenReturn(List.of(new CommandOption.Builder().longName("first").build(),
new CommandOption.Builder().longName("firstname").build()));
Expand Down Expand Up @@ -373,7 +373,7 @@ static Stream<Arguments> completeWithTwoOptionsWhereOneIsSubsetOfOtherData() {

@ParameterizedTest
@MethodSource("completeWithHiddenCommandsData")
public void testCompleteWithHiddenCommands(List<String> words, List<String> expectedValues) {
void testCompleteWithHiddenCommands(List<String> words, List<String> expectedValues) {
// given
when(command.getName()).thenReturn("hello visible");
when(command.getOptions()).thenReturn(List.of());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package org.springframework.shell.jline;

import java.util.Collections;
import java.util.List;

import org.jline.reader.LineReader;
import org.jline.utils.AttributedString;
import org.jline.utils.AttributedStyle;
import org.jline.utils.Colors;
import org.junit.jupiter.api.Test;

import org.springframework.shell.core.command.Command;
import org.springframework.shell.core.command.CommandRegistry;
import org.springframework.shell.jline.tui.style.ThemeResolver;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.Mockito.*;

/**
* @author Piotr Olaszewski
*/
class CommandHighlighterTest {

private final LineReader lineReader = mock(LineReader.class);

private final CommandRegistry commandRegistry = new CommandRegistry();

private final CommandHighlighter highlighter = new CommandHighlighter(commandRegistry);

@Test
void shouldHighlightCommandNameInBold() {
Command command = mock(Command.class);
when(command.getName()).thenReturn("help");
when(command.getAliases()).thenReturn(Collections.emptyList());

commandRegistry.registerCommand(command);

AttributedString result = highlighter.highlight(lineReader, "help argument");

assertThat(result).hasToString("help argument");
assertThat(result.styleAt(0).getStyle()).isEqualTo(AttributedStyle.BOLD.getStyle());
assertThat(result.styleAt(5).getStyle()).isNotEqualTo(AttributedStyle.BOLD.getStyle());
}

@Test
void shouldHighlightAliasInBold() {
Command command = mock(Command.class);
when(command.getName()).thenReturn("help");
when(command.getAliases()).thenReturn(List.of("h"));

commandRegistry.registerCommand(command);

AttributedString result = highlighter.highlight(lineReader, "h argument");

assertThat(result).hasToString("h argument");
assertThat(result.styleAt(0).getStyle()).isEqualTo(AttributedStyle.BOLD.getStyle());
assertThat(result.styleAt(2).getStyle()).isNotEqualTo(AttributedStyle.BOLD.getStyle());
}

@Test
void shouldHighlightRedWhenNoMatchFound() {
AttributedString result = highlighter.highlight(lineReader, "unknown");

assertThat(result).hasToString("unknown");
assertThat(ThemeResolver.resolveValues(result.styleAt(0)).foreground())
.isEqualTo(Colors.DEFAULT_COLORS_256[AttributedStyle.RED]);
}

}
Loading