package net.sourceforge.phpeclipse.phpeditor; /********************************************************************** Copyright (c) 2000, 2002 IBM Corp. and others. All rights reserved. This program and the accompanying materials are made available under the terms of the Common Public License v1.0 which accompanies this distribution, and is available at http://www.eclipse.org/legal/cpl-v10.html Contributors: IBM Corporation - Initial implementation Klaus Hartlage - www.eclipseproject.de **********************************************************************/ import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.BadPositionCategoryException; import org.eclipse.jface.text.DefaultPositionUpdater; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.IPositionUpdater; import org.eclipse.jface.text.Position; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.ITreeContentProvider; import org.eclipse.jface.viewers.LabelProvider; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jface.viewers.TreeViewer; import org.eclipse.jface.viewers.Viewer; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.ui.texteditor.IDocumentProvider; import org.eclipse.ui.texteditor.ITextEditor; import org.eclipse.ui.views.contentoutline.ContentOutlinePage; /** * A content outline page which always represents the functions of the * connected PHPEditor. */ public class PHPContentOutlinePage extends ContentOutlinePage { /** * A segment element. */ protected static class Segment { public String name; public Position position; public Segment(String name, Position position) { this.name = name; this.position = position; } public String toString() { return name; } }; protected static class SegmentComparator implements Comparator { public int compare(Object o1, Object o2) { return ((Segment)o1).name.compareToIgnoreCase(((Segment)o2).name); } } /** * Divides the editor's document into ten segments and provides elements for them. */ protected class ContentProvider implements ITreeContentProvider { protected final static String SEGMENTS = "__php_segments"; //$NON-NLS-1$ protected IPositionUpdater fPositionUpdater = new DefaultPositionUpdater(SEGMENTS); protected List fContent = new ArrayList(10); protected List fVariables = new ArrayList(100); private String getIdentifier(String text, int firstIndex) { int i = firstIndex; char c; int textLength = text.length(); StringBuffer identifier = new StringBuffer(); while (i < textLength) { c = text.charAt(i++); if (Character.isJavaIdentifierPart(c) || (c=='$')) { identifier.append(c); } else if ( (i==firstIndex+1) && (c=='$')) { identifier.append(c); } else { return identifier.toString(); } } return null; } protected void parse(IDocument document) { int lines = document.getNumberOfLines(); int increment = Math.max(Math.round((float) (lines / 10)), 10); String text = document.get(); int lastIndex = 0; int i = 0; // lastIndex = text.indexOf("function ", lastIndex); // while (lastIndex > 0) { // // try { // i = lastIndex + 9; // while ((i < text.length()) && Character.isJavaIdentifierPart(text.charAt(i))) { // i++; // } // Position p = new Position(lastIndex, i - lastIndex); // document.addPosition(SEGMENTS, p); // fContent.add(new Segment(text.substring(lastIndex, i), p)); // // MessageFormat.format("function", new Object[] { new Integer(lastIndex)}), p)); //$NON-NLS-1$ // lastIndex = text.indexOf("function", lastIndex + 1); // } catch (BadLocationException e) { // } catch (BadPositionCategoryException e) { // } // // } boolean lineCommentMode = false; boolean multiLineCommentMode = false; boolean stringMode = false; boolean functionMode = false; String identifier; int c; int c2; int textLength = text.length() - 10; while (i < textLength) { c = text.charAt(i++); if (c == '\n') { lineCommentMode = false; // read until end of line } else if (c == '#') { // read until end of line lineCommentMode = true; continue; } else if (c == '/') { c2 = text.charAt(i++); if (c2 == '/') { lineCommentMode = true; continue; } else if (c2 == '*') { multiLineCommentMode = true; continue; } else { i--; } } else if (c == '*' && multiLineCommentMode) { c2 = text.charAt(i++); if (c2 == '/') { multiLineCommentMode = false; continue; } else { i--; } } else if (c == '\\' && stringMode) { c2 = text.charAt(i++); if (c2 == '"') { continue; } else { i--; } } else if (c == '"') { if (stringMode) { stringMode = false; } else { stringMode = true; } continue; } if (lineCommentMode || multiLineCommentMode || stringMode) { continue; } if (functionMode && Character.isJavaIdentifierPart((char) c)) { functionMode = false; lastIndex = i-1; identifier = getIdentifier(text, lastIndex); try { i += identifier.length()-1; Position p = new Position(lastIndex, i - lastIndex); document.addPosition(SEGMENTS, p); fContent.add(new Segment(text.substring(lastIndex, i), p)); // MessageFormat.format("function", new Object[] { new Integer(lastIndex)}), p)); //$NON-NLS-1$ // lastIndex = text.indexOf("function", lastIndex + 1); } catch (BadLocationException e) { } catch (BadPositionCategoryException e) { } } else if (c == 'f') { identifier = getIdentifier(text, i - 1); if (identifier.equals("function")) { functionMode = true; i+=8; } } else if (c == '$') { // get the variable name identifier = getIdentifier(text, i - 1); fVariables.add( identifier ); } } Collections.sort(fContent, new SegmentComparator()); Collections.sort(fVariables); // for (int line = 0; line < lines; line += increment) { // // int length = increment; // if (line + increment > lines) // length = lines - line; // // try { // // int offset = document.getLineOffset(line); // int end = document.getLineOffset(line + length); // length = end - offset; // Position p = new Position(offset, length); // document.addPosition(SEGMENTS, p); // fContent.add(new Segment(MessageFormat.format(PHPEditorMessages.getString("OutlinePage.segment.title_pattern"), new Object[] { new Integer(offset)}), p)); //$NON-NLS-1$ // // } catch (BadPositionCategoryException x) { // } catch (BadLocationException x) { // } // } } /* * @see IContentProvider#inputChanged(Viewer, Object, Object) */ public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { if (oldInput != null) { IDocument document = fDocumentProvider.getDocument(oldInput); if (document != null) { try { document.removePositionCategory(SEGMENTS); } catch (BadPositionCategoryException x) { } document.removePositionUpdater(fPositionUpdater); } } fContent.clear(); fVariables.clear(); if (newInput != null) { IDocument document = fDocumentProvider.getDocument(newInput); if (document != null) { document.addPositionCategory(SEGMENTS); document.addPositionUpdater(fPositionUpdater); parse(document); } } } /* * @see IContentProvider#dispose */ public void dispose() { if (fContent != null) { fContent.clear(); fContent = null; } if (fVariables != null) { fVariables.clear(); fVariables = null; } } /* * @see IContentProvider#isDeleted(Object) */ public boolean isDeleted(Object element) { return false; } /* * @see IStructuredContentProvider#getElements(Object) */ public Object[] getElements(Object element) { return fContent.toArray(); } /** * returns all PHP variables */ public Object[] getVariables() { return fVariables.toArray(); } /* * @see ITreeContentProvider#hasChildren(Object) */ public boolean hasChildren(Object element) { return element == fInput; } /* * @see ITreeContentProvider#getParent(Object) */ public Object getParent(Object element) { if (element instanceof Segment) return fInput; return null; } /* * @see ITreeContentProvider#getChildren(Object) */ public Object[] getChildren(Object element) { if (element == fInput) return fContent.toArray(); return new Object[0]; } }; protected Object fInput; protected IDocumentProvider fDocumentProvider; protected ITextEditor fTextEditor; /** * Creates a content outline page using the given provider and the given editor. */ public PHPContentOutlinePage(IDocumentProvider provider, ITextEditor editor) { super(); fDocumentProvider = provider; fTextEditor = editor; } /* (non-Javadoc) * Method declared on ContentOutlinePage */ public void createControl(Composite parent) { super.createControl(parent); TreeViewer viewer = getTreeViewer(); viewer.setContentProvider(new ContentProvider()); viewer.setLabelProvider(new LabelProvider()); viewer.addSelectionChangedListener(this); if (fInput != null) viewer.setInput(fInput); } /* (non-Javadoc) * Method declared on ContentOutlinePage */ public void selectionChanged(SelectionChangedEvent event) { super.selectionChanged(event); ISelection selection = event.getSelection(); if (selection.isEmpty()) fTextEditor.resetHighlightRange(); else { Segment segment = (Segment) ((IStructuredSelection) selection).getFirstElement(); int start = segment.position.getOffset(); int length = segment.position.getLength(); try { fTextEditor.setHighlightRange(start, length, true); } catch (IllegalArgumentException x) { fTextEditor.resetHighlightRange(); } } } /** * Sets the input of the outline page */ public void setInput(Object input) { fInput = input; update(); } /** * Updates the outline page. */ public void update() { TreeViewer viewer = getTreeViewer(); if (viewer != null) { Control control = viewer.getControl(); if (control != null && !control.isDisposed()) { control.setRedraw(false); viewer.setInput(fInput); viewer.expandAll(); control.setRedraw(true); } } } }