PageRenderTime 31ms CodeModel.GetById 9ms app.highlight 17ms RepoModel.GetById 1ms app.codeStats 0ms

/jEdit/tags/jedit-4-1-pre5/org/gjt/sp/jedit/Abbrevs.java

#
Java | 582 lines | 391 code | 69 blank | 122 comment | 77 complexity | eb96874ef0bc57f6f7b027251b33b153 MD5 | raw file
  1/*
  2 * Abbrevs.java - Abbreviation manager
  3 * :tabSize=8:indentSize=8:noTabs=false:
  4 * :folding=explicit:collapseFolds=1:
  5 *
  6 * Copyright (C) 1999, 2000, 2001 Slava Pestov
  7 *
  8 * This program is free software; you can redistribute it and/or
  9 * modify it under the terms of the GNU General Public License
 10 * as published by the Free Software Foundation; either version 2
 11 * of the License, or any later version.
 12 *
 13 * This program is distributed in the hope that it will be useful,
 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 16 * GNU General Public License for more details.
 17 *
 18 * You should have received a copy of the GNU General Public License
 19 * along with this program; if not, write to the Free Software
 20 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 21 */
 22
 23package org.gjt.sp.jedit;
 24
 25//{{{ Imports
 26import javax.swing.text.Element;
 27import javax.swing.*;
 28import java.io.*;
 29import java.util.*;
 30import org.gjt.sp.jedit.gui.AddAbbrevDialog;
 31import org.gjt.sp.jedit.textarea.*;
 32import org.gjt.sp.util.Log;
 33//}}}
 34
 35/**
 36 * Abbreviation manager.
 37 * @author Slava Pestov
 38 * @version $Id: Abbrevs.java 4292 2002-08-09 21:24:12Z spestov $
 39 */
 40public class Abbrevs
 41{
 42	//{{{ getExpandOnInput() method
 43	/**
 44	 * Returns if abbreviations should be expanded after the
 45	 * user finishes typing a word.
 46	 */
 47	public static boolean getExpandOnInput()
 48	{
 49		return expandOnInput;
 50	} //}}}
 51
 52	//{{{ setExpandOnInput() method
 53	/**
 54	 * Sets if abbreviations should be expanded after the
 55	 * user finishes typing a word.
 56	 * @param true If true, typing a non-alphanumeric characater will
 57	 * automatically attempt to expand the current abbrev
 58	 */
 59	public static void setExpandOnInput(boolean expandOnInput)
 60	{
 61		Abbrevs.expandOnInput = expandOnInput;
 62	} //}}}
 63
 64	//{{{ expandAbbrev() method
 65	/**
 66	 * Expands the abbrev at the caret position in the specified
 67	 * view.
 68	 * @param view The view
 69	 * @param add If true and abbrev not found, will ask user if
 70	 * it should be added
 71	 * @since jEdit 2.6pre4
 72	 */
 73	public static boolean expandAbbrev(View view, boolean add)
 74	{
 75		//{{{ Figure out some minor things
 76		Buffer buffer = view.getBuffer();
 77		JEditTextArea textArea = view.getTextArea();
 78		if(!buffer.isEditable())
 79		{
 80			view.getToolkit().beep();
 81			return false;
 82		}
 83
 84		int line = textArea.getCaretLine();
 85		int lineStart = buffer.getLineStartOffset(line);
 86		int caret = textArea.getCaretPosition();
 87
 88		String lineText = buffer.getLineText(line);
 89		if(lineText.length() == 0)
 90		{
 91			if(add)
 92				view.getToolkit().beep();
 93			return false;
 94		}
 95
 96		int pos = caret - lineStart;
 97		if(pos == 0)
 98		{
 99			if(add)
100				view.getToolkit().beep();
101			return false;
102		} //}}}
103
104		// we reuse the 'pp' vector to save time
105		pp.removeAllElements();
106
107		int wordStart;
108		String abbrev;
109
110		//{{{ Handle abbrevs of the form abbrev#pos1#pos2#pos3#...
111		if(lineText.charAt(pos-1) == '#')
112		{
113			wordStart = lineText.indexOf('#');
114			wordStart = TextUtilities.findWordStart(lineText,wordStart,
115				buffer.getStringProperty("noWordSep") + '#');
116
117			abbrev = lineText.substring(wordStart,pos - 1);
118
119			// positional parameters will be inserted where $1, $2, $3, ...
120			// occurs in the expansion
121
122			int lastIndex = 0;
123			for(int i = 0; i < abbrev.length(); i++)
124			{
125				if(abbrev.charAt(i) == '#')
126				{
127					pp.addElement(abbrev.substring(lastIndex,i));
128					lastIndex = i + 1;
129				}
130			}
131
132			pp.addElement(abbrev.substring(lastIndex));
133
134			// the first element of pp is the abbrev itself
135			abbrev = (String)pp.elementAt(0);
136			pp.removeElementAt(0);
137		} //}}}
138		//{{{ Handle ordinary abbrevs
139		else
140		{
141			wordStart = TextUtilities.findWordStart(lineText,pos - 1,
142				buffer.getStringProperty("noWordSep"));
143
144			abbrev = lineText.substring(wordStart,pos);
145		} //}}}
146
147		Expansion expand = expandAbbrev(buffer.getMode().getName(),
148			abbrev,(buffer.getBooleanProperty("noTabs") ?
149			buffer.getTabSize() : 0),pp);
150
151		//{{{ Maybe show add abbrev dialog
152		if(expand == null)
153		{
154			if(add)
155				new AddAbbrevDialog(view,abbrev);
156
157			return false;
158		} //}}}
159		//{{{ Insert the expansion
160		else
161		{
162			buffer.beginCompoundEdit();
163			try
164			{
165				// obtain the leading indent for later use
166				lineText = buffer.getText(lineStart,wordStart);
167				int leadingIndent = MiscUtilities.getLeadingWhiteSpaceWidth(
168					lineText,buffer.getTabSize());
169
170				buffer.remove(lineStart + wordStart,pos - wordStart);
171				buffer.insert(lineStart + wordStart,expand.text);
172				if(expand.caretPosition != -1)
173				{
174					textArea.setCaretPosition(lineStart + wordStart
175						+ expand.caretPosition);
176				}
177
178				String whiteSpace = MiscUtilities.createWhiteSpace(
179					leadingIndent,buffer.getBooleanProperty("noTabs")
180					? 0 : buffer.getTabSize());
181
182				// note that if expand.lineCount is 0, we
183				// don't do any indentation at all
184				for(int i = line + 1; i <= line + expand.lineCount; i++)
185				{
186					buffer.insert(buffer.getLineStartOffset(i),
187						whiteSpace);
188				}
189			}
190			finally
191			{
192				buffer.endCompoundEdit();
193			}
194
195			if(expand.posParamCount != pp.size())
196			{
197				view.getStatus().setMessageAndClear(
198					jEdit.getProperty(
199					"view.status.incomplete-abbrev",
200					new Integer[] { new Integer(pp.size()),
201					new Integer(expand.posParamCount) }));
202			}
203
204			return true;
205		} //}}}
206	} //}}}
207
208	//{{{ getGlobalAbbrevs() method
209	/**
210	 * Returns the global abbreviation set.
211	 * @since jEdit 2.3pre1
212	 */
213	public static Hashtable getGlobalAbbrevs()
214	{
215		if(!loaded)
216			load();
217
218		return globalAbbrevs;
219	} //}}}
220
221	//{{{ setGlobalAbbrevs() method
222	/**
223	 * Sets the global abbreviation set.
224	 * @param globalAbbrevs The new global abbrev set
225	 * @since jEdit 2.3pre1
226	 */
227	public static void setGlobalAbbrevs(Hashtable globalAbbrevs)
228	{
229		abbrevsChanged = true;
230		Abbrevs.globalAbbrevs = globalAbbrevs;
231	} //}}}
232
233	//{{{ getModeAbbrevs() method
234	/**
235	 * Returns the mode-specific abbreviation set.
236	 * @since jEdit 2.3pre1
237	 */
238	public static Hashtable getModeAbbrevs()
239	{
240		if(!loaded)
241			load();
242
243		return modes;
244	} //}}}
245
246	//{{{ setModeAbbrevs() method
247	/**
248	 * Sets the mode-specific abbreviation set.
249	 * @param globalAbbrevs The new global abbrev set
250	 * @since jEdit 2.3pre1
251	 */
252	public static void setModeAbbrevs(Hashtable modes)
253	{
254		abbrevsChanged = true;
255		Abbrevs.modes = modes;
256	} //}}}
257
258	//{{{ addGlobalAbbrev() method
259	/**
260	 * Adds an abbreviation to the global abbreviation list.
261	 * @param abbrev The abbreviation
262	 * @param expansion The expansion
263	 * @since jEdit 3.1pre1
264	 */
265	public static void addGlobalAbbrev(String abbrev, String expansion)
266	{
267		if(!loaded)
268			load();
269
270		globalAbbrevs.put(abbrev,expansion);
271		abbrevsChanged = true;
272	} //}}}
273
274	//{{{ addModeAbbrev() method
275	/**
276	 * Adds a mode-specific abbrev.
277	 * @param mode The edit mode
278	 * @param abbrev The abbrev
279	 * @param expansion The expansion
280	 * @since jEdit 3.1pre1
281	 */
282	public static void addModeAbbrev(String mode, String abbrev, String expansion)
283	{
284		if(!loaded)
285			load();
286
287		Hashtable modeAbbrevs = (Hashtable)modes.get(mode);
288		if(modeAbbrevs == null)
289		{
290			modeAbbrevs = new Hashtable();
291			modes.put(mode,modeAbbrevs);
292		}
293		modeAbbrevs.put(abbrev,expansion);
294		abbrevsChanged = true;
295	} //}}}
296
297	//{{{ save() method
298	static void save()
299	{
300		jEdit.setBooleanProperty("view.expandOnInput",expandOnInput);
301
302		String settings = jEdit.getSettingsDirectory();
303		if(abbrevsChanged && settings != null)
304		{
305			File file1 = new File(MiscUtilities.constructPath(settings,"#abbrevs#save#"));
306			File file2 = new File(MiscUtilities.constructPath(settings,"abbrevs"));
307			if(file2.exists() && file2.lastModified() != abbrevsModTime)
308			{
309				Log.log(Log.WARNING,Abbrevs.class,file2 + " changed on disk;"
310					+ " will not save abbrevs");
311			}
312			else
313			{
314				jEdit.backupSettingsFile(file2);
315
316				try
317				{
318					saveAbbrevs(new FileWriter(file1));
319				}
320				catch(Exception e)
321				{
322					Log.log(Log.ERROR,Abbrevs.class,"Error while saving " + file1);
323					Log.log(Log.ERROR,Abbrevs.class,e);
324				}
325				file2.delete();
326				file1.renameTo(file2);
327				abbrevsModTime = file2.lastModified();
328			}
329		}
330	} //}}}
331
332	//{{{ Private members
333
334	//{{{ Instance variables
335	private static boolean loaded;
336	private static boolean abbrevsChanged;
337	private static long abbrevsModTime;
338	private static boolean expandOnInput;
339	private static Hashtable globalAbbrevs;
340	private static Hashtable modes;
341	private static Vector pp = new Vector();
342	//}}}
343
344	private Abbrevs() {}
345
346	static
347	{
348		expandOnInput = jEdit.getBooleanProperty("view.expandOnInput");
349	}
350
351	//{{{ load() method
352	private static void load()
353	{
354		globalAbbrevs = new Hashtable();
355		modes = new Hashtable();
356
357		String settings = jEdit.getSettingsDirectory();
358		if(settings != null)
359		{
360			File file = new File(MiscUtilities.constructPath(settings,"abbrevs"));
361			abbrevsModTime = file.lastModified();
362
363			try
364			{
365				loadAbbrevs(new FileReader(file));
366				loaded = true;
367			}
368			catch(FileNotFoundException fnf)
369			{
370			}
371			catch(Exception e)
372			{
373				Log.log(Log.ERROR,Abbrevs.class,"Error while loading " + file);
374				Log.log(Log.ERROR,Abbrevs.class,e);
375			}
376		}
377
378		// only load global abbrevs if user abbrevs file could not be loaded
379		if(!loaded)
380		{
381			try
382			{
383				loadAbbrevs(new InputStreamReader(Abbrevs.class
384					.getResourceAsStream("default.abbrevs")));
385			}
386			catch(Exception e)
387			{
388				Log.log(Log.ERROR,Abbrevs.class,"Error while loading default.abbrevs");
389				Log.log(Log.ERROR,Abbrevs.class,e);
390			}
391			loaded = true;
392		}
393	} //}}}
394
395	//{{{ expandAbbrev() method
396	private static Expansion expandAbbrev(String mode, String abbrev,
397		int softTabSize, Vector pp)
398	{
399		if(!loaded)
400			load();
401
402		// try mode-specific abbrevs first
403		String expand = null;
404		Hashtable modeAbbrevs = (Hashtable)modes.get(mode);
405		if(modeAbbrevs != null)
406			expand = (String)modeAbbrevs.get(abbrev);
407
408		if(expand == null)
409			expand = (String)globalAbbrevs.get(abbrev);
410
411		if(expand == null)
412			return null;
413		else
414			return new Expansion(expand,softTabSize,pp);
415	} //}}}
416
417	//{{{ loadAbbrevs() method
418	private static void loadAbbrevs(Reader _in) throws Exception
419	{
420		BufferedReader in = new BufferedReader(_in);
421
422		Hashtable currentAbbrevs = null;
423
424		String line;
425		while((line = in.readLine()) != null)
426		{
427			if(line.length() == 0)
428				continue;
429			else if(line.startsWith("[") && line.indexOf('|') == -1)
430			{
431				if(line.equals("[global]"))
432					currentAbbrevs = globalAbbrevs;
433				else
434				{
435					String mode = line.substring(1,
436						line.length() - 1);
437					currentAbbrevs = (Hashtable)modes.get(mode);
438					if(currentAbbrevs == null)
439					{
440						currentAbbrevs = new Hashtable();
441						modes.put(mode,currentAbbrevs);
442					}
443				}
444			}
445			else
446			{
447				int index = line.indexOf('|');
448				currentAbbrevs.put(line.substring(0,index),
449					line.substring(index + 1));
450			}
451		}
452
453		in.close();
454	} //}}}
455
456	//{{{ saveAbbrevs() method
457	private static void saveAbbrevs(Writer _out) throws Exception
458	{
459		BufferedWriter out = new BufferedWriter(_out);
460		String lineSep = System.getProperty("line.separator");
461
462		// write global abbrevs
463		out.write("[global]");
464		out.write(lineSep);
465
466		saveAbbrevs(out,globalAbbrevs);
467
468		// write mode abbrevs
469		Enumeration keys = modes.keys();
470		Enumeration values = modes.elements();
471		while(keys.hasMoreElements())
472		{
473			out.write('[');
474			out.write((String)keys.nextElement());
475			out.write(']');
476			out.write(lineSep);
477			saveAbbrevs(out,(Hashtable)values.nextElement());
478		}
479
480		out.close();
481	} //}}}
482
483	//{{{ saveAbbrevs() method
484	private static void saveAbbrevs(Writer out, Hashtable abbrevs)
485		throws Exception
486	{
487		String lineSep = System.getProperty("line.separator");
488
489		Enumeration keys = abbrevs.keys();
490		Enumeration values = abbrevs.elements();
491		while(keys.hasMoreElements())
492		{
493			String abbrev = (String)keys.nextElement();
494			out.write(abbrev);
495			out.write('|');
496			out.write(values.nextElement().toString());
497			out.write(lineSep);
498		}
499	} //}}}
500
501	//}}}
502
503	//{{{ Expansion class
504	static class Expansion
505	{
506		String text;
507		int caretPosition = -1;
508		int lineCount;
509
510		// number of positional parameters in abbreviation expansion
511		int posParamCount;
512
513		//{{{ Expansion constructor
514		Expansion(String text, int softTabSize, Vector pp)
515		{
516			StringBuffer buf = new StringBuffer();
517			boolean backslash = false;
518
519			for(int i = 0; i < text.length(); i++)
520			{
521				char ch = text.charAt(i);
522				//{{{ Handle backslash
523				if(backslash)
524				{
525					backslash = false;
526
527					if(ch == '|')
528						caretPosition = buf.length();
529					else if(ch == 'n')
530					{
531						buf.append('\n');
532						lineCount++;
533					}
534					else if(ch == 't')
535					{
536						if(softTabSize == 0)
537							buf.append('\t');
538						else
539						{
540							for(int j = 0; j < softTabSize; j++)
541								buf.append(' ');
542						}
543					}
544					else
545						buf.append(ch);
546				}
547				else if(ch == '\\')
548					backslash = true;
549				//}}}
550				//{{{ Handle $
551				else if(ch == '$')
552				{
553					if(i != text.length() - 1)
554					{
555						ch = text.charAt(i + 1);
556						if(Character.isDigit(ch) && ch != '0')
557						{
558							i++;
559
560							int pos = ch - '0';
561							posParamCount = Math.max(pos,posParamCount);
562							// $n is 1-indexed, but vector
563							// contents is zero indexed
564							if(pos <= pp.size())
565								buf.append(pp.elementAt(pos - 1));
566						}
567						else
568						{
569							// $key will be $key, for
570							// example
571							buf.append('$');
572						}
573					}
574				} //}}}
575				else
576					buf.append(ch);
577			}
578
579			this.text = buf.toString();
580		} //}}}
581	} //}}}
582}