Source code: com/memoire/bu/BuAutoStyledDocument.java
1 /**
2 * @modification $Date: 2002/12/16 18:56:24 $
3 * @statut unstable
4 * @file BuAutoStyledDocument.java
5 * @version 0.36
6 * @author Guillaume Desnoix
7 * @email guillaume@desnoix.com
8 * @license GNU General Public License 2 (GPL2)
9 * @copyright 1998-2001 Guillaume Desnoix
10 */
11
12 package com.memoire.bu;
13
14 import com.memoire.bu.*;
15 import com.memoire.dnd.*;
16 import com.memoire.fu.*;
17 import com.memoire.re.*;
18
19
20 import java.awt.*;
21 import java.io.*;
22 import java.util.*;
23
24 import javax.swing.*;
25 import javax.swing.text.*;
26
27 /**
28 * A auto-colorized document (using regular expressions).
29 */
30 public class BuAutoStyledDocument
31 extends DefaultStyledDocument
32 {
33 public static final String KEYWORD ="KEYWORD";
34 public static final String MODIFIER="MODIFIER";
35 public static final String TYPE ="TYPE";
36 public static final String SPECIAL ="SPECIAL";
37 public static final String OPERATOR="OPERATOR";
38 public static final String BLOCK ="BLOCK";
39 public static final String NUMBER ="NUMBER";
40 public static final String COMMENT ="COMMENT";
41 public static final String PRAGMA ="PRAGMA";
42 public static final String STRING ="STRING";
43
44 /*
45 * A faire: n'utiliser regexp que qd c'est necessaire.
46 * TYPE:STYLE
47 * boolean: replace is different than find if
48 * indexes are not the same.
49 */
50
51 public static final byte IS_KEYWORD =1;
52 public static final byte IS_SIMPLE_STRING =2;
53 public static final byte IS_SIMPLE_CHAR =3;
54 public static final byte IS_ONELINE_REGEXP =4;
55 public static final byte IS_MULTILINE_REGEXP=5;
56
57 private class Key
58 {
59 public String match;
60 public String replace;
61 public String style;
62 public byte type;
63 public RE regexp_match;
64 public RE regexp_replace;
65
66 Key(String _match,String _replace, String _style, byte _type)
67 {
68 match =_match;
69 replace=_replace;
70 style =_style;
71 type =_type;
72
73 /*
74 if( (type==IS_ONELINE_REGEXP)
75 ||(type==IS_MULTILINE_REGEXP))
76 */
77 {
78 try
79 {
80 regexp_match=new RE(match,RE.REG_MULTILINE,
81 RESyntax.RE_SYNTAX_PERL5);
82 regexp_replace=new RE(replace,RE.REG_MULTILINE,
83 RESyntax.RE_SYNTAX_PERL5);
84
85 while(keys_.contains(this))
86 keys_.removeElement(this);
87
88 keys_.addElement(this);
89 }
90 catch(REException ex)
91 {
92 System.err.println("Bad regular exception\n "+match+"\n "+replace);
93 }
94 }
95 }
96
97 public boolean equals(Object _ok)
98 {
99 boolean r=false;
100
101 if(_ok instanceof Key)
102 {
103 Key k=(Key)_ok;
104 r=(match.equals(k.match)&&replace.equals(k.replace));
105 }
106
107 return r;
108 }
109 }
110
111 public Key createKey(String _match,String _replace,String _style)
112 { return new Key(_match,_replace,_style,IS_MULTILINE_REGEXP); }
113
114 public Key createKey(String _match,String _style)
115 { return createKey(_match,_match,_style); }
116
117 public Key createKeyword(String _word,String _style)
118 {
119 return createKey("(^|[^a-zA-Z0-9_])"+_word+"($|[^a-zA-Z0-9_])",
120 _word,_style);
121 }
122
123 public Key createKeyword(String _word)
124 {
125 return createKeyword(_word,KEYWORD);
126 }
127
128 private Vector keys_;
129
130 public BuAutoStyledDocument(String _text)
131 {
132 this(_text,null);
133 }
134
135 public BuAutoStyledDocument(String _text, String _syntax)
136 {
137 this();
138 if(_syntax!=null) setSyntax(_syntax);
139 try { insertString(0,_text,null); }
140 catch(BadLocationException ex) { ex.printStackTrace(); }
141 }
142
143 public BuAutoStyledDocument()
144 {
145 super(new StyleContext());
146 ta_ =null;
147 keys_=new Vector();
148
149 createStyle(KEYWORD ,null ,true ,false);
150 createStyle(TYPE ,new Color(0,128, 0),false,false);
151 createStyle(MODIFIER,new Color(0,128,128),false,false);
152 createStyle(SPECIAL ,new Color(128,128,0),false,true );
153 createStyle(OPERATOR,new Color(192, 96,0),true ,false);
154 createStyle(BLOCK ,Color.red ,false,false);
155 createStyle(NUMBER ,Color.blue ,false,false);
156 createStyle(COMMENT ,new Color(64,64,160),false,true );
157 createStyle(PRAGMA ,new Color(160,0,160),false,true );
158 createStyle(STRING ,Color.magenta ,false,false);
159
160 // createKey("(^|[^a-zA-Z0-9_.])[0-9]*\\.[0-9]*[df]?($|[^a-zA-Z0-9_.])",
161 // "[0-9]*\\.[0-9]*[df]?",
162 // NUMBER);
163 // createKey("(^|[^a-zA-Z0-9_])[0-9][0-9]*[l]?($|[^a-zA-Z0-9_])",
164 // "[0-9][0-9]*[l]?",
165 // NUMBER);
166 // createKeyword("hello");
167 // createKeyword("world");
168 // createKeyword("this" );
169 // createKeyword("super");
170 // createKey("\\{" ,BLOCK);
171 // createKey("\\}" ,BLOCK);
172 // createKey("\\(" ,BLOCK);
173 // createKey("\\)" ,BLOCK);
174 // createKey("\"[^\"]*\"" ,STRING);
175 // createKey("\'[^\']*\'" ,STRING);
176 // createKey("/\\*[^/*]*\\*/",COMMENT);
177 // createKey("//.*$" ,COMMENT);
178
179 setColorized(true);
180 }
181
182 public Style createStyle(String _name,Color _fg,
183 boolean _bold,boolean _italic)
184 {
185 Style style=((StyleContext)getAttributeContext()).addStyle(_name,null);
186 if(_fg!=null) style.addAttribute(StyleConstants.Foreground,_fg);
187 if(_bold ) style.addAttribute(StyleConstants.Bold ,Boolean.TRUE);
188 if(_italic ) style.addAttribute(StyleConstants.Italic ,Boolean.TRUE);
189 return style;
190 }
191
192 public void loadSyntax(String _language)
193 throws IOException
194 {
195 LineNumberReader lnr=new LineNumberReader
196 (new InputStreamReader
197 (getClass().getResourceAsStream(_language+"Syntax.txt")));
198
199 String l;
200 while(true)
201 {
202 l=lnr.readLine();
203 if(l==null) return;
204 if("".equals(l)) continue;
205
206 if("INHERIT".equals(l))
207 {
208 String f=lnr.readLine();
209 System.err.println("load parent syntax: "+f);
210 loadSyntax(f);
211 }
212 if("KEY".equals(l))
213 {
214 String m=lnr.readLine();
215 String r=lnr.readLine();
216 String s=lnr.readLine();
217 createKey(m,r,s);
218 }
219 if( "OPERATOR".equals(l)
220 ||"BLOCK" .equals(l)
221 ||"STRING" .equals(l)
222 ||"COMMENT" .equals(l))
223 {
224 String s=l;
225 while(true)
226 {
227 l=lnr.readLine();
228 if(l==null) return;
229 if("".equals(l)) break;
230 createKey(l,s);
231 }
232 }
233 else
234 {
235 String s=l;
236 while(true)
237 {
238 l=lnr.readLine();
239 if(l==null) return;
240 if("".equals(l)) break;
241 createKeyword(l,s);
242 }
243 }
244 }
245 }
246
247 private BuTextPane ta_;
248
249 public BuTextPane getTextPane()
250 { return ta_; }
251
252 public void setTextPane(BuTextPane _ta)
253 {
254 ta_=_ta;
255 if(ta_!=null) ta_.repaint(0);
256 }
257
258 private boolean colorized_=false;
259 public boolean isColorized() { return colorized_; }
260 public void setColorized(boolean _b) { colorized_=_b; }
261
262 private String syntax_;
263 public String getSyntax() { return syntax_; }
264 public void setSyntax(String _syntax)
265 {
266 //System.err.println("set-syntax: "+_syntax);
267
268 if((_syntax==null)||(!_syntax.equals(syntax_)))
269 {
270 syntax_=_syntax;
271
272 keys_.removeAllElements();
273 try { loadSyntax(syntax_); }
274 catch(Exception ex)
275 {
276 System.err.println("syntax '"+_syntax+"' not available.");
277 //ex.printStackTrace();
278 }
279
280 if(isColorized()) colorize();
281 else uncolorize();
282 }
283 }
284
285 private int getLineCount()
286 {
287 return getDefaultRootElement().getElementCount();
288 }
289
290 private int getLineStartOffset(int _offset)
291 {
292 return getParagraphElement(_offset).getStartOffset();
293 }
294
295 private int getLineEndOffset(int _offset)
296 {
297 return getParagraphElement(_offset).getEndOffset();
298 }
299
300 /*
301 private int getLineStartOffset(int _offset)
302 {
303 int r=0;
304 try
305 {
306 int n=getLineCount()-1;
307 int p=getLength();
308 while((p>=_offset)&&(n>=0))
309 {
310 p=getDefaultRootElement().getElement(n).getStartOffset();
311 n--;
312 }
313 r=p;
314 }
315 catch(Exception ex) { System.err.println(ex); }
316 // System.err.println("Start="+r);
317 return r;
318 }
319 */
320
321 /*
322 private int getLineEndOffset(int _offset)
323 {
324 int r=getLength();
325 try
326 {
327 int nmax=getLineCount()-1;
328 int p=0;
329 int n=0;
330 while((p<_offset)&&(n<=nmax))
331 {
332 p=getDefaultRootElement().getElement(n).getEndOffset();
333 n++;
334 }
335 r=p;
336 }
337 catch(Exception ex) { System.err.println(ex); }
338 // System.err.println("End ="+r);
339 return r;
340 }
341 */
342
343 /*
344 public void setText(String _string)
345 {
346 try
347 {
348 super.remove(0,getLength());
349 super.insertString(0,_string,null);
350 // colorize();
351 }
352 catch(Exception ex) { }
353 }
354 */
355
356 public void insertString(int _offset, String _string, AttributeSet _attr)
357 throws BadLocationException
358 {
359 if(_string!=null)
360 {
361 // replace tabs ?
362 //_string=BuPrinter.replace(_string,"\t"," ");
363 super.insertString(_offset,_string,_attr);
364
365 if(_string.length()<120)
366 {
367 if(_string.indexOf('\n')>=0)
368 colorize(_offset,_string.length());
369 }
370
371 if(_string.length()==1)
372 if((ta_!=null)&&(ta_.getDocument()==this))
373 {
374 try
375 {
376 Rectangle r=ta_.modelToView(ta_.getCaretPosition());
377 r.x=Math.max(0,r.x-10);
378 r.y=Math.max(0,r.y-10);
379 r.width =20;
380 r.height=20;
381 ta_.scrollRectToVisible(r);
382 }
383 catch(Exception ex) { }
384 }
385 }
386 }
387
388 /*
389 public Dimension computePreferredSize()
390 {
391 Style fs=getStyle(StyleContext.DEFAULT_STYLE);
392 FontMetrics fm=ta_.getFontMetrics
393 (new Font(StyleConstants.getFontFamily(fs),
394 Font.PLAIN,
395 StyleConstants.getFontSize(fs)));
396
397 int n=getDefaultRootElement().getElementCount();
398 int c=0;
399 for(int i=0;i<n;i++)
400 {
401 int start=getLineStartOffset(i);
402 int end =getLineEndOffset(i);
403 c=Math.max(c,end-start+1);
404 }
405 int w=fm.stringWidth("m")*c;
406 int h=fm.getHeight()*n;
407
408 return new Dimension(w,h);
409 }
410 */
411
412 public void uncolorize()
413 {
414 try
415 {
416 String text=getText(0,getLength());
417 super.remove(0,getLength());
418 super.insertString(0,text,null);
419 }
420 catch(BadLocationException ex) { }
421 }
422
423 public void colorize()
424 {
425 if(!colorized_) return;
426 __colorize(0,getLength());
427 }
428
429 public void colorize(final int _start, final int _len)
430 {
431 if(!colorized_) return;
432
433 //try
434 {
435 int start=getLineStartOffset(_start);
436 int end =getLineEndOffset (_start+_len);
437 //System.err.println("start="+start+",offset="+_start+",end="+end);
438 //try { System.err.println(getText(start,Math.max(0,end-start-1))); }
439 //catch(BadLocationException ex) { }
440 __colorize(start,Math.max(0,end-start-1));
441 }
442 //catch(BadLocationException ex)
443 // { System.err.println("BAD LOC"); }
444
445 /*
446 try
447 {
448 int start=getLineStartOffset(_start);
449 int len =getText(start,80).indexOf('\n');
450 if((len<0)||(len<_len+_start-start)) len=_len+_start-start;
451 // System.err.println("start="+start+",offset="+_start+",len="+len);
452 // System.err.println(getText(start,len));
453 __colorize(start,len);
454 }
455 catch(BadLocationException ex) { }
456 */
457 }
458
459 public void colorize(BuCommonInterface _app)
460 {
461 Colorizer t=new Colorizer(_app);
462 t.setPriority(Thread.MIN_PRIORITY);
463 t.start();
464 }
465
466 class Colorizer extends BuTask
467 {
468 private BuCommonInterface app_;
469
470 public Colorizer(BuCommonInterface _app)
471 {
472 super();
473 app_=_app;
474
475 setName(BuResource.BU.getString("Coloration"));
476 if(app_!=null) setTaskView(app_.getMainPanel().getTaskView());
477 }
478
479 public void start()
480 {
481 super.start();
482 }
483
484 public void run()
485 {
486 boolean editable=false;
487
488 // System.err.println("TA="+ta_);
489 setProgression(2);
490
491 if(ta_!=null)
492 {
493 editable=ta_.isEditable();
494 ta_.setEditable(false);
495 ta_.setDocument(new BuAutoStyledDocument(ta_.getText()));
496 BuAutoStyledDocument.this.removeDocumentListener(ta_);
497 BuAutoStyledDocument.this.removeUndoableEditListener(ta_);
498 }
499
500 boolean colorized=isColorized();
501 setColorized(true);
502 setProgression(5);
503
504 try { __colorize(0,getLength()); }
505 catch(Exception ex) { }
506
507 setColorized(colorized);
508
509 if(ta_!=null)
510 {
511 setProgression(97);
512 ta_.setDocument(BuAutoStyledDocument.this);
513 BuAutoStyledDocument.this.addDocumentListener(ta_);
514 BuAutoStyledDocument.this.addUndoableEditListener(ta_);
515 ta_.setEditable(editable);
516 }
517
518 setProgression(100);
519 super.run();
520 }
521 }
522
523 private void __colorize(int _start, int _len)
524 // throws BadLocationException
525 {
526 String text;
527
528 try { text=getText(_start,_len); }
529 catch(BadLocationException ex) { return; }
530
531 int pos=0;
532 if(ta_!=null) pos=ta_.getCaretPosition();
533
534 //System.err.println("Colorize ["+_start+","+(_start+_len)+"]");
535 //System.err.println("Text="+text);
536
537 try
538 {
539 super.remove(_start,_len);
540 super.insertString(_start,text,null);
541 }
542 catch(BadLocationException ex)
543 {
544 System.err.println("BuAutoStyledDocument: bad location 1");
545 return;
546 }
547
548 int l=keys_.size();
549 for(int i=0;i<l;i++)
550 {
551 Thread t=Thread.currentThread();
552 if(t instanceof Colorizer)
553 ((Colorizer)t).setProgression(5+90*i/l);
554
555 // System.err.print("\r"+(100*i/keys_.size())+"%");
556
557 Key key=(Key)keys_.elementAt(i);
558 try
559 {
560 if(key.regexp_match==null)
561 {
562 System.err.println("Bad regular expression: "+key.match);
563 break;
564 }
565
566 if(key.regexp_replace==null)
567 {
568 System.err.println("Bad regular expression: "+key.replace);
569 break;
570 }
571
572 REMatch mm;
573 int p=0;
574 int q=0;
575
576 while((mm=key.regexp_match.getMatch(text,p))!=null)
577 {
578 String oldtext=text.substring(mm.getStartIndex(),
579 mm.getEndIndex ());
580
581 REMatch mr=key.regexp_replace.getMatch(oldtext);
582
583 /*
584 q++;
585 if(q==500)
586 {
587 System.err.println("MAYBE LOOP : AUTOSTYLED");
588 System.err.println
589 ("ERROR COLORIZE: MATCH "+key.match);
590 System.err.println
591 (" : REPL. "+key.replace);
592 System.err.println
593 (" : "+oldtext);
594 break;
595 }
596 */
597
598 if(mr==null)
599 {
600 System.err.println("MR=NULL : AUTOSTYLED");
601 System.err.println
602 ("ERROR COLORIZE: MATCH "+key.match);
603 System.err.println
604 (" : REPL. "+key.replace);
605 System.err.println
606 (" : "+oldtext);
607 break;
608 }
609
610 String newtext=oldtext.substring(mr.getStartIndex(),
611 mr.getEndIndex ());
612
613 /*
614 if(oldtext.equals(newtext))
615 {
616 System.err.println("SAMETEXT : AUTOSTYLED");
617 System.err.println
618 ("ERROR COLORIZE: MATCH "+key.match);
619 System.err.println
620 (" : REPL. "+key.replace);
621 System.err.println
622 (" : "+oldtext);
623 break;
624 }
625 */
626
627 int si=_start+mm.getStartIndex()+mr.getStartIndex();
628 int ei=_start+mm.getStartIndex()+mr.getEndIndex ();
629
630 super.remove(si,ei-si);
631 super.insertString(si,newtext,getStyle(key.style));
632
633 p=mm.getStartIndex()+mr.getStartIndex()+newtext.length();
634 }
635 }
636 catch(BadLocationException ex)
637 { System.err.println("BuAutoStyledDocument: bad location 2"); }
638 catch(Error ex)
639 { System.err.println(""+ex); }
640 }
641
642 if(ta_!=null)
643 {
644 ta_.setCaretPosition(pos);
645 ta_.repaint(0);
646 }
647 }
648
649 public void remove(int _start, int _len)
650 throws BadLocationException
651 {
652 super.remove(_start,_len);
653 colorize(_start,0);
654 }
655 }
656