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