001 /** 002 * jline - Java console input library 003 * Copyright (c) 2002,2003 Marc Prud'hommeaux mwp1@cornell.edu 004 * 005 * This library is free software; you can redistribute it and/or 006 * modify it under the terms of the GNU Lesser General Public 007 * License as published by the Free Software Foundation; either 008 * version 2.1 of the License, or (at your option) any later version. 009 * 010 * This library is distributed in the hope that it will be useful, 011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 013 * Lesser General Public License for more details. 014 * 015 * You should have received a copy of the GNU Lesser General Public 016 * License along with this library; if not, write to the Free Software 017 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 018 */ 019 package jline; 020 021 import java.io.*; 022 import java.util.*; 023 024 025 /** 026 * A file name completor takes the buffer and issues a list of 027 * potential completions. 028 * 029 * <p> 030 * This completor tries to behave as similar as possible to 031 * <i>bash</i>'s file name completion (using GNU readline) 032 * with the following exceptions: 033 * 034 * <ul> 035 * <li>Candidates that are directories will end with "/"</li> 036 * <li>Wildcard regular expressions are not evaluated or replaced</li> 037 * <li>The "~" character can be used to represent the user's home, 038 * but it cannot complete to other users' homes, since java does 039 * not provide any way of determining that easily</li> 040 * </ul> 041 * 042 * <p>TODO</p> 043 * <ul> 044 * <li>Handle files with spaces in them</li> 045 * <li>Have an option for file type color highlighting</li> 046 * </ul> 047 * 048 * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a> 049 */ 050 public class FileNameCompletor 051 implements Completor 052 { 053 public int complete (final String buf, final int cursor, 054 final List candidates) 055 { 056 String buffer = buf == null ? "" : buf; 057 058 String translated = buffer; 059 060 // special character: ~ maps to the user's home directory 061 if (translated.startsWith ("~" + File.separator)) 062 { 063 translated = System.getProperty ("user.home") 064 + translated.substring (1); 065 } 066 else if (translated.startsWith ("~")) 067 { 068 translated = new File (System.getProperty ("user.home")) 069 .getParentFile ().getAbsolutePath (); 070 } 071 else if (!(translated.startsWith (File.separator))) 072 { 073 translated = new File ("").getAbsolutePath () 074 + File.separator + translated; 075 } 076 077 File f = new File (translated); 078 079 final File dir; 080 081 if (translated.endsWith (File.separator)) 082 dir = f; 083 else 084 dir = f.getParentFile (); 085 086 final File [] entries = dir == null ? new File [0] : dir.listFiles (); 087 088 try 089 { 090 return matchFiles (buffer, translated, entries, candidates); 091 } 092 finally 093 { 094 // we want to output a sorted list of files 095 sortFileNames (candidates); 096 } 097 } 098 099 100 protected void sortFileNames (final List fileNames) 101 { 102 Collections.sort (fileNames); 103 } 104 105 106 /** 107 * Match the specified <i>buffer</i> to the array of <i>entries</i> 108 * and enter the matches into the list of <i>candidates</i>. This method 109 * can be overridden in a subclass that wants to do more 110 * sophisticated file name completion. 111 * 112 * @param buffer the untranslated buffer 113 * @param translated the buffer with common characters replaced 114 * @param entries the list of files to match 115 * @param candidates the list of candidates to populate 116 * 117 * @return the offset of the match 118 */ 119 public int matchFiles (String buffer, String translated, 120 File [] entries, List candidates) 121 { 122 if (entries == null) 123 return -1; 124 125 int matches = 0; 126 127 // first pass: just count the matches 128 for (int i = 0; i < entries.length; i++) 129 { 130 if (entries [i].getAbsolutePath ().startsWith (translated)) 131 { 132 matches++; 133 } 134 } 135 136 // green - executable 137 // blue - directory 138 // red - compressed 139 // cyan - symlink 140 for (int i = 0; i < entries.length; i++) 141 { 142 if (entries [i].getAbsolutePath ().startsWith (translated)) 143 { 144 String name = entries [i].getName () 145 + (matches == 1 && entries [i].isDirectory () 146 ? File.separator : " "); 147 148 /* 149 if (entries [i].isDirectory ()) 150 { 151 name = new ANSIBuffer ().blue (name).toString (); 152 } 153 */ 154 155 candidates.add (name); 156 } 157 } 158 159 160 final int index = buffer.lastIndexOf (File.separator); 161 return index + File.separator.length (); 162 } 163 } 164