Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Basic editor support for build scripts #585

Merged
merged 1 commit into from Oct 6, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
8 changes: 8 additions & 0 deletions org.eclipse.buildship.core/plugin.xml
Expand Up @@ -78,4 +78,12 @@
</runtimeClasspathEntryResolver>
</extension>

<extension point="org.eclipse.core.contenttype.contentTypes">
<content-type
file-extensions="gradle"
id="org.eclipse.buildship.core.files.gradlebuildscript"
name="Gradle Build Script"
priority="low"
/>
</extension>
</plugin>
3 changes: 2 additions & 1 deletion org.eclipse.buildship.ui/META-INF/MANIFEST.MF
Expand Up @@ -24,6 +24,7 @@ Require-Bundle: org.eclipse.buildship.core,
com.google.guava;bundle-version="[15.0.0,16.0.0)",
com.gradleware.tooling.model;bundle-version="[0.19.3,0.20.0)",
com.gradleware.tooling.client;bundle-version="[0.19.3,0.20.0)",
com.gradleware.tooling.utils;bundle-version="[0.19.3,0.20.0)"
com.gradleware.tooling.utils;bundle-version="[0.19.3,0.20.0)",
org.eclipse.ui.workbench.texteditor
Bundle-ActivationPolicy: lazy
Bundle-ClassPath: .
18 changes: 13 additions & 5 deletions org.eclipse.buildship.ui/plugin.xml
Expand Up @@ -65,12 +65,13 @@
<extension
point="org.eclipse.ui.editors">
<editor
id="org.eclipse.buildship.ui.gradlebuildscripteditor"
class="org.eclipse.ui.editors.text.TextEditor"
icon="icons/full/obj16/gradle_file.png"
name="Gradle Build Script Editor"
class="org.eclipse.buildship.ui.editor.GradleEditor"
contributorClass="org.eclipse.buildship.ui.editor.GradleEditorContributor"
default="false"
extensions="gradle">
extensions="gradle"
icon="icons/full/obj16/gradle_file.png"
id="org.eclipse.buildship.ui.gradlebuildscripteditor"
name="Gradle Build Script Editor">
</editor>
</extension>

Expand Down Expand Up @@ -550,4 +551,11 @@
</enabledWhen>
</page>
</extension>
<extension
point="org.eclipse.core.filebuffers.documentSetup">
<participant
class="org.eclipse.buildship.ui.editor.GradleDocumentSetupParticipant"
contentTypeId="org.eclipse.buildship.core.files.gradlebuildscript">
</participant>
</extension>
</plugin>
Expand Up @@ -72,6 +72,8 @@ public final class UiPluginConstants {

public static final String IMPORT_WIZARD_ID = "org.eclipse.buildship.ui.wizards.project.import";

public static final String REFRESHPROJECT_COMMAND_ID = "org.eclipse.buildship.ui.commands.refreshproject";

private UiPluginConstants() {
}

Expand Down
@@ -0,0 +1,47 @@
/*
* Copyright (c) 2017 the original author or authors.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.buildship.ui.editor;

import org.eclipse.core.filebuffers.IDocumentSetupParticipant;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IDocumentExtension3;
import org.eclipse.jface.text.IDocumentPartitioner;
import org.eclipse.jface.text.rules.FastPartitioner;

/**
* The document setup participant for a properties file document.
*
* @author Christophe Moine
*/
public class GradleDocumentSetupParticipant implements IDocumentSetupParticipant {

/*
* @see org.eclipse.core.filebuffers.IDocumentSetupParticipant#setup(org.eclipse.jface.text.IDocument)
*/
@Override
public void setup(IDocument document) {
IDocumentPartitioner partitioner= createDocumentPartitioner();
if (document instanceof IDocumentExtension3) {
IDocumentExtension3 extension3= (IDocumentExtension3) document;
extension3.setDocumentPartitioner(IGradlePartitions.PARTITIONING, partitioner);
} else {
document.setDocumentPartitioner(partitioner);
}
partitioner.connect(document);
}

/**
* Factory method for creating a properties file document specific document
* partitioner.
*
* @return a newly created properties file document partitioner
*/
private static IDocumentPartitioner createDocumentPartitioner() {
return new FastPartitioner(new GradlePartitionScanner(), IGradlePartitions.PARTITIONS);
}
}
@@ -0,0 +1,35 @@
/*
* Copyright (c) 2017 the original author or authors.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.buildship.ui.editor;

import org.eclipse.ui.editors.text.EditorsUI;
import org.eclipse.ui.editors.text.TextEditor;
import org.eclipse.ui.texteditor.AbstractDecoratedTextEditorPreferenceConstants;

/**
* Gradle Editor entry point
*
* @author Christophe Moine
*/
public class GradleEditor extends TextEditor {
@Override
protected void initializeEditor() {
setSourceViewerConfiguration(new GradleTextViewerConfiguration());
super.initializeEditor();
}

/*
* @see org.eclipse.ui.texteditor.AbstractDecoratedTextEditor#isTabsToSpacesConversionEnabled()
* @since 3.7
*/
@Override
protected boolean isTabsToSpacesConversionEnabled() {
// Can't use our own preference store because JDT disables this functionality in its preferences.
return EditorsUI.getPreferenceStore().getBoolean(AbstractDecoratedTextEditorPreferenceConstants.EDITOR_SPACES_FOR_TABS);
}
}
@@ -0,0 +1,41 @@
/*
* Copyright (c) 2017 the original author or authors.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.buildship.ui.editor;

import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.swt.widgets.Event;
import org.eclipse.ui.handlers.IHandlerService;
import org.eclipse.ui.part.EditorActionBarContributor;

import org.eclipse.buildship.ui.PluginImage;
import org.eclipse.buildship.ui.PluginImages;
import org.eclipse.buildship.ui.UiPlugin;
import org.eclipse.buildship.ui.UiPluginConstants;

/**
* Action contributor for the Gradle Editor
*
* @author Christophe Moine
*/
public class GradleEditorContributor extends EditorActionBarContributor {
@Override
public void contributeToToolBar(IToolBarManager toolBarManager) {
toolBarManager.add(new Action("Refresh Gradle Project", PluginImages.REFRESH.withState(PluginImage.ImageState.ENABLED).getImageDescriptor()) {
@Override
public void runWithEvent(Event event) {
IHandlerService service=getActionBars().getServiceLocator().getService(IHandlerService.class);
try {
service.executeCommand(UiPluginConstants.REFRESHPROJECT_COMMAND_ID, event);
} catch (Exception e) {
UiPlugin.logger().error("Failed to invoke command "+UiPluginConstants.REFRESHPROJECT_COMMAND_ID, e);
}
}
});
}
}
@@ -0,0 +1,71 @@
/*
* Copyright (c) 2017 the original author or authors.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.buildship.ui.editor;

import org.eclipse.jface.text.rules.EndOfLineRule;
import org.eclipse.jface.text.rules.ICharacterScanner;
import org.eclipse.jface.text.rules.IPredicateRule;
import org.eclipse.jface.text.rules.IToken;
import org.eclipse.jface.text.rules.IWordDetector;
import org.eclipse.jface.text.rules.MultiLineRule;
import org.eclipse.jface.text.rules.RuleBasedPartitionScanner;
import org.eclipse.jface.text.rules.SingleLineRule;
import org.eclipse.jface.text.rules.Token;
import org.eclipse.jface.text.rules.WordRule;


/**
* @author Christophe Moine
*
*/
public class GradlePartitionScanner extends RuleBasedPartitionScanner {
private static class EmptyCommentDetector implements IWordDetector {

@Override
public boolean isWordStart(char c) {
return (c == '/');
}

@Override
public boolean isWordPart(char c) {
return (c == '*' || c == '/');
}
}

private static class EmptyCommentPredicateRule extends WordRule implements IPredicateRule {
private final IToken successToken;

public EmptyCommentPredicateRule(IToken successToken) {
super(new EmptyCommentDetector());
this.successToken = successToken;
addWord("/**/", successToken); //$NON-NLS-1$
}

@Override
public IToken evaluate(ICharacterScanner scanner, boolean resume) {
return super.evaluate(scanner);
}

@Override
public IToken getSuccessToken() {
return this.successToken;
}
}

public GradlePartitionScanner() {
IToken jsDocToken = new Token(IGradlePartitions.GRADLEDOC);
IToken multilineCommentToken = new Token(IGradlePartitions.MULTILINE_COMMENT);

setPredicateRules(new IPredicateRule[] { new EndOfLineRule("//", Token.UNDEFINED), //$NON-NLS-1$
new SingleLineRule("\"", "\"", Token.UNDEFINED, '\\'), //$NON-NLS-2$ //$NON-NLS-1$
new SingleLineRule("'", "'", Token.UNDEFINED, '\\'), //$NON-NLS-2$ //$NON-NLS-1$
new EmptyCommentPredicateRule(multilineCommentToken), new MultiLineRule("/**", "*/", jsDocToken, (char) 0, true), //$NON-NLS-1$ //$NON-NLS-2$
new MultiLineRule("/*", "*/", multilineCommentToken, (char) 0, true) //$NON-NLS-1$ //$NON-NLS-2$
});
}
}
@@ -0,0 +1,130 @@
/*
* Copyright (c) 2017 the original author or authors.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.buildship.ui.editor;

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import com.google.common.collect.ImmutableMap;

import org.eclipse.core.runtime.preferences.IEclipsePreferences;
import org.eclipse.core.runtime.preferences.IEclipsePreferences.IPreferenceChangeListener;
import org.eclipse.core.runtime.preferences.IEclipsePreferences.PreferenceChangeEvent;
import org.eclipse.jdt.ui.JavaUI;
import org.eclipse.jdt.ui.text.IJavaColorConstants;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.ITextViewer;
import org.eclipse.jface.text.TextAttribute;
import org.eclipse.jface.text.presentation.PresentationReconciler;
import org.eclipse.jface.text.rules.BufferedRuleBasedScanner;
import org.eclipse.jface.text.rules.DefaultDamagerRepairer;
import org.eclipse.jface.text.rules.EndOfLineRule;
import org.eclipse.jface.text.rules.IRule;
import org.eclipse.jface.text.rules.IToken;
import org.eclipse.jface.text.rules.ITokenScanner;
import org.eclipse.jface.text.rules.IWordDetector;
import org.eclipse.jface.text.rules.MultiLineRule;
import org.eclipse.jface.text.rules.NumberRule;
import org.eclipse.jface.text.rules.SingleLineRule;
import org.eclipse.jface.text.rules.Token;
import org.eclipse.jface.text.rules.WordRule;

import org.eclipse.buildship.core.util.preference.EclipsePreferencesUtils;

/**
* Presentation Reconcilier for the Gradle Editor
*
* @author Christophe Moine
*/
public class GradlePresentationReconciler extends PresentationReconciler {
private static final List<String> KEYWORDS = Arrays.asList("assert", "if", "else", "void", "null", "new", "return", "try", "catch", "def", "allprojects", //
"artifacts", "buildscript", "configurations", "dependencies", "repositories", "sourceSets", "subprojects", "publishing", "task", "apply", "sourceCompatibility", //
"targetCompatibility", "test", "project", "ext", "plugins", "jar", "shadowJar", "for", "while");

private final Map<String, Token> idToTokens=ImmutableMap.<String, Token>builder()
.put(IJavaColorConstants.JAVA_KEYWORD, new Token(null)) //
.put(IJavaColorConstants.JAVA_SINGLE_LINE_COMMENT, new Token(null)) //
.put(IJavaColorConstants.JAVA_MULTI_LINE_COMMENT, new Token(null)) //
.put(IJavaColorConstants.JAVA_STRING, new Token(null)) //
.put(IJavaColorConstants.JAVA_DEFAULT, new Token(null)) //
.build();

private IEclipsePreferences preferencesNode = EclipsePreferencesUtils.getInstanceScope().getNode(JavaUI.ID_PLUGIN);

@Override
public void install(final ITextViewer viewer) {
this.preferencesNode.addPreferenceChangeListener(new IPreferenceChangeListener() {
@Override
public void preferenceChange(PreferenceChangeEvent event) {
if(updateToken(event.getKey())) {
viewer.invalidateTextPresentation();
}
}
});
for(Entry<String, Token> entry: this.idToTokens.entrySet()) {
updateToken(entry.getKey());
}

BufferedRuleBasedScanner scanner = new BufferedRuleBasedScanner() {};
scanner.setRules(new IRule[] { new EndOfLineRule("//", this.idToTokens.get(IJavaColorConstants.JAVA_SINGLE_LINE_COMMENT)), // $NON-NLS-2$
new MultiLineRule("/*", "*/", this.idToTokens.get(IJavaColorConstants.JAVA_MULTI_LINE_COMMENT), (char) 0, true), //$NON-NLS-2$
new SingleLineRule("\"", "\"", this.idToTokens.get(IJavaColorConstants.JAVA_STRING)), //
new SingleLineRule("'", "'", this.idToTokens.get(IJavaColorConstants.JAVA_STRING)), //
new KeywordRule(this.idToTokens.get(IJavaColorConstants.JAVA_KEYWORD)), //
new WordRule(new WordDetector(), this.idToTokens.get(IJavaColorConstants.JAVA_DEFAULT)),
new NumberRule(this.idToTokens.get(IJavaColorConstants.JAVA_DEFAULT)),
});
setDamagerRepairer(scanner, IDocument.DEFAULT_CONTENT_TYPE);
MultilineCommentScanner multilineCommentScanner=new MultilineCommentScanner(this.idToTokens.get(IJavaColorConstants.JAVA_MULTI_LINE_COMMENT));
setDamagerRepairer(multilineCommentScanner, IGradlePartitions.MULTILINE_COMMENT);
setDamagerRepairer(multilineCommentScanner, IGradlePartitions.GRADLEDOC);

super.install(viewer);
}

private void setDamagerRepairer(ITokenScanner scanner, String tokenType) {
DefaultDamagerRepairer damagerRepairer = new DefaultDamagerRepairer(scanner);
setDamager(damagerRepairer, tokenType);
setRepairer(damagerRepairer, tokenType);
}

private boolean updateToken(String key) {
Token token = this.idToTokens.get(key);
if(token!=null) {
token.setData(new TextAttribute(JavaUI.getColorManager().getColor(key)));
}
return token!=null;
}



private static class KeywordRule extends WordRule {
public KeywordRule(IToken token) {
super(new WordDetector());
for (String word : KEYWORDS) {
addWord(word, token);
}
}
}

private static class WordDetector implements IWordDetector {

@Override
public boolean isWordPart(char c) {
return Character.isJavaIdentifierPart(c);
}

@Override
public boolean isWordStart(char c) {
return Character.isJavaIdentifierStart(c);
}

}
}