PageRenderTime 101ms CodeModel.GetById 44ms app.highlight 50ms RepoModel.GetById 1ms app.codeStats 0ms

/jEdit/tags/jedit-4-2-pre14/org/gjt/sp/jedit/Abbrevs.java

#
Java | 591 lines | 399 code | 72 blank | 120 comment | 82 complexity | 8cfe278ab1b9224b2ac8a3dccaebed55 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, 2004 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 java.io.*;
 27import java.util.*;
 28import org.gjt.sp.jedit.gui.AddAbbrevDialog;
 29import org.gjt.sp.jedit.textarea.*;
 30import org.gjt.sp.util.Log;
 31//}}}
 32
 33/**
 34 * Abbreviation manager.
 35 * @author Slava Pestov
 36 * @version $Id: Abbrevs.java 5053 2004-05-29 01:55:26Z spestov $
 37 */
 38public class Abbrevs
 39{
 40	public static final String ENCODING = "UTF8";
 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 expandOnInput If true, typing a non-alphanumeric character
 57	 * will 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.remove(lineStart + wordStart,
163				pos - wordStart);
164
165			int whitespace = buffer.insertIndented(
166				lineStart + wordStart,
167				expand.text);
168
169			int newlines = countNewlines(expand.text,
170				expand.caretPosition);
171
172			if(expand.caretPosition != -1)
173			{
174				textArea.setCaretPosition(lineStart + wordStart
175					+ expand.caretPosition
176					+ newlines * whitespace);
177			}
178			if(expand.posParamCount != pp.size())
179			{
180				view.getStatus().setMessageAndClear(
181					jEdit.getProperty(
182					"view.status.incomplete-abbrev",
183					new Integer[] { new Integer(pp.size()),
184					new Integer(expand.posParamCount) }));
185			}
186
187			return true;
188		} //}}}
189	} //}}}
190
191	//{{{ getGlobalAbbrevs() method
192	/**
193	 * Returns the global abbreviation set.
194	 * @since jEdit 2.3pre1
195	 */
196	public static Hashtable getGlobalAbbrevs()
197	{
198		if(!loaded)
199			load();
200
201		return globalAbbrevs;
202	} //}}}
203
204	//{{{ setGlobalAbbrevs() method
205	/**
206	 * Sets the global abbreviation set.
207	 * @param globalAbbrevs The new global abbrev set
208	 * @since jEdit 2.3pre1
209	 */
210	public static void setGlobalAbbrevs(Hashtable globalAbbrevs)
211	{
212		abbrevsChanged = true;
213		Abbrevs.globalAbbrevs = globalAbbrevs;
214	} //}}}
215
216	//{{{ getModeAbbrevs() method
217	/**
218	 * Returns the mode-specific abbreviation set.
219	 * @since jEdit 2.3pre1
220	 */
221	public static Hashtable getModeAbbrevs()
222	{
223		if(!loaded)
224			load();
225
226		return modes;
227	} //}}}
228
229	//{{{ setModeAbbrevs() method
230	/**
231	 * Sets the mode-specific abbreviation set.
232	 * @param modes The new mode abbrev set
233	 * @since jEdit 2.3pre1
234	 */
235	public static void setModeAbbrevs(Hashtable modes)
236	{
237		abbrevsChanged = true;
238		Abbrevs.modes = modes;
239	} //}}}
240
241	//{{{ addGlobalAbbrev() method
242	/**
243	 * Adds an abbreviation to the global abbreviation list.
244	 * @param abbrev The abbreviation
245	 * @param expansion The expansion
246	 * @since jEdit 3.1pre1
247	 */
248	public static void addGlobalAbbrev(String abbrev, String expansion)
249	{
250		if(!loaded)
251			load();
252
253		globalAbbrevs.put(abbrev,expansion);
254		abbrevsChanged = true;
255	} //}}}
256
257	//{{{ addModeAbbrev() method
258	/**
259	 * Adds a mode-specific abbrev.
260	 * @param mode The edit mode
261	 * @param abbrev The abbrev
262	 * @param expansion The expansion
263	 * @since jEdit 3.1pre1
264	 */
265	public static void addModeAbbrev(String mode, String abbrev, String expansion)
266	{
267		if(!loaded)
268			load();
269
270		Hashtable modeAbbrevs = (Hashtable)modes.get(mode);
271		if(modeAbbrevs == null)
272		{
273			modeAbbrevs = new Hashtable();
274			modes.put(mode,modeAbbrevs);
275		}
276		modeAbbrevs.put(abbrev,expansion);
277		abbrevsChanged = true;
278	} //}}}
279
280	//{{{ save() method
281	static void save()
282	{
283		jEdit.setBooleanProperty("view.expandOnInput",expandOnInput);
284
285		String settings = jEdit.getSettingsDirectory();
286		if(abbrevsChanged && settings != null)
287		{
288			File file1 = new File(MiscUtilities.constructPath(settings,"#abbrevs#save#"));
289			File file2 = new File(MiscUtilities.constructPath(settings,"abbrevs"));
290			if(file2.exists() && file2.lastModified() != abbrevsModTime)
291			{
292				Log.log(Log.WARNING,Abbrevs.class,file2 + " changed on disk;"
293					+ " will not save abbrevs");
294			}
295			else
296			{
297				jEdit.backupSettingsFile(file2);
298
299				try
300				{
301					saveAbbrevs(new OutputStreamWriter(
302						new FileOutputStream(file1),
303						ENCODING));
304					file2.delete();
305					file1.renameTo(file2);
306				}
307				catch(Exception e)
308				{
309					Log.log(Log.ERROR,Abbrevs.class,"Error while saving " + file1);
310					Log.log(Log.ERROR,Abbrevs.class,e);
311				}
312				abbrevsModTime = file2.lastModified();
313			}
314		}
315	} //}}}
316
317	//{{{ Private members
318
319	//{{{ Instance variables
320	private static boolean loaded;
321	private static boolean abbrevsChanged;
322	private static long abbrevsModTime;
323	private static boolean expandOnInput;
324	private static Hashtable globalAbbrevs;
325	private static Hashtable modes;
326	private static Vector pp = new Vector();
327	//}}}
328
329	private Abbrevs() {}
330
331	static
332	{
333		expandOnInput = jEdit.getBooleanProperty("view.expandOnInput");
334	}
335
336	//{{{ load() method
337	private static void load()
338	{
339		globalAbbrevs = new Hashtable();
340		modes = new Hashtable();
341
342		String settings = jEdit.getSettingsDirectory();
343		if(settings != null)
344		{
345			File file = new File(MiscUtilities.constructPath(settings,"abbrevs"));
346			abbrevsModTime = file.lastModified();
347
348			try
349			{
350				loadAbbrevs(new InputStreamReader(
351					new FileInputStream(file),ENCODING));
352				loaded = true;
353			}
354			catch(FileNotFoundException fnf)
355			{
356			}
357			catch(Exception e)
358			{
359				Log.log(Log.ERROR,Abbrevs.class,"Error while loading " + file);
360				Log.log(Log.ERROR,Abbrevs.class,e);
361			}
362		}
363
364		// only load global abbrevs if user abbrevs file could not be loaded
365		if(!loaded)
366		{
367			try
368			{
369				loadAbbrevs(new InputStreamReader(Abbrevs.class
370					.getResourceAsStream("default.abbrevs"),
371					ENCODING));
372			}
373			catch(Exception e)
374			{
375				Log.log(Log.ERROR,Abbrevs.class,"Error while loading default.abbrevs");
376				Log.log(Log.ERROR,Abbrevs.class,e);
377			}
378			loaded = true;
379		}
380	} //}}}
381
382	//{{{ countNewlines() method
383	private static int countNewlines(String s, int end)
384	{
385		int counter = 0;
386
387		for(int i = 0; i < end; i++)
388		{
389			if(s.charAt(i) == '\n')
390				counter++;
391		}
392
393		return counter;
394	} //}}}
395
396	//{{{ expandAbbrev() method
397	private static Expansion expandAbbrev(String mode, String abbrev,
398		int softTabSize, Vector pp)
399	{
400		if(!loaded)
401			load();
402
403		// try mode-specific abbrevs first
404		String expand = null;
405		Hashtable modeAbbrevs = (Hashtable)modes.get(mode);
406		if(modeAbbrevs != null)
407			expand = (String)modeAbbrevs.get(abbrev);
408
409		if(expand == null)
410			expand = (String)globalAbbrevs.get(abbrev);
411
412		if(expand == null)
413			return null;
414		else
415			return new Expansion(expand,softTabSize,pp);
416	} //}}}
417
418	//{{{ loadAbbrevs() method
419	private static void loadAbbrevs(Reader _in) throws Exception
420	{
421		BufferedReader in = new BufferedReader(_in);
422
423		try
424		{
425			Hashtable currentAbbrevs = null;
426
427			String line;
428			while((line = in.readLine()) != null)
429			{
430				int index = line.indexOf('|');
431
432				if(line.length() == 0)
433					continue;
434				else if(line.startsWith("[") && index == -1)
435				{
436					if(line.equals("[global]"))
437						currentAbbrevs = globalAbbrevs;
438					else
439					{
440						String mode = line.substring(1,
441							line.length() - 1);
442						currentAbbrevs = (Hashtable)modes.get(mode);
443						if(currentAbbrevs == null)
444						{
445							currentAbbrevs = new Hashtable();
446							modes.put(mode,currentAbbrevs);
447						}
448					}
449				}
450				else if(index != -1)
451				{
452					currentAbbrevs.put(line.substring(0,index),
453						line.substring(index + 1));
454				}
455			}
456		}
457		finally
458		{
459			in.close();
460		}
461	} //}}}
462
463	//{{{ saveAbbrevs() method
464	private static void saveAbbrevs(Writer _out) throws Exception
465	{
466		BufferedWriter out = new BufferedWriter(_out);
467		String lineSep = System.getProperty("line.separator");
468
469		// write global abbrevs
470		out.write("[global]");
471		out.write(lineSep);
472
473		saveAbbrevs(out,globalAbbrevs);
474
475		// write mode abbrevs
476		Enumeration keys = modes.keys();
477		Enumeration values = modes.elements();
478		while(keys.hasMoreElements())
479		{
480			out.write('[');
481			out.write((String)keys.nextElement());
482			out.write(']');
483			out.write(lineSep);
484			saveAbbrevs(out,(Hashtable)values.nextElement());
485		}
486
487		out.close();
488	} //}}}
489
490	//{{{ saveAbbrevs() method
491	private static void saveAbbrevs(Writer out, Hashtable abbrevs)
492		throws Exception
493	{
494		String lineSep = System.getProperty("line.separator");
495
496		Enumeration keys = abbrevs.keys();
497		Enumeration values = abbrevs.elements();
498		while(keys.hasMoreElements())
499		{
500			String abbrev = (String)keys.nextElement();
501			out.write(abbrev);
502			out.write('|');
503			out.write(values.nextElement().toString());
504			out.write(lineSep);
505		}
506	} //}}}
507
508	//}}}
509
510	//{{{ Expansion class
511	static class Expansion
512	{
513		String text;
514		int caretPosition = -1;
515		int lineCount;
516
517		// number of positional parameters in abbreviation expansion
518		int posParamCount;
519
520		//{{{ Expansion constructor
521		Expansion(String text, int softTabSize, Vector pp)
522		{
523			StringBuffer buf = new StringBuffer();
524			boolean backslash = false;
525
526			for(int i = 0; i < text.length(); i++)
527			{
528				char ch = text.charAt(i);
529				//{{{ Handle backslash
530				if(backslash)
531				{
532					backslash = false;
533
534					if(ch == '|')
535						caretPosition = buf.length();
536					else if(ch == 'n')
537					{
538						buf.append('\n');
539						lineCount++;
540					}
541					else if(ch == 't')
542					{
543						if(softTabSize == 0)
544							buf.append('\t');
545						else
546						{
547							for(int j = 0; j < softTabSize; j++)
548								buf.append(' ');
549						}
550					}
551					else
552						buf.append(ch);
553				}
554				else if(ch == '\\')
555					backslash = true;
556				//}}}
557				//{{{ Handle $
558				else if(ch == '$')
559				{
560					if(i != text.length() - 1)
561					{
562						ch = text.charAt(i + 1);
563						if(Character.isDigit(ch) && ch != '0')
564						{
565							i++;
566
567							int pos = ch - '0';
568							posParamCount = Math.max(pos,posParamCount);
569							// $n is 1-indexed, but vector
570							// contents is zero indexed
571							if(pos <= pp.size())
572								buf.append(pp.elementAt(pos - 1));
573						}
574						else
575						{
576							// $key will be $key, for
577							// example
578							buf.append('$');
579						}
580					}
581					else
582						buf.append('$'); // $ at end is literal
583				} //}}}
584				else
585					buf.append(ch);
586			}
587
588			this.text = buf.toString();
589		} //}}}
590	} //}}}
591}