View Javadoc

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@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 }