diff --git a/CHANGELOG.md b/CHANGELOG.md index ce0e4792c..431d44b52 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,6 +35,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - [#115](https://github.com/gemoc/ale-lang/pull/115) The interpreter can be run by right-clicking on an ALE project - [#129](https://github.com/gemoc/ale-lang/pull/129) The editor warns when the `+=` and `-=` operators ared used on the `result` variable in a void method - [#131](https://github.com/gemoc/ale-lang/pull/131) The editor autocompletes attributes and methods of local variables and method parameters (support limited to model instances) +- [#153](https://github.com/gemoc/ale-lang/pull/153) The editor shows a warning when editing an _.ale_ source file that is not part of the project's ALE environment. A quick fix allows to automatically add the _.ale_ source file to the project's ALE environment - [#169](https://github.com/gemoc/ale-lang/pull/169) The editor shows documentation about the _open_ and _behavior_ keywords on hover - [#169](https://github.com/gemoc/ale-lang/pull/169) The editor shows the fully qualified name of an open class on hover as well as information about its EPackage diff --git a/plugins/org.eclipse.emf.ecoretools.ale.xtext.ui/src/org/eclipse/emf/ecoretools/ui/quickfix/AleQuickfixProvider.xtend b/plugins/org.eclipse.emf.ecoretools.ale.xtext.ui/src/org/eclipse/emf/ecoretools/ui/quickfix/AleQuickfixProvider.xtend index bfb6f4e31..e1aa924f0 100644 --- a/plugins/org.eclipse.emf.ecoretools.ale.xtext.ui/src/org/eclipse/emf/ecoretools/ui/quickfix/AleQuickfixProvider.xtend +++ b/plugins/org.eclipse.emf.ecoretools.ale.xtext.ui/src/org/eclipse/emf/ecoretools/ui/quickfix/AleQuickfixProvider.xtend @@ -3,7 +3,26 @@ */ package org.eclipse.emf.ecoretools.ui.quickfix +import java.util.Optional +import java.util.Set +import org.eclipse.core.resources.IFile +import org.eclipse.core.resources.ProjectScope +import org.eclipse.core.runtime.preferences.IEclipsePreferences +import org.eclipse.core.runtime.preferences.IScopeContext +import org.eclipse.emf.common.util.URI +import org.eclipse.emf.ecoretools.ale.core.env.IAleEnvironment +import org.eclipse.emf.ecoretools.ale.core.env.impl.FileBasedAleEnvironment +import org.eclipse.emf.ecoretools.ale.core.io.IOResources +import org.eclipse.emf.ecoretools.ale.ide.project.IAleProject +import org.eclipse.emf.ecoretools.ale.xtext.ui.internal.XtextActivator +import org.eclipse.emf.ecoretools.validation.AleMarkerTypes import org.eclipse.xtext.ui.editor.quickfix.DefaultQuickfixProvider +import org.eclipse.xtext.ui.editor.quickfix.Fix +import org.eclipse.xtext.ui.editor.quickfix.IssueResolutionAcceptor +import org.eclipse.xtext.validation.Issue +import org.osgi.service.prefs.BackingStoreException + +import static org.eclipse.emf.ecoretools.ale.ide.project.AleProjectPreferences.ALE_SOURCE_FILES /** * Custom quickfixes. @@ -11,14 +30,46 @@ import org.eclipse.xtext.ui.editor.quickfix.DefaultQuickfixProvider * See https://www.eclipse.org/Xtext/documentation/310_eclipse_support.html#quick-fixes */ class AleQuickfixProvider extends DefaultQuickfixProvider { + + public static final String CORE_PLUGIN_ID = "org.eclipse.emf.ecoretools.ale.core"; -// @Fix(AleValidator.INVALID_NAME) -// def capitalizeName(Issue issue, IssueResolutionAcceptor acceptor) { -// acceptor.accept(issue, 'Capitalize name', 'Capitalize the name.', 'upcase.png') [ -// context | -// val xtextDocument = context.xtextDocument -// val firstLetter = xtextDocument.get(issue.offset, 1) -// xtextDocument.replace(issue.offset, 1, firstLetter.toUpperCase) -// ] -// } + @Fix(AleMarkerTypes.SOURCE_FILE_NOT_IN_ENV) + def capitalize(Issue issue, IssueResolutionAcceptor acceptor) { + acceptor.accept(issue, 'Add file to ALE environment', 'Adds the file to the project\'s ALE environment (either in the configured .dsl file\nor in project`s preferences)', 'upcase.png') [ + context | + val xtextDocument = context.xtextDocument + val Optional aleFile = IOResources.toIFile(xtextDocument.resourceURI) + + if (aleFile.isPresent) { + val IAleProject project = IAleProject.from(aleFile.get.project) + val IAleEnvironment env = project.environment + + val Set aleSourceFilesPath = env.behaviorsSources + val URI aleFileURI = URI.createPlatformResourceURI(aleFile.get.fullPath.toString, true) + aleSourceFilesPath += aleFileURI.toString + + if (project.isConfiguredFromPreferences) { + val IScopeContext projectContext = new ProjectScope(aleFile.get.project); + val IEclipsePreferences preferences = projectContext.getNode(CORE_PLUGIN_ID); + val commaSeparatedPaths = aleSourceFilesPath.map[trim].filter[!isEmpty].join(",") + + try { + preferences.put(ALE_SOURCE_FILES.property, commaSeparatedPaths) + preferences.flush() + } + catch (IllegalStateException | BackingStoreException unlikelyToHappen) { + XtextActivator.instance.log.error("An unexpected error occurred while saving preferences", unlikelyToHappen); + } + } + else { + val dsl = project.findDslFile + if (dsl.isPresent) { + val FileBasedAleEnvironment dslEnv = IAleEnvironment.fromFile(dsl.get) + val IAleEnvironment newEnv = IAleEnvironment.fromPaths(env.metamodelsSources, aleSourceFilesPath) + dslEnv.save(newEnv) + } + } + } + ] + } } diff --git a/plugins/org.eclipse.emf.ecoretools.ale.xtext/plugin.xml b/plugins/org.eclipse.emf.ecoretools.ale.xtext/plugin.xml index 27a060f24..5ad16c022 100644 --- a/plugins/org.eclipse.emf.ecoretools.ale.xtext/plugin.xml +++ b/plugins/org.eclipse.emf.ecoretools.ale.xtext/plugin.xml @@ -14,6 +14,15 @@ + + + + + + + - diff --git a/plugins/org.eclipse.emf.ecoretools.ale.xtext/src/org/eclipse/emf/ecoretools/validation/AleMarkerTypes.xtend b/plugins/org.eclipse.emf.ecoretools.ale.xtext/src/org/eclipse/emf/ecoretools/validation/AleMarkerTypes.xtend new file mode 100644 index 000000000..f42db70d0 --- /dev/null +++ b/plugins/org.eclipse.emf.ecoretools.ale.xtext/src/org/eclipse/emf/ecoretools/validation/AleMarkerTypes.xtend @@ -0,0 +1,19 @@ +package org.eclipse.emf.ecoretools.validation + +/** + * IDs of ALE markers. + */ +class AleMarkerTypes { + + /** + * Default marker ID. + */ + public static val String DEFAULT = "org.eclipse.emf.ecoretools.ale.xtext.AleMarker" + + /** + * Marker indicating that the source file is not in the project's ALE environment. + */ + public static val String SOURCE_FILE_NOT_IN_ENV = "org.eclipse.emf.ecoretools.ale.xtext.SourceFileNotInEnvironmentMarker" + + +} \ No newline at end of file diff --git a/plugins/org.eclipse.emf.ecoretools.ale.xtext/src/org/eclipse/emf/ecoretools/validation/AleValidator.xtend b/plugins/org.eclipse.emf.ecoretools.ale.xtext/src/org/eclipse/emf/ecoretools/validation/AleValidator.xtend index 9b88e812f..f4a368e09 100644 --- a/plugins/org.eclipse.emf.ecoretools.ale.xtext/src/org/eclipse/emf/ecoretools/validation/AleValidator.xtend +++ b/plugins/org.eclipse.emf.ecoretools.ale.xtext/src/org/eclipse/emf/ecoretools/validation/AleValidator.xtend @@ -34,14 +34,13 @@ import org.eclipse.xtext.Keyword import org.eclipse.xtext.nodemodel.impl.HiddenLeafNode import org.eclipse.xtext.nodemodel.util.NodeModelUtils import org.eclipse.xtext.validation.Check +import org.eclipse.xtext.validation.Issue /** * Delegate validation to ALE validator */ class AleValidator extends AbstractAleValidator { - public static String ALE_MARKER = "org.eclipse.emf.ecoretools.ale.xtext.AleMarker"; - @Check def checkIsValid(Unit root) { @@ -56,6 +55,13 @@ class AleValidator extends AbstractAleValidator { interpreter.initScope(Sets.newHashSet(),Sets.newHashSet(#[project.name])) val parsedSemantics = env.behaviors.parsedFiles + val parsed = parsedSemantics.findFirst[sem | aleFile == IOResources.toIFile(new File(sem.sourceFile))] + val aleFileIsNotInEnv = parsed === null + if (aleFileIsNotInEnv) { + aleFile.createFileNotInEnvMarker() + return + } + /* * Register services */ @@ -92,7 +98,7 @@ class AleValidator extends AbstractAleValidator { .forEach[msg | markerFactory.doSwitch(msg)] } catch (Exception e) { - val marker = aleFile.createMarker(ALE_MARKER) + val marker = aleFile.createMarker(AleMarkerTypes.DEFAULT) marker.setAttribute(IMarker.MESSAGE, "An internal error occurred while validating the file: " + e.message) marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_ERROR) marker.setAttribute(IMarker.CHAR_START, 0) @@ -162,6 +168,35 @@ class AleValidator extends AbstractAleValidator { } private def cleanUpMarkers(IFile file) { - file.deleteMarkers(ALE_MARKER, true, IResource.DEPTH_ZERO); + file.deleteMarkers(AleMarkerTypes.DEFAULT, true, IResource.DEPTH_ZERO); + file.deleteMarkers(AleMarkerTypes.SOURCE_FILE_NOT_IN_ENV, true, IResource.DEPTH_ZERO); + } + + /** Adds a marker warning about the file not being part of ALE environment */ + private static def createFileNotInEnvMarker(IFile file) { + val marker = file.createMarker(AleMarkerTypes.SOURCE_FILE_NOT_IN_ENV) + marker.setAttribute(IMarker.MESSAGE, "This file is not part of the project's ALE environment, it won't be validated") + marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_WARNING) + marker.setAttribute(IMarker.CHAR_START, 0) + marker.setAttribute(IMarker.LINE_NUMBER, 0) + marker.setAttribute(IMarker.LOCATION, "line: " + 0 + " " + file.fullPath.toString()) + + // Attributes used by Xtext to find the associated quick fix + + marker.setAttribute(Issue.CODE_KEY, AleMarkerTypes.SOURCE_FILE_NOT_IN_ENV) + marker.setAttribute(Issue.COLUMN_KEY, 0) + marker.setAttribute(Issue.URI_KEY, URI.createPlatformResourceURI(file.fullPath.toString(), true).toString) + marker.setAttribute("FIXABLE_KEY", true); + + /* + * MUST BE SET LAST. + * + * Looks like the editor is updated when CHAR_END is set, + * and the editor must be updated once all other attributes + * are properly set. + * + * See https://www.eclipse.org/forums/index.php?t=msg&th=1104367&goto=1829410&#msg_1829410 + */ + marker.setAttribute(IMarker.CHAR_END, 0) } }