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