Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
14 changes: 9 additions & 5 deletions builtins/src/main/java/org/jline/builtins/ScreenTerminal.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
import java.util.List;
import java.util.concurrent.TimeUnit;

import org.jline.terminal.Size;
import org.jline.terminal.Sized;
import org.jline.utils.Colors;
import org.jline.utils.WCWidth;

Expand All @@ -46,7 +46,7 @@
* It follows the ECMA-48 standard for terminal control sequences.
* </p>
*/
public class ScreenTerminal {
public class ScreenTerminal implements Sized {

public static final int MIN_SIZE = 2;
public static final int MAX_SIZE = 4096;
Expand Down Expand Up @@ -1710,6 +1710,7 @@
*
* @return the current number of columns
*/
@Override
public synchronized int getColumns() {
return columns;
}
Expand All @@ -1719,6 +1720,7 @@
*
* @return the number of rows
*/
@Override
public synchronized int getRows() {
return rows;
}
Expand Down Expand Up @@ -1750,11 +1752,11 @@
/**
* Resize the terminal to the specified columns and rows.
*
* @param size the new Size whose columns and rows will be applied
* @param sized the new Size whose columns and rows will be applied
* @return true if the size was set successfully, false otherwise
*/
public synchronized boolean setSize(Size size) {
return setSize(size.getColumns(), size.getRows());
public synchronized boolean setSize(Sized sized) {
return setSize(sized.getColumns(), sized.getRows());
Comment on lines +1758 to +1759
Copy link
Copy Markdown

@coderabbitai coderabbitai bot Apr 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Guard against null in setSize(Sized)

Line [1758] accepts a nullable reference and Line [1759] dereferences it unconditionally. A defensive check avoids avoidable runtime NPEs from public API callers.

Suggested fix
 public synchronized boolean setSize(Sized sized) {
+    if (sized == null) {
+        return false;
+    }
     return setSize(sized.getColumns(), sized.getRows());
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public synchronized boolean setSize(Sized sized) {
return setSize(sized.getColumns(), sized.getRows());
public synchronized boolean setSize(Sized sized) {
if (sized == null) {
return false;
}
return setSize(sized.getColumns(), sized.getRows());
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@builtins/src/main/java/org/jline/builtins/ScreenTerminal.java` around lines
1758 - 1759, The public synchronized method setSize(Sized) dereferences its
Sized parameter without a null check; add a defensive null guard at the start of
setSize(Sized) (e.g., if sized == null return false or throw
IllegalArgumentException) before calling sized.getColumns()/getRows(), so
callers cannot cause an NPE; update the method body of setSize(Sized) (which
delegates to setSize(int,int)) to perform this check.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While valid, this method mimics the logic in all similar methods introduced in 7d21f09

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
gh api repos/jline/jline3/commits/7d21f09e65bb4305db1c87086ee7e5a2ed872215 --jq '.files[].filename' 2>/dev/null
gh api "repos/jline/jline3/commits/7d21f09e65bb4305db1c87086ee7e5a2ed872215" 2>/dev/null | python3 -c "
import sys, json
data = json.load(sys.stdin)
for f in data.get('files', []):
    print('=== FILE:', f['filename'], '===')
    print(f.get('patch',''))
    print()
" 2>/dev/null | head -200

Length of output: 11790


🏁 Script executed:

#!/bin/bash
gh api "repos/jline/jline3/commits/7d21f09e65bb4305db1c87086ee7e5a2ed872215" 2>/dev/null | python3 -c "
import sys, json
data = json.load(sys.stdin)
for f in data.get('files', []):
    if any(x in f['filename'] for x in ['Terminal.java', 'LineDiscipline', 'AbstractPosix', 'AbstractWindows', 'DumbTerminal', 'Display.java', 'Size.java']):
        print('=== FILE:', f['filename'], '===')
        print(f.get('patch',''))
        print()
" 2>/dev/null

Length of output: 46937


@Elec332, that's a fair point. Looking at the commit you referenced, the established pattern throughout the codebase — including setSize(Size size) in ScreenTerminal, Terminal.setSize(Size), and Display.resize(Size) — consistently omits null guards and dereferences the parameter directly. The current setSize(Sized sized) faithfully follows that same convention. I'll withdraw this suggestion.


✏️ Learnings added
Learnt from: Elec332
Repo: jline/jline3 PR: 1731
File: builtins/src/main/java/org/jline/builtins/ScreenTerminal.java:1758-1759
Timestamp: 2026-04-09T00:49:13.994Z
Learning: In jline/jline3, the established pattern for `setSize(Size/Sized)` and similar methods (e.g., `Display.resize(Size)`) across `ScreenTerminal`, `Terminal`, `LineDisciplineTerminal`, and related classes is to dereference the parameter directly without null checks. Do not suggest null guards for these methods.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


🧠 Learnings used
Learnt from: CR
Repo: jline/jline3 PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T12:03:07.932Z
Learning: Applies to **/terminal/**/*.java : Follow existing patterns for terminal feature pairs, such as hasFocusSupport()/trackFocus() method naming

}

/**
Expand All @@ -1767,7 +1769,9 @@
* @param rows the target number of rows (2–256)
* @return `true` if the size was changed; `false` if the requested dimensions are out of range
*/
@Deprecated
@SuppressWarnings("java:S1133") // Intentional deprecation; removal planned for a future major version
public synchronized boolean setSize(int columns, int rows) {

Check warning on line 1774 in builtins/src/main/java/org/jline/builtins/ScreenTerminal.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Add the missing @deprecated Javadoc tag.

See more on https://sonarcloud.io/project/issues?id=jline_jline3&issues=AZ1vjCuEKubTsH7o02ji&open=AZ1vjCuEKubTsH7o02ji&pullRequest=1731
if (columns < MIN_SIZE || columns > MAX_SIZE || rows < MIN_SIZE || rows > MAX_SIZE) {
return false;
}
Expand Down
34 changes: 33 additions & 1 deletion builtins/src/main/java/org/jline/builtins/SwingTerminal.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import javax.swing.*;

import org.jline.terminal.Size;
import org.jline.terminal.Sized;
import org.jline.terminal.impl.LineDisciplineTerminal;
import org.jline.utils.Curses;
import org.jline.utils.InfoCmp;
Expand Down Expand Up @@ -246,6 +247,13 @@ public void dispose() {
component.dispose();
}

@Override
public void setSize(Sized sz) {
if (component.setSize(sz)) {
super.setSize(component);
}
}

/**
* Custom OutputStream that writes to the TerminalComponent.
* Uses proper ByteBuffer and CharsetDecoder to handle byte-to-char transformation.
Expand Down Expand Up @@ -442,7 +450,7 @@ private void decodeAndOutput(boolean endOfInput) {
* JComponent that renders the terminal display.
* This is the inner class that contains the original ScreenTerminal-based implementation.
*/
public static class TerminalComponent extends JComponent implements KeyListener {
public static class TerminalComponent extends JComponent implements KeyListener, Sized {

private static final long serialVersionUID = 1L;

Expand Down Expand Up @@ -580,6 +588,30 @@ private void updatePreferredSize() {
setPreferredSize(new Dimension(width, height));
}

/**
* Resize the terminal to the specified columns and rows.
*
* @param size the new Size whose columns and rows will be applied
* @return true if the size was set successfully, false otherwise
*/
public boolean setSize(Sized size) {
if (screenTerminal.setSize(size)) {
updatePreferredSize();
return true;
}
return false;
}

@Override
public int getColumns() {
return screenTerminal.getColumns();
}

@Override
public int getRows() {
return screenTerminal.getRows();
}

/**
* Paints this Swing component and its terminal contents.
*
Expand Down
7 changes: 4 additions & 3 deletions builtins/src/main/java/org/jline/builtins/Tmux.java
Original file line number Diff line number Diff line change
Expand Up @@ -2010,7 +2010,7 @@ protected void doClose() throws IOException {
closer.accept(VirtualConsole.this);
}
};
this.console.setSize(new Size(columns, rows));
this.console.setSize(this.terminal);
this.layout = layout;
}

Expand Down Expand Up @@ -2073,8 +2073,9 @@ public OutputStream getMasterInputOutput() {
public void resize(int left, int top, int width, int height) {
this.left = left;
this.top = top;
console.setSize(new Size(width, height));
terminal.setSize(width, height);
// Resize ScreenTerminal first, as it may not resize depending on requested size.
terminal.setSize(new Size(width, height));
console.setSize(this.terminal);
console.raise(Signal.WINCH);
}

Expand Down
12 changes: 12 additions & 0 deletions builtins/src/main/java/org/jline/builtins/WebTerminal.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.zip.GZIPOutputStream;

import org.jline.terminal.Sized;
import org.jline.terminal.impl.LineDisciplineTerminal;

import com.sun.net.httpserver.HttpExchange;
Expand Down Expand Up @@ -207,14 +208,23 @@
return component.dump(timeout, forceUpdate);
}

@Override
public void setSize(Sized sz) {
if (component.setSize(sz)) {
super.setSize(component);
}
}

/**
* Set the terminal dimensions in columns and rows.
*
* @param columns the new number of columns
* @param rows the new number of rows
* @return `true` if the terminal was resized, `false` otherwise
*/
@Deprecated
@SuppressWarnings("java:S1133")
public boolean setSize(int columns, int rows) {

Check warning on line 227 in builtins/src/main/java/org/jline/builtins/WebTerminal.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Add the missing @deprecated Javadoc tag.

See more on https://sonarcloud.io/project/issues?id=jline_jline3&issues=AZ1vjCxDKubTsH7o02jj&open=AZ1vjCxDKubTsH7o02jj&pullRequest=1731
return component.setSize(columns, rows);
}

Expand Down Expand Up @@ -544,7 +554,9 @@
* @return true if the size was applied, false if the provided dimensions are out of range
*/
@Override
@Deprecated
@SuppressWarnings("deprecation")
public synchronized boolean setSize(int columns, int rows) {

Check warning on line 559 in builtins/src/main/java/org/jline/builtins/WebTerminal.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Do not forget to remove this deprecated code someday.

See more on https://sonarcloud.io/project/issues?id=jline_jline3&issues=AZ1vjCxDKubTsH7o02jk&open=AZ1vjCxDKubTsH7o02jk&pullRequest=1731

Check warning on line 559 in builtins/src/main/java/org/jline/builtins/WebTerminal.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Add the missing @deprecated Javadoc tag.

See more on https://sonarcloud.io/project/issues?id=jline_jline3&issues=AZ1vjCxDKubTsH7o02jl&open=AZ1vjCxDKubTsH7o02jl&pullRequest=1731
if (columns < 10 || rows < 5 || columns > MAX_SIZE || rows > MAX_SIZE) {
return false;
}
Expand Down
19 changes: 10 additions & 9 deletions builtins/src/test/java/org/jline/builtins/ScreenTerminalTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

import java.time.Duration;

import org.jline.terminal.Size;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.*;
Expand Down Expand Up @@ -157,7 +158,7 @@ public void testScrollAreaUpMultipleLines() {
public void testSetSizeWidthShrink() {
ScreenTerminal terminal = new ScreenTerminal(80, 24);
terminal.write("Hello, World!");
terminal.setSize(40, 24);
terminal.setSize(new Size(40, 24));

// Should not throw on any subsequent operation
String content = terminal.toString();
Expand All @@ -184,7 +185,7 @@ public void testDECRCClampsAfterResize() {
terminal.write("\0337");

// Shrink terminal
terminal.setSize(40, 12);
terminal.setSize(new Size(40, 12));

// DECRC - restore cursor
terminal.write("\0338");
Expand All @@ -207,7 +208,7 @@ public void testCSI_RCP_ClampsAfterResize() {
terminal.write("\033[s");

// Shrink terminal
terminal.setSize(40, 12);
terminal.setSize(new Size(40, 12));

// CSI u - restore cursor position
terminal.write("\033[u");
Expand Down Expand Up @@ -337,9 +338,9 @@ public void testCSI3JClearsScrollback() throws InterruptedException {
terminal.write("\033[3J");

// Shrink terminal to try pulling from scrollback
terminal.setSize(10, 3);
terminal.setSize(new Size(10, 3));
// Grow terminal back — if scrollback is empty, new lines should be blank
terminal.setSize(10, 8);
terminal.setSize(new Size(10, 8));

String content = terminal.toString();
String[] lines = content.split("\n", -1);
Expand Down Expand Up @@ -442,7 +443,7 @@ public void testResizeOnAltScreen() {
terminal.write("ALT");

// Resize while on alt-screen
terminal.setSize(20, 10);
terminal.setSize(new Size(20, 10));

// Switch back
terminal.write("\033[?1049l");
Expand Down Expand Up @@ -715,12 +716,12 @@ public void testHistoryLinesWidthAdjustmentOnResize() throws InterruptedExceptio

// Reduce the height to push more lines into history
int reducedHeight = 10;
terminal.setSize(initialWidth, reducedHeight);
terminal.setSize(new Size(initialWidth, reducedHeight));

// Now increase the width and height
int newWidth = 100;
int newHeight = 20;
terminal.setSize(newWidth, newHeight);
terminal.setSize(new Size(newWidth, newHeight));

// The test passes if no exception is thrown during rendering
// We can verify this by dumping the terminal content
Expand Down Expand Up @@ -754,7 +755,7 @@ public void testScreenLinesSpaceFillingOnWidthIncrease() throws InterruptedExcep

// Increase the width
int newWidth = 100;
terminal.setSize(newWidth, initialHeight);
terminal.setSize(new Size(newWidth, initialHeight));

// Dump the terminal content
String dump = terminal.dump(0, true);
Expand Down
16 changes: 8 additions & 8 deletions builtins/src/test/java/org/jline/builtins/TerminalTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ public void testScreenTerminalBaseFunctionality() {
// Test that both terminals inherit ScreenTerminal functionality

// Test size setting
assertTrue(webTerminal.setSize(30, 15));
assertTrue(webTerminal.getComponent().setSize(new Size(30, 15)));
swingTerminal.setSize(new Size(30, 15));

// Test writing
Expand Down Expand Up @@ -247,7 +247,7 @@ public void testCursorMovement() {
@Test
public void testTerminalResizing() {
// Test resizing functionality
assertTrue(webTerminal.setSize(40, 20));
assertTrue(webTerminal.getComponent().setSize(new Size(40, 20)));
swingTerminal.setSize(new Size(40, 20));

// Write content after resize
Expand All @@ -267,14 +267,14 @@ public void testTerminalResizing() {
@Test
public void testInvalidSizes() {
// Test that invalid sizes are rejected
assertFalse(webTerminal.setSize(1, 10)); // Too small width
assertFalse(webTerminal.setSize(10, 1)); // Too small height
assertFalse(webTerminal.getComponent().setSize(new Size(1, 10))); // Too small width
assertFalse(webTerminal.getComponent().setSize(new Size(10, 1))); // Too small height
// Boundary checks at MAX_SIZE
int max = ScreenTerminal.MAX_SIZE;
assertTrue(webTerminal.setSize(max, 10)); // Exactly at max width
assertTrue(webTerminal.setSize(10, max)); // Exactly at max height
assertFalse(webTerminal.setSize(max + 1, 10)); // One past max width
assertFalse(webTerminal.setSize(10, max + 1)); // One past max height
assertTrue(webTerminal.getComponent().setSize(new Size(max, 10))); // Exactly at max width
assertTrue(webTerminal.getComponent().setSize(new Size(10, max))); // Exactly at max height
assertFalse(webTerminal.getComponent().setSize(new Size(max + 1, 10))); // One past max width
assertFalse(webTerminal.getComponent().setSize(new Size(10, max + 1))); // One past max height

swingTerminal.setSize(new Size(1, 10));
swingTerminal.setSize(new Size(10, 1));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ private boolean isSupported() {
private static final class ZeroSizeLineReader extends LineReaderImpl {
private ZeroSizeLineReader(Terminal terminal) throws IOException {
super(terminal);
display.resize(0, 0);
display.resize(new Size(0, 0));
}
}

Expand Down
32 changes: 26 additions & 6 deletions terminal/src/main/java/org/jline/terminal/Size.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
* <p>
* Size objects are typically obtained from a {@link Terminal} using {@link Terminal#getSize()},
* and can be used to adjust display formatting or to set the terminal size using
* {@link Terminal#setSize(Size)}.
* {@link Terminal#setSize(Sized)}.
* </p>
*
* <p>Example usage:</p>
Expand All @@ -45,9 +45,9 @@
* </pre>
*
* @see Terminal#getSize()
* @see Terminal#setSize(Size)
* @see Terminal#setSize(Sized)
*/
public class Size {
public class Size implements Sized {

private int rows;
private int cols;
Expand Down Expand Up @@ -80,10 +80,10 @@ public Size(int columns, int rows) {
/**
* Constructs a new Size with the same columns and rows as the given size.
*
* @param size the source Size from which to copy columns and rows
* @param sized the source Size from which to copy columns and rows
*/
public Size(Size size) {
this(size.getColumns(), size.getRows());
public Size(Sized sized) {
this(sized.getColumns(), sized.getRows());
}

/**
Expand All @@ -96,6 +96,7 @@ public Size(Size size) {
* @return the number of columns
* @see #setColumns(int)
*/
@Override
public int getColumns() {
return cols;
}
Expand Down Expand Up @@ -124,6 +125,7 @@ public void setColumns(int columns) {
* @return the number of rows
* @see #setRows(int)
*/
@Override
public int getRows() {
return rows;
}
Expand Down Expand Up @@ -167,7 +169,25 @@ public int cursorPos(int row, int col) {
*
* @param size the Size object to copy dimensions from
*/
// Deprecation is awkward, as in its current state copy(Size) would always cause deprecation warnings,
// because it doesn't auto-map to setSize(Sized)
// @Deprecated
// @SuppressWarnings("java:S1133") // Intentional deprecation; removal planned for a future major version
public void copy(Size size) {
copy((Sized) size);
}

/**
* Copies the dimensions from another Size object to this one.
*
* <p>
* This method updates this Size object to have the same dimensions
* (rows and columns) as the specified Size object.
* </p>
*
* @param size the Size object to copy dimensions from
*/
public void copy(Sized size) {
setColumns(size.getColumns());
setRows(size.getRows());
}
Expand Down
Loading
Loading