From 0cd159ea57bc5d8a1dcadb03bc5a240e2fe1ca80 Mon Sep 17 00:00:00 2001
From: Joe Wu
Date: Wed, 9 Apr 2025 11:14:35 -0700
Subject: [PATCH 1/6] Add argumen parser feature to the LSP.
---
.../amazon/smithy/lsp/ArgumentParser.java | 82 ++++++++++
.../java/software/amazon/smithy/lsp/Main.java | 104 ++-----------
.../amazon/smithy/lsp/ServerArguments.java | 83 ++++++++++
.../amazon/smithy/lsp/ServerLauncher.java | 95 +++++++++++
.../amazon/smithy/lsp/ArgumentParserTest.java | 92 +++++++++++
.../amazon/smithy/lsp/ServerLauncherTest.java | 147 ++++++++++++++++++
6 files changed, 511 insertions(+), 92 deletions(-)
create mode 100644 src/main/java/software/amazon/smithy/lsp/ArgumentParser.java
create mode 100644 src/main/java/software/amazon/smithy/lsp/ServerArguments.java
create mode 100644 src/main/java/software/amazon/smithy/lsp/ServerLauncher.java
create mode 100644 src/test/java/software/amazon/smithy/lsp/ArgumentParserTest.java
create mode 100644 src/test/java/software/amazon/smithy/lsp/ServerLauncherTest.java
diff --git a/src/main/java/software/amazon/smithy/lsp/ArgumentParser.java b/src/main/java/software/amazon/smithy/lsp/ArgumentParser.java
new file mode 100644
index 00000000..fec6c431
--- /dev/null
+++ b/src/main/java/software/amazon/smithy/lsp/ArgumentParser.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package software.amazon.smithy.lsp;
+
+import java.util.List;
+import software.amazon.smithy.cli.Arguments;
+import software.amazon.smithy.cli.CliError;
+import software.amazon.smithy.cli.CliPrinter;
+import software.amazon.smithy.cli.HelpPrinter;
+
+final class ArgumentParser {
+ private static final int PORT_NUMBER_INDEX = 0;
+ private final ServerArguments argumentReceiver = new ServerArguments();
+ private final Arguments arguments;
+ private final CliPrinter cliPrinter;
+ private List positional;
+ private int portNumber;
+
+ ArgumentParser(String[] args) {
+ this(args, CliPrinter.fromOutputStream(System.out));
+ }
+
+ ArgumentParser(String[] args, CliPrinter cliPrinter) {
+ arguments = Arguments.of(args);
+ arguments.addReceiver(argumentReceiver);
+ this.cliPrinter = cliPrinter;
+ }
+
+ private void printHelp() {
+ HelpPrinter helpPrinter = HelpPrinter.fromArguments("java -jar smithy-lsp.jar", arguments);
+ helpPrinter.summary("Options for the Smithy Language Server:");
+ helpPrinter.print(argumentReceiver.colorSetting(), cliPrinter);
+ cliPrinter.flush();
+ }
+
+ /**
+ * Parse the arguments received.
+ *
+ */
+ public void parse() {
+ positional = arguments.getPositional();
+ if (argumentReceiver.help()) {
+ printHelp();
+ } else {
+ parsePortNumber();
+ }
+ }
+
+ /**
+ * Check if the help flag was passed.
+ *
+ * @return True if the help flag was passed, false otherwise.
+ */
+ public boolean isHelp() {
+ return argumentReceiver.help();
+ }
+
+ /**
+ * Parse the port number from the arguments.
+ *
+ */
+ public void parsePortNumber() {
+ portNumber = argumentReceiver.getPortNumber();
+ if (!positional.isEmpty() && portNumber == ServerArguments.DEFAULT_PORT) {
+ portNumber = argumentReceiver.validatePortNumber(positional.get(PORT_NUMBER_INDEX));
+ }
+ if (portNumber == ServerArguments.INVALID_PORT) {
+ throw new CliError("Invalid port number.");
+ }
+ }
+
+ /**
+ * Get the port number based on the positional and flag arguments.
+ * @return The port number of the Smithy Language Server.
+ */
+ public int getPortNumber() {
+ return portNumber;
+ }
+}
diff --git a/src/main/java/software/amazon/smithy/lsp/Main.java b/src/main/java/software/amazon/smithy/lsp/Main.java
index 87add549..2a36faed 100644
--- a/src/main/java/software/amazon/smithy/lsp/Main.java
+++ b/src/main/java/software/amazon/smithy/lsp/Main.java
@@ -15,15 +15,6 @@
package software.amazon.smithy.lsp;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.Socket;
-import java.util.Optional;
-import org.eclipse.lsp4j.jsonrpc.Launcher;
-import org.eclipse.lsp4j.launch.LSPLauncher;
-import org.eclipse.lsp4j.services.LanguageClient;
-
/**
* Main launcher for the Language server, started by the editor.
*/
@@ -32,90 +23,19 @@ private Main() {
}
/**
- * Launch the LSP and wait for it to terminate.
- *
- * @param in input stream for communication
- * @param out output stream for communication
- * @return Empty Optional if service terminated successfully, error otherwise
+ * Main entry point for the language server.
+ * @param args Arguments passed to the server.
+ * @throws Exception If there is an error starting the server.
*/
- public static Optional launch(InputStream in, OutputStream out) {
- SmithyLanguageServer server = new SmithyLanguageServer();
- Launcher launcher = LSPLauncher.createServerLauncher(
- server,
- exitOnClose(in),
- out);
-
- LanguageClient client = launcher.getRemoteProxy();
-
- server.connect(client);
- try {
- launcher.startListening().get();
- return Optional.empty();
- } catch (Exception e) {
- return Optional.of(e);
- }
- }
-
- private static InputStream exitOnClose(InputStream delegate) {
- return new InputStream() {
- @Override
- public int read() throws IOException {
- int result = delegate.read();
- if (result < 0) {
- System.exit(0);
- }
- return result;
- }
- };
- }
-
- /**
- * @param args Arguments passed to launch server. First argument must either be
- * a port number for socket connection, or 0 to use STDIN and STDOUT
- * for communication
- */
- public static void main(String[] args) {
-
- Socket socket = null;
- InputStream in;
- OutputStream out;
-
- try {
- String port = args[0];
- // If port is set to "0", use System.in/System.out.
- if (port.equals("0")) {
- in = System.in;
- out = System.out;
- } else {
- socket = new Socket("localhost", Integer.parseInt(port));
- in = socket.getInputStream();
- out = socket.getOutputStream();
- }
-
- Optional launchFailure = launch(in, out);
-
- if (launchFailure.isPresent()) {
- throw launchFailure.get();
- } else {
- System.out.println("Server terminated without errors");
- }
- } catch (ArrayIndexOutOfBoundsException e) {
- System.out.println("Missing port argument");
- } catch (NumberFormatException e) {
- System.out.println("Port number must be a valid integer");
- } catch (Exception e) {
- System.out.println(e);
-
- e.printStackTrace();
- } finally {
- try {
- if (socket != null) {
- socket.close();
- }
- } catch (Exception e) {
- System.out.println("Failed to close the socket");
- System.out.println(e);
- }
+ public static void main(String[] args) throws Exception {
+ ArgumentParser parser = new ArgumentParser(args);
+ parser.parse();
+ if (parser.isHelp()) {
+ System.exit(0);
}
+ ServerLauncher launcher = new ServerLauncher(parser.getPortNumber());
+ launcher.initConnection();
+ launcher.launch();
+ launcher.closeConnection();
}
}
diff --git a/src/main/java/software/amazon/smithy/lsp/ServerArguments.java b/src/main/java/software/amazon/smithy/lsp/ServerArguments.java
new file mode 100644
index 00000000..9363dbfb
--- /dev/null
+++ b/src/main/java/software/amazon/smithy/lsp/ServerArguments.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package software.amazon.smithy.lsp;
+
+import java.util.function.Consumer;
+import software.amazon.smithy.cli.AnsiColorFormatter;
+import software.amazon.smithy.cli.ArgumentReceiver;
+import software.amazon.smithy.cli.HelpPrinter;
+
+/**
+ * Options and Params available for LSP.
+ */
+final class ServerArguments implements ArgumentReceiver {
+
+ static final int MIN_PORT = 0;
+ static final int MAX_PORT = 65535;
+ static final int DEFAULT_PORT = 0; // Default value for unset port number.
+ static final int INVALID_PORT = -1;
+ static final String HELP = "--help";
+ static final String HELP_SHORT = "-h";
+ static final String PORT_NUMBER = "--port-number";
+ static final String PORT_NUMBER_SHORT = "-p";
+ static final String PORT_NUMBER_POSITIONAL = "";
+ private int portNumberInt = DEFAULT_PORT;
+ private boolean help = false;
+ private AnsiColorFormatter colorSetting = AnsiColorFormatter.AUTO;
+
+ @Override
+ public void registerHelp(HelpPrinter printer) {
+ printer.option(HELP, HELP_SHORT, "Print this help output.");
+ printer.param(PORT_NUMBER, PORT_NUMBER_SHORT, "PORT_NUMBER",
+ "The port number to be used by the Smithy Language Server. Default port number is 0 if not specified.");
+ printer.option(PORT_NUMBER_POSITIONAL, null, "Positional port-number.");
+ }
+
+ @Override
+ public boolean testOption(String name) {
+ if (name.equals(HELP) || name.equals(HELP_SHORT)) {
+ help = true;
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public Consumer testParameter(String name) {
+ if (name.equals(PORT_NUMBER_SHORT) || name.equals(PORT_NUMBER)) {
+ return value -> {
+ portNumberInt = validatePortNumber(value);
+ };
+ }
+ return null;
+ }
+
+ public int getPortNumber() {
+ return portNumberInt;
+ }
+
+ public boolean help() {
+ return help;
+ }
+
+ public int validatePortNumber(String portNumberStr) {
+ try {
+ int portNumber = Integer.parseInt(portNumberStr);
+ if (portNumber < MIN_PORT || portNumber > MAX_PORT) {
+ return INVALID_PORT;
+ } else {
+ return portNumber;
+ }
+ } catch (NumberFormatException e) {
+ return INVALID_PORT;
+ }
+ }
+
+ public AnsiColorFormatter colorSetting() {
+ return colorSetting;
+ }
+
+}
diff --git a/src/main/java/software/amazon/smithy/lsp/ServerLauncher.java b/src/main/java/software/amazon/smithy/lsp/ServerLauncher.java
new file mode 100644
index 00000000..a8c6f0fa
--- /dev/null
+++ b/src/main/java/software/amazon/smithy/lsp/ServerLauncher.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package software.amazon.smithy.lsp;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.Socket;
+import java.util.concurrent.ExecutionException;
+import java.util.logging.Logger;
+import org.eclipse.lsp4j.jsonrpc.Launcher;
+import org.eclipse.lsp4j.launch.LSPLauncher;
+import org.eclipse.lsp4j.services.LanguageClient;
+
+class ServerLauncher {
+ private static final String SOCKET_HOST = "localhost";
+ private static final Logger LOGGER = Logger.getLogger(ServerLauncher.class.getName());
+ private final int portNumber;
+ private InputStream in;
+ private OutputStream out;
+ private Socket socket;
+
+ ServerLauncher(int portNumber) {
+ this.portNumber = portNumber;
+ }
+
+ Socket createSocket() throws IOException {
+ return new Socket(SOCKET_HOST, portNumber);
+ }
+
+ void initConnection() throws IOException {
+ if (portNumber == ServerArguments.DEFAULT_PORT) {
+ in = System.in;
+ out = System.out;
+ } else {
+ socket = createSocket();
+ in = socket.getInputStream();
+ out = socket.getOutputStream();
+ }
+ }
+
+ InputStream getInputStream() {
+ return in;
+ }
+
+ OutputStream getOutputStream() {
+ return out;
+ }
+
+ void closeConnection() {
+ try {
+ if (socket != null) {
+ socket.close();
+ }
+ } catch (IOException e) {
+ LOGGER.severe("Failed to close the socket");
+ }
+ }
+
+ void handleExit() {
+ closeConnection();
+ System.exit(0);
+ }
+
+ private InputStream exitOnClose(InputStream delegate) {
+ return new InputStream() {
+ @Override
+ public int read() throws IOException {
+ int result = delegate.read();
+ if (result < 0) {
+ handleExit();
+ }
+ return result;
+ }
+ };
+ }
+
+ /**
+ * Launch the LSP and wait for it to terminate.
+ *
+ */
+ void launch() throws InterruptedException, ExecutionException {
+ SmithyLanguageServer server = new SmithyLanguageServer();
+ Launcher launcher = LSPLauncher.createServerLauncher(
+ server,
+ exitOnClose(in),
+ out);
+ LanguageClient client = launcher.getRemoteProxy();
+ server.connect(client);
+ launcher.startListening().get();
+ }
+}
diff --git a/src/test/java/software/amazon/smithy/lsp/ArgumentParserTest.java b/src/test/java/software/amazon/smithy/lsp/ArgumentParserTest.java
new file mode 100644
index 00000000..a7994f65
--- /dev/null
+++ b/src/test/java/software/amazon/smithy/lsp/ArgumentParserTest.java
@@ -0,0 +1,92 @@
+package software.amazon.smithy.lsp;
+
+import java.io.ByteArrayOutputStream;
+import org.junit.jupiter.api.Test;
+import software.amazon.smithy.cli.CliError;
+import software.amazon.smithy.cli.CliPrinter;
+
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class ArgumentParserTest {
+ @Test
+ void validPositionalPortNumber() {
+ String[] args = {"1"};
+ ArgumentParser parser = new ArgumentParser(args);
+ parser.parse();
+ assertEquals(1, parser.getPortNumber());
+ }
+
+ @Test
+ void invalidPositionalPortNumber() {
+ String[] args = {"65536"};
+ ArgumentParser parser = new ArgumentParser(args);
+ CliError error = assertThrows(CliError.class, parser::parse);
+ assertEquals("Invalid port number.", error.getMessage());
+ }
+
+ @Test
+ void invalidFlagPortNumber() {
+ String[] args = {"-p","65536"};
+ ArgumentParser parser = new ArgumentParser(args);
+ CliError error = assertThrows(CliError.class, parser::parse);
+ assertEquals("Invalid port number.", error.getMessage());
+ }
+
+ @Test
+ void validFlagPortNumberShort() {
+ String[] args = {"-p","100"};
+ ArgumentParser parser = new ArgumentParser(args);
+ parser.parse();
+ assertEquals(100, parser.getPortNumber());
+ }
+
+ @Test
+ void defaultPortNumber() {
+ String[] args = {};
+ ArgumentParser parser = new ArgumentParser(args);
+ parser.parse();
+ assertEquals(0, parser.getPortNumber());
+ }
+
+ @Test
+ void defaultPortNumberInArg() {
+ String[] args = {"0"};
+ ArgumentParser parser = new ArgumentParser(args);
+ parser.parse();
+ assertEquals(0, parser.getPortNumber());
+ }
+
+ @Test
+ void validFlagPortNumber() {
+ String[] args = {"--port-number","200"};
+ ArgumentParser parser = new ArgumentParser(args);
+ parser.parse();
+ assertEquals(200, parser.getPortNumber());
+ }
+
+ @Test
+ void validHelp() {
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ CliPrinter testPrinter = CliPrinter.fromOutputStream(outputStream);
+ String[] args = {"--help"};
+
+ ArgumentParser parser = new ArgumentParser(args, testPrinter);
+
+ parser.parse();
+
+ String output = outputStream.toString();
+ assertTrue(output.contains("Usage: java -jar smithy-lsp.jar [--help | -h] "));
+ assertTrue(output.contains("[--port-number | -p PORT_NUMBER] "));
+ }
+
+ @Test
+ void invalidFlag() {
+ String[] args = {"--foo"};
+ ArgumentParser parser = new ArgumentParser(args);
+ CliError error = assertThrows(CliError.class, parser::parse);
+ assertEquals("Unexpected CLI argument: --foo", error.getMessage());
+ }
+}
diff --git a/src/test/java/software/amazon/smithy/lsp/ServerLauncherTest.java b/src/test/java/software/amazon/smithy/lsp/ServerLauncherTest.java
new file mode 100644
index 00000000..471922a8
--- /dev/null
+++ b/src/test/java/software/amazon/smithy/lsp/ServerLauncherTest.java
@@ -0,0 +1,147 @@
+package software.amazon.smithy.lsp;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.net.Socket;
+import org.junit.jupiter.api.Test;
+
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class ServerLauncherTest {
+ private static class MockInputStream extends InputStream {
+ private boolean closed = false;
+ @Override
+ public int read() throws IOException {
+ return -1;
+ }
+
+ @Override
+ public void close() {
+ closed = true;
+ }
+ }
+
+ private static class MockOutputStream extends OutputStream {
+ private boolean used = false;
+ private boolean closed = false;
+
+ @Override
+ public void write(int b) {
+ used = true;
+ }
+
+ @Override
+ public void close() {
+ closed = true;
+ }
+ }
+
+ private static class TestServerLauncher extends ServerLauncher {
+ private final MockSocket mockSocket = new MockSocket();
+ private boolean exited = false;
+ TestServerLauncher(int portNumber) {
+ super(portNumber);
+ }
+
+ @Override
+ void handleExit() {
+ exited = true;
+ }
+
+ boolean exited() {
+ return exited;
+ }
+
+ @Override
+ Socket createSocket() {
+ return mockSocket;
+ }
+
+ Socket getMockSocket() {
+ return mockSocket;
+ }
+
+ }
+
+ private static class MockSocket extends Socket {
+ private boolean isClosed = false;
+ private final MockInputStream inputStream = new MockInputStream();
+ private final MockOutputStream outputStream = new MockOutputStream();
+
+ @Override
+ public InputStream getInputStream() {
+ return inputStream;
+ }
+
+ @Override
+ public OutputStream getOutputStream() {
+ return outputStream;
+ }
+
+ @Override
+ public void close() {
+ isClosed = true;
+ }
+
+ public boolean isClosed() {
+ return isClosed;
+ }
+ }
+
+ @Test
+ void testCustomPortConnection() throws IOException {
+ TestServerLauncher launcher = new TestServerLauncher(8080);
+ launcher.initConnection();
+
+ assertFalse(launcher.getMockSocket().isClosed());
+ assertNotNull(launcher.getMockSocket().getInputStream());
+ assertNotNull(launcher.getMockSocket().getOutputStream());
+ }
+
+ @Test
+ void testConnectionClose() throws IOException {
+ TestServerLauncher launcher = new TestServerLauncher(8080);
+ launcher.initConnection();
+ launcher.closeConnection();
+
+ assertTrue(launcher.getMockSocket().isClosed());
+ }
+
+ @Test
+ void testDefaultPortConnection() throws IOException {
+ ByteArrayInputStream testIn = new ByteArrayInputStream("test".getBytes());
+ ByteArrayOutputStream testOut = new ByteArrayOutputStream();
+ PrintStream testPrintStream = new PrintStream(testOut);
+
+ System.setIn(testIn);
+ System.setOut(testPrintStream);
+
+ ServerLauncher launcher = new ServerLauncher(ServerArguments.DEFAULT_PORT);
+ launcher.initConnection();
+
+ assertEquals(System.in, launcher.getInputStream());
+ assertEquals(System.out, launcher.getOutputStream());
+ testIn.close();
+ testOut.close();
+ }
+
+ @Test
+ void testLaunchWithDefaultPort() throws IOException {
+ TestServerLauncher launcher = new TestServerLauncher(ServerArguments.DEFAULT_PORT);
+ launcher.initConnection();
+ assertDoesNotThrow(launcher::launch);
+ launcher.closeConnection();
+ assertTrue(launcher.exited());
+ }
+
+}
+
From b0151c0ea9e6a39dafa002c181eda5b3cf7e9a0f Mon Sep 17 00:00:00 2001
From: Joe Wu
Date: Thu, 10 Apr 2025 11:02:46 -0700
Subject: [PATCH 2/6] Moved ArgumentParser functionalities to ServerArguments
---
.../amazon/smithy/lsp/ArgumentParser.java | 82 ----------------
.../java/software/amazon/smithy/lsp/Main.java | 7 +-
.../amazon/smithy/lsp/ServerArguments.java | 39 ++++++--
.../amazon/smithy/lsp/ArgumentParserTest.java | 92 ------------------
.../smithy/lsp/ServerArgumentsTest.java | 96 +++++++++++++++++++
5 files changed, 129 insertions(+), 187 deletions(-)
delete mode 100644 src/main/java/software/amazon/smithy/lsp/ArgumentParser.java
delete mode 100644 src/test/java/software/amazon/smithy/lsp/ArgumentParserTest.java
create mode 100644 src/test/java/software/amazon/smithy/lsp/ServerArgumentsTest.java
diff --git a/src/main/java/software/amazon/smithy/lsp/ArgumentParser.java b/src/main/java/software/amazon/smithy/lsp/ArgumentParser.java
deleted file mode 100644
index fec6c431..00000000
--- a/src/main/java/software/amazon/smithy/lsp/ArgumentParser.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package software.amazon.smithy.lsp;
-
-import java.util.List;
-import software.amazon.smithy.cli.Arguments;
-import software.amazon.smithy.cli.CliError;
-import software.amazon.smithy.cli.CliPrinter;
-import software.amazon.smithy.cli.HelpPrinter;
-
-final class ArgumentParser {
- private static final int PORT_NUMBER_INDEX = 0;
- private final ServerArguments argumentReceiver = new ServerArguments();
- private final Arguments arguments;
- private final CliPrinter cliPrinter;
- private List positional;
- private int portNumber;
-
- ArgumentParser(String[] args) {
- this(args, CliPrinter.fromOutputStream(System.out));
- }
-
- ArgumentParser(String[] args, CliPrinter cliPrinter) {
- arguments = Arguments.of(args);
- arguments.addReceiver(argumentReceiver);
- this.cliPrinter = cliPrinter;
- }
-
- private void printHelp() {
- HelpPrinter helpPrinter = HelpPrinter.fromArguments("java -jar smithy-lsp.jar", arguments);
- helpPrinter.summary("Options for the Smithy Language Server:");
- helpPrinter.print(argumentReceiver.colorSetting(), cliPrinter);
- cliPrinter.flush();
- }
-
- /**
- * Parse the arguments received.
- *
- */
- public void parse() {
- positional = arguments.getPositional();
- if (argumentReceiver.help()) {
- printHelp();
- } else {
- parsePortNumber();
- }
- }
-
- /**
- * Check if the help flag was passed.
- *
- * @return True if the help flag was passed, false otherwise.
- */
- public boolean isHelp() {
- return argumentReceiver.help();
- }
-
- /**
- * Parse the port number from the arguments.
- *
- */
- public void parsePortNumber() {
- portNumber = argumentReceiver.getPortNumber();
- if (!positional.isEmpty() && portNumber == ServerArguments.DEFAULT_PORT) {
- portNumber = argumentReceiver.validatePortNumber(positional.get(PORT_NUMBER_INDEX));
- }
- if (portNumber == ServerArguments.INVALID_PORT) {
- throw new CliError("Invalid port number.");
- }
- }
-
- /**
- * Get the port number based on the positional and flag arguments.
- * @return The port number of the Smithy Language Server.
- */
- public int getPortNumber() {
- return portNumber;
- }
-}
diff --git a/src/main/java/software/amazon/smithy/lsp/Main.java b/src/main/java/software/amazon/smithy/lsp/Main.java
index 2a36faed..9265ae60 100644
--- a/src/main/java/software/amazon/smithy/lsp/Main.java
+++ b/src/main/java/software/amazon/smithy/lsp/Main.java
@@ -28,12 +28,11 @@ private Main() {
* @throws Exception If there is an error starting the server.
*/
public static void main(String[] args) throws Exception {
- ArgumentParser parser = new ArgumentParser(args);
- parser.parse();
- if (parser.isHelp()) {
+ var serverArguments = ServerArguments.create(args);
+ if (serverArguments.help()) {
System.exit(0);
}
- ServerLauncher launcher = new ServerLauncher(parser.getPortNumber());
+ ServerLauncher launcher = new ServerLauncher(serverArguments.getPortNumber());
launcher.initConnection();
launcher.launch();
launcher.closeConnection();
diff --git a/src/main/java/software/amazon/smithy/lsp/ServerArguments.java b/src/main/java/software/amazon/smithy/lsp/ServerArguments.java
index 9363dbfb..f0d8793c 100644
--- a/src/main/java/software/amazon/smithy/lsp/ServerArguments.java
+++ b/src/main/java/software/amazon/smithy/lsp/ServerArguments.java
@@ -8,6 +8,9 @@
import java.util.function.Consumer;
import software.amazon.smithy.cli.AnsiColorFormatter;
import software.amazon.smithy.cli.ArgumentReceiver;
+import software.amazon.smithy.cli.Arguments;
+import software.amazon.smithy.cli.CliError;
+import software.amazon.smithy.cli.CliPrinter;
import software.amazon.smithy.cli.HelpPrinter;
/**
@@ -18,15 +21,28 @@ final class ServerArguments implements ArgumentReceiver {
static final int MIN_PORT = 0;
static final int MAX_PORT = 65535;
static final int DEFAULT_PORT = 0; // Default value for unset port number.
- static final int INVALID_PORT = -1;
static final String HELP = "--help";
static final String HELP_SHORT = "-h";
static final String PORT_NUMBER = "--port-number";
static final String PORT_NUMBER_SHORT = "-p";
static final String PORT_NUMBER_POSITIONAL = "";
- private int portNumberInt = DEFAULT_PORT;
+ private int portNumber = DEFAULT_PORT;
private boolean help = false;
- private AnsiColorFormatter colorSetting = AnsiColorFormatter.AUTO;
+
+
+ static ServerArguments create(String[] args) {
+ Arguments arguments = Arguments.of(args);
+ var serverArguments = new ServerArguments();
+ arguments.addReceiver(serverArguments);
+ var positional = arguments.getPositional();
+ if (serverArguments.help()) {
+ serverArguments.printHelp(arguments);
+ }
+ if (!positional.isEmpty()) {
+ serverArguments.portNumber = serverArguments.validatePortNumber(positional.getFirst());
+ }
+ return serverArguments;
+ }
@Override
public void registerHelp(HelpPrinter printer) {
@@ -49,14 +65,14 @@ public boolean testOption(String name) {
public Consumer testParameter(String name) {
if (name.equals(PORT_NUMBER_SHORT) || name.equals(PORT_NUMBER)) {
return value -> {
- portNumberInt = validatePortNumber(value);
+ portNumber = validatePortNumber(value);
};
}
return null;
}
public int getPortNumber() {
- return portNumberInt;
+ return portNumber;
}
public boolean help() {
@@ -67,17 +83,22 @@ public int validatePortNumber(String portNumberStr) {
try {
int portNumber = Integer.parseInt(portNumberStr);
if (portNumber < MIN_PORT || portNumber > MAX_PORT) {
- return INVALID_PORT;
+ throw new CliError("Invalid port number!");
} else {
return portNumber;
}
} catch (NumberFormatException e) {
- return INVALID_PORT;
+ throw new CliError("Invalid port number!");
}
}
- public AnsiColorFormatter colorSetting() {
- return colorSetting;
+
+ private void printHelp(Arguments arguments) {
+ CliPrinter printer = CliPrinter.fromOutputStream(System.out);
+ HelpPrinter helpPrinter = HelpPrinter.fromArguments("java -jar smithy-lsp.jar", arguments);
+ helpPrinter.summary("Options for the Smithy Language Server:");
+ helpPrinter.print(AnsiColorFormatter.AUTO, printer);
+ printer.flush();
}
}
diff --git a/src/test/java/software/amazon/smithy/lsp/ArgumentParserTest.java b/src/test/java/software/amazon/smithy/lsp/ArgumentParserTest.java
deleted file mode 100644
index a7994f65..00000000
--- a/src/test/java/software/amazon/smithy/lsp/ArgumentParserTest.java
+++ /dev/null
@@ -1,92 +0,0 @@
-package software.amazon.smithy.lsp;
-
-import java.io.ByteArrayOutputStream;
-import org.junit.jupiter.api.Test;
-import software.amazon.smithy.cli.CliError;
-import software.amazon.smithy.cli.CliPrinter;
-
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertThrows;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
-public class ArgumentParserTest {
- @Test
- void validPositionalPortNumber() {
- String[] args = {"1"};
- ArgumentParser parser = new ArgumentParser(args);
- parser.parse();
- assertEquals(1, parser.getPortNumber());
- }
-
- @Test
- void invalidPositionalPortNumber() {
- String[] args = {"65536"};
- ArgumentParser parser = new ArgumentParser(args);
- CliError error = assertThrows(CliError.class, parser::parse);
- assertEquals("Invalid port number.", error.getMessage());
- }
-
- @Test
- void invalidFlagPortNumber() {
- String[] args = {"-p","65536"};
- ArgumentParser parser = new ArgumentParser(args);
- CliError error = assertThrows(CliError.class, parser::parse);
- assertEquals("Invalid port number.", error.getMessage());
- }
-
- @Test
- void validFlagPortNumberShort() {
- String[] args = {"-p","100"};
- ArgumentParser parser = new ArgumentParser(args);
- parser.parse();
- assertEquals(100, parser.getPortNumber());
- }
-
- @Test
- void defaultPortNumber() {
- String[] args = {};
- ArgumentParser parser = new ArgumentParser(args);
- parser.parse();
- assertEquals(0, parser.getPortNumber());
- }
-
- @Test
- void defaultPortNumberInArg() {
- String[] args = {"0"};
- ArgumentParser parser = new ArgumentParser(args);
- parser.parse();
- assertEquals(0, parser.getPortNumber());
- }
-
- @Test
- void validFlagPortNumber() {
- String[] args = {"--port-number","200"};
- ArgumentParser parser = new ArgumentParser(args);
- parser.parse();
- assertEquals(200, parser.getPortNumber());
- }
-
- @Test
- void validHelp() {
- ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
- CliPrinter testPrinter = CliPrinter.fromOutputStream(outputStream);
- String[] args = {"--help"};
-
- ArgumentParser parser = new ArgumentParser(args, testPrinter);
-
- parser.parse();
-
- String output = outputStream.toString();
- assertTrue(output.contains("Usage: java -jar smithy-lsp.jar [--help | -h] "));
- assertTrue(output.contains("[--port-number | -p PORT_NUMBER] "));
- }
-
- @Test
- void invalidFlag() {
- String[] args = {"--foo"};
- ArgumentParser parser = new ArgumentParser(args);
- CliError error = assertThrows(CliError.class, parser::parse);
- assertEquals("Unexpected CLI argument: --foo", error.getMessage());
- }
-}
diff --git a/src/test/java/software/amazon/smithy/lsp/ServerArgumentsTest.java b/src/test/java/software/amazon/smithy/lsp/ServerArgumentsTest.java
new file mode 100644
index 00000000..b5ada80d
--- /dev/null
+++ b/src/test/java/software/amazon/smithy/lsp/ServerArgumentsTest.java
@@ -0,0 +1,96 @@
+package software.amazon.smithy.lsp;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import org.junit.jupiter.api.Test;
+import software.amazon.smithy.cli.CliError;
+
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class ServerArgumentsTest {
+ @Test
+ void validPositionalPortNumber() {
+ String[] args = {"1"};
+ ServerArguments serverArguments = ServerArguments.create(args);
+ assertEquals(1, serverArguments.getPortNumber());
+ }
+
+ @Test
+ void invalidPositionalPortNumber() {
+ String[] args = {"65536"};
+ assertThrows(CliError.class,()-> {ServerArguments.create(args);});
+ }
+
+ @Test
+ void invalidFlagPortNumber() {
+ String[] args = {"-p","65536"};
+ assertThrows(CliError.class,()-> {ServerArguments.create(args);});
+ }
+
+ @Test
+ void validFlagPortNumberShort() {
+ String[] args = {"-p","100"};
+ ServerArguments serverArguments = ServerArguments.create(args);
+ assertEquals(100, serverArguments.getPortNumber());
+ }
+
+ @Test
+ void defaultPortNumber() {
+ String[] args = {};
+ ServerArguments serverArguments = ServerArguments.create(args);
+
+ assertEquals(0, serverArguments.getPortNumber());
+ }
+
+ @Test
+ void defaultPortNumberInArg() {
+ String[] args = {"0"};
+ ServerArguments serverArguments = ServerArguments.create(args);
+
+ assertEquals(0, serverArguments.getPortNumber());
+ }
+
+ @Test
+ void validFlagPortNumber() {
+ String[] args = {"--port-number","200"};
+ ServerArguments serverArguments = ServerArguments.create(args);
+ assertEquals(200, serverArguments.getPortNumber());
+ }
+
+ @Test
+ void validHelp() {
+ ByteArrayOutputStream outContent = new ByteArrayOutputStream();
+ PrintStream originalOut = System.out;
+ System.setOut(new PrintStream(outContent));
+
+ try {
+ ServerArguments.create(new String[]{"--help"});
+
+ String output = outContent.toString().trim();
+
+ assertTrue(output.contains("Options for the Smithy Language Server:"));
+ assertTrue(output.contains("--help"));
+ assertTrue(output.contains("-h"));
+ assertTrue(output.contains("Print this help output."));
+ assertTrue(output.contains("--port-number"));
+ assertTrue(output.contains("-p"));
+ assertTrue(output.contains("PORT_NUMBER"));
+ assertTrue(output.contains("The port number to be used by the Smithy Language Server."));
+ assertTrue(output.contains(""));
+ assertTrue(output.contains("Positional port-number."));
+
+ } finally {
+ // Restore original System.out
+ System.setOut(originalOut);
+ }
+ }
+
+ @Test
+ void invalidFlag() {
+ String[] args = {"--foo"};
+ assertThrows(CliError.class,()-> {ServerArguments.create(args);});
+ }
+}
From c049c77a19fb34e830b795ffb193e081310d3da8 Mon Sep 17 00:00:00 2001
From: Joe Wu
Date: Fri, 11 Apr 2025 11:12:01 -0700
Subject: [PATCH 3/6] Removed ServerLauncher class and moved functions to Main
directly
---
.../java/software/amazon/smithy/lsp/Main.java | 31 +++-
.../amazon/smithy/lsp/ServerLauncher.java | 95 -----------
.../amazon/smithy/lsp/ServerLauncherTest.java | 147 ------------------
3 files changed, 27 insertions(+), 246 deletions(-)
delete mode 100644 src/main/java/software/amazon/smithy/lsp/ServerLauncher.java
delete mode 100644 src/test/java/software/amazon/smithy/lsp/ServerLauncherTest.java
diff --git a/src/main/java/software/amazon/smithy/lsp/Main.java b/src/main/java/software/amazon/smithy/lsp/Main.java
index 9265ae60..64184647 100644
--- a/src/main/java/software/amazon/smithy/lsp/Main.java
+++ b/src/main/java/software/amazon/smithy/lsp/Main.java
@@ -15,6 +15,11 @@
package software.amazon.smithy.lsp;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.Socket;
+import org.eclipse.lsp4j.launch.LSPLauncher;
+
/**
* Main launcher for the Language server, started by the editor.
*/
@@ -32,9 +37,27 @@ public static void main(String[] args) throws Exception {
if (serverArguments.help()) {
System.exit(0);
}
- ServerLauncher launcher = new ServerLauncher(serverArguments.getPortNumber());
- launcher.initConnection();
- launcher.launch();
- launcher.closeConnection();
+
+ launch(serverArguments);
+ }
+
+ private static void launch(ServerArguments serverArguments) throws Exception {
+ if (serverArguments.getPortNumber() == ServerArguments.DEFAULT_PORT) {
+ startServer(System.in, System.out);
+ } else {
+ try (var socket = new Socket("localhost", serverArguments.getPortNumber())) {
+ startServer(socket.getInputStream(), socket.getOutputStream());
+ }
+ }
+ }
+
+ private static void startServer(InputStream in, OutputStream out) throws Exception {
+ var server = new SmithyLanguageServer();
+ var launcher = LSPLauncher.createServerLauncher(server, in, out);
+
+ var client = launcher.getRemoteProxy();
+ server.connect(client);
+
+ launcher.startListening().get();
}
}
diff --git a/src/main/java/software/amazon/smithy/lsp/ServerLauncher.java b/src/main/java/software/amazon/smithy/lsp/ServerLauncher.java
deleted file mode 100644
index a8c6f0fa..00000000
--- a/src/main/java/software/amazon/smithy/lsp/ServerLauncher.java
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package software.amazon.smithy.lsp;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.Socket;
-import java.util.concurrent.ExecutionException;
-import java.util.logging.Logger;
-import org.eclipse.lsp4j.jsonrpc.Launcher;
-import org.eclipse.lsp4j.launch.LSPLauncher;
-import org.eclipse.lsp4j.services.LanguageClient;
-
-class ServerLauncher {
- private static final String SOCKET_HOST = "localhost";
- private static final Logger LOGGER = Logger.getLogger(ServerLauncher.class.getName());
- private final int portNumber;
- private InputStream in;
- private OutputStream out;
- private Socket socket;
-
- ServerLauncher(int portNumber) {
- this.portNumber = portNumber;
- }
-
- Socket createSocket() throws IOException {
- return new Socket(SOCKET_HOST, portNumber);
- }
-
- void initConnection() throws IOException {
- if (portNumber == ServerArguments.DEFAULT_PORT) {
- in = System.in;
- out = System.out;
- } else {
- socket = createSocket();
- in = socket.getInputStream();
- out = socket.getOutputStream();
- }
- }
-
- InputStream getInputStream() {
- return in;
- }
-
- OutputStream getOutputStream() {
- return out;
- }
-
- void closeConnection() {
- try {
- if (socket != null) {
- socket.close();
- }
- } catch (IOException e) {
- LOGGER.severe("Failed to close the socket");
- }
- }
-
- void handleExit() {
- closeConnection();
- System.exit(0);
- }
-
- private InputStream exitOnClose(InputStream delegate) {
- return new InputStream() {
- @Override
- public int read() throws IOException {
- int result = delegate.read();
- if (result < 0) {
- handleExit();
- }
- return result;
- }
- };
- }
-
- /**
- * Launch the LSP and wait for it to terminate.
- *
- */
- void launch() throws InterruptedException, ExecutionException {
- SmithyLanguageServer server = new SmithyLanguageServer();
- Launcher launcher = LSPLauncher.createServerLauncher(
- server,
- exitOnClose(in),
- out);
- LanguageClient client = launcher.getRemoteProxy();
- server.connect(client);
- launcher.startListening().get();
- }
-}
diff --git a/src/test/java/software/amazon/smithy/lsp/ServerLauncherTest.java b/src/test/java/software/amazon/smithy/lsp/ServerLauncherTest.java
deleted file mode 100644
index 471922a8..00000000
--- a/src/test/java/software/amazon/smithy/lsp/ServerLauncherTest.java
+++ /dev/null
@@ -1,147 +0,0 @@
-package software.amazon.smithy.lsp;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.PrintStream;
-import java.net.Socket;
-import org.junit.jupiter.api.Test;
-
-
-import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertNotNull;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
-public class ServerLauncherTest {
- private static class MockInputStream extends InputStream {
- private boolean closed = false;
- @Override
- public int read() throws IOException {
- return -1;
- }
-
- @Override
- public void close() {
- closed = true;
- }
- }
-
- private static class MockOutputStream extends OutputStream {
- private boolean used = false;
- private boolean closed = false;
-
- @Override
- public void write(int b) {
- used = true;
- }
-
- @Override
- public void close() {
- closed = true;
- }
- }
-
- private static class TestServerLauncher extends ServerLauncher {
- private final MockSocket mockSocket = new MockSocket();
- private boolean exited = false;
- TestServerLauncher(int portNumber) {
- super(portNumber);
- }
-
- @Override
- void handleExit() {
- exited = true;
- }
-
- boolean exited() {
- return exited;
- }
-
- @Override
- Socket createSocket() {
- return mockSocket;
- }
-
- Socket getMockSocket() {
- return mockSocket;
- }
-
- }
-
- private static class MockSocket extends Socket {
- private boolean isClosed = false;
- private final MockInputStream inputStream = new MockInputStream();
- private final MockOutputStream outputStream = new MockOutputStream();
-
- @Override
- public InputStream getInputStream() {
- return inputStream;
- }
-
- @Override
- public OutputStream getOutputStream() {
- return outputStream;
- }
-
- @Override
- public void close() {
- isClosed = true;
- }
-
- public boolean isClosed() {
- return isClosed;
- }
- }
-
- @Test
- void testCustomPortConnection() throws IOException {
- TestServerLauncher launcher = new TestServerLauncher(8080);
- launcher.initConnection();
-
- assertFalse(launcher.getMockSocket().isClosed());
- assertNotNull(launcher.getMockSocket().getInputStream());
- assertNotNull(launcher.getMockSocket().getOutputStream());
- }
-
- @Test
- void testConnectionClose() throws IOException {
- TestServerLauncher launcher = new TestServerLauncher(8080);
- launcher.initConnection();
- launcher.closeConnection();
-
- assertTrue(launcher.getMockSocket().isClosed());
- }
-
- @Test
- void testDefaultPortConnection() throws IOException {
- ByteArrayInputStream testIn = new ByteArrayInputStream("test".getBytes());
- ByteArrayOutputStream testOut = new ByteArrayOutputStream();
- PrintStream testPrintStream = new PrintStream(testOut);
-
- System.setIn(testIn);
- System.setOut(testPrintStream);
-
- ServerLauncher launcher = new ServerLauncher(ServerArguments.DEFAULT_PORT);
- launcher.initConnection();
-
- assertEquals(System.in, launcher.getInputStream());
- assertEquals(System.out, launcher.getOutputStream());
- testIn.close();
- testOut.close();
- }
-
- @Test
- void testLaunchWithDefaultPort() throws IOException {
- TestServerLauncher launcher = new TestServerLauncher(ServerArguments.DEFAULT_PORT);
- launcher.initConnection();
- assertDoesNotThrow(launcher::launch);
- launcher.closeConnection();
- assertTrue(launcher.exited());
- }
-
-}
-
From 9a030a0ae3b5b3fd522cc67ed9e8d8c69678aae7 Mon Sep 17 00:00:00 2001
From: Joe Wu
Date: Fri, 11 Apr 2025 15:15:49 -0700
Subject: [PATCH 4/6] Address comments, improved help messages
---
.../java/software/amazon/smithy/lsp/Main.java | 8 +--
.../amazon/smithy/lsp/ServerArguments.java | 57 +++++++++++--------
.../smithy/lsp/ServerArgumentsTest.java | 22 +++----
3 files changed, 45 insertions(+), 42 deletions(-)
diff --git a/src/main/java/software/amazon/smithy/lsp/Main.java b/src/main/java/software/amazon/smithy/lsp/Main.java
index 64184647..504b3828 100644
--- a/src/main/java/software/amazon/smithy/lsp/Main.java
+++ b/src/main/java/software/amazon/smithy/lsp/Main.java
@@ -42,12 +42,12 @@ public static void main(String[] args) throws Exception {
}
private static void launch(ServerArguments serverArguments) throws Exception {
- if (serverArguments.getPortNumber() == ServerArguments.DEFAULT_PORT) {
- startServer(System.in, System.out);
- } else {
- try (var socket = new Socket("localhost", serverArguments.getPortNumber())) {
+ if (serverArguments.useSocket()) {
+ try (var socket = new Socket("localhost", serverArguments.port())) {
startServer(socket.getInputStream(), socket.getOutputStream());
}
+ } else {
+ startServer(System.in, System.out);
}
}
diff --git a/src/main/java/software/amazon/smithy/lsp/ServerArguments.java b/src/main/java/software/amazon/smithy/lsp/ServerArguments.java
index f0d8793c..77b22718 100644
--- a/src/main/java/software/amazon/smithy/lsp/ServerArguments.java
+++ b/src/main/java/software/amazon/smithy/lsp/ServerArguments.java
@@ -18,15 +18,15 @@
*/
final class ServerArguments implements ArgumentReceiver {
- static final int MIN_PORT = 0;
- static final int MAX_PORT = 65535;
- static final int DEFAULT_PORT = 0; // Default value for unset port number.
- static final String HELP = "--help";
- static final String HELP_SHORT = "-h";
- static final String PORT_NUMBER = "--port-number";
- static final String PORT_NUMBER_SHORT = "-p";
- static final String PORT_NUMBER_POSITIONAL = "";
- private int portNumber = DEFAULT_PORT;
+ private static final int MIN_PORT = 0;
+ private static final int MAX_PORT = 65535;
+ private static final int DEFAULT_PORT = 0; // Default value for unset port number.
+ private static final String HELP = "--help";
+ private static final String HELP_SHORT = "-h";
+ private static final String PORT = "--port";
+ private static final String PORT_SHORT = "-p";
+ private static final String PORT_POSITIONAL = "";
+ private int port = DEFAULT_PORT;
private boolean help = false;
@@ -39,7 +39,7 @@ static ServerArguments create(String[] args) {
serverArguments.printHelp(arguments);
}
if (!positional.isEmpty()) {
- serverArguments.portNumber = serverArguments.validatePortNumber(positional.getFirst());
+ serverArguments.port = serverArguments.validatePortNumber(positional.getFirst());
}
return serverArguments;
}
@@ -47,9 +47,12 @@ static ServerArguments create(String[] args) {
@Override
public void registerHelp(HelpPrinter printer) {
printer.option(HELP, HELP_SHORT, "Print this help output.");
- printer.param(PORT_NUMBER, PORT_NUMBER_SHORT, "PORT_NUMBER",
- "The port number to be used by the Smithy Language Server. Default port number is 0 if not specified.");
- printer.option(PORT_NUMBER_POSITIONAL, null, "Positional port-number.");
+ printer.param(PORT, PORT_SHORT, "PORT",
+ "The port to use for talking to the client. When not specified, or set to 0, "
+ + "standard in/out is used. Standard in/out is preferred, "
+ + "so usually this shouldn't be specified.");
+ printer.option(PORT_POSITIONAL, null, "Deprecated: use --port instead. When not specified, or set to 0, "
+ + "standard in/out is used. Standard in/out is preferred, so usually this shouldn't be specified.");
}
@Override
@@ -63,40 +66,44 @@ public boolean testOption(String name) {
@Override
public Consumer testParameter(String name) {
- if (name.equals(PORT_NUMBER_SHORT) || name.equals(PORT_NUMBER)) {
+ if (name.equals(PORT_SHORT) || name.equals(PORT)) {
return value -> {
- portNumber = validatePortNumber(value);
+ port = validatePortNumber(value);
};
}
return null;
}
- public int getPortNumber() {
- return portNumber;
+ int port() {
+ return port;
}
- public boolean help() {
+ boolean help() {
return help;
}
- public int validatePortNumber(String portNumberStr) {
+ public boolean useSocket() {
+ return port != 0;
+ }
+
+ private int validatePortNumber(String portStr) {
try {
- int portNumber = Integer.parseInt(portNumberStr);
+ int portNumber = Integer.parseInt(portStr);
if (portNumber < MIN_PORT || portNumber > MAX_PORT) {
- throw new CliError("Invalid port number!");
+ throw new CliError("Invalid port number: should be an integer between "
+ + MIN_PORT + " and " + MAX_PORT + ", inclusive.");
} else {
return portNumber;
}
} catch (NumberFormatException e) {
- throw new CliError("Invalid port number!");
+ throw new CliError("Invalid port number: Can not parse " + portStr);
}
}
-
private void printHelp(Arguments arguments) {
CliPrinter printer = CliPrinter.fromOutputStream(System.out);
- HelpPrinter helpPrinter = HelpPrinter.fromArguments("java -jar smithy-lsp.jar", arguments);
- helpPrinter.summary("Options for the Smithy Language Server:");
+ HelpPrinter helpPrinter = HelpPrinter.fromArguments("smithy-language-server", arguments);
+ helpPrinter.summary("Run the Smithy Language Server.");
helpPrinter.print(AnsiColorFormatter.AUTO, printer);
printer.flush();
}
diff --git a/src/test/java/software/amazon/smithy/lsp/ServerArgumentsTest.java b/src/test/java/software/amazon/smithy/lsp/ServerArgumentsTest.java
index b5ada80d..80d677d3 100644
--- a/src/test/java/software/amazon/smithy/lsp/ServerArgumentsTest.java
+++ b/src/test/java/software/amazon/smithy/lsp/ServerArgumentsTest.java
@@ -15,7 +15,7 @@ public class ServerArgumentsTest {
void validPositionalPortNumber() {
String[] args = {"1"};
ServerArguments serverArguments = ServerArguments.create(args);
- assertEquals(1, serverArguments.getPortNumber());
+ assertEquals(1, serverArguments.port());
}
@Test
@@ -34,7 +34,7 @@ void invalidFlagPortNumber() {
void validFlagPortNumberShort() {
String[] args = {"-p","100"};
ServerArguments serverArguments = ServerArguments.create(args);
- assertEquals(100, serverArguments.getPortNumber());
+ assertEquals(100, serverArguments.port());
}
@Test
@@ -42,7 +42,7 @@ void defaultPortNumber() {
String[] args = {};
ServerArguments serverArguments = ServerArguments.create(args);
- assertEquals(0, serverArguments.getPortNumber());
+ assertEquals(0, serverArguments.port());
}
@Test
@@ -50,14 +50,14 @@ void defaultPortNumberInArg() {
String[] args = {"0"};
ServerArguments serverArguments = ServerArguments.create(args);
- assertEquals(0, serverArguments.getPortNumber());
+ assertEquals(0, serverArguments.port());
}
@Test
void validFlagPortNumber() {
- String[] args = {"--port-number","200"};
+ String[] args = {"--port","200"};
ServerArguments serverArguments = ServerArguments.create(args);
- assertEquals(200, serverArguments.getPortNumber());
+ assertEquals(200, serverArguments.port());
}
@Test
@@ -71,16 +71,12 @@ void validHelp() {
String output = outContent.toString().trim();
- assertTrue(output.contains("Options for the Smithy Language Server:"));
assertTrue(output.contains("--help"));
assertTrue(output.contains("-h"));
- assertTrue(output.contains("Print this help output."));
- assertTrue(output.contains("--port-number"));
+ assertTrue(output.contains("--port"));
assertTrue(output.contains("-p"));
- assertTrue(output.contains("PORT_NUMBER"));
- assertTrue(output.contains("The port number to be used by the Smithy Language Server."));
- assertTrue(output.contains(""));
- assertTrue(output.contains("Positional port-number."));
+ assertTrue(output.contains("PORT"));
+ assertTrue(output.contains(""));
} finally {
// Restore original System.out
From 0bc0b252bc059ae03ac409cecd12b1bdb0f3a903 Mon Sep 17 00:00:00 2001
From: Joe Wu
Date: Mon, 14 Apr 2025 14:48:41 -0700
Subject: [PATCH 5/6] Addres comment, move printhelp to main from
serverargument, add helper function for error
---
.../java/software/amazon/smithy/lsp/Main.java | 13 +++++
.../amazon/smithy/lsp/ServerArguments.java | 22 +++------
.../smithy/lsp/ServerArgumentsTest.java | 49 ++++++++++---------
3 files changed, 44 insertions(+), 40 deletions(-)
diff --git a/src/main/java/software/amazon/smithy/lsp/Main.java b/src/main/java/software/amazon/smithy/lsp/Main.java
index 504b3828..d7a7054f 100644
--- a/src/main/java/software/amazon/smithy/lsp/Main.java
+++ b/src/main/java/software/amazon/smithy/lsp/Main.java
@@ -19,6 +19,9 @@
import java.io.OutputStream;
import java.net.Socket;
import org.eclipse.lsp4j.launch.LSPLauncher;
+import software.amazon.smithy.cli.AnsiColorFormatter;
+import software.amazon.smithy.cli.CliPrinter;
+import software.amazon.smithy.cli.HelpPrinter;
/**
* Main launcher for the Language server, started by the editor.
@@ -35,6 +38,7 @@ private Main() {
public static void main(String[] args) throws Exception {
var serverArguments = ServerArguments.create(args);
if (serverArguments.help()) {
+ printHelp(serverArguments);
System.exit(0);
}
@@ -60,4 +64,13 @@ private static void startServer(InputStream in, OutputStream out) throws Excepti
launcher.startListening().get();
}
+
+ private static void printHelp(ServerArguments serverArguments) {
+ CliPrinter printer = CliPrinter.fromOutputStream(System.out);
+ HelpPrinter helpPrinter = new HelpPrinter("smithy-language-server");
+ serverArguments.registerHelp(helpPrinter);
+ helpPrinter.summary("Run the Smithy Language Server.");
+ helpPrinter.print(AnsiColorFormatter.AUTO, printer);
+ printer.flush();
+ }
}
diff --git a/src/main/java/software/amazon/smithy/lsp/ServerArguments.java b/src/main/java/software/amazon/smithy/lsp/ServerArguments.java
index 77b22718..345bb3ba 100644
--- a/src/main/java/software/amazon/smithy/lsp/ServerArguments.java
+++ b/src/main/java/software/amazon/smithy/lsp/ServerArguments.java
@@ -6,11 +6,9 @@
package software.amazon.smithy.lsp;
import java.util.function.Consumer;
-import software.amazon.smithy.cli.AnsiColorFormatter;
import software.amazon.smithy.cli.ArgumentReceiver;
import software.amazon.smithy.cli.Arguments;
import software.amazon.smithy.cli.CliError;
-import software.amazon.smithy.cli.CliPrinter;
import software.amazon.smithy.cli.HelpPrinter;
/**
@@ -35,9 +33,6 @@ static ServerArguments create(String[] args) {
var serverArguments = new ServerArguments();
arguments.addReceiver(serverArguments);
var positional = arguments.getPositional();
- if (serverArguments.help()) {
- serverArguments.printHelp(arguments);
- }
if (!positional.isEmpty()) {
serverArguments.port = serverArguments.validatePortNumber(positional.getFirst());
}
@@ -82,7 +77,7 @@ boolean help() {
return help;
}
- public boolean useSocket() {
+ boolean useSocket() {
return port != 0;
}
@@ -90,22 +85,17 @@ private int validatePortNumber(String portStr) {
try {
int portNumber = Integer.parseInt(portStr);
if (portNumber < MIN_PORT || portNumber > MAX_PORT) {
- throw new CliError("Invalid port number: should be an integer between "
- + MIN_PORT + " and " + MAX_PORT + ", inclusive.");
+ throw invalidPort(portStr);
} else {
return portNumber;
}
} catch (NumberFormatException e) {
- throw new CliError("Invalid port number: Can not parse " + portStr);
+ throw invalidPort(portStr);
}
}
- private void printHelp(Arguments arguments) {
- CliPrinter printer = CliPrinter.fromOutputStream(System.out);
- HelpPrinter helpPrinter = HelpPrinter.fromArguments("smithy-language-server", arguments);
- helpPrinter.summary("Run the Smithy Language Server.");
- helpPrinter.print(AnsiColorFormatter.AUTO, printer);
- printer.flush();
+ private static CliError invalidPort(String portStr) {
+ return new CliError("Invalid port number: expected an integer between "
+ + MIN_PORT + " and " + MAX_PORT + ", inclusive. Was: " + portStr);
}
-
}
diff --git a/src/test/java/software/amazon/smithy/lsp/ServerArgumentsTest.java b/src/test/java/software/amazon/smithy/lsp/ServerArgumentsTest.java
index 80d677d3..48cb92c6 100644
--- a/src/test/java/software/amazon/smithy/lsp/ServerArgumentsTest.java
+++ b/src/test/java/software/amazon/smithy/lsp/ServerArgumentsTest.java
@@ -7,6 +7,7 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -16,12 +17,15 @@ void validPositionalPortNumber() {
String[] args = {"1"};
ServerArguments serverArguments = ServerArguments.create(args);
assertEquals(1, serverArguments.port());
+ assertFalse(serverArguments.help());
+ assertTrue(serverArguments.useSocket());
}
@Test
void invalidPositionalPortNumber() {
String[] args = {"65536"};
assertThrows(CliError.class,()-> {ServerArguments.create(args);});
+
}
@Test
@@ -35,6 +39,8 @@ void validFlagPortNumberShort() {
String[] args = {"-p","100"};
ServerArguments serverArguments = ServerArguments.create(args);
assertEquals(100, serverArguments.port());
+ assertFalse(serverArguments.help());
+ assertTrue(serverArguments.useSocket());
}
@Test
@@ -43,14 +49,17 @@ void defaultPortNumber() {
ServerArguments serverArguments = ServerArguments.create(args);
assertEquals(0, serverArguments.port());
+ assertFalse(serverArguments.help());
+ assertFalse(serverArguments.useSocket());
}
@Test
void defaultPortNumberInArg() {
String[] args = {"0"};
ServerArguments serverArguments = ServerArguments.create(args);
-
assertEquals(0, serverArguments.port());
+ assertFalse(serverArguments.help());
+ assertFalse(serverArguments.useSocket());
}
@Test
@@ -61,32 +70,24 @@ void validFlagPortNumber() {
}
@Test
- void validHelp() {
- ByteArrayOutputStream outContent = new ByteArrayOutputStream();
- PrintStream originalOut = System.out;
- System.setOut(new PrintStream(outContent));
-
- try {
- ServerArguments.create(new String[]{"--help"});
-
- String output = outContent.toString().trim();
-
- assertTrue(output.contains("--help"));
- assertTrue(output.contains("-h"));
- assertTrue(output.contains("--port"));
- assertTrue(output.contains("-p"));
- assertTrue(output.contains("PORT"));
- assertTrue(output.contains(""));
+ void invalidFlag() {
+ String[] args = {"--foo"};
+ assertThrows(CliError.class,()-> {ServerArguments.create(args);});
+ }
- } finally {
- // Restore original System.out
- System.setOut(originalOut);
- }
+ @Test
+ void validHelpShort() {
+ String[] args = {"-h"};
+ ServerArguments serverArguments = ServerArguments.create(args);
+ assertTrue(serverArguments.help());
+ assertFalse(serverArguments.useSocket());
}
@Test
- void invalidFlag() {
- String[] args = {"--foo"};
- assertThrows(CliError.class,()-> {ServerArguments.create(args);});
+ void validHelp() {
+ String[] args = {"--help"};
+ ServerArguments serverArguments = ServerArguments.create(args);
+ assertTrue(serverArguments.help());
+ assertFalse(serverArguments.useSocket());
}
}
From 3a50a9bea8d0554a7a4088081dc6519a1a105638 Mon Sep 17 00:00:00 2001
From: Joe Wu
Date: Mon, 14 Apr 2025 15:09:02 -0700
Subject: [PATCH 6/6] Add tests for default port using flag arguments.
---
.../amazon/smithy/lsp/ServerArgumentsTest.java | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)
diff --git a/src/test/java/software/amazon/smithy/lsp/ServerArgumentsTest.java b/src/test/java/software/amazon/smithy/lsp/ServerArgumentsTest.java
index 48cb92c6..048b83a1 100644
--- a/src/test/java/software/amazon/smithy/lsp/ServerArgumentsTest.java
+++ b/src/test/java/software/amazon/smithy/lsp/ServerArgumentsTest.java
@@ -62,6 +62,24 @@ void defaultPortNumberInArg() {
assertFalse(serverArguments.useSocket());
}
+ @Test
+ void defaultPortNumberWithFlag() {
+ String[] args = {"--port","0"};
+ ServerArguments serverArguments = ServerArguments.create(args);
+ assertEquals(0, serverArguments.port());
+ assertFalse(serverArguments.help());
+ assertFalse(serverArguments.useSocket());
+ }
+
+ @Test
+ void defaultPortNumberWithShotFlag() {
+ String[] args = {"-p","0"};
+ ServerArguments serverArguments = ServerArguments.create(args);
+ assertEquals(0, serverArguments.port());
+ assertFalse(serverArguments.help());
+ assertFalse(serverArguments.useSocket());
+ }
+
@Test
void validFlagPortNumber() {
String[] args = {"--port","200"};