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
95 ░▒▓██████████████▓▒░ ░▒▓█▓▒░░▒▓█▓▒░░▒▓███████▓▒░ ░▒▓███████▓▒░░▒▓█▓▒░░▒▓█▓▒░\s
96 ░▒▓█▓▒░░▒▓█▓▒░░▒▓█▓▒░░▒▓█▓▒░░▒▓█▓▒░░▒▓█▓▒░░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░\s
97 ░▒▓█▓▒░░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░\s
98 ░▒▓█▓▒░░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░ ░▒▓██████▓▒░ ░▒▓████████▓▒░\s
99 ░▒▓█▓▒░░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░░▒▓█▓▒░\s
100 ░▒▓█▓▒░░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░░▒▓█▓▒░\s
101 ░▒▓█▓▒░░▒▓█▓▒░░▒▓█▓▒░ ░▒▓██▓▒░ ░▒▓█▓▒░░▒▓█▓▒░░▒▓███████▓▒░ ░▒▓█▓▒░░▒▓█▓▒░""";
102 context.writer.accept(banner);
103 if (!context.options().showVersion().orElse(false)) {
104 context.writer.accept(CLIReportingUtils.showVersionMinimal());
105 }
106 context.writer.accept("");
107
108 try (holder) {
109 SimpleSystemRegistryImpl systemRegistry =
110 new SimpleSystemRegistryImpl(parser, context.terminal, context.cwd, configPath) {
111 @Override
112 public boolean isCommandOrScript(String command) {
113 return command.startsWith("!") || super.isCommandOrScript(command);
114 }
115 };
116 systemRegistry.setCommandRegistries(holder.getCommandRegistries());
117
118 Path history = context.userDirectory.resolve(".mvnsh_history");
119 LineReader reader = LineReaderBuilder.builder()
120 .terminal(context.terminal)
121 .history(new DefaultHistory())
122 .highlighter(new ReplHighlighter())
123 .completer(systemRegistry.completer())
124 .parser(parser)
125 .variable(LineReader.LIST_MAX, 50)
126 .variable(LineReader.HISTORY_FILE, history)
127 .variable(LineReader.OTHERS_GROUP_NAME, "Others")
128 .variable(LineReader.COMPLETION_STYLE_GROUP, "fg:blue,bold")
129 .variable("HELP_COLORS", "ti=1;34:co=38:ar=3:op=33:de=90")
130 .option(LineReader.Option.GROUP_PERSIST, true)
131 .build();
132 builtins.setLineReader(reader);
133 systemRegistry.setLineReader(reader);
134 new TailTipWidgets(reader, systemRegistry::commandDescription, 5, TailTipWidgets.TipType.COMPLETER);
135 KeyMap<Binding> keyMap = reader.getKeyMaps().get("main");
136 keyMap.bind(new Reference("tailtip-toggle"), KeyMap.alt("s"));
137
138
139 AtomicReference<Exception> failure = new AtomicReference<>();
140 while (true) {
141 try {
142 failure.set(null);
143 systemRegistry.cleanUp();
144 Thread commandThread = new Thread(() -> {
145 try {
146 systemRegistry.execute(reader.readLine(
147 context.cwd.get().getFileName().toString() + " mvnsh> ",
148 null,
149 (MaskingCallback) null,
150 null));
151 } catch (Exception e) {
152 failure.set(e);
153 }
154 });
155 context.terminal.handle(Terminal.Signal.INT, signal -> commandThread.interrupt());
156 commandThread.start();
157 commandThread.join();
158 if (failure.get() != null) {
159 throw failure.get();
160 }
161 } catch (UserInterruptException e) {
162
163
164 } catch (EndOfFileException e) {
165 return OK;
166 } catch (SystemRegistryImpl.UnknownCommandException e) {
167 context.writer.accept(context.invokerRequest
168 .messageBuilderFactory()
169 .builder()
170 .error(e.getMessage())
171 .build());
172 } catch (Exception e) {
173 systemRegistry.trace(e);
174 context.writer.accept(context.invokerRequest
175 .messageBuilderFactory()
176 .builder()
177 .error("Error: " + e.getMessage())
178 .build());
179 if (context.options().showErrors().orElse(false)) {
180 e.printStackTrace(context.terminal.writer());
181 }
182 return ERROR;
183 }
184 }
185 }
186 }
187
188 private static class ReplHighlighter extends DefaultHighlighter {
189 @Override
190 protected void commandStyle(LineReader reader, AttributedStringBuilder sb, boolean enable) {
191 if (enable) {
192 if (reader.getTerminal().getNumericCapability(InfoCmp.Capability.max_colors) >= 256) {
193 sb.style(AttributedStyle.DEFAULT.bold().foreground(69));
194 } else {
195 sb.style(AttributedStyle.DEFAULT.foreground(AttributedStyle.CYAN));
196 }
197 } else {
198 sb.style(AttributedStyle.DEFAULT.boldOff().foregroundOff());
199 }
200 }
201 }
202 }