PageRenderTime 99ms CodeModel.GetById 74ms app.highlight 19ms RepoModel.GetById 1ms app.codeStats 1ms

/bundles/plugins-trunk/SideKick/sidekick/SideKick.java

#
Java | 623 lines | 438 code | 86 blank | 99 comment | 100 complexity | b4448012776b0682a627092bc05c0013 MD5 | raw file
  1/*
  2 * SideKick.java
  3 * :tabSize=8:indentSize=8:noTabs=false:
  4 * :folding=explicit:collapseFolds=1:
  5 *
  6 * Copyright (C) 2003, 2005 Slava Pestov 
  7 * Copyright (c) 2006, 2009 by the jEdit developer team
  8 *
  9 * This program is free software; you can redistribute it and/or
 10 * modify it under the terms of the GNU General Public License
 11 * as published by the Free Software Foundation; either version 2
 12 * of the License, or any later version.
 13 *
 14 * This program is distributed in the hope that it will be useful,
 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 17 * GNU General Public License for more details.
 18 *
 19 * You should have received a copy of the GNU General Public License
 20 * along with this program; if not, write to the Free Software
 21 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 22 */
 23
 24package sidekick;
 25
 26//{{{ Imports
 27import java.awt.event.ActionEvent;
 28import java.awt.event.ActionListener;
 29
 30import javax.swing.SwingWorker;
 31import javax.swing.Timer;
 32import javax.swing.tree.DefaultMutableTreeNode;
 33
 34import org.gjt.sp.jedit.Buffer;
 35import org.gjt.sp.jedit.EditBus;
 36import org.gjt.sp.jedit.EditPane;
 37import org.gjt.sp.jedit.View;
 38import org.gjt.sp.jedit.jEdit;
 39import org.gjt.sp.jedit.EditBus.EBHandler;
 40import org.gjt.sp.jedit.buffer.BufferAdapter;
 41import org.gjt.sp.jedit.buffer.JEditBuffer;
 42import org.gjt.sp.jedit.msg.BufferUpdate;
 43import org.gjt.sp.jedit.msg.BufferChanging;
 44import org.gjt.sp.jedit.msg.EditPaneUpdate;
 45import org.gjt.sp.jedit.msg.EditorExiting;
 46import org.gjt.sp.jedit.msg.PluginUpdate;
 47import org.gjt.sp.jedit.msg.PropertiesChanged;
 48import org.gjt.sp.jedit.msg.ViewUpdate;
 49import org.gjt.sp.util.Log;
 50
 51import errorlist.DefaultErrorSource;
 52import errorlist.ErrorSource;
 53//}}}
 54/** This is an EBComponent that manages a SideKick parser.
 55 * Each jEdit View needs its own parser.  
 56 * 
 57 * SideKick is not a visible component itself, but rather, serves as the underlying 
 58 * data model for a tree in a dockable Component, as determined by dockables.xml.
 59 * It happens to be  sidekick.enhanced.SourceTree. 
 60 *  
 61 */
 62public class SideKick
 63{
 64	//{{{ static members
 65	public static final String BUFFER_CHANGE = "sidekick.buffer-change-parse";
 66	public static final String BUFFER_SAVE = "sidekick.buffer-save-parse";
 67	public static final String FOLLOW_CARET = "sidekick-tree.follows-caret";
 68	public static final String AUTO_EXPAND_DEPTH = "sidekick-tree.auto-expand-tree-depth";
 69	public static final String SHOW_STATUS= "sidekick.showStatusWindow.label";
 70	public static final String FILTER_VISIBLE = "sidekick.filter-visible-assets";
 71	public static final String SHOW_FILTER = "sidekick.showFilter";
 72	static private boolean exiting = false;
 73	// }}}
 74
 75	//{{{ Instance variables
 76	private View view;
 77	private EditPane editPane;
 78	private Buffer buffer;
 79	private SideKickParser parser;
 80	private DefaultErrorSource errorSource;
 81	private boolean showParsingMessage;
 82	private int delay;
 83	private Timer keystrokeTimer;
 84	private BufferChangeListener bufferListener;
 85	private boolean addedBufferListener;
 86	//}}}
 87
 88
 89	//{{{ SideKick constructor
 90	SideKick(View view)
 91	{
 92		this.view = view;
 93		editPane = view.getEditPane();
 94		bufferListener = new BufferChangeListener();
 95
 96		propertiesChanged();
 97
 98		keystrokeTimer = new Timer(0,new ActionListener()
 99		{
100			public void actionPerformed(ActionEvent evt)
101			{
102				parse(false);
103			}
104		});
105
106		buffer = view.getBuffer();
107		EditBus.addToBus(this);
108	} //}}}
109
110	// {{{ property getters/setters
111	public static boolean isFollowCaret() {
112		try {
113			String currentMode = jEdit.getActiveView().getBuffer().getMode().getName();
114			return AbstractModeOptionPane.getBooleanProperty(currentMode, SideKick.FOLLOW_CARET);
115		}
116		catch (NullPointerException npe) {
117			return jEdit.getBooleanProperty(SideKick.FOLLOW_CARET) ;
118		}
119	}
120	public static boolean isGlobalFollowCaret() {
121		return jEdit.getBooleanProperty(SideKick.FOLLOW_CARET);
122	}
123	public static void setGlobalFollowCaret(boolean fc) {
124		jEdit.setBooleanProperty( SideKick.FOLLOW_CARET, fc);
125	}
126
127	public static void setParseOnSave(boolean val) {
128		jEdit.setBooleanProperty(BUFFER_SAVE, val);
129	}
130
131	public static void setParseOnChange(boolean val) {
132		jEdit.setBooleanProperty(BUFFER_CHANGE, val);
133	}
134
135	public static boolean isParseOnSave() {
136		return jEdit.getBooleanProperty(BUFFER_SAVE);
137	}
138
139	public static boolean isParseOnChange() {
140		return jEdit.getBooleanProperty(BUFFER_CHANGE);
141	} // }}}
142
143	//{{{ parse() method
144	/**
145	 * Immediately begins parsing the current buffer in a background thread.
146	 * @param showParsingMessage Clear the tree and show a status message
147	 * there?
148	 */
149	void parse(final boolean showParsingMessage)
150	{
151		if(keystrokeTimer.isRunning())
152			keystrokeTimer.stop();
153
154		if(!buffer.isLoaded())
155			return;
156
157		if(SideKickPlugin.isParsingBuffer(buffer))
158			return;
159		else
160			SideKickPlugin.startParsingBuffer(buffer);
161
162		this.showParsingMessage = showParsingMessage;
163
164
165		// unconditionally get the right parser
166		parser = SideKickPlugin.getParserForBuffer(buffer);
167		//{{{ check for unknown file
168		if (parser == null) {
169			Log.log(Log.DEBUG,this,"No parser");
170			setErrorSource(null);
171			showNotParsedMessage();
172			SideKickPlugin.finishParsingBuffer(buffer);
173			return;
174		} //}}}
175		//{{{ Show 'parsing in progress' message
176		else if(showParsingMessage)
177		{
178			SideKickParsedData data = new SideKickParsedData(buffer.getName());
179			data.root.add(new DefaultMutableTreeNode(
180				jEdit.getProperty("sidekick-tree.parsing")));
181			SideKickParsedData.setParsedData(view,data);
182
183			sendUpdate();
184		} //}}}
185
186		///DefaultErrorSource errorSource = new DefaultErrorSource("SideKick");	/// why is this here? Why not have ParseRequest create it?
187		///SideKickParsedData[] data = new SideKickParsedData[1];		/// why is this an array? Why is this even here?
188
189		///ParseRequest parseRequest = new ParseRequest(
190		///	parser, buffer, errorSource, data);
191		ParseRequestWorker parseRequest = new ParseRequestWorker(parser, buffer);
192		SideKickPlugin.execute(view, parseRequest);
193	} //}}}
194
195	//{{{ dispose() method
196	void dispose()
197	{
198		setErrorSource(null);
199		EditBus.removeFromBus(this);
200		removeBufferChangeListener(buffer);
201	} //}}}
202
203	//{{{ getParser() method
204	SideKickParser getParser()
205	{
206		return parser;
207	} //}}}
208
209	//{{{ setParser() method
210	void setParser(Buffer newBuffer)
211	{
212		deactivateParser();
213		if (newBuffer != null) buffer = newBuffer;
214		parser = SideKickPlugin.getParserForBuffer(buffer);
215		activateParser();
216//		autoParse();
217	} //}}}
218
219	//{{{ handlePropertiesChanged() method
220	@EBHandler
221	public void handlePropertiesChanged(PropertiesChanged msg)
222	{
223		propertiesChanged();
224	} //}}}
225
226	//{{{ handleEditorExiting() method
227	@EBHandler
228	public void handleEditorExiting(EditorExiting msg)
229	{
230		exiting = true;
231	} //}}}
232
233	//{{{ handleBufferUpdate() method
234	@EBHandler
235	public void handleBufferUpdate(BufferUpdate bmsg)
236	{
237		if (bmsg.getWhat() == BufferUpdate.PROPERTIES_CHANGED &&
238			view.getBuffer() == bmsg.getBuffer()) {
239			String prevMode =
240				buffer.getStringProperty(SideKickPlugin.PARSER_MODE_PROPERTY);
241			String currMode = (buffer.getMode() != null) ? buffer.getMode().getName() : "";
242			if (! currMode.equals(prevMode)) {
243				buffer.unsetProperty(SideKickPlugin.PARSER_PROPERTY);
244				setParser(view.getBuffer());
245			}
246		}
247
248		if (bmsg.getView() != view &&
249			(bmsg.getView() != null || bmsg.getBuffer() != view.getBuffer()))
250			return;
251
252		if (bmsg.getWhat() == BufferUpdate.SAVED && isParseOnSave()) 
253			setParser(buffer);
254		else if (bmsg.getWhat() == BufferUpdate.LOADED && isParseOnChange())
255			parse(true);
256		else if(bmsg.getWhat() == BufferUpdate.CLOSED)
257			setErrorSource(null);
258
259	} //}}}
260	
261	//{{{ handleBufferChange() method
262	@EBHandler
263	public void handleBufferChange(BufferChanging bmsg) {
264		buffer = bmsg.getBuffer();
265		buffer.setProperty(SideKickPlugin.PARSER_PROPERTY, null);
266	} //}}}
267	
268
269	//{{{ handleEditPaneUpdate() method
270	@EBHandler
271	public void handleEditPaneUpdate(EditPaneUpdate epu)
272	{
273		editPane = epu.getEditPane();
274		View v = editPane.getView();
275		if (v == null) v=jEdit.getActiveView();
276		if (v == null || v != view )
277			return;
278
279		if(epu.getWhat() == EditPaneUpdate.DESTROYED)
280		{
281			// check if this is the currently focused edit pane
282			if(editPane == v.getEditPane())
283			{
284				removeBufferChangeListener(this.buffer);
285				deactivateParser();
286			}
287		}
288		else if(epu.getWhat() == EditPaneUpdate.BUFFER_CHANGED)
289		{
290			if (!isParseOnChange()) {
291				SideKickTree tree = (SideKickTree) view.getDockableWindowManager().getDockable("sidekick");
292				if (tree != null) tree.reloadParserCombo();
293				return;
294			}
295			// check if this is the currently focused edit pane
296
297			if(editPane == view.getEditPane())
298			{
299				removeBufferChangeListener(this.buffer);
300				deactivateParser();
301				buffer = editPane.getBuffer();
302				if (! buffer.isLoaded())
303					return;
304				parser = SideKickPlugin.getParserForBuffer(buffer);
305				activateParser();
306
307				parse(true);
308			}
309		}
310	} //}}}
311
312	//{{{ handleViewUpdate() method
313	@EBHandler
314	public void handleViewUpdate(ViewUpdate vu)
315	{
316		if(vu.getView() == view && buffer != view.getBuffer()
317			&& vu.getWhat() == ViewUpdate.EDIT_PANE_CHANGED)
318		{
319			if (!isParseOnChange()) return;
320
321			removeBufferChangeListener(this.buffer);
322			deactivateParser();
323
324			buffer = view.getBuffer();
325			this.editPane = view.getEditPane();
326
327			parser = SideKickPlugin.getParserForBuffer(buffer);
328			activateParser();
329
330			parse(true);
331		}
332	} //}}}
333
334	//{{{ handlePluginUpdate() method
335	@EBHandler
336	public void handlePluginUpdate(PluginUpdate pmsg)
337	{
338		if(!exiting)
339		{
340			if(pmsg.getWhat() == PluginUpdate.UNLOADED
341				|| pmsg.getWhat() == PluginUpdate.LOADED)
342			{
343				/* Pick a parser again in case our parser
344				plugin was loaded or unloaded. */
345				setParser(null);
346			}
347		}
348	} //}}}
349
350	/*
351	//{{{ autoParse() method
352	private void autoParse()
353	{
354		if(buffer.getBooleanProperty(
355			"sidekick.buffer-change-parse") ||
356			|| buffer.getBooleanProperty(
357			"sidekick.keystroke-parse"))
358		{
359			parse(true);
360		}
361		else
362		{
363			showNotParsedMessage();
364		}
365	} //}}}
366	*/
367	//{{{ setErrorSource() method
368	private void setErrorSource(DefaultErrorSource errorSource)
369	{
370
371		if(this.errorSource != null)
372		{
373			ErrorSource.unregisterErrorSource(this.errorSource);
374			this.errorSource.clear();
375		}
376		this.errorSource = errorSource;
377
378		if(errorSource != null)
379		{
380			int errorCount = errorSource.getErrorCount();
381			if(errorCount != 0)
382				ErrorSource.registerErrorSource(errorSource);
383		}
384	} //}}}
385
386	//{{{ addBufferChangeListener() method
387	private void addBufferChangeListener(Buffer buffer)
388	{
389		if(!addedBufferListener)
390		{
391			buffer.addBufferListener(bufferListener = new BufferChangeListener());
392			addedBufferListener = true;
393		}
394	} //}}}
395
396	//{{{ removeBufferChangeListener() method
397	private void removeBufferChangeListener(Buffer buffer)
398	{
399		if(addedBufferListener)
400		{
401			buffer.removeBufferListener(bufferListener);
402			addedBufferListener = false;
403		}
404	} //}}}
405
406	//{{{ propertiesChanged() method
407	/** called when the sidekick's properties are changed */
408	@EBHandler
409	private void propertiesChanged()
410	{
411		if (!isParseOnChange()) return;
412		try
413		{
414			delay = Integer.parseInt(jEdit.getProperty(
415				"sidekick.auto-parse-delay"));
416		}
417		catch(NumberFormatException nf)
418		{
419			delay = 1500;
420		}
421
422	} //}}}
423
424	//{{{ showNotParsedMessage() method
425	private void showNotParsedMessage()
426	{
427		setErrorSource(null);
428
429		SideKickParsedData data = new SideKickParsedData(buffer.getName());
430		data.root.add(new DefaultMutableTreeNode(
431			jEdit.getProperty("sidekick-tree.not-parsed")));
432
433		SideKickParsedData.setParsedData(view,data);
434
435		sendUpdate();
436	} //}}}
437
438	//{{{ parseWithDelay() method
439	private void parseWithDelay()
440	{
441		if(parser != null)
442		{
443			parser.stop();
444
445			if(keystrokeTimer.isRunning())
446				keystrokeTimer.stop();
447
448			keystrokeTimer.setInitialDelay(delay);
449			keystrokeTimer.setRepeats(false);
450			keystrokeTimer.start();
451		}
452	} //}}}
453
454	//{{{ sendUpdate() method
455	private void sendUpdate()
456	{
457		if(view.isClosed())
458			return;
459
460		EditBus.send(new SideKickUpdate(view));
461	} //}}}
462
463	//{{{ deactivateParser() method
464	private void deactivateParser()
465	{
466		if(parser != null)
467		{
468			if(this.editPane == null)
469				Log.log(Log.ERROR,this,"Null editPane!");
470			else
471				parser.deactivate(this.editPane);
472//			this.editPane = null;
473			SideKickTree tree = (SideKickTree) view.getDockableWindowManager().getDockable("sidekick-tree");
474			if (tree == null) return;
475			tree.removeParserPanel();
476		}
477	} //}}}
478
479	//{{{ activateParser() method
480	private void activateParser()
481	{
482		EditPane editPane = view.getEditPane();
483
484		if(parser != null)
485		{
486			addBufferChangeListener(buffer);
487			this.editPane = editPane;
488			parser.activate(editPane);
489		}
490		else
491			removeBufferChangeListener(buffer);
492
493		SideKickTree tree = (SideKickTree) view.getDockableWindowManager().getDockable("sidekick-tree");
494		if (tree == null) return;
495		tree.reloadParserCombo();
496		if (parser != null)
497			tree.addParserPanel(parser);
498		parse(true);
499	} //}}}
500	
501	
502	//{{{ Inner classes
503	
504	class ParseRequestWorker extends SwingWorker<SideKickParsedData, Object>
505	{
506		SideKickParser parser;
507		Buffer buffer;
508		DefaultErrorSource errorSource;
509
510		ParseRequestWorker(SideKickParser parser, Buffer buffer)
511		{
512			this.parser = parser;
513			this.buffer = buffer;
514			errorSource = new DefaultErrorSource("SideKick");
515		}
516		
517		@Override
518		public SideKickParsedData doInBackground()
519		{
520			SideKickTree tree = (SideKickTree) view.getDockableWindowManager().getDockable("sidekick-tree");
521			if (tree != null)
522			{
523				tree.showStopButton(true);	
524			}
525			try
526			{
527				buffer.readLock();
528				SideKickParsedData data = parser.parse(buffer, errorSource);
529				return data;
530			}
531			finally
532			{
533				buffer.readUnlock();
534			}
535		}
536		
537		@Override
538		public void done() 
539		{
540			SideKickPlugin.cleanup(view);
541			try
542			{
543				if (isCancelled())
544				{
545					parser.stop();
546					return;
547				}
548				SideKickParsedData data = get();
549				
550				setErrorSource(errorSource);
551
552				int errorCount = errorSource.getErrorCount();
553
554				if(showParsingMessage || errorCount != 0)
555				{
556					String label = jEdit.getProperty("sidekick.parser."
557						+ parser.getName() + ".label");
558					Object[] pp = { label, Integer.valueOf(errorCount)};
559					view.getStatus().setMessageAndClear(jEdit.getProperty(
560						"sidekick.parsing-complete",pp));
561				}
562
563				buffer.setProperty(SideKickPlugin.PARSED_DATA_PROPERTY, data);
564				if(buffer.getProperty("folding").equals("sidekick"))
565					buffer.invalidateCachedFoldLevels();
566
567				View _view = jEdit.getFirstView();
568				while(_view != null)
569				{
570					if(_view.getBuffer() == buffer)
571						SideKickParsedData.setParsedData(_view, data);
572					_view = _view.getNext();
573				}
574
575				sendUpdate();
576			}
577			catch(Exception ie) 
578			{
579				showNotParsedMessage();	
580			}
581			finally
582			{
583				SideKickPlugin.finishParsingBuffer(buffer);
584				SideKickTree tree = (SideKickTree) view.getDockableWindowManager().getDockable("sidekick-tree");
585				if (tree != null)
586				{
587					tree.showStopButton(false);	
588				}
589			}
590		}
591	}
592
593	// {{{ BufferChangeListener class
594	/**
595	 * @since jedit 4.3pre2
596	 */
597	class BufferChangeListener extends BufferAdapter {
598
599		private void parseOnKeyStroke(JEditBuffer buffer)
600		{
601			if(buffer != SideKick.this.buffer)
602			{
603				return;
604			}
605
606			if(buffer.getBooleanProperty("sidekick.keystroke-parse"))
607				parseWithDelay();
608		}
609
610
611		public void contentInserted(JEditBuffer buffer, int startLine, int offset, int numLines, int length)
612		{
613			parseOnKeyStroke(buffer);
614		}
615
616		public void contentRemoved(JEditBuffer buffer, int startLine, int offset, int numLines, int length)
617		{
618			parseOnKeyStroke(buffer);
619		}
620
621
622	} //}}}
623}