Package org.jboss.forge.shell.completer

Source Code of org.jboss.forge.shell.completer.OptionAwareCompletionHandler

/*
* JBoss, Home of Professional Open Source
* Copyright 2011, Red Hat, Inc., and individual contributors
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.forge.shell.completer;

import jline.console.ConsoleReader;
import jline.console.CursorBuffer;
import jline.console.completer.CandidateListCompletionHandler;
import jline.console.completer.CompletionHandler;

import org.jboss.forge.shell.Shell;
import org.jboss.forge.shell.ShellColor;
import org.jboss.forge.shell.command.CommandMetadata;
import org.jboss.forge.shell.command.OptionMetadata;

import java.io.IOException;
import java.util.*;

/**
* @author <a href="mailto:lincolnbaxter@gmail.com">Lincoln Baxter, III</a>
*
*/
public class OptionAwareCompletionHandler implements CompletionHandler
{
   // TODO: handle quotes and escaped quotes && enable automatic escaping of whitespace
   private final CompletedCommandHolder commandHolder;
   private final Shell shell;

   public OptionAwareCompletionHandler(final CompletedCommandHolder commandHolder, final Shell shell)
   {
      this.commandHolder = commandHolder;
      this.shell = shell;
   }

   @Override
   public boolean complete(final ConsoleReader reader, final List<CharSequence> candidates, final int pos) throws
            IOException
   {
      CursorBuffer buf = reader.getCursorBuffer();

      PluginCommandCompleterState state = commandHolder.getState();
      if (state != null)
      {
         if (((candidates.size() == 1) && "".equals(candidates.get(0)))
                  || (state.isDuplicateBuffer() && state.isFinalTokenComplete()))
         {
            if (commandHolder.getState().getOption() != null)
            {
               OptionMetadata option = commandHolder.getState().getOption();
               reader.println();
               reader.println(option.getOptionDescriptor());
               if (candidates.size() == 1)
               {
                  reader.println();
                  reader.drawLine();
                  return true;
               }
            }
         }
      }

      // if there is only one completion, then fill in the buffer
      if (candidates.size() == 1)
      {
         CharSequence value = candidates.get(0);

         // fail if the only candidate is the same as the current buffer
         if (value.equals(buf.toString()))
         {
            return false;
         }

         setBuffer(reader, value, pos);

         return true;
      }
      else if (candidates.size() > 1)
      {
         String value = getUnambiguousCompletions(candidates);
         setBuffer(reader, value, pos);
      }

      printCandidates(reader, candidates);

      // redraw the current console buffer
      reader.drawLine();

      return true;
   }

   public static void setBuffer(final ConsoleReader reader, final CharSequence value, final int offset) throws
            IOException
   {
      while ((reader.getCursorBuffer().cursor > offset) && reader.backspace())
      {
         // empty
      }

      reader.putString(value);
      reader.setCursorPosition(offset + value.length());
   }

   /**
    * Print out the candidates. If the size of the candidates is greater than the
    * {@link ConsoleReader#getAutoprintThreshold}, they prompt with a warning.
    *
    * @param candidates the list of candidates to print
    */
   public void printCandidates(final ConsoleReader reader, Collection<CharSequence> candidates) throws
            IOException
   {
      Set<CharSequence> distinct = new HashSet<CharSequence>(candidates);

      if (distinct.size() > reader.getAutoprintThreshold())
      {
         // noinspection StringConcatenation
         reader.print(Messages.DISPLAY_CANDIDATES.format(candidates.size()));
         reader.flush();

         int c;

         String noOpt = Messages.DISPLAY_CANDIDATES_NO.format();
         String yesOpt = Messages.DISPLAY_CANDIDATES_YES.format();
         char[] allowed = { yesOpt.charAt(0), noOpt.charAt(0) };

         while ((c = reader.readCharacter(allowed)) != -1)
         {
            String tmp = new String(new char[] { (char) c });

            if (noOpt.startsWith(tmp))
            {
               reader.println();
               return;
            }
            else if (yesOpt.startsWith(tmp))
            {
               break;
            }
            else
            {
               reader.beep();
            }
         }
      }

      // copy the values and make them distinct, without otherwise affecting the ordering. Only do it if the sizes
      // differ.
      if (distinct.size() != candidates.size())
      {
         Collection<CharSequence> copy = new ArrayList<CharSequence>();

         for (CharSequence next : candidates)
         {
            if (!copy.contains(next))
            {
               copy.add(next);
            }
         }

         candidates = copy;
      }

      reader.println();

      Collection<CharSequence> colorizedCandidates = new ArrayList<CharSequence>();
      for (CharSequence seq : candidates)
      {
         boolean processed = false;
         CommandMetadata command = commandHolder.getState().getCommand();
         if ((command != null) && seq.toString().startsWith("--"))
         {
            String str = seq.toString().trim();
            if (str.startsWith("--"))
            {
               str = str.substring(2);
            }

            if (command.hasOption(str) && command.getNamedOption(str).isRequired())
            {
               seq = shell.renderColor(ShellColor.BLUE, seq.toString());
               colorizedCandidates.add(seq);
               processed = true;
            }
         }

         if (!processed)
         {
            colorizedCandidates.add(seq);
         }
      }

      reader.printColumns(colorizedCandidates);
   }

   /**
    * Returns a root that matches all the {@link String} elements of the specified {@link List}, or null if there are no
    * commonalities. For example, if the list contains <i>foobar</i>, <i>foobaz</i>, <i>foobuz</i>, the method will
    * return <i>foob</i>.
    */
   private String getUnambiguousCompletions(final List<CharSequence> candidates)
   {
      if ((candidates == null) || candidates.isEmpty())
      {
         return null;
      }

      // convert to an array for speed
      String[] strings = candidates.toArray(new String[candidates.size()]);

      String first = strings[0];
      StringBuilder candidate = new StringBuilder();

      for (int i = 0; i < first.length(); i++)
      {
         if (startsWith(first.substring(0, i + 1), strings))
         {
            candidate.append(first.charAt(i));
         }
         else
         {
            break;
         }
      }

      return candidate.toString();
   }

   /**
    * @return true is all the elements of <i>candidates</i> start with <i>starts</i>
    */
   private boolean startsWith(final String starts, final String[] candidates)
   {
      for (String candidate : candidates)
      {
         if (!candidate.startsWith(starts))
         {
            return false;
         }
      }

      return true;
   }

   private static enum Messages
   {
       DISPLAY_CANDIDATES,
       DISPLAY_CANDIDATES_YES,
       DISPLAY_CANDIDATES_NO, ;

      private static final ResourceBundle bundle =
               ResourceBundle.getBundle(CandidateListCompletionHandler.class.getName());

      public String format(final Object... args)
      {
         return String.format(bundle.getString(name()), args);
      }
   }
}
TOP

Related Classes of org.jboss.forge.shell.completer.OptionAwareCompletionHandler

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.