Skip to content

Commit

Permalink
Command less: implemented tabs option
Browse files Browse the repository at this point in the history
  • Loading branch information
mattirn committed Jun 1, 2019
1 parent db70837 commit a4acf40
Show file tree
Hide file tree
Showing 6 changed files with 112 additions and 17 deletions.
13 changes: 8 additions & 5 deletions builtins/src/main/java/org/jline/builtins/Commands.java
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ public static void less(Terminal terminal, InputStream in, PrintStream out, Prin
" -S --chop-long-lines Do not fold long lines",
" -i --ignore-case Search ignores lowercase case",
" -I --IGNORE-CASE Search ignores all case",
" -x --tabs Set tab stops",
" -x --tabs=N[,...] Set tab stops",
" -N --LINE-NUMBERS Display line number for each line"
};

Expand All @@ -135,17 +135,20 @@ public static void less(Terminal terminal, InputStream in, PrintStream out, Prin
throw new HelpException(opt.usage());
}

Less less = new Less(terminal);
List<Integer> tabs = new ArrayList<>();
if (opt.isSet("tabs")) {
for (String s: opt.get("tabs").split(",")) {
tabs.add(parseInteger(s));
}
}
Less less = new Less(terminal).tabs(tabs);
less.quitAtFirstEof = opt.isSet("QUIT-AT-EOF");
less.quitAtSecondEof = opt.isSet("quit-at-eof");
less.quiet = opt.isSet("quiet");
less.veryQuiet = opt.isSet("QUIET");
less.chopLongLines = opt.isSet("chop-long-lines");
less.ignoreCaseAlways = opt.isSet("IGNORE-CASE");
less.ignoreCaseCond = opt.isSet("ignore-case");
if (opt.isSet("tabs")) {
less.tabs = opt.getNumber("tabs");
}
less.printLineNumbers = opt.isSet("LINE-NUMBERS");
List<Source> sources = new ArrayList<>();
if (opt.args().isEmpty()) {
Expand Down
9 changes: 7 additions & 2 deletions builtins/src/main/java/org/jline/builtins/Less.java
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,8 @@ public class Less {
public boolean ignoreCaseAlways;
public boolean noKeypad;
public boolean noInit;
public int tabs = 4;


protected List<Integer> tabs = Arrays.asList(4);
protected final Terminal terminal;
protected final Display display;
protected final BindingReader bindingReader;
Expand Down Expand Up @@ -95,6 +95,11 @@ public Less(Terminal terminal) {
this.bindingReader = new BindingReader(terminal.reader());
}

public Less tabs(List<Integer> tabs) {
this.tabs = tabs;
return this;
}

public void handle(Signal signal) {
size.copy(terminal.getSize());
try {
Expand Down
21 changes: 16 additions & 5 deletions demo/src/main/java/org/apache/felix/gogo/jline/Posix.java
Original file line number Diff line number Diff line change
Expand Up @@ -942,7 +942,7 @@ protected void less(CommandSession session, Process process, String[] argv) thro
" -S --chop-long-lines Do not fold long lines",
" -i --ignore-case Search ignores lowercase case",
" -I --IGNORE-CASE Search ignores all case",
" -x --tabs Set tab stops",
" -x --tabs=N[,...] Set tab stops",
" -N --LINE-NUMBERS Display line number for each line",
" --no-init Disable terminal initialization",
" --no-keypad Disable keypad handling"
Expand Down Expand Up @@ -978,17 +978,20 @@ protected void less(CommandSession session, Process process, String[] argv) thro
return;
}

Less less = new Less(Shell.getTerminal(session));
List<Integer> tabs = new ArrayList<>();
if (opt.isSet("tabs")) {
for (String s: opt.get("tabs").split(",")) {
tabs.add(parseInteger(s));
}
}
Less less = new Less(Shell.getTerminal(session)).tabs(tabs);
less.quitAtFirstEof = opt.isSet("QUIT-AT-EOF");
less.quitAtSecondEof = opt.isSet("quit-at-eof");
less.quiet = opt.isSet("quiet");
less.veryQuiet = opt.isSet("QUIET");
less.chopLongLines = opt.isSet("chop-long-lines");
less.ignoreCaseAlways = opt.isSet("IGNORE-CASE");
less.ignoreCaseCond = opt.isSet("ignore-case");
if (opt.isSet("tabs")) {
less.tabs = opt.getNumber("tabs");
}
less.printLineNumbers = opt.isSet("LINE-NUMBERS");
if (hasExtendedOptions) {
Less.class.getField("quitIfOneScreen").set(less, opt.isSet("quit-if-one-screen"));
Expand All @@ -998,6 +1001,14 @@ protected void less(CommandSession session, Process process, String[] argv) thro
less.run(sources);
}

private int parseInteger(String s) throws IllegalArgumentException {
try {
return Integer.parseInt(s);
} catch (NumberFormatException ex) {
throw new IllegalArgumentException();
}
}

protected void sort(CommandSession session, Process process, String[] argv) throws Exception {
final String[] usage = {
"sort - writes sorted standard input to standard output.",
Expand Down
12 changes: 12 additions & 0 deletions terminal/src/main/java/org/jline/utils/AttributedString.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

import java.security.InvalidParameterException;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
Expand Down Expand Up @@ -107,6 +108,17 @@ public static AttributedString fromAnsi(String ansi, int tabs) {
.toAttributedString();
}

public static AttributedString fromAnsi(String ansi, List<Integer> tabs) {
if (ansi == null) {
return null;
}
return new AttributedStringBuilder(ansi.length())
.tabs(tabs)
.ansiAppend(ansi)
.toAttributedString();
}


public static String stripAnsi(String ansi) {
if (ansi == null) {
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
*/
package org.jline.utils;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
Expand All @@ -25,7 +26,7 @@ public class AttributedStringBuilder extends AttributedCharSequence implements A
private char[] buffer;
private int[] style;
private int length;
private int tabs = 0;
private TabStops tabs = new TabStops(0);
private int lastLineLength = 0;
private AttributedStyle current = AttributedStyle.DEFAULT;

Expand Down Expand Up @@ -152,7 +153,7 @@ public AttributedStringBuilder append(AttributedCharSequence str, int start, int
for (int i = start; i < end; i++) {
char c = str.charAt(i);
int s = str.styleCodeAt(i) & ~current.getMask() | current.getStyle();
if (tabs > 0 && c == '\t') {
if (tabs.defined() && c == '\t') {
insertTab(new AttributedStyle(s, 0));
} else {
ensureCapacity(length + 1);
Expand Down Expand Up @@ -333,7 +334,7 @@ public AttributedStringBuilder ansiAppend(String ansi) {
// This is not a SGR code, so ignore
ansiState = 0;
}
} else if (c == '\t' && tabs > 0) {
} else if (c == '\t' && tabs.defined()) {
insertTab(current);
} else {
ensureCapacity(length + 1);
Expand All @@ -351,7 +352,7 @@ public AttributedStringBuilder ansiAppend(String ansi) {
}

protected void insertTab(AttributedStyle s) {
int nb = tabs - lastLineLength % tabs;
int nb = tabs.spaces(lastLineLength);
ensureCapacity(length + nb);
for (int i = 0; i < nb; i++) {
buffer[length] = ' ';
Expand Down Expand Up @@ -380,10 +381,18 @@ public AttributedStringBuilder tabs(int tabsize) {
if (tabsize < 0) {
throw new IllegalArgumentException("Tab size must be non negative");
}
this.tabs = tabsize;
this.tabs = new TabStops(tabsize);
return this;
}

public AttributedStringBuilder tabs(List<Integer> tabs) {
if (length > 0) {
throw new IllegalStateException("Cannot change tab size after appending text");
}
this.tabs = new TabStops(tabs);
return this;
}

public AttributedStringBuilder styleMatches(Pattern pattern, AttributedStyle s) {
Matcher matcher = pattern.matcher(this);
while (matcher.find()) {
Expand All @@ -406,5 +415,48 @@ public AttributedStringBuilder styleMatches(Pattern pattern, List<AttributedStyl
}
return this;
}

private class TabStops {
private List<Integer> tabs = new ArrayList<>();
private int lastStop = 0;
private int lastSize = 0;

public TabStops(int tabs) {
this.lastSize = tabs;
}

public TabStops(List<Integer> tabs) {
this.tabs = tabs;
int p = 0;
for (int s: tabs) {
if (s < p) {
continue;
}
lastStop = s;
lastSize = s - p;
p = s;
}
}

boolean defined() {
return lastSize != 0;
}

int spaces(int lastLineLength) {
int out = 0;
if (lastLineLength >= lastStop) {
out = lastSize - (lastLineLength - lastStop) % lastSize;
} else {
for (int s: tabs) {
if (s > lastLineLength) {
out = s - lastLineLength;
break;
}
}
}
return out;
}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
*/
package org.jline.utils;

import java.util.Arrays;
import java.util.List;

import org.junit.Test;

import static org.junit.Assert.*;
Expand All @@ -28,6 +31,15 @@ public void testTabSize() {
sb = new AttributedStringBuilder().tabs(5);
sb.append("hello\tWorld");
assertEquals(TAB_SIZE_ERR_MSG, "hello World", sb.toString());

sb = new AttributedStringBuilder().tabs(Arrays.asList(5));
sb.append("hello\tWorld");
assertEquals(TAB_SIZE_ERR_MSG, "hello World", sb.toString());

sb = new AttributedStringBuilder().tabs(Arrays.asList(6,13));
sb.append("one\ttwo\tthree\tfour");
assertEquals(TAB_SIZE_ERR_MSG, "one two three four", sb.toString());

}

/**
Expand Down

0 comments on commit a4acf40

Please sign in to comment.