PageRenderTime 116ms CodeModel.GetById 40ms app.highlight 38ms RepoModel.GetById 18ms app.codeStats 1ms

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

#
Java | 594 lines | 400 code | 73 blank | 121 comment | 82 complexity | 48c3cb77af08c2a767f47f4bcc1ccff7 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 5337 2006-01-23 23:04:25Z ezust $
 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		m_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					m_pp.addElement(abbrev.substring(lastIndex,i));
128					lastIndex = i + 1;
129				}
130			}
131
132			m_pp.addElement(abbrev.substring(lastIndex));
133
134			// the first element of pp is the abbrev itself
135			abbrev = (String)m_pp.elementAt(0);
136			m_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),m_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 != m_pp.size())
179			{
180				view.getStatus().setMessageAndClear(
181					jEdit.getProperty(
182					"view.status.incomplete-abbrev",
183					new Integer[] { new Integer(m_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	
327	/**  Vector of Positional Parameters */
328	private static Vector m_pp = new Vector();
329	//}}}
330
331	private Abbrevs() {}
332
333	static
334	{
335		expandOnInput = jEdit.getBooleanProperty("view.expandOnInput");
336	}
337
338	//{{{ load() method
339	private static void load()
340	{
341		globalAbbrevs = new Hashtable();
342		modes = new Hashtable();
343
344		String settings = jEdit.getSettingsDirectory();
345		if(settings != null)
346		{
347			File file = new File(MiscUtilities.constructPath(settings,"abbrevs"));
348			abbrevsModTime = file.lastModified();
349
350			try
351			{
352				loadAbbrevs(new InputStreamReader(
353					new FileInputStream(file),ENCODING));
354				loaded = true;
355			}
356			catch(FileNotFoundException fnf)
357			{
358			}
359			catch(Exception e)
360			{
361				Log.log(Log.ERROR,Abbrevs.class,"Error while loading " + file);
362				Log.log(Log.ERROR,Abbrevs.class,e);
363			}
364		}
365
366		// only load global abbrevs if user abbrevs file could not be loaded
367		if(!loaded)
368		{
369			try
370			{
371				loadAbbrevs(new InputStreamReader(Abbrevs.class
372					.getResourceAsStream("default.abbrevs"),
373					ENCODING));
374			}
375			catch(Exception e)
376			{
377				Log.log(Log.ERROR,Abbrevs.class,"Error while loading default.abbrevs");
378				Log.log(Log.ERROR,Abbrevs.class,e);
379			}
380			loaded = true;
381		}
382	} //}}}
383
384	//{{{ countNewlines() method
385	private static int countNewlines(String s, int end)
386	{
387		int counter = 0;
388
389		for(int i = 0; i < end; i++)
390		{
391			if(s.charAt(i) == '\n')
392				counter++;
393		}
394
395		return counter;
396	} //}}}
397
398	//{{{ expandAbbrev() method
399	private static Expansion expandAbbrev(String mode, String abbrev,
400		int softTabSize, Vector pp)
401	{
402		m_pp = pp;
403		if(!loaded)
404			load();
405
406		// try mode-specific abbrevs first
407		String expand = null;
408		Hashtable modeAbbrevs = (Hashtable)modes.get(mode);
409		if(modeAbbrevs != null)
410			expand = (String)modeAbbrevs.get(abbrev);
411
412		if(expand == null)
413			expand = (String)globalAbbrevs.get(abbrev);
414
415		if(expand == null)
416			return null;
417		else
418			return new Expansion(expand,softTabSize,m_pp);
419	} //}}}
420
421	//{{{ loadAbbrevs() method
422	private static void loadAbbrevs(Reader _in) throws Exception
423	{
424		BufferedReader in = new BufferedReader(_in);
425
426		try
427		{
428			Hashtable currentAbbrevs = globalAbbrevs;
429
430			String line;
431			while((line = in.readLine()) != null)
432			{
433				int index = line.indexOf('|');
434
435				if(line.length() == 0)
436					continue;
437				else if(line.startsWith("[") && index == -1)
438				{
439					if(line.equals("[global]"))
440						currentAbbrevs = globalAbbrevs;
441					else
442					{
443						String mode = line.substring(1,
444							line.length() - 1);
445						currentAbbrevs = (Hashtable)modes.get(mode);
446						if(currentAbbrevs == null)
447						{
448							currentAbbrevs = new Hashtable();
449							modes.put(mode,currentAbbrevs);
450						}
451					}
452				}
453				else if(index != -1)
454				{
455					currentAbbrevs.put(line.substring(0,index),
456						line.substring(index + 1));
457				}
458			}
459		}
460		finally
461		{
462			in.close();
463		}
464	} //}}}
465
466	//{{{ saveAbbrevs() method
467	private static void saveAbbrevs(Writer _out) throws Exception
468	{
469		BufferedWriter out = new BufferedWriter(_out);
470		String lineSep = System.getProperty("line.separator");
471
472		// write global abbrevs
473		out.write("[global]");
474		out.write(lineSep);
475
476		saveAbbrevs(out,globalAbbrevs);
477
478		// write mode abbrevs
479		Enumeration keys = modes.keys();
480		Enumeration values = modes.elements();
481		while(keys.hasMoreElements())
482		{
483			out.write('[');
484			out.write((String)keys.nextElement());
485			out.write(']');
486			out.write(lineSep);
487			saveAbbrevs(out,(Hashtable)values.nextElement());
488		}
489
490		out.close();
491	} //}}}
492
493	//{{{ saveAbbrevs() method
494	private static void saveAbbrevs(Writer out, Hashtable abbrevs)
495		throws Exception
496	{
497		String lineSep = System.getProperty("line.separator");
498
499		Enumeration keys = abbrevs.keys();
500		Enumeration values = abbrevs.elements();
501		while(keys.hasMoreElements())
502		{
503			String abbrev = (String)keys.nextElement();
504			out.write(abbrev);
505			out.write('|');
506			out.write(values.nextElement().toString());
507			out.write(lineSep);
508		}
509	} //}}}
510
511	//}}}
512
513	//{{{ Expansion class
514	static class Expansion
515	{
516		String text;
517		int caretPosition = -1;
518		int lineCount;
519
520		// number of positional parameters in abbreviation expansion
521		int posParamCount;
522
523		//{{{ Expansion constructor
524		Expansion(String text, int softTabSize, Vector pp)
525		{
526			StringBuffer buf = new StringBuffer();
527			boolean backslash = false;
528
529			for(int i = 0; i < text.length(); i++)
530			{
531				char ch = text.charAt(i);
532				//{{{ Handle backslash
533				if(backslash)
534				{
535					backslash = false;
536
537					if(ch == '|')
538						caretPosition = buf.length();
539					else if(ch == 'n')
540					{
541						buf.append('\n');
542						lineCount++;
543					}
544					else if(ch == 't')
545					{
546						if(softTabSize == 0)
547							buf.append('\t');
548						else
549						{
550							for(int j = 0; j < softTabSize; j++)
551								buf.append(' ');
552						}
553					}
554					else
555						buf.append(ch);
556				}
557				else if(ch == '\\')
558					backslash = true;
559				//}}}
560				//{{{ Handle $
561				else if(ch == '$')
562				{
563					if(i != text.length() - 1)
564					{
565						ch = text.charAt(i + 1);
566						if(Character.isDigit(ch) && ch != '0')
567						{
568							i++;
569
570							int pos = ch - '0';
571							posParamCount = Math.max(pos,posParamCount);
572							// $n is 1-indexed, but vector
573							// contents is zero indexed
574							if(pos <= pp.size())
575								buf.append(pp.elementAt(pos - 1));
576						}
577						else
578						{
579							// $key will be $key, for
580							// example
581							buf.append('$');
582						}
583					}
584					else
585						buf.append('$'); // $ at end is literal
586				} //}}}
587				else
588					buf.append(ch);
589			}
590
591			this.text = buf.toString();
592		} //}}}
593	} //}}}
594}