diff --git a/core/src/main/java/com/taobao/arthas/core/shell/cli/impl/CliTokenImpl.java b/core/src/main/java/com/taobao/arthas/core/shell/cli/impl/CliTokenImpl.java index 6ade0a554..ca956e556 100644 --- a/core/src/main/java/com/taobao/arthas/core/shell/cli/impl/CliTokenImpl.java +++ b/core/src/main/java/com/taobao/arthas/core/shell/cli/impl/CliTokenImpl.java @@ -3,6 +3,7 @@ package com.taobao.arthas.core.shell.cli.impl; import com.taobao.arthas.core.shell.cli.CliToken; import io.termd.core.readline.LineStatus; +import java.util.ArrayList; import java.util.LinkedList; import java.util.List; @@ -70,10 +71,53 @@ public class CliTokenImpl implements CliToken { tokenize(s, 0, tokens); + tokens = correctPipeChar(tokens); return tokens; } + /** + * fix pipe char '|' problem: https://github.com/alibaba/arthas/issues/1151 + * supported: + * 1) thread| grep xxx + * [thread|, grep] -> [thread, |, grep] + * 2) thread |grep xxx + * [thread, |grep] -> [thread, |, grep] + * + * unsupported: + * 3) thread|grep xxx + * 4) trace -E classA|classB methodA|methodB|grep classA + * @param tokens + * @return + */ + private static List correctPipeChar(List tokens) { + List newTokens = new ArrayList(tokens.size()+4); + for (CliToken token : tokens) { + String tokenValue = token.value(); + if (tokenValue.length()>1 && tokenValue.endsWith("|")) { + //split last char '|' + tokenValue = tokenValue.substring(0, tokenValue.length()-1); + String rawValue = token.raw(); + rawValue = rawValue.substring(0, rawValue.length()-1); + newTokens.add(new CliTokenImpl(token.isText(), rawValue, tokenValue)); + //add '|' char + newTokens.add(new CliTokenImpl(true, "|", "|")); + + } else if (tokenValue.length()>1 && tokenValue.startsWith("|")) { + //add '|' char + newTokens.add(new CliTokenImpl(true, "|", "|")); + //remove first char '|' + tokenValue = tokenValue.substring(1); + String rawValue = token.raw(); + rawValue = rawValue.substring(1); + newTokens.add(new CliTokenImpl(token.isText(), rawValue, tokenValue)); + } else { + newTokens.add(token); + } + } + return newTokens; + } + private static void tokenize(String s, int index, List builder) { while (index < s.length()) { char c = s.charAt(index); diff --git a/core/src/test/java/com/taobao/arthas/core/shell/cli/impl/CliTokenImplTest.java b/core/src/test/java/com/taobao/arthas/core/shell/cli/impl/CliTokenImplTest.java new file mode 100644 index 000000000..fbaeb1b23 --- /dev/null +++ b/core/src/test/java/com/taobao/arthas/core/shell/cli/impl/CliTokenImplTest.java @@ -0,0 +1,110 @@ +package com.taobao.arthas.core.shell.cli.impl; + +import com.taobao.arthas.core.shell.cli.CliToken; +import org.junit.Assert; +import org.junit.Test; + +import java.util.Iterator; +import java.util.List; + +public class CliTokenImplTest { + + /** + * supported: + *

+ * case1: + * thread| grep xxx + * [thread|, grep, xxx] -> [thread, |, grep, xxx] + * case:2 + * thread | grep xxx + * [thread, |, grep, xxx] -> [thread, |, grep, xxx] + * case3: + * thread |grep xxx + * [thread, |grep] -> [thread, |, grep, xxx] + */ + @Test + public void testSupportedPipeCharWithoutRegex() { + String[] expectedTextTokenValue = new String[]{"thread", "|", "grep", "xxx"}; + List actualTokens = CliTokenImpl.tokenize("thread| grep xxx"); + assertEquals(expectedTextTokenValue, actualTokens); + + actualTokens = CliTokenImpl.tokenize("thread | grep xxx"); + assertEquals(expectedTextTokenValue, actualTokens); + + actualTokens = CliTokenImpl.tokenize("thread |grep xxx"); + assertEquals(expectedTextTokenValue, actualTokens); + } + + /** + * supported: + *

+ * case1: + * trace -E classA|classB methodA|methodB| grep classA + * [trace, -E, classA|classB, methodA|methodB|, grep, classA] -> [trace, -E, classA|classB, methodA|methodB, |, grep, classA] + * case2: + * trace -E classA|classB methodA|methodB | grep classA + * [trace, -E, classA|classB, methodA|methodB, |, grep, classA] -> [trace, -E, classA|classB, methodA|methodB, |, grep, classA] + * case3: + * trace -E classA|classB methodA|methodB |grep classA + * [trace, -E, classA|classB, methodA|methodB, |grep, classA] -> [trace, -E, classA|classB, methodA|methodB, |, grep, classA] + */ + @Test + public void testSupportedPipeCharWithRegex() { + String[] expectedTextTokenValue = new String[]{"trace", "-E", "classA|classB", "methodA|methodB", "|", "grep", "classA"}; + List actualTokens = CliTokenImpl.tokenize("trace -E classA|classB methodA|methodB| grep classA"); + assertEquals(expectedTextTokenValue, actualTokens); + + actualTokens = CliTokenImpl.tokenize("trace -E classA|classB methodA|methodB | grep classA"); + assertEquals(expectedTextTokenValue, actualTokens); + + actualTokens = CliTokenImpl.tokenize("trace -E classA|classB methodA|methodB |grep classA"); + assertEquals(expectedTextTokenValue, actualTokens); + } + + /** + * unsupported: + *

+ * case1: + * thread|grep xxx + * [thread|grep, xxx] -> [thread|grep, xxx] + * case2: + * trace -E classA|classB methodA|methodB|grep classA + * [trace, -E, classA|classB, methodA|methodB|grep, classA] -> [trace, -E, classA|classB, methodA|methodB|grep, classA] + * case3: + * trace -E classA|classB| methodA|methodB | grep classA + * [trace, -E, classA|classB|, methodA|methodB, |, grep, classA] -> [trace, -E, classA|classB,|, methodA|methodB, |, grep, classA] + */ + @Test + public void testUnSupportedPipeChar() { + String[] expectedTextTokenValue = new String[]{"thread|grep", "xxx"}; + List actualTokens = CliTokenImpl.tokenize("thread|grep xxx"); + assertEquals(expectedTextTokenValue, actualTokens); + + expectedTextTokenValue = new String[]{"trace", "-E", "classA|classB", "methodA|methodB|grep", "classA"}; + actualTokens = CliTokenImpl.tokenize("trace -E classA|classB methodA|methodB|grep classA"); + assertEquals(expectedTextTokenValue, actualTokens); + + expectedTextTokenValue = new String[]{"trace", "-E", "classA|classB", "|", "methodA|methodB", "|", "grep", "classA"}; + actualTokens = CliTokenImpl.tokenize("trace -E classA|classB| methodA|methodB | grep classA"); + assertEquals(expectedTextTokenValue, actualTokens); + } + + private void assertEquals(String[] expectedTextTokenValue, List actualTokens) { + removeBlankToken(actualTokens); + for (int i = 0; i < expectedTextTokenValue.length; i++) { + Assert.assertEquals(expectedTextTokenValue[i], actualTokens.get(i).value()); + } + } + + private void removeBlankToken(List cliTokens) { + CliToken blankToken = new CliTokenImpl(false, " "); + Iterator it = cliTokens.iterator(); + while (it.hasNext()) { + CliToken token = it.next(); + if (blankToken.equals(token)) { + it.remove(); + } + } + } + +}