Skip to content
Merged
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
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2021 the original author or authors.
* Copyright 2021-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.
Expand All @@ -17,7 +17,6 @@

import org.jline.reader.impl.history.DefaultHistory;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
Expand All @@ -30,7 +29,7 @@ public class JLineAutoConfiguration {
public static class JLineHistoryConfiguration {

@Bean
public org.jline.reader.History history(@Value("${spring.application.name:spring-shell}.log") String historyPath) {
public org.jline.reader.History history() {
return new DefaultHistory();
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
package org.springframework.shell.boot;

import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.regex.Pattern;

Expand All @@ -28,17 +29,25 @@
import org.jline.utils.AttributedString;
import org.jline.utils.AttributedStringBuilder;
import org.jline.utils.AttributedStyle;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.context.event.EventListener;
import org.springframework.shell.command.CommandCatalog;
import org.springframework.shell.config.UserConfigPathProvider;
import org.springframework.util.StringUtils;

@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(SpringShellProperties.class)
public class LineReaderAutoConfiguration {

private final static Logger log = LoggerFactory.getLogger(LineReaderAutoConfiguration.class);

private Terminal terminal;

private Completer completer;
Expand All @@ -50,15 +59,21 @@ public class LineReaderAutoConfiguration {
private org.jline.reader.History jLineHistory;

@Value("${spring.application.name:spring-shell}.log")
private String historyPath;
private String fallbackHistoryFileName;

private SpringShellProperties springShellProperties;
private UserConfigPathProvider userConfigPathProvider;

public LineReaderAutoConfiguration(Terminal terminal, Completer completer, Parser parser,
CommandCatalog commandRegistry, org.jline.reader.History jLineHistory) {
CommandCatalog commandRegistry, org.jline.reader.History jLineHistory,
SpringShellProperties springShellProperties, UserConfigPathProvider userConfigPathProvider) {
this.terminal = terminal;
this.completer = completer;
this.parser = parser;
this.commandRegistry = commandRegistry;
this.jLineHistory = jLineHistory;
this.springShellProperties = springShellProperties;
this.userConfigPathProvider = userConfigPathProvider;
}

@EventListener
Expand Down Expand Up @@ -104,7 +119,20 @@ public void setErrorIndex(int errorIndex) {
.parser(parser);

LineReader lineReader = lineReaderBuilder.build();
lineReader.setVariable(LineReader.HISTORY_FILE, Paths.get(historyPath));
if (this.springShellProperties.getHistory().isEnabled()) {
// Discover history location
Path userConfigPath = this.userConfigPathProvider.provide();
log.debug("Resolved userConfigPath {}", userConfigPath);
String historyFileName = this.springShellProperties.getHistory().getName();
if (!StringUtils.hasText(historyFileName)) {
historyFileName = fallbackHistoryFileName;
}
log.debug("Resolved historyFileName {}", historyFileName);
String historyPath = userConfigPath.resolve(historyFileName).toAbsolutePath().toString();
log.debug("Resolved historyPath {}", historyPath);
// set history file
lineReader.setVariable(LineReader.HISTORY_FILE, Paths.get(historyPath));
}
lineReader.unsetOpt(LineReader.Option.INSERT_TAB); // This allows completion on an empty buffer, rather than inserting a tab
jLineHistory.attach(lineReader);
return lineReader;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,20 +25,38 @@
@ConfigurationProperties(prefix = "spring.shell")
public class SpringShellProperties {

private History history = new History();
private Config config = new Config();
private Script script = new Script();
private Interactive interactive = new Interactive();
private Noninteractive noninteractive = new Noninteractive();
private Theme theme = new Theme();
private Command command = new Command();

public void setScript(Script script) {
this.script = script;
public void setConfig(Config config) {
this.config = config;
}

public Config getConfig() {
return config;
}

public History getHistory() {
return history;
}

public void setHistory(History history) {
this.history = history;
}

public Script getScript() {
return script;
}

public void setScript(Script script) {
this.script = script;
}

public void setInteractive(Interactive interactive) {
this.interactive = interactive;
}
Expand Down Expand Up @@ -71,6 +89,50 @@ public void setCommand(Command command) {
this.command = command;
}

public static class Config {

private String env;
private String location;

public String getEnv() {
return env;
}

public void setEnv(String env) {
this.env = env;
}

public String getLocation() {
return location;
}

public void setLocation(String location) {
this.location = location;
}
}

public static class History {

private String name;
private boolean enabled = true;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public boolean isEnabled() {
return enabled;
}

public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
}

public static class Script {

private boolean enabled = true;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/*
* 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.boot;

import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.function.Function;

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.config.UserConfigPathProvider;
import org.springframework.util.StringUtils;

@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(SpringShellProperties.class)
public class UserConfigAutoConfiguration {

@Bean
@ConditionalOnMissingBean(UserConfigPathProvider.class)
public UserConfigPathProvider userConfigPathProvider(SpringShellProperties springShellProperties) {
return () -> {
LocationResolver resolver = new LocationResolver(springShellProperties.getConfig().getEnv(),
springShellProperties.getConfig().getLocation());
return resolver.resolve();
};
}

static class LocationResolver {

private final static String XDG_CONFIG_HOME = "XDG_CONFIG_HOME";
private final static String APP_DATA = "APP_DATA";
private static final String USERCONFIG_PLACEHOLDER = "{userconfig}";
private Function<String, Path> pathProvider = (path) -> Paths.get(path);
private final String configDirEnv;
private final String configDirLocation;

LocationResolver(String configDirEnv, String configDirLocation) {
this.configDirEnv = configDirEnv;
this.configDirLocation = configDirLocation;
}

Path resolve() {
String location;
if (StringUtils.hasText(configDirEnv) && StringUtils.hasText(System.getenv(configDirEnv))) {
location = System.getenv(configDirEnv);
}
else if (StringUtils.hasText(configDirLocation)) {
location = configDirLocation;
}
else {
location = "";
}
if (usesUserConfigLocation(location)) {
location = resolveUserConfigLocation(location);
}
return pathProvider.apply(location);
}

private boolean usesUserConfigLocation(String location) {
return location.contains(USERCONFIG_PLACEHOLDER);
}

private String resolveUserConfigLocation(String location) {
String userConfigHome = "";
if (StringUtils.hasText(System.getenv(XDG_CONFIG_HOME))) {
userConfigHome = System.getenv(XDG_CONFIG_HOME);
}
else if (isWindows() && StringUtils.hasText(System.getenv(APP_DATA))) {
userConfigHome = System.getenv(APP_DATA);
}
else {
userConfigHome = System.getProperty("user.home") + "/" + ".config";
}
return location.replace(USERCONFIG_PLACEHOLDER, userConfigHome);
}

private boolean isWindows() {
String os = System.getProperty("os.name");
return os.startsWith("Windows");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ org.springframework.shell.boot.ApplicationRunnerAutoConfiguration,\
org.springframework.shell.boot.CommandCatalogAutoConfiguration,\
org.springframework.shell.boot.LineReaderAutoConfiguration,\
org.springframework.shell.boot.CompleterAutoConfiguration,\
org.springframework.shell.boot.UserConfigAutoConfiguration,\
org.springframework.shell.boot.JLineAutoConfiguration,\
org.springframework.shell.boot.JLineShellAutoConfiguration,\
org.springframework.shell.boot.ParameterResolverAutoConfiguration,\
Expand Down
Loading