diff --git a/org.nodeclipse.enide.editors.gradle/META-INF/MANIFEST.MF b/org.nodeclipse.enide.editors.gradle/META-INF/MANIFEST.MF index 083781f4..fa87cf91 100644 --- a/org.nodeclipse.enide.editors.gradle/META-INF/MANIFEST.MF +++ b/org.nodeclipse.enide.editors.gradle/META-INF/MANIFEST.MF @@ -9,7 +9,8 @@ Require-Bundle: org.eclipse.ui, org.eclipse.core.resources, org.eclipse.ui.ide, org.eclipse.jface.text, - org.eclipse.ui.editors + org.eclipse.ui.editors, + org.eclipse.ui.views Bundle-ActivationPolicy: lazy Bundle-RequiredExecutionEnvironment: JavaSE-1.6 Export-Package: org.nodeclipse.enide.editors.gradle diff --git a/org.nodeclipse.enide.editors.gradle/src/org/nodeclipse/enide/editors/gradle/editors/GradleEditor.java b/org.nodeclipse.enide.editors.gradle/src/org/nodeclipse/enide/editors/gradle/editors/GradleEditor.java index cb444ad7..b375512c 100644 --- a/org.nodeclipse.enide.editors.gradle/src/org/nodeclipse/enide/editors/gradle/editors/GradleEditor.java +++ b/org.nodeclipse.enide.editors.gradle/src/org/nodeclipse/enide/editors/gradle/editors/GradleEditor.java @@ -1,15 +1,22 @@ package org.nodeclipse.enide.editors.gradle.editors; +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.core.runtime.CoreException; import org.eclipse.jface.preference.IPreferenceStore; +import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.IDocumentExtension3; import org.eclipse.jface.text.source.DefaultCharacterPairMatcher; +import org.eclipse.ui.IEditorInput; import org.eclipse.ui.editors.text.TextEditor; +import org.eclipse.ui.texteditor.IDocumentProvider; import org.eclipse.ui.texteditor.SourceViewerDecorationSupport; - +import org.eclipse.ui.views.contentoutline.IContentOutlinePage; /** * copied from NodeclipseNodejsEditor - * TODO base classes, color theme support + * TODO consider base classes * * @author Paul Verest */ @@ -21,6 +28,12 @@ public class GradleEditor extends TextEditor { public final static String EDITOR_MATCHING_BRACKETS_COLOR = "matchingBracketsColor"; private DefaultCharacterPairMatcher matcher; + + //+ + private GradleOutline fOutlinePage = null; + private boolean pageDirty = true; + private GradlePage page; + IDocument oldDoc = null; //used only in doSetInput(IEditorInput input) public GradleEditor() { setSourceViewerConfiguration(new NodeSourceViewerConfiguration()); @@ -48,6 +61,71 @@ protected void configureSourceViewerDecorationSupport(SourceViewerDecorationSupp store.setDefault(EDITOR_MATCHING_BRACKETS, true); store.setDefault(EDITOR_MATCHING_BRACKETS_COLOR, "128,128,128"); } + + //+ block for Outline taken from Winterstein Markdown Editor { + public Object getAdapter(Class required) { + if (IContentOutlinePage.class.equals(required)) { + if (fOutlinePage == null) { + fOutlinePage= new GradleOutline(getDocumentProvider(), this); + if (getEditorInput() != null) + fOutlinePage.setInput(getEditorInput()); + } + return fOutlinePage; + } + return super.getAdapter(required); + } + + public GradlePage getGradlePage() { + if (pageDirty) updateGradlePage(); + return page; + } + + private void updateGradlePage() { + String text = getText(); + if (text==null) text=""; + page = new GradlePage(text); //GradleParser.parse(text); / + pageDirty = false; + } + + /** + * @return The text of the editor's document, or null if unavailable. + */ + public String getText() { + IDocument doc = getDocument(); + return doc==null? null : doc.get(); + } + + public IDocument getDocument() { + IEditorInput input = getEditorInput(); + IDocumentProvider docProvider = getDocumentProvider(); + return docProvider==null? null : docProvider.getDocument(input); + } + + public static GradleEditor getEditor(IDocument doc) { + return doc2editor.get(doc); + } + private static final Map doc2editor = new HashMap(); + + @Override + protected void doSetInput(IEditorInput input) throws CoreException { + // Detach from old + if (oldDoc!= null) { + //oldDoc.removeDocumentListener(this); + if (doc2editor.get(oldDoc) == this) doc2editor.remove(oldDoc); + } + // Set + super.doSetInput(input); + // Attach as a listener to new doc + IDocument doc = getDocument(); + oldDoc = doc; + if (doc==null) return; + //doc.addDocumentListener(this); + doc2editor.put(doc, this); +// // Initialise code folding +// haveRunFolding = false; +// updateSectionFoldingAnnotations(null); + } + //} } diff --git a/org.nodeclipse.enide.editors.gradle/src/org/nodeclipse/enide/editors/gradle/editors/GradleOutline.java b/org.nodeclipse.enide.editors.gradle/src/org/nodeclipse/enide/editors/gradle/editors/GradleOutline.java new file mode 100644 index 00000000..38c64807 --- /dev/null +++ b/org.nodeclipse.enide.editors.gradle/src/org/nodeclipse/enide/editors/gradle/editors/GradleOutline.java @@ -0,0 +1,202 @@ +package org.nodeclipse.enide.editors.gradle.editors; + +import org.eclipse.jface.text.DocumentEvent; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.IDocumentListener; +import org.eclipse.jface.viewers.ITreeContentProvider; +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.views.contentoutline.ContentOutlinePage; + + + +public class GradleOutline extends ContentOutlinePage { + + private final IDocumentProvider fDocumentProvider; + private final GradleEditor fTextEditor; + private Object fInput = null; + + public GradleOutline(IDocumentProvider documentProvider, GradleEditor gradleEditor) { + this.fDocumentProvider=documentProvider; + this.fTextEditor=gradleEditor; + } + + /** + * Sets the input of the outline page + * + * @param input + * the input of this 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); + } + } + } + + /* + * (non-Javadoc) Method declared on ContentOutlinePage + */ + @Override + public void createControl(Composite parent) { + super.createControl(parent); + TreeViewer viewer = getTreeViewer(); + viewer.setContentProvider(new ContentProvider()); + //... + viewer.addSelectionChangedListener(this); + + if (fInput != null) + viewer.setInput(fInput); + + + } + + public final class ContentProvider implements ITreeContentProvider, IDocumentListener { + + private GradlePage fContent; + // protected List fContent= new ArrayList(10); + private GradleEditor fTextEditor; + + private void parse() { + if (fTextEditor==null){ + System.out.println("fTextEditor==null"); + return; + } + fContent = fTextEditor.getGradlePage(); + } + + + /* + * @see IContentProvider#inputChanged(Viewer, Object, Object) + */ + @Override + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + // Detach from old + if (oldInput != null) { + IDocument document = fDocumentProvider.getDocument(oldInput); + if (document != null) { + document.removeDocumentListener(this); + } + } + fContent = null; + // Attach to new + if (newInput == null) + return; + IDocument document = fDocumentProvider.getDocument(newInput); + if (document == null) + return; + fTextEditor = GradleEditor.getEditor(document); + document.addDocumentListener(this); + parse(); + } + + @Override + public void documentAboutToBeChanged(DocumentEvent event) { + // nothing + } + + @Override + public void documentChanged(DocumentEvent event) { + parse(); + update(); + } + + /* + * @see IContentProvider#dispose + */ + @Override + public void dispose() { + fContent = null; + } + +// /* +// * @see IContentProvider#isDeleted(Object) +// */ +// @Override +// public boolean isDeleted(Object element) { +// return false; +// } + + /* + * @see IStructuredContentProvider#getElements(Object) + */ + @Override + public Object[] getElements(Object inputElement) { +// //TODO +// if (fContent==null){ +// System.out.println("fContent==null"); +// return null; +// } +// if (fContent.nodes==null){ +// System.out.println("fContent.nodes==null"); +// return null; +// } + return fContent.nodes.toArray(); + } + + /* + * @see ITreeContentProvider#hasChildren(Object) + */ + @Override + public boolean hasChildren(Object element) { + if (element == fInput) { + return true; + } + if (element instanceof GradlePage.Node) { + GradlePage.Node node = (GradlePage.Node) element; + //return header.getSubHeaders().size() > 0; + return false; + } + ; + return false; + } + + /* + * @see ITreeContentProvider#getParent(Object) + */ + @Override + public Object getParent(Object element) { + if (!(element instanceof GradlePage.Node)) + return null; + //TODO + return null; + } + + /* + * @see ITreeContentProvider#getChildren(Object) + */ + @Override + public Object[] getChildren(Object element) { + if (element == fInput) { + return getElements(null); + } + if (!(element instanceof GradlePage.Node)) + return null; + //return ((MarkdownPage.Header) element).getSubHeaders().toArray(); + //TODO + return null; + } + + + + } + +} diff --git a/org.nodeclipse.enide.editors.gradle/src/org/nodeclipse/enide/editors/gradle/editors/GradlePage.java b/org.nodeclipse.enide.editors.gradle/src/org/nodeclipse/enide/editors/gradle/editors/GradlePage.java new file mode 100644 index 00000000..8578111e --- /dev/null +++ b/org.nodeclipse.enide.editors.gradle/src/org/nodeclipse/enide/editors/gradle/editors/GradlePage.java @@ -0,0 +1,203 @@ +package org.nodeclipse.enide.editors.gradle.editors; + +import java.util.LinkedList; +import java.util.List; + +/** + * Gradle file structure + * + * parsing only first level {} block, i.e. it is made not to be perfect, but minimal enough to give Outline + * + * Does not depend on Groovy, Greclipse or Gradle Tooling API + * */ +//specially made package private +class GradlePage { + + static class Element{ + public static final Element EMPTY = new Element(""); + + String name; + int line; + int position; + + Element() { + } + Element(String string) { + name = string; + } + } + + static class Node extends Element{ + //the same as Element plus: + List children = new LinkedList(); + + Node(String string) { + super(string); + } + Node(Element el) { + name = el.name; + line = el.line; + position = el.position; + } + + @Override + public String toString() { + return name+" (L"+line+','+position+")"; + } + } + + String text = ""; + int curLine = 1; + int curPosition = 1; + int curIndex = 0; + List elements = new LinkedList(); + List nodes = new LinkedList(); + + GradlePage(String text) { + this.text=text; + parse(); + } + + void parse() { + Element lexem = nextLexem(); + while ( lexem != Element.EMPTY ){ + if (lexem.name.equals("{")){ + createNode(lexem); + parseBlock(); + } + lexem = nextLexem(); + } + }//parse + + private void createNode(Element el) { + // get previous + int index = elements.indexOf(el); + if (index>0){ //get previous + index--; + el = elements.get(index); + } + nodes.add(new Node(el)); + + }//createNode + + + private void parseBlock() { + Element prev, lexem; + lexem = nextLexem(); + while ( !lexem.name.equals("}") && lexem != Element.EMPTY ){ + if (lexem.name.equals("{")) { + createNode(lexem); + parseBlock(); + } + prev = lexem; + lexem = nextLexem(); + } + } + + + private Element nextLexem() { + Element el = nextLexemWithoutLog(); + elements.add(el); + log(el.name); + //log(el.toString()); + return el; + } + + + public static final String LETTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + public static final String LETTERS_ALL_CASES = LETTERS + LETTERS.toLowerCase(); + public static final String LETTERS_ALL_CASES_AND_UNDERLINE = LETTERS_ALL_CASES + '_'; + public static final String DIGITS = "0123456789"; + public static final String NUMBER_PARTS = DIGITS + ".eE"; + + /** get lexical element literal, number or other like (){}/*-+ + */ + //TODO comments and strings + private Element nextLexemWithoutLog() { + if (curIndex >= text.length()) { + return Element.EMPTY; + } + char c; + // skip white spaces + loop1: while (curIndex < text.length()) { + c = text.charAt(curIndex); + switch (c) { + case '\n': { + curLine++; + curPosition = 1; + break; + } + //TODO look at Character.isWhitespace(c) + case '\r': + case '\t': + case ' ': + break; + default: + break loop1; + } + curIndex++; + } + + if (curIndex >= text.length()) {// when file ends with spaces or new line + return Element.EMPTY; + } + + c = text.charAt(curIndex); + if (LETTERS_ALL_CASES.indexOf(c, 0) >= 0){ +// Element el = new Element(); +// el.line = curLine; +// el.position = curPosition; +// el.name = ""+c; +// +// curIndex++; +// while (curIndex < text.length()) { +// c = text.charAt(curIndex); +// if (LETTERS_ALL_CASES_AND_UNDERLINE.indexOf(c, 0) < 0){ +// return el; +// } +// el.name += c; +// curIndex++; +// } +// return el; //when file ends with literal + return readElementTail(c, LETTERS_ALL_CASES_AND_UNDERLINE); + } + // + else if (DIGITS.indexOf(c, 0) >= 0){ + return readElementTail(c, NUMBER_PARTS); + } + +// Element el = new Element(); +// el.line = curLine; +// el.position = curPosition; +// el.name = ""+c; +// curIndex++; +// +// return el; + + return readElementTail(c, ""); + }//nextLexem + + + private Element readElementTail(char c, final String CSET) { + Element el = new Element(); + el.line = curLine; + el.position = curPosition; + el.name = ""+c; + curIndex++; + + while (curIndex < text.length()) { + c = text.charAt(curIndex); + if (CSET.indexOf(c, 0) < 0){ + return el; + } + el.name += c; + curIndex++; + } + return el; //when file ends with literal + } + + static void log(String s){ + System.out.println(s); + } + +} diff --git a/org.nodeclipse.enide.editors.gradle/src/org/nodeclipse/enide/editors/gradle/editors/GradlePageTest.java b/org.nodeclipse.enide.editors.gradle/src/org/nodeclipse/enide/editors/gradle/editors/GradlePageTest.java new file mode 100644 index 00000000..49c5b93a --- /dev/null +++ b/org.nodeclipse.enide.editors.gradle/src/org/nodeclipse/enide/editors/gradle/editors/GradlePageTest.java @@ -0,0 +1,44 @@ +package org.nodeclipse.enide.editors.gradle.editors; + +import java.io.IOException; +import java.nio.charset.Charset; +import java.nio.file.Files; +import java.nio.file.Paths; + +import org.nodeclipse.enide.editors.gradle.editors.GradlePage.Node; + +//Testing using string and files from this project +public class GradlePageTest { + + static void log(String s) { + System.out.println(s); + } + + static void processString(String s){ + log(s); + GradlePage model = new GradlePage(s); + log("-- Resulted Nodes:"); + for (Node node : model.nodes) { + log(node.name); + } + } + + static void processFromFile(String path) throws IOException{ + byte[] encoded = Files.readAllBytes(Paths.get(path)); + String text = new String(encoded, Charset.defaultCharset()); + log(text); + processString(text); + } + + public static void main(String[] args) throws IOException { + String s = "task initSourceFolders { // add << before { to prevent executing during configuration phase"; + //processString(s); + + // docs/java/examples/build.gradle + processFromFile("docs/java/examples/build.gradle"); + + ///org.nodeclipse.enide.editors.gradle/docs/java/classic/build.gradle + //processFromFile("docs/java/classic/build.gradle"); + } + +}