1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.cling.invoker.mvnsh;
20
21 import java.nio.file.Path;
22 import java.util.Map;
23 import java.util.concurrent.atomic.AtomicReference;
24 import java.util.function.Consumer;
25
26 import org.apache.maven.api.annotations.Nullable;
27 import org.apache.maven.api.cli.InvokerRequest;
28 import org.apache.maven.api.services.Lookup;
29 import org.apache.maven.cling.invoker.LookupContext;
30 import org.apache.maven.cling.invoker.LookupInvoker;
31 import org.apache.maven.cling.utils.CLIReportingUtils;
32 import org.jline.builtins.ConfigurationPath;
33 import org.jline.console.impl.Builtins;
34 import org.jline.console.impl.SimpleSystemRegistryImpl;
35 import org.jline.console.impl.SystemRegistryImpl;
36 import org.jline.keymap.KeyMap;
37 import org.jline.reader.Binding;
38 import org.jline.reader.EndOfFileException;
39 import org.jline.reader.LineReader;
40 import org.jline.reader.LineReaderBuilder;
41 import org.jline.reader.MaskingCallback;
42 import org.jline.reader.Reference;
43 import org.jline.reader.UserInterruptException;
44 import org.jline.reader.impl.DefaultHighlighter;
45 import org.jline.reader.impl.DefaultParser;
46 import org.jline.reader.impl.history.DefaultHistory;
47 import org.jline.terminal.Terminal;
48 import org.jline.utils.AttributedStringBuilder;
49 import org.jline.utils.AttributedStyle;
50 import org.jline.utils.InfoCmp;
51 import org.jline.widget.TailTipWidgets;
52
53
54
55
56 public class ShellInvoker extends LookupInvoker<LookupContext> {
57
58 public ShellInvoker(Lookup protoLookup, @Nullable Consumer<LookupContext> contextConsumer) {
59 super(protoLookup, contextConsumer);
60 }
61
62 @Override
63 protected LookupContext createContext(InvokerRequest invokerRequest) {
64 return new LookupContext(invokerRequest, true, invokerRequest.options().orElse(null));
65 }
66
67 public static final int OK = 0;
68 public static final int ERROR = 1;
69
70 @Override
71 protected int execute(LookupContext context) throws Exception {
72
73 ConfigurationPath configPath = new ConfigurationPath(context.cwd.get(), context.cwd.get());
74 Builtins builtins = new Builtins(context.cwd, configPath, null);
75 builtins.rename(Builtins.Command.TTOP, "top");
76 builtins.alias("zle", "widget");
77 builtins.alias("bindkey", "keymap");
78
79 ShellCommandRegistryHolder holder = new ShellCommandRegistryHolder();
80 holder.addCommandRegistry(builtins);
81
82
83 Map<String, ShellCommandRegistryFactory> factories =
84 context.lookup.lookupMap(ShellCommandRegistryFactory.class);
85 for (Map.Entry<String, ShellCommandRegistryFactory> entry : factories.entrySet()) {
86 holder.addCommandRegistry(entry.getValue().createShellCommandRegistry(context));
87 }
88
89 DefaultParser parser = new DefaultParser();
90 parser.setRegexCommand("[:]{0,1}[a-zA-Z!]{1,}\\S*");
91
92 String banner = """
93
94 ░▒▓██████████████▓▒░ ░▒▓█▓▒░░▒▓█▓▒░░▒▓███████▓▒░ ░▒▓███████▓▒░░▒▓█▓▒░░▒▓█▓▒░\s
95 ░▒▓█▓▒░░▒▓█▓▒░░▒▓█▓▒░░▒▓█▓▒░░▒▓█▓▒░░▒▓█▓▒░░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░\s
96 ░▒▓█▓▒░░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░\s
97 ░▒▓█▓▒░░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░ ░▒▓██████▓▒░ ░▒▓████████▓▒░\s
98 ░▒▓█▓▒░░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░░▒▓█▓▒░\s
99 ░▒▓█▓▒░░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░░▒▓█▓▒░\s
100 ░▒▓█▓▒░░▒▓█▓▒░░▒▓█▓▒░ ░▒▓██▓▒░ ░▒▓█▓▒░░▒▓█▓▒░░▒▓███████▓▒░ ░▒▓█▓▒░░▒▓█▓▒░""";
101 context.writer.accept(banner);
102 if (!context.options().showVersion().orElse(false)) {
103 context.writer.accept(CLIReportingUtils.showVersionMinimal());
104 }
105 context.writer.accept("");
106
107 try (holder) {
108 SimpleSystemRegistryImpl systemRegistry =
109 new SimpleSystemRegistryImpl(parser, context.terminal, context.cwd, configPath) {
110 @Override
111 public boolean isCommandOrScript(String command) {
112 return command.startsWith("!") || super.isCommandOrScript(command);
113 }
114 };
115 systemRegistry.setCommandRegistries(holder.getCommandRegistries());
116
117 Path history = context.userDirectory.resolve(".mvnsh_history");
118 LineReader reader = LineReaderBuilder.builder()
119 .terminal(context.terminal)
120 .history(new DefaultHistory())
121 .highlighter(new ReplHighlighter())
122 .completer(systemRegistry.completer())
123 .parser(parser)
124 .variable(LineReader.LIST_MAX, 50)
125 .variable(LineReader.HISTORY_FILE, history)
126 .variable(LineReader.OTHERS_GROUP_NAME, "Others")
127 .variable(LineReader.COMPLETION_STYLE_GROUP, "fg:blue,bold")
128 .variable("HELP_COLORS", "ti=1;34:co=38:ar=3:op=33:de=90")
129 .option(LineReader.Option.GROUP_PERSIST, true)
130 .build();
131 builtins.setLineReader(reader);
132 systemRegistry.setLineReader(reader);
133 new TailTipWidgets(reader, systemRegistry::commandDescription, 5, TailTipWidgets.TipType.COMPLETER);
134 KeyMap<Binding> keyMap = reader.getKeyMaps().get("main");
135 keyMap.bind(new Reference("tailtip-toggle"), KeyMap.alt("s"));
136
137
138 AtomicReference<Exception> failure = new AtomicReference<>();
139 while (true) {
140 try {
141 failure.set(null);
142 systemRegistry.cleanUp();
143 Thread commandThread = new Thread(() -> {
144 try {
145 systemRegistry.execute(reader.readLine(
146 context.cwd.get().getFileName().toString() + " mvnsh> ",
147 null,
148 (MaskingCallback) null,
149 null));
150 } catch (Exception e) {
151 failure.set(e);
152 }
153 });
154 context.terminal.handle(Terminal.Signal.INT, signal -> commandThread.interrupt());
155 commandThread.start();
156 commandThread.join();
157 if (failure.get() != null) {
158 throw failure.get();
159 }
160 } catch (UserInterruptException e) {
161
162
163 } catch (EndOfFileException e) {
164 return OK;
165 } catch (SystemRegistryImpl.UnknownCommandException e) {
166 context.writer.accept(context.invokerRequest
167 .messageBuilderFactory()
168 .builder()
169 .error(e.getMessage())
170 .build());
171 } catch (Exception e) {
172 systemRegistry.trace(e);
173 context.writer.accept(context.invokerRequest
174 .messageBuilderFactory()
175 .builder()
176 .error("Error: " + e.getMessage())
177 .build());
178 if (context.options().showErrors().orElse(false)) {
179 e.printStackTrace(context.terminal.writer());
180 }
181 return ERROR;
182 }
183 }
184 }
185 }
186
187 private static class ReplHighlighter extends DefaultHighlighter {
188 @Override
189 protected void commandStyle(LineReader reader, AttributedStringBuilder sb, boolean enable) {
190 if (enable) {
191 if (reader.getTerminal().getNumericCapability(InfoCmp.Capability.max_colors) >= 256) {
192 sb.style(AttributedStyle.DEFAULT.bold().foreground(69));
193 } else {
194 sb.style(AttributedStyle.DEFAULT.foreground(AttributedStyle.CYAN));
195 }
196 } else {
197 sb.style(AttributedStyle.DEFAULT.boldOff().foregroundOff());
198 }
199 }
200 }
201 }