Home | History | Annotate | Download | only in jline
      1 /*
      2  * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved.
      3  *
      4  * This software is distributable under the BSD license. See the terms of the
      5  * BSD license in the documentation provided with this software.
      6  */
      7 package jline;
      8 
      9 import java.io.*;
     10 import java.util.*;
     11 
     12 /**
     13  *  A file name completor takes the buffer and issues a list of
     14  *  potential completions.
     15  *
     16  *  <p>
     17  *  This completor tries to behave as similar as possible to
     18  *  <i>bash</i>'s file name completion (using GNU readline)
     19  *  with the following exceptions:
     20  *
     21  *  <ul>
     22  *  <li>Candidates that are directories will end with "/"</li>
     23  *  <li>Wildcard regular expressions are not evaluated or replaced</li>
     24  *  <li>The "~" character can be used to represent the user's home,
     25  *  but it cannot complete to other users' homes, since java does
     26  *  not provide any way of determining that easily</li>
     27  *  </ul>
     28  *
     29  *  <p>TODO</p>
     30  *  <ul>
     31  *  <li>Handle files with spaces in them</li>
     32  *  <li>Have an option for file type color highlighting</li>
     33  *  </ul>
     34  *
     35  *  @author  <a href="mailto:mwp1 (at) cornell.edu">Marc Prud'hommeaux</a>
     36  */
     37 public class FileNameCompletor implements Completor {
     38     public int complete(final String buf, final int cursor,
     39                         final List candidates) {
     40         String buffer = (buf == null) ? "" : buf;
     41 
     42         String translated = buffer;
     43 
     44         // special character: ~ maps to the user's home directory
     45         if (translated.startsWith("~" + File.separator)) {
     46             translated = System.getProperty("user.home")
     47                          + translated.substring(1);
     48         } else if (translated.startsWith("~")) {
     49             translated = new File(System.getProperty("user.home")).getParentFile()
     50                                                                   .getAbsolutePath();
     51         } else if (!(translated.startsWith(File.separator))) {
     52             translated = new File("").getAbsolutePath() + File.separator
     53                          + translated;
     54         }
     55 
     56         File f = new File(translated);
     57 
     58         final File dir;
     59 
     60         if (translated.endsWith(File.separator)) {
     61             dir = f;
     62         } else {
     63             dir = f.getParentFile();
     64         }
     65 
     66         final File[] entries = (dir == null) ? new File[0] : dir.listFiles();
     67 
     68         try {
     69             return matchFiles(buffer, translated, entries, candidates);
     70         } finally {
     71             // we want to output a sorted list of files
     72             sortFileNames(candidates);
     73         }
     74     }
     75 
     76     protected void sortFileNames(final List fileNames) {
     77         Collections.sort(fileNames);
     78     }
     79 
     80     /**
     81      *  Match the specified <i>buffer</i> to the array of <i>entries</i>
     82      *  and enter the matches into the list of <i>candidates</i>. This method
     83      *  can be overridden in a subclass that wants to do more
     84      *  sophisticated file name completion.
     85      *
     86      *  @param        buffer                the untranslated buffer
     87      *  @param        translated        the buffer with common characters replaced
     88      *  @param        entries                the list of files to match
     89      *  @param        candidates        the list of candidates to populate
     90      *
     91      *  @return  the offset of the match
     92      */
     93     public int matchFiles(String buffer, String translated, File[] entries,
     94                           List candidates) {
     95         if (entries == null) {
     96             return -1;
     97         }
     98 
     99         int matches = 0;
    100 
    101         // first pass: just count the matches
    102         for (int i = 0; i < entries.length; i++) {
    103             if (entries[i].getAbsolutePath().startsWith(translated)) {
    104                 matches++;
    105             }
    106         }
    107 
    108         // green - executable
    109         // blue - directory
    110         // red - compressed
    111         // cyan - symlink
    112         for (int i = 0; i < entries.length; i++) {
    113             if (entries[i].getAbsolutePath().startsWith(translated)) {
    114                 String name =
    115                     entries[i].getName()
    116                     + (((matches == 1) && entries[i].isDirectory())
    117                        ? File.separator : " ");
    118 
    119                 /*
    120                 if (entries [i].isDirectory ())
    121                 {
    122                         name = new ANSIBuffer ().blue (name).toString ();
    123                 }
    124                 */
    125                 candidates.add(name);
    126             }
    127         }
    128 
    129         final int index = buffer.lastIndexOf(File.separator);
    130 
    131         return index + File.separator.length();
    132     }
    133 }
    134