diff --git a/core/src/main/java/com/taobao/arthas/core/command/klass100/SearchMethodCommand.java b/core/src/main/java/com/taobao/arthas/core/command/klass100/SearchMethodCommand.java index 38af97cfc..96710cf24 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/klass100/SearchMethodCommand.java +++ b/core/src/main/java/com/taobao/arthas/core/command/klass100/SearchMethodCommand.java @@ -1,6 +1,9 @@ package com.taobao.arthas.core.command.klass100; import com.taobao.arthas.core.command.Constants; +import com.taobao.arthas.core.shell.cli.CliToken; +import com.taobao.arthas.core.shell.cli.Completion; +import com.taobao.arthas.core.shell.cli.CompletionUtils; import com.taobao.arthas.core.shell.command.AnnotatedCommand; import com.taobao.arthas.core.shell.command.CommandProcess; import com.taobao.arthas.core.util.matcher.Matcher; @@ -23,6 +26,7 @@ import java.lang.instrument.Instrumentation; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.util.HashSet; +import java.util.List; import java.util.Set; import static com.taobao.text.Decoration.bold; @@ -84,7 +88,7 @@ public class SearchMethodCommand extends AnnotatedCommand { for (Class clazz : matchedClasses) { Set methodNames = new HashSet(); - for (Constructor constructor : clazz.getDeclaredConstructors()) { + for (Constructor constructor : clazz.getDeclaredConstructors()) { if (!methodNameMatcher.matching("")) { continue; } @@ -146,7 +150,7 @@ public class SearchMethodCommand extends AnnotatedCommand { return table; } - private Element renderConstructor(Constructor constructor) { + private Element renderConstructor(Constructor constructor) { TableElement table = new TableElement().leftCellPadding(1).rightCellPadding(1); table.row(label("declaring-class").style(bold.bold()), label(constructor.getDeclaringClass().getName())) @@ -157,4 +161,23 @@ public class SearchMethodCommand extends AnnotatedCommand { .row(label("exceptions").style(bold.bold()), label(TypeRenderUtils.drawExceptions(constructor))); return table; } + + @Override + public void complete(Completion completion) { + int argumentIndex = CompletionUtils.detectArgumentIndex(completion); + + if(argumentIndex == 1) { + if(!CompletionUtils.completeClassName(completion)) { + super.complete(completion); + } + return; + }else if(argumentIndex == 2) { + if(!CompletionUtils.completeMethodName(completion)) { + super.complete(completion); + } + return; + } + + super.complete(completion); + } } diff --git a/core/src/main/java/com/taobao/arthas/core/shell/cli/CompletionUtils.java b/core/src/main/java/com/taobao/arthas/core/shell/cli/CompletionUtils.java index d52a89254..365f55355 100644 --- a/core/src/main/java/com/taobao/arthas/core/shell/cli/CompletionUtils.java +++ b/core/src/main/java/com/taobao/arthas/core/shell/cli/CompletionUtils.java @@ -2,6 +2,7 @@ package com.taobao.arthas.core.shell.cli; import com.taobao.arthas.core.shell.session.Session; import com.taobao.arthas.core.shell.term.Tty; +import com.taobao.arthas.core.util.SearchUtils; import com.taobao.arthas.core.util.StringUtils; import com.taobao.arthas.core.util.usage.StyledUsageFormatter; import com.taobao.middleware.cli.CLI; @@ -11,6 +12,7 @@ import io.termd.core.util.Helper; import java.io.File; import java.lang.instrument.Instrumentation; +import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -54,15 +56,20 @@ public class CompletionUtils { */ public static boolean complete(Completion completion, Collection searchScope) { List tokens = completion.lineTokens(); - CliToken lastToken = tokens.get(tokens.size() - 1); + String lastToken = tokens.get(tokens.size() - 1).value(); List candidates = new ArrayList(); + + if (StringUtils.isBlank(lastToken)) { + lastToken = ""; + } + for (String name: searchScope) { - if (" ".equals(lastToken.value()) || name.startsWith(lastToken.value())) { + if (name.startsWith(lastToken)) { candidates.add(name); } } if (candidates.size() == 1) { - completion.complete(candidates.get(0).substring(lastToken.value().length()), true); + completion.complete(candidates.get(0).substring(lastToken.length()), true); return true; } else { completion.complete(candidates); @@ -182,6 +189,81 @@ public class CompletionUtils { return true; } + public static boolean completeMethodName(Completion completion) { + List tokens = completion.lineTokens(); + String lastToken = completion.lineTokens().get(tokens.size() - 1).value(); + + // retrieve the class name + String className; + if (StringUtils.isBlank(lastToken)) { + // tokens = { " ", "CLASS_NAME", " "} + className = tokens.get(tokens.size() - 2).value(); + } else { + // tokens = { " ", "CLASS_NAME", " ", "PARTIAL_METHOD_NAME"} + className = tokens.get(tokens.size() - 3).value(); + } + + Set> results = SearchUtils.searchClassOnly(completion.session().getInstrumentation(), className, 2); + if (results.isEmpty() || results.size() > 1) { + // no class found or multiple class found + completion.complete(Collections.emptyList()); + return true; + } + + Class clazz = results.iterator().next(); + + List res = new ArrayList(); + + for (Method method : clazz.getDeclaredMethods()) { + if (StringUtils.isBlank(lastToken)) { + res.add(method.getName()); + } else if (method.getName().startsWith(lastToken)) { + res.add(method.getName()); + } + } + + if (res.size() == 1) { + completion.complete(res.get(0).substring(lastToken.length()), true); + return true; + } else { + CompletionUtils.complete(completion, res); + return true; + } + } + + /** + * 推断输入到哪一个 argument + * @param completion + * @return + */ + public static int detectArgumentIndex(Completion completion) { + List tokens = completion.lineTokens(); + CliToken lastToken = tokens.get(tokens.size() - 1); + + if (lastToken.value().startsWith("-") || lastToken.value().startsWith("--")) { + return -1; + } + + if (StringUtils.isBlank((lastToken.value())) && tokens.size() == 1) { + return 1; + } + + int tokenCount = 0; + + for (CliToken token : tokens) { + if (StringUtils.isBlank(token.value()) || token.value().startsWith("-") || token.value().startsWith("--")) { + // filter irrelevant tokens + continue; + } + tokenCount++; + } + + if (StringUtils.isBlank((lastToken.value())) && tokens.size() != 1) { + tokenCount++; + } + return tokenCount; + } + public static void completeShortOption(Completion completion, CliToken lastToken, List