/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
Possible License(s): BSD-3-Clause, AGPL-1.0, Apache-2.0, LGPL-2.0, LGPL-3.0, GPL-2.0, CC-BY-SA-3.0, LGPL-2.1, GPL-3.0, MPL-2.0-no-copyleft-exception, IPL-1.0
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}