You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@grails.apache.org by jd...@apache.org on 2026/06/30 21:37:16 UTC

(grails-intellij-plugin) 01/01: Import Grails plugin code from JetBrains/intellij-obsolete-plugins

This is an automated email from the ASF dual-hosted git repository.

jdaugherty pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/grails-intellij-plugin.git

commit 3d4bc40c8c31832c84415a9a11ee68deba3277e4
Author: James Daugherty <jd...@jdresources.net>
AuthorDate: Tue Jun 30 17:36:34 2026 -0400

    Import Grails plugin code from JetBrains/intellij-obsolete-plugins
---
 LICENSE.txt                                        |  202 +
 build.gradle.kts                                   |  128 +
 compatibilityResources/icons/grails.png            |  Bin 0 -> 312 bytes
 compatibilityResources/icons/grails@2x.png         |  Bin 0 -> 588 bytes
 compatibilityResources/icons/grails_13.png         |  Bin 0 -> 300 bytes
 compatibilityResources/icons/grails_13@2x.png      |  Bin 0 -> 562 bytes
 compatibilityResources/icons/grails_app.png        |  Bin 0 -> 346 bytes
 compatibilityResources/icons/grails_app@2x.png     |  Bin 0 -> 672 bytes
 .../icons/grails_app@2x_dark.png                   |  Bin 0 -> 671 bytes
 compatibilityResources/icons/grails_app_dark.png   |  Bin 0 -> 347 bytes
 compatibilityResources/icons/grails_sdk.png        |  Bin 0 -> 281 bytes
 compatibilityResources/icons/grails_sdk@2x.png     |  Bin 0 -> 519 bytes
 compatibilityResources/icons/grails_test.png       |  Bin 0 -> 399 bytes
 compatibilityResources/icons/grails_test@2x.png    |  Bin 0 -> 740 bytes
 compatibilityResources/icons/gsp_logo.png          |  Bin 0 -> 216 bytes
 compatibilityResources/icons/gsp_logo@2x.png       |  Bin 0 -> 384 bytes
 compatibilityResources/icons/taglib.png            |  Bin 0 -> 289 bytes
 compatibilityResources/icons/taglib@2x.png         |  Bin 0 -> 501 bytes
 copyright/build.gradle.kts                         |   43 +
 .../resources/intellij.groovy.grails.copyright.xml |   11 +
 .../copyright/UpdateGspCopyrightsProvider.java     |   25 +
 coverage/build.gradle.kts                          |   46 +
 .../resources/intellij.groovy.grails.coverage.xml  |   11 +
 .../grails/coverage/GrailsCoverageExtension.kt     |   14 +
 .../jetbrains/plugins/grails/GroovyMvcIcons.java   |   26 +
 .../lang/gsp/lexer/core/_GspDirectiveLexer.java    |  795 ++
 .../grails/lang/gsp/lexer/core/_GspLexer.java      | 1190 +++
 gradle-tooling/build.gradle.kts                    |   40 +
 ...ains.plugins.gradle.tooling.ModelBuilderService |    2 +
 .../gradle/tooling/builder/GrailsModule.java       |   20 +
 .../gradle/tooling/builder/GrailsModuleImpl.java   |   46 +
 .../builder/GrailsModuleModelBuilderImpl.java      |  137 +
 gradle.properties                                  |   14 +
 gradle/wrapper/gradle-wrapper.jar                  |  Bin 0 -> 46175 bytes
 gradle/wrapper/gradle-wrapper.properties           |    7 +
 gradlew                                            |  248 +
 gradlew.bat                                        |   93 +
 grails-compiler-patch/build.gradle.kts             |   42 +
 grails-compiler-patch/resources/log4j.properties   |   10 +
 .../grails/compiler/EmptyGrailsAwarePatcher.java   |   31 +
 .../grails/compiler/Grails2_0_JUnitPatcher.java    |  116 +
 .../grails/compiler/GrailsCompilerRtMarker.java    |    7 +
 .../grails/compiler/GrailsDomainClassPatcher.java  |   26 +
 .../groovy/grails/compiler/GrailsJUnitPatcher.java |  149 +
 grails-rt/build.gradle.kts                         |   30 +
 grails-rt/resources/META-INF/MANIFEST.MF           |    2 +
 .../commands/intellij-command-proxy.groovy         |   20 +
 grails-rt/resources/idea-injected-traits.groovy    |   28 +
 .../rt/AddAgentJarToClassPathTransformer.java      |   47 +
 .../src/org/jetbrains/groovy/grails/rt/Agent.java  |   26 +
 .../org/jetbrains/groovy/grails/rt/AgentUtils.java |   11 +
 .../groovy/grails/rt/ForkListenerTransformer.java  |   52 +
 .../groovy/grails/rt/GrailsIdeaTestListener.java   |   98 +
 .../jetbrains/groovy/grails/rt/GrailsRtMarker.java |    5 +
 hibernate/build.gradle.kts                         |   41 +
 .../resources/intellij.groovy.grails.hibernate.xml |   15 +
 .../hibernate/GormHibernateFacetProvider.java      |   59 +
 .../grails/hibernate/GormSessionFactory.java       |  168 +
 .../hibernate/GormSessionFactoryContributor.java   |   28 +
 i18n/build.gradle.kts                              |   47 +
 .../I18nized GSP Expression with injections.gsp.ft |    1 +
 ...18nized GSP Expression with injections.gsp.html |   58 +
 .../code/I18nized GSP Expression.gsp.ft            |    1 +
 .../code/I18nized GSP Expression.gsp.html          |   53 +
 .../I18nized Groovy string with injections.gsp.ft  |    1 +
 ...I18nized Groovy string with injections.gsp.html |   58 +
 .../code/I18nized Groovy string.gsp.ft             |    1 +
 .../code/I18nized Groovy string.gsp.html           |   53 +
 .../InvalidI18nProperty.html                       |    5 +
 .../inspectionDescriptions/metaInformation.json    |    8 +
 i18n/resources/intellij.groovy.grails.i18n.xml     |   30 +
 .../GspI18nIntention/after.gsp.template            |    8 +
 .../GspI18nIntention/before.gsp.template           |    8 +
 .../GspI18nIntention/description.html              |    5 +
 .../i18n/GrailsI18nGroovyQuickFixHandler.java      |  175 +
 .../groovy/grails/i18n/GrailsI18nInspection.java   |  162 +
 .../i18n/GrailsI18nPropertyReferenceProvider.java  |  111 +
 .../grails/i18n/GrailsI18nQuickFixHandler.java     |  249 +
 .../i18n/GrailsI18nReferenceContributor.java       |   16 +
 .../groovy/grails/i18n/GrailsI18nizeProvider.java  |   52 +
 .../grails/i18n/GrailsI18nizeQuickFixDialog.java   |   62 +
 .../groovy/grails/i18n/GspI18nIntention.java       |  116 +
 jps-plugin/build.gradle.kts                        |   42 +
 ...s.jps.incremental.groovy.GroovyBuilderExtension |    1 +
 .../jps/grails/builder/GrailsBuilderExtension.java |   98 +
 langInjection/build.gradle.kts                     |   35 +
 .../resources/META-INF/languageInjections.xml      |    7 +
 .../intellij.groovy.grails.langInjection.xml       |   11 +
 maven/build.gradle.kts                             |   47 +
 maven/resources/intellij.groovy.grails.maven.xml   |   18 +
 .../grails/maven/GrailsMavenApplication.java       |   47 +
 .../maven/GrailsMavenApplicationProvider.java      |   30 +
 .../grails/maven/GrailsMavenDebuggerRunner.java    |  201 +
 .../groovy/grails/maven/GrailsMavenImporter.java   |   67 +
 .../groovy/grails/maven/MavenCommandExecutor.java  |  217 +
 .../groovy/grails/maven/MavenNodeProvider.kt       |   24 +
 resources/IdeaPrintProjectSettings.groovy          |  127 +
 resources/META-INF/grails-css-integration.xml      |    6 +
 resources/META-INF/grails-js-integration.xml       |    5 +
 resources/META-INF/grails-method-descriptors.xml   |  237 +
 resources/META-INF/plugin.xml                      |  498 ++
 resources/META-INF/pluginIcon.svg                  |    3 +
 .../internal/Groovy Server Page.gsp.ft             |   16 +
 .../internal/Groovy Server Page.gsp.html           |   30 +
 resources/icons/action_method.svg                  |    6 +
 resources/icons/grails.svg                         |    3 +
 resources/icons/grails_13.svg                      |    3 +
 resources/icons/grails_13_dark.svg                 |    3 +
 resources/icons/grails_app.svg                     |    6 +
 resources/icons/grails_sdk.svg                     |    6 +
 resources/icons/grails_test.svg                    |    7 +
 resources/icons/groovy_mvc_plugin.svg              |    7 +
 resources/icons/gsp_logo.svg                       |   10 +
 resources/icons/taglib.svg                         |    9 +
 .../inspectionDescriptions/GspInspection.html      |   10 +
 .../inspectionDescriptions/metaInformation.json    |    8 +
 .../GenerateViewIntention/after.gsp.template       |    9 +
 .../GenerateViewIntention/before.groovy.template   |    9 +
 .../GenerateViewIntention/description.html         |    5 +
 .../after.groovy.template                          |    8 +
 .../before.groovy.template                         |    6 +
 .../description.html                               |    5 +
 .../MakeNullableIntention/after.groovy.template    |    8 +
 .../MakeNullableIntention/before.groovy.template   |    7 +
 .../MakeNullableIntention/description.html         |    5 +
 .../MakeUniqueIntention/after.groovy.template      |    8 +
 .../MakeUniqueIntention/before.groovy.template     |    7 +
 .../MakeUniqueIntention/description.html           |    5 +
 resources/liveTemplates/gsp.xml                    |   36 +
 resources/messages/GrailsBundle.properties         |  311 +
 .../grails/lang/gsp/resolve/taglib/tld/grails.tld  | 2674 +++++++
 resources/standardDsls/GrailsStandardDSL.gdsl      |   51 +
 settings.gradle.kts                                |   14 +
 .../jetbrains/plugins/grails/GantFileDetector.java |   31 +
 .../grails/GrailsAttachSourcesProvider.java        |  114 +
 src/org/jetbrains/plugins/grails/GrailsBundle.java |   26 +
 .../grails/GrailsCompletionContributor.java        |   85 +
 .../grails/GrailsConsoleFilterProvider.java        |  258 +
 .../grails/GrailsFlashMapContentProvider.java      |   24 +
 .../GrailsGroovyCodeReferenceContributor.java      |   89 +
 .../grails/GrailsGspReferenceContributor.java      |   53 +
 .../grails/GrailsHtmlReferenceContributor.java     |   24 +
 .../grails/GrailsImplicitUsageProvider.java        |   91 +
 .../GrailsPluginFieldCompletionProvider.java       |   46 +
 .../GrailsStaticFieldCompletionProvider.java       |  181 +
 .../jetbrains/plugins/grails/GsonConstants.java    |    8 +
 .../plugins/grails/GspCompletionContributor.java   |  128 +
 .../plugins/grails/GspFindUsagesProvider.java      |   13 +
 .../plugins/grails/GspTemplateContextType.java     |   20 +
 .../grails/TemplateHighlightErrorFilter.java       |   52 +
 .../grails/actions/DomainFieldIntention.java       |  228 +
 .../grails/actions/GenerateViewIntention.java      |  162 +
 .../plugins/grails/actions/GrailsActionGroup.java  |   24 +
 .../plugins/grails/actions/GrailsActionUtil.kt     |   86 +
 .../grails/actions/GrailsConfigureSDKAction.java   |   44 +
 .../GrailsCreateActionByUsageIntention.java        |  152 +
 .../grails/actions/GrailsGenerateViewAction.java   |  130 +
 .../grails/actions/GrailsGoToClassContributor.java |  128 +
 .../grails/actions/GrailsGotoRelatedProvider.java  |  134 +
 .../actions/GrailsGroovySourceFolderDetector.java  |   37 +
 .../grails/actions/GrailsRunCommandAction.java     |   71 +
 .../plugins/grails/actions/GrailsWarAction.java    |   46 +
 .../grails/actions/MakeNullableIntention.java      |   27 +
 .../grails/actions/MakeUniqueIntention.java        |   31 +
 .../grails/actions/NewGrailsControllerAction.java  |   51 +
 .../grails/actions/NewGrailsDomainClassAction.java |   43 +
 .../grails/actions/NewGrailsFilterAction.java      |   40 +
 .../grails/actions/NewGrailsInterceptorAction.java |   38 +
 .../grails/actions/NewGrailsScriptAction.java      |   45 +
 .../grails/actions/NewGrailsServiceAction.java     |   43 +
 .../grails/actions/NewGrailsTagLibAction.java      |   57 +
 .../plugins/grails/actions/NewGrailsXXXAction.java |  276 +
 .../plugins/grails/actions/NewGspAction.java       |  104 +
 .../grails/actions/UpdateGrailsSettingsAction.java |   32 +
 .../grails/addins/GrailsIntegrationUtil.java       |   32 +
 .../grails/addins/js/CssIntegrationUtil.java       |   16 +
 .../grails/addins/js/GrailsJavaScriptInjector.java |   95 +
 .../addins/js/JavaScriptIntegrationUtil.java       |   51 +
 .../grails/artefact/api/GrailsArtefactHandler.kt   |   20 +
 .../api/GrailsDisplayableArtefactHandler.kt        |   24 +
 .../plugins/grails/artefact/api/IconOwner.kt       |   13 +
 .../plugins/grails/artefact/api/constants.kt       |   56 +
 .../artefact/impl/ControllerArtefactHandler.kt     |   34 +
 .../grails/artefact/impl/DomainArtefactHandler.kt  |   31 +
 .../grails/artefact/impl/FilterArtefactHandler.kt  |   22 +
 .../artefact/impl/InterceptorArtefactHandler.kt    |   28 +
 .../grails/artefact/impl/ServiceArtefactHandler.kt |   25 +
 .../grails/artefact/impl/TaglibArtefactHandler.kt  |   27 +
 .../grails/artefact/impl/controllers/functions.kt  |   49 +
 .../jetbrains/plugins/grails/artefact/impl/util.kt |  191 +
 .../grails/commands/Grails3CommandProvider.java    |   55 +
 .../commands/GrailsCommandCompletionUtil.java      |   85 +
 .../grails/commands/GrailsCommandProvider.java     |   12 +
 .../grails/commands/OldGrailsCommandProvider.java  |   21 +
 .../grails/config/AbstractConfigSupport.java       |   55 +
 .../BuildConfigClosureMemberContributor.java       |   77 +
 .../config/BuildConfigMemberContributor.java       |   46 +
 .../plugins/grails/config/BuildConfigSupport.java  |   37 +
 .../grails/config/ConfigMemberContributor.java     |   35 +
 .../plugins/grails/config/ConfigSupport.java       |   67 +
 .../plugins/grails/config/DataSourcesSupport.java  |   34 +
 .../grails/config/GradleParentProjectForm.form     |   46 +
 .../grails/config/GradleParentProjectForm.java     |  236 +
 .../grails/config/GrailsAbstractConfigSupport.java |   36 +
 .../config/GrailsConfigUnresolvedFileFilter.java   |   15 +
 .../plugins/grails/config/GrailsConfigUtils.java   |  169 +
 .../plugins/grails/config/GrailsConstants.java     |   19 +
 .../GrailsEnvironmentClosureMemberContributor.java |   50 +
 .../plugins/grails/config/GrailsFramework.java     |  845 ++
 .../config/GrailsGradleAwareModuleBuilder.kt       |  100 +
 .../plugins/grails/config/GrailsIgnoredProvider.kt |   31 +
 .../config/GrailsLibraryPresentationProvider.java  |  134 +
 .../plugins/grails/config/GrailsModuleBuilder.java |  223 +
 .../grails/config/GrailsModuleStructureUtil.java   |  149 +
 .../grails/config/GrailsOptionsWizardStep.form     |   93 +
 .../grails/config/GrailsOptionsWizardStep.java     |   93 +
 .../GrailsPluginConfigMethodContributor.java       |   48 +
 .../GrailsPluginNameCompletionContributor.java     |  165 +
 .../GrailsPluginWebHelpReferenceProvider.java      |   46 +
 .../config/GrailsProjectStructureDetector.java     |  126 +
 .../grails/config/GrailsPropertiesFileCache.java   |   53 +
 .../grails/config/GrailsSettingSynchronizer.java   |  669 ++
 .../plugins/grails/config/GrailsSettings.java      |   75 +
 .../grails/config/GrailsSettingsService.java       |  170 +
 .../plugins/grails/config/GrailsStructure.java     |  189 +
 .../plugins/grails/config/GroovyConfigReader.java  |  183 +
 .../config/PrintGrailsSettingsConstants.java       |   42 +
 .../grails/database/GrailsDataSourceDetector.java  |   84 +
 .../plugins/grails/editor/GenerateTestsAction.java |  122 +
 .../plugins/grails/editor/GrailsEditorDecorator.kt |   90 +
 .../grails/editor/ViewGrailsEditorToolbarAction.kt |   40 +
 .../selection/GrailsDependencySelectioner.java     |   90 +
 .../GrailsGroovyInjectionSelectioner.java          |   38 +
 .../grails/editor/toolbar/GrailsGenerateActions.kt |   75 +
 .../editor/toolbar/GrailsGoToArtefactActionBase.kt |   33 +
 .../editor/toolbar/GrailsToolbarActionBase.kt      |   47 +
 .../grails/editor/toolbar/GrailsToolbarActions.kt  |  119 +
 .../editor/toolbar/GrailsToolbarTargetAction.kt    |   39 +
 .../editor/toolbar/GrailsToolbarVfileAction.kt     |   21 +
 .../feature/forge/GrailsForgeModuleBuilder.kt      |  290 +
 .../grails/feature/linkable/namedArguments.kt      |   73 +
 .../plugins/grails/feature/linkable/transform.kt   |   40 +
 .../plugins/grails/fileType/GspFileType.java       |  104 +
 .../plugins/grails/gorm/GormClassNames.java        |   10 +
 .../jetbrains/plugins/grails/gorm/gormVersion.kt   |   75 +
 .../plugins/grails/gradle/GrailsModuleData.java    |   71 +
 .../gradle/GrailsModuleGradleDataService.java      |   56 +
 .../gradle/GrailsProjectResolverExtension.java     |   61 +
 .../grails/gradle/GrailsTaskManagerExtension.java  |   89 +
 .../GrailsStreamingJsonBuilderContributor.java     |   14 +
 .../grails/gson/GsonControllerReferences.kt        |   75 +
 .../grails/gson/GsonExpectedPackageProvider.kt     |   15 +
 .../plugins/grails/gson/GsonImportContributor.kt   |   23 +
 .../grails/gson/GsonModelArgumentsProvider.kt      |   26 +
 .../gson/GsonModelFieldArgumentDescriptor.kt       |   23 +
 .../grails/gson/GsonPositionManagerDelegate.kt     |   58 +
 .../grails/gson/GsonReferenceContributor.kt        |   15 +
 .../plugins/grails/gson/GsonRenderReferences.kt    |  152 +
 .../grails/gson/GsonTemplateRendererContributor.kt |   39 +
 .../grails/gson/GsonTransformationSupport.kt       |   39 +
 src/org/jetbrains/plugins/grails/gson/GsonUtils.kt |   67 +
 .../inspections/GrailsMethodMayBeStaticFilter.java |   23 +
 .../grails/inspections/GspAndGroovyInspection.java |   59 +
 .../grails/inspections/GspInspectionUtil.java      |   11 +
 .../grails/inspections/GspSuppressionProvider.java |   43 +
 .../grails/lang/DirtyCheckableContributor.kt       |   55 +
 .../plugins/grails/lang/GormTraitContributor.kt    |  120 +
 .../lang/Grails3TraitInjectorContributor.java      |   45 +
 .../plugins/grails/lang/gsp/GspCssInjector.java    |   46 +
 .../plugins/grails/lang/gsp/GspDirectiveKind.java  |   51 +
 .../plugins/grails/lang/gsp/GspErrorFilter.java    |   58 +
 .../grails/lang/gsp/GspFileViewProvider.java       |  106 +
 .../lang/gsp/GspFileviewProviderFactory.java       |   17 +
 .../plugins/grails/lang/gsp/GspLanguage.java       |   19 +
 .../lang/gsp/GspTagDocumentationProvider.java      |   42 +
 .../plugins/grails/lang/gsp/GspTagInspection.java  |   31 +
 .../gsp/completion/GspXmlFileNSInfoProvider.java   |   52 +
 .../lang/gsp/debug/GspCodeFragmentFactory.java     |   56 +
 .../grails/lang/gsp/debug/GspPositionManager.java  |  169 +
 .../lang/gsp/debug/GspPositionManagerFactory.java  |   15 +
 .../gsp/editor/actions/GspEditorActionsUtil.java   |   57 +
 .../lang/gsp/editor/actions/GspEnterHandler.java   |   66 +
 .../lang/gsp/editor/actions/GspTypedHandler.java   |  259 +
 .../grails/lang/gsp/folding/GspFoldingBuilder.java |  132 +
 .../lang/gsp/formatter/AbstractGspBlock.java       |   11 +
 .../lang/gsp/formatter/ForeignLanguageBlock.java   |   63 +
 .../grails/lang/gsp/formatter/GspBlock.java        |  158 +
 .../lang/gsp/formatter/GspBlockGenerator.java      |  325 +
 .../lang/gsp/formatter/GspFormattingHelper.java    |  279 +
 .../gsp/formatter/GspFormattingModelBuilder.java   |   50 +
 .../grails/lang/gsp/formatter/GspGroovyBlock.java  |  193 +
 .../grails/lang/gsp/formatter/GspHtmlBlock.java    |  397 +
 .../lang/gsp/formatter/GspTextSyntheticBlock.java  |  113 +
 .../formatter/processors/GspIndentProcessor.java   |   68 +
 .../formatter/processors/GspSpacingProcessor.java  |  124 +
 .../settings/GspCodeStyleSettingsProvider.java     |   56 +
 .../GspLanguageCodeStyleSettingsProvider.java      |   41 +
 .../grails/lang/gsp/gspIndex/GspIncludeIndex.java  |  365 +
 .../grails/lang/gsp/gspIndex/GspIncludeInfo.java   |   46 +
 .../grails/lang/gsp/highlighter/GspCommenter.java  |   33 +
 .../gsp/highlighter/GspDirectiveHighlighter.java   |   59 +
 .../highlighter/GspDirectiveHighlightingLexer.java |   24 +
 .../lang/gsp/highlighter/GspEditorHighlighter.java |   42 +
 .../highlighter/GspEditorHighlighterProvider.kt    |   21 +
 .../lang/gsp/highlighter/GspFileBraceMatcher.java  |   44 +
 .../gsp/highlighter/GspIndexPatternBuilder.java    |   76 +
 .../gsp/highlighter/GspPairedBraceMatcher.java     |   41 +
 .../lang/gsp/highlighter/GspSyntaxHighlighter.java |   52 +
 .../lang/gsp/lexer/GspDirectiveFlexLexer.java      |   22 +
 .../grails/lang/gsp/lexer/GspFlexLexer.java        |   29 +
 .../grails/lang/gsp/lexer/GspTokenTypesEx.java     |   47 +
 .../grails/lang/gsp/lexer/IGspElementType.java     |   12 +
 .../grails/lang/gsp/lexer/core/GspTokenTypes.java  |   63 +
 .../plugins/grails/lang/gsp/lexer/core/gsp.flex    |  425 +
 .../grails/lang/gsp/lexer/core/gsp_directive.flex  |  123 +
 .../grails/lang/gsp/parsing/GspElementTypes.java   |   98 +
 .../lang/gsp/parsing/GspGroovyElementTypes.java    |   48 +
 .../grails/lang/gsp/parsing/GspPsiCreator.java     |   22 +
 .../parsing/composite/GspCompositeElementType.java |   12 +
 .../gsp/parsing/groovy/GspAwareGroovyParser.java   |   51 +
 .../lang/gsp/parsing/groovy/GspGroovyParser.java   |   29 +
 .../gsp/parsing/groovy/GspTemplateStmtParsing.java |   96 +
 .../chameleons/GroovyDeclarationsElementType.kt    |   13 +
 .../GroovyDeclarationsInGspFileRoot.java           |   39 +
 .../chameleons/GroovyExpressionElementType.kt      |   13 +
 .../chameleons/GroovyMapAttributeValueType.kt      |   13 +
 .../groovy/chameleons/GspGroovyLazyElementType.kt  |   24 +
 .../gsp/parsing/groovy/lexer/GspGroovyLexer.java   |  188 +
 .../grails/lang/gsp/parsing/gsp/GrailsTag.java     |  183 +
 .../grails/lang/gsp/parsing/gsp/GspParser.java     |  118 +
 .../lang/gsp/parsing/gsp/GspParserDefinition.java  |  104 +
 .../gsp/chameleons/GspDirectiveElement.java        |  136 +
 .../lang/gsp/parsing/gsp/lexer/GspLexer.java       |   35 +
 .../gsp/parsing/html/GspHtmlTemplateRootType.java  |  170 +
 .../gsp/parsing/html/elements/GspXmlDocument.java  |   30 +
 .../parsing/outers/GspGroovyCodeElementType.java   |   21 +
 .../gsp/parsing/outers/GspInHtmlElementType.java   |   23 +
 .../parsing/outers/GspTemplateDataElementType.java |   21 +
 .../grails/lang/gsp/psi/GspPsiElementFactory.java  |   29 +
 .../lang/gsp/psi/GspPsiElementFactoryImpl.java     |   59 +
 .../plugins/grails/lang/gsp/psi/GspPsiUtil.java    |   30 +
 .../grails/lang/gsp/psi/groovy/GspLazyElement.java |   21 +
 .../grails/lang/gsp/psi/groovy/api/GrGspClass.java |   22 +
 .../gsp/psi/groovy/api/GrGspDeclarationHolder.java |   16 +
 .../gsp/psi/groovy/api/GrGspExprInjection.java     |   15 +
 .../lang/gsp/psi/groovy/api/GrGspRunBlock.java     |   11 +
 .../lang/gsp/psi/groovy/api/GrGspRunMethod.java    |   11 +
 .../gsp/psi/groovy/api/GrMapAttributeValue.java    |    6 +
 .../lang/gsp/psi/groovy/api/GspGroovyFile.java     |   13 +
 .../gsp/psi/groovy/api/GspOuterGroovyElement.java  |    8 +
 .../gsp/psi/groovy/api/GspOuterHtmlElement.java    |    9 +
 .../groovy/api/template/GspTemplateStatement.java  |    6 +
 .../lang/gsp/psi/groovy/impl/GrGspClassImpl.java   |  445 ++
 .../groovy/impl/GrGspDeclarationHolderImpl.java    |   73 +
 .../psi/groovy/impl/GrGspExprInjectionImpl.java    |   43 +
 .../gsp/psi/groovy/impl/GrGspRunBlockImpl.java     |  124 +
 .../gsp/psi/groovy/impl/GrGspRunMethodImpl.java    |   29 +
 .../gsp/psi/groovy/impl/GrMapAttrValueImpl.java    |   42 +
 .../gsp/psi/groovy/impl/GspGroovyFileImpl.java     |  121 +
 .../gsp/psi/groovy/impl/GspModelVariableModel.java |  425 +
 .../psi/groovy/impl/GspOuterGroovyElementImpl.java |   22 +
 .../psi/groovy/impl/GspOuterHtmlElementImpl.java   |   79 +
 .../impl/GspOuterHtmlElementManipulator.java       |   26 +
 .../lang/gsp/psi/groovy/impl/GspPageSkeleton.java  |  361 +
 .../impl/ModelVariableDeclarationSearcher.java     |  118 +
 .../impl/template/GspTemplateStatementImpl.java    |   21 +
 .../lang/gsp/psi/gsp/api/GspDeclarationTag.java    |    6 +
 .../lang/gsp/psi/gsp/api/GspExpressionTag.java     |    6 +
 .../grails/lang/gsp/psi/gsp/api/GspFile.java       |   44 +
 .../lang/gsp/psi/gsp/api/GspScriptletTag.java      |    6 +
 .../grails/lang/gsp/psi/gsp/api/GspTag.java        |    8 +
 .../grails/lang/gsp/psi/gsp/api/GspXmlTagBase.java |   13 +
 .../gsp/psi/gsp/api/directive/GspDirective.java    |   14 +
 .../gsp/api/directive/GspDirectiveAttribute.java   |    8 +
 .../api/directive/GspDirectiveAttributeValue.java  |    8 +
 .../lang/gsp/psi/gsp/api/gtag/GspAttribute.java    |    8 +
 .../gsp/psi/gsp/api/gtag/GspAttributeValue.java    |    9 +
 .../lang/gsp/psi/gsp/api/gtag/GspGrailsTag.java    |   14 +
 .../gsp/psi/gsp/api/gtag/GspTaggedElement.java     |    9 +
 .../lang/gsp/psi/gsp/api/gtag/GspXmlRootTag.java   |    8 +
 .../gsp/psi/gsp/impl/GspDeclarationTagImpl.java    |   19 +
 .../lang/gsp/psi/gsp/impl/GspExprTagImpl.java      |   18 +
 .../grails/lang/gsp/psi/gsp/impl/GspFileImpl.java  |  315 +
 .../lang/gsp/psi/gsp/impl/GspScriptletTagImpl.java |   19 +
 .../lang/gsp/psi/gsp/impl/GspXmlTagBaseImpl.java   |   45 +
 .../impl/directive/GspDirectiveAttributeImpl.java  |   41 +
 .../directive/GspDirectiveAttributeValueImpl.java  |   78 +
 .../psi/gsp/impl/directive/GspDirectiveImpl.java   |  106 +
 .../gsp/psi/gsp/impl/gtag/GspAttributeImpl.java    |   68 +
 .../psi/gsp/impl/gtag/GspAttributeValueImpl.java   |   18 +
 .../impl/gtag/GspContentTagDescriptorProvider.java |  193 +
 .../gsp/impl/gtag/GspElementDescriptorBase.java    |  128 +
 .../gsp/psi/gsp/impl/gtag/GspGrailsTagImpl.java    |  156 +
 .../gsp/impl/gtag/GspLinkElementDescriptor.java    |  211 +
 .../gsp/impl/gtag/GspLinkNamespaceDescriptor.java  |   95 +
 .../psi/gsp/impl/gtag/GspNamespaceDescriptor.java  |  130 +
 .../impl/gtag/GspPropertyElementDescriptor.java    |   49 +
 .../psi/gsp/impl/gtag/GspSdkElementDescriptor.java |   53 +
 .../psi/gsp/impl/gtag/GspTagDescriptorService.java |  242 +
 .../gsp/psi/gsp/impl/gtag/GspTagRuleProvider.java  |   35 +
 .../gtag/GspTagVariableDeclarationSearcher.java    |   68 +
 .../gsp/impl/gtag/GspTmplNamespaceDescriptor.java  |  191 +
 .../gsp/psi/gsp/impl/gtag/GspXmlRootTagImpl.java   |  208 +
 .../DefaultCodecDirectiveReferenceProvider.java    |   18 +
 .../GspCssClassReferenceContributor.java           |   60 +
 .../lang/gsp/psi/html/api/GspHtmlOuterElement.java |    8 +
 .../grails/lang/gsp/psi/html/api/GspLikeFile.java  |   13 +
 .../lang/gsp/psi/html/impl/GspHtmlFileImpl.java    |  199 +
 .../gsp/psi/html/impl/GspHtmlOuterElementImpl.java |   21 +
 .../gsp/psi/html/impl/GspHtmlXmlExtension.java     |   15 +
 .../grails/lang/gsp/psi/impl/gspImportUtil.kt      |   68 +
 .../taglib/BuiltInTagReferenceSearcher.java        |   42 +
 .../taglib/GrailsTaglibNamedArgumentProvider.java  |   85 +
 .../lang/gsp/resolve/taglib/GspTagLibUtil.java     |  428 +
 .../resolve/taglib/TagLibNamespaceDescriptor.java  |  262 +
 .../gsp/search/GspControllerReferenceSearcher.java |   42 +
 .../lang/gsp/search/GspPageReferenceSearcher.java  |   56 +
 .../lang/gsp/search/JspPageReferenceSearcher.java  |   42 +
 .../plugins/grails/lang/gsp/util/GspUtil.java      |   78 +
 .../perspectives/DomainClassNodeRenderer.java      |  145 +
 .../perspectives/DomainClassesRelationsEditor.java |   83 +
 .../DomainClassesRelationsEditorProvider.java      |   51 +
 .../PerspectiveFileEditorComponent.java            |  133 +
 .../perspectives/create/CreateNewRelation.form     |   84 +
 .../perspectives/create/CreateNewRelation.java     |  311 +
 .../create/RelationsCreationsProvider.java         |  313 +
 .../delete/RelationDeleteProvider.java             |  241 +
 .../DataModelAndSelectionModificationTracker.java  |   23 +
 .../graph/DomainClassDependencyPresentation.java   |  107 +
 .../grails/perspectives/graph/DomainClassNode.java |   43 +
 .../graph/DomainClassRelationsInfo.java            |  109 +
 .../graph/DomainClassesRelationsDataModel.java     |  133 +
 .../assetPipeline/AssetScriptInjector.kt           |   39 +
 .../pluginSupport/assetPipeline/references.kt      |  101 +
 .../GrailsBuildTestDataMemberProvider.java         |  113 +
 .../resources/GrailsResourceModuleReference.java   |   67 +
 .../resources/GrailsResourceReferenceSearcher.java |   74 +
 .../GrailsResourcesClosureMethodContributor.java   |   31 +
 .../GrailsResourcesPomDeclarationSearcher.java     |   42 +
 .../GrailsResourcesReferenceProvider.java          |  111 +
 .../resources/GrailsResourcesUtil.java             |  210 +
 .../seachable/GrailsSearchableMemberProvider.java  |  130 +
 .../GrailsSearchableReturnTypeCalculator.java      |  142 +
 .../seachable/GrailsSearchableUtil.java            |   43 +
 .../SearchableFieldReferenceProvider.java          |   67 +
 .../SearchableMemberMethodContributor.java         |   76 +
 .../GrailsShiroAccessControlMethodProvider.java    |   35 +
 .../shiro/GrailsShiroReferenceProvider.java        |   37 +
 .../pluginSupport/spock/GrailsSpockEnhancer.java   |  111 +
 .../WebFlowEventDefinitionMethodContributor.java   |   54 +
 .../webflow/WebFlowMethodContributor.java          |   66 +
 .../webflow/WebFlowPomDeclarationSearcher.java     |   36 +
 .../webflow/WebFlowStateNameReferenceProvider.java |   56 +
 .../grails/pluginSupport/webflow/WebFlowUtils.java |  129 +
 .../plugins/grails/plugins/descriptors.kt          |   34 +
 src/org/jetbrains/plugins/grails/plugins/impl.kt   |   55 +
 .../grails/projectView/GrailsIconProvider.java     |   30 +
 .../grails/projectView/GrailsPluginsNode.kt        |   36 +
 .../grails/projectView/GrailsProjectViewPane.kt    |   52 +
 .../projectView/GrailsTreeStructureProvider.kt     |   46 +
 .../plugins/grails/projectView/NodeWeights.java    |   20 +
 .../projectView/api/GrailsSingleNodeProvider.kt    |   18 +
 .../projectView/api/GrailsViewNodeProvider.kt      |   14 +
 .../jetbrains/plugins/grails/projectView/api/ep.kt |    9 +
 .../grails/projectView/impl/Grails3NodeProvider.kt |   47 +
 .../projectView/impl/GrailsAppNodeProvider.kt      |   39 +
 .../projectView/impl/GrailsNodeComparator.kt       |   48 +
 .../projectView/impl/OldGrailsNodeProvider.kt      |   89 +
 .../plugins/grails/projectView/impl/impl.kt        |   40 +
 .../grails/projectView/nodes/CompactedFqn.kt       |   53 +
 .../grails/projectView/nodes/GrailsActionNode.kt   |   32 +
 .../projectView/nodes/GrailsApplicationNode.kt     |   47 +
 .../projectView/nodes/GrailsArtefactHandlerNode.kt |   29 +
 .../nodes/GrailsArtefactHandlerNodeBase.kt         |   25 +
 .../projectView/nodes/GrailsArtefactPackageNode.kt |   36 +
 .../projectView/nodes/GrailsControllerNode.kt      |   27 +
 .../projectView/nodes/GrailsFlatPackageNode.kt     |   41 +
 .../grails/projectView/nodes/GrailsPluginNode.kt   |   44 +
 .../projectView/nodes/GrailsPsiDirectoryNode.kt    |   30 +
 .../projectView/nodes/GrailsRegularPackageNode.kt  |   68 +
 .../grails/projectView/nodes/GrailsRootNode.kt     |   23 +
 .../projectView/nodes/OldGrailsPluginsNode.kt      |   40 +
 .../projectView/nodes/OtherGrailsAppSourcesNode.kt |   75 +
 .../plugins/grails/projectView/nodes/impl.kt       |  100 +
 .../plugins/grails/projectView/nodes/util.kt       |   34 +
 .../plugins/grails/projectView/showHide.kt         |   29 +
 .../references/CodecNonCodeMemberContributor.java  |  106 +
 .../grails/references/EnhancesTraitsCollector.kt   |   51 +
 .../GrailsArtifactNonCodeMemberProcessor.java      |   90 +
 .../references/GrailsArtifactRenameProcessor.java  |   63 +
 .../references/GrailsClosureMemberContributor.java |   63 +
 ...GrailsMethodNamedArgumentReferenceProvider.java |  349 +
 .../GrailsResourceClassMemberContributor.java      |   48 +
 .../plugins/grails/references/MemberProvider.java  |   22 +
 .../grails/references/TraitInjectorService.kt      |  323 +
 .../bootstrap/GrailsBootStrapMemberProvider.java   |   23 +
 .../BuildConfigFileReferenceProvider.java          |   73 +
 .../common/ArtifactFieldReferenceSearcher.java     |   38 +
 .../ArtifactPropertyGetterReferenceSearcher.java   |   84 +
 .../grails/references/common/CodecReference.java   |   53 +
 .../references/common/ContentTypeReference.java    |   37 +
 .../common/ContentTypeReferenceProvider.java       |   16 +
 .../common/ContextPathReferenceProvider.java       |   75 +
 .../common/GrailsEncodingReferenceProvider.java    |   39 +
 .../common/GrailsFileReferenceSetBase.java         |   97 +
 .../common/GrailsLayoutFileReferenceSet.java       |  134 +
 .../common/GrailsRootBasedFileReferenceSet.java    |   80 +
 .../common/GrailsViewFileReferenceSet.java         |  157 +
 .../common/GroovyGspAttributeWrapper.java          |   39 +
 .../references/common/GroovyGspTagWrapper.java     |   73 +
 .../references/common/GspAttributeWrapper.java     |   20 +
 .../grails/references/common/GspTagWrapper.java    |   30 +
 .../grails/references/common/PluginReference.java  |   48 +
 .../common/PluginSupportFileReferenceSet.java      |  246 +
 .../ResourceDirAttributeFileReferenceSet.java      |   90 +
 .../common/TemplateFileReferenceSet.java           |  294 +
 .../common/WebAppFolderFileReferenceSet.java       |   53 +
 .../references/common/XmlGspAttributeWrapper.java  |   31 +
 .../grails/references/common/XmlGspTagWrapper.java |   66 +
 .../references/constraints/GormRegexInjector.java  |   54 +
 ...GrailsConstraintGroupNamedArgumentProvider.java |   53 +
 .../GrailsConstraintNamedArgumentProvider.java     |  277 +
 .../GrailsConstraintsClosureMemberProvider.java    |   54 +
 ...railsConstraintsGroupClosureMemberProvider.java |   51 +
 .../GrailsConstraintsSharedReferenceProvider.java  |   96 +
 .../constraints/GrailsConstraintsUtil.java         |   70 +
 .../GrailsValidationMethodsContributor.java        |   44 +
 .../GrailsValidatorParamTypeEnhancer.java          |   42 +
 .../references/controller/ActionReference.java     |  121 +
 .../controller/ActionRenameProcessor.java          |   31 +
 .../ControllerAllowedMethodCompletionProvider.java |   69 +
 .../ControllerAllowedMethodReferenceProvider.java  |   88 +
 .../ControllerFieldReferenceProvider.java          |   46 +
 .../ControllerLayoutReferenceProvider.java         |   25 +
 .../controller/ControllerMembersProvider.java      |  159 +
 .../references/controller/ControllerReference.java |  101 +
 .../controller/ControllerReferenceProvider.java    |  190 +
 .../controller/ControllerRenameProcessor.java      |   40 +
 .../ControllerRenderTemplateRefProvider.java       |   52 +
 .../controller/GrailsNamespaceReference.java       |   76 +
 .../GrailsWithFormatClosureMethodContributor.java  |   67 +
 .../controller/GspTagMethodComparator.java         |   33 +
 .../controller/GspTagMethodOverloadResolver.kt     |   15 +
 .../controller/HttpRequestMemberContributor.java   |  106 +
 .../controller/HttpResponseMemberContributor.java  |   50 +
 .../grails/references/domain/DomainClassUtils.java |  703 ++
 .../grails/references/domain/DomainDescriptor.java |  662 ++
 .../domain/DomainFieldRenameProcessor.java         |  205 +
 .../references/domain/DomainMembersProvider.java   |  249 +
 .../domain/GormAddToNamedArgumentProvider.java     |   68 +
 .../domain/GormAstTransformationContributor.java   |  225 +
 .../domain/GormConstraintMethodProvider.java       |   50 +
 .../GormDynamicFinderCompletionProvider.java       |  314 +
 .../GormEmbeddedPropertyListReferenceProvider.java |   49 +
 .../domain/GormFetchModeCompletionProvider.java    |   82 +
 .../domain/GormFetchModeReferenceProvider.java     |   48 +
 ...GormGetPersistentValueReturnTypeCalculator.java |   34 +
 .../domain/GormMappingMethodProvider.java          |  107 +
 .../domain/GormNamedArgumentReferenceProvider.java |   26 +
 .../GormPersistentPropertiesNamedArgProvider.java  |   53 +
 ...rmPropertiesListReferenceReferenceProvider.java |   84 +
 .../GormPropertyConstraintReferenceProvider.java   |   99 +
 .../references/domain/GormPropertyReference.java   |   56 +
 .../domain/GormPropertyReferenceUnique.java        |   49 +
 .../domain/GormSortNamedArgumentProvider.java      |   51 +
 .../GormUniqueConstraintReferenceProvider.java     |   78 +
 .../grails/references/domain/GormUtils.java        |  103 +
 .../GrailsHasManyBelongsToReferencesProvider.java  |  116 +
 ...lsHasManyBelongsToValuesReferencesProvider.java |  114 +
 .../GrailsSyntheticFieldDeclarationSearcher.java   |   64 +
 .../CriteriaBuilderImplicitMemberContributor.java  |   98 +
 .../CriteriaBuilderNamedArgumentProvider.java      |   66 +
 .../domain/criteria/CriteriaBuilderUtil.java       |  346 +
 .../criteria/CriteriaClosureMemberContributor.java |   84 +
 .../CriteriaPropertyReferenceProvider.java         |   51 +
 .../criteria/CriteriaReturnTypeCalculator.java     |  107 +
 .../DetachedCriteriaClosureMemberProvider.java     |   88 +
 .../DetachedCriteriaDelegatesToProvider.kt         |   41 +
 .../DetachedCriteriaMemberProvider.java            |   49 +
 .../DetachedCriteriaPropertyNamedArgProvider.java  |   23 +
 .../DetachedCriteriaReferenceProvider.java         |   55 +
 .../detachedCriteria/DetachedCriteriaUtil.java     |   62 +
 .../GormNamedQueryCallTypeCalculator.java          |   50 +
 .../GormNamedQueryDeclarationSearcher.java         |   32 +
 .../NamedCriteriaProxyMembersContributor.java      |   65 +
 .../NamedCriteriaProxyNamedArgumentProvider.java   |   22 +
 .../NamedCriteriaProxyTypeCalculator.java          |   81 +
 .../domain/namedQuery/NamedQueryDescriptor.java    |  119 +
 .../domain/persistent/GormBasicAttribute.java      |   43 +
 .../domain/persistent/GormCollectionAttribute.java |   32 +
 .../domain/persistent/GormEmbeddedAttribute.java   |   31 +
 .../references/domain/persistent/GormEntity.java   |  168 +
 .../domain/persistent/GormIdAttribute.java         |   25 +
 .../domain/persistent/GormPersistenceMapping.java  |   90 +
 .../domain/persistent/GormPersistentAttribute.java |   93 +
 .../filter/FilterClosureMemberProvider.java        |   64 +
 .../references/filter/FilterMemberProvider.java    |   30 +
 .../references/filter/FilterReferenceProvider.java |  153 +
 .../GrailsFilterClosureMemberContributor.java      |   55 +
 .../grails/references/filter/GrailsFilterUtil.java |   41 +
 .../references/jobs/JobClosureMethodProvider.java  |   30 +
 .../grails/references/jobs/JobsMemberProvider.java |   28 +
 ...ailsTemplateNamedArgumentReferenceProvider.java |   29 +
 .../GrailsPluginClassMemberContributor.java        |   71 +
 .../GrailsPluginExcludeReferenceProvider.java      |   28 +
 .../GrailsPluginListReferenceProvider.java         |  131 +
 .../pluginClass/GrailsPluginParameterEnhancer.java |   48 +
 .../providers/GspImportListReferenceProvider.java  |   93 +
 .../tagSupport/CodecAttributeSupport.java          |   26 +
 .../tagSupport/GspActionAttributeSupport.java      |   61 +
 .../GspApplyLayoutNameAttributeSupport.java        |   24 +
 .../tagSupport/GspContextPathAttributeSupport.java |   25 +
 .../tagSupport/GspControllerAttributeSupport.java  |   26 +
 .../tagSupport/GspFieldValueTagSupport.java        |  152 +
 .../tagSupport/GspIncludeViewAttributeSupport.java |   35 +
 .../tagSupport/GspLayoutReferenceProvider.java     |   22 +
 .../tagSupport/GspMappingAttributeSupport.java     |   56 +
 .../references/tagSupport/GspMetaTagSupport.java   |   64 +
 .../tagSupport/GspPluginAttributeSupport.java      |   29 +
 .../tagSupport/GspRResourceModuleAttrSupport.java  |   23 +
 .../GspRResourceRequireTagModulesAttrSupport.java  |   23 +
 .../GspRResourceUriAttributeSupport.java           |   43 +
 .../GspResourceContextPathAttributeSupport.java    |   46 +
 .../tagSupport/GspResourceDirAttributeSupport.java |   69 +
 .../GspResourceFileAttributeSupport.java           |  103 +
 .../references/tagSupport/GspSelectTagSupport.java |  104 +
 .../tagSupport/GspSrcJsAttributeSupport.java       |   43 +
 .../GspTagSupportGspReferenceProvider.java         |  114 +
 .../tagSupport/GspTemplateAttributeSupport.java    |   64 +
 .../tagSupport/GspUpdateAttributeSupport.java      |   30 +
 .../tagSupport/TagAttributeReferenceProvider.java  |   68 +
 .../taglib/TaglibClosureParamEnhancer.java         |   53 +
 .../references/taglib/TaglibMembersProvider.java   |   78 +
 .../UrlMappingConstraintClosureMemberProvider.java |   77 +
 .../urlMappings/UrlMappingMemberProvider.java      |   83 +
 .../UrlMappingNamedArgumentProvider.java           |   67 +
 .../urlMappings/UrlMappingReferenceProvider.java   |   53 +
 .../UrlMappingUnresolvedHighlightingFilter.java    |   34 +
 .../references/urlMappings/UrlMappingUtil.java     |  263 +
 .../plugins/grails/references/util/CodecUtil.java  |  191 +
 .../grails/references/util/PsiFieldReference.java  |   36 +
 .../runner/GrailsAppEngineDebuggerRunner.java      |   45 +
 .../grails/runner/GrailsCommandExecutor.java       |   44 +
 .../grails/runner/GrailsCommandExecutorUtil.java   |   99 +
 .../grails/runner/GrailsCommandLineExecutor.java   |   93 +
 .../grails/runner/GrailsConfigurationFactory.java  |   46 +
 .../plugins/grails/runner/GrailsConsole.java       |  397 +
 .../plugins/grails/runner/GrailsConsoleState.kt    |    9 +
 .../plugins/grails/runner/GrailsDebuggerRunner.kt  |   99 +
 .../grails/runner/GrailsInstallationExecutor.java  |   19 +
 .../runner/GrailsRerunFailedTestsAction.java       |   68 +
 .../grails/runner/GrailsRunConfiguration.java      |  219 +
 .../runner/GrailsRunConfigurationExtension.java    |   40 +
 .../grails/runner/GrailsRunConfigurationType.java  |   50 +
 .../GrailsScriptRunConfigurationProducer.java      |   76 +
 ...atableRunConfigurationWithCommonParameters.java |   89 +
 .../runner/impl/BaseGrailsCommandLineState.kt      |   25 +
 .../runner/impl/Grails3GradleCommandExecutor.java  |   45 +
 .../impl/Grails3InstallationCommandExecutor.java   |   95 +
 .../runner/impl/Grails3TestCommandLineState.kt     |   76 +
 .../grails/runner/impl/GrailsCommandLineState.java |  117 +
 .../impl/GrailsInstallationCommandExecutor.java    |  379 +
 .../runner/impl/GrailsRunAppCommandLineState.kt    |   31 +
 .../runner/impl/GrailsTestAppCommandLineState.kt   |   15 +
 .../runner/impl/GrailsTestCommandLineState.java    |   73 +
 src/org/jetbrains/plugins/grails/runner/setup.kt   |   85 +
 .../runner/ui/GrailsApplicationCombobox.java       |   79 +
 .../runner/ui/GrailsRunConfigurationEditor.form    |  101 +
 .../runner/ui/GrailsRunConfigurationEditor.java    |  185 +
 .../ui/GrailsRunConfigurationEditorListener.java   |   12 +
 .../GrailsRunConfigurationEditorWithListener.java  |   11 +
 .../grails/runner/util/GrailsExecutionUtils.java   |  125 +
 .../jetbrains/plugins/grails/sdk/GrailsSDK.java    |   50 +
 .../plugins/grails/sdk/GrailsSDKManager.java       |   83 +
 .../grails/service/GrailsBackgroundService.java    |   27 +
 .../spring/GrLightVariableRenameProcessor.java     |   36 +
 .../grails/spring/GrailsCustomSpringComponent.java |   29 +
 .../GrailsInjectedBeanCompletionContributor.java   |   78 +
 .../grails/spring/GrailsResourceBeanExtractor.java |  218 +
 .../GrailsResourcesGroovyMemberContributor.java    |  255 +
 .../grails/spring/GrailsSpringBeanDiscoverer.java  |  237 +
 .../grails/spring/GrailsSpringFacetProvider.java   |   87 +
 .../GrailsSpringMethodReferenceProvider.java       |   24 +
 .../grails/spring/GrailsSpringSemContributor.kt    |   25 +
 ...ilsSpringStereotypeAliasingPsiTargetMapper.java |   56 +
 ...ilsWebApplicationContextMembersContributor.java |   72 +
 .../grails/spring/InjectedSpringBeanProvider.java  |   80 +
 .../grails/spring/SpringBeanReferenceSearcher.java |   76 +
 .../grails/spring/SpringGrailsTargetMapper.java    |   63 +
 .../grails/spring/SpringInjectionAnnotator.java    |   62 +
 .../plugins/grails/structure/Grails3Application.kt |   12 +
 .../plugins/grails/structure/GrailsApplication.kt  |   42 +
 .../grails/structure/GrailsApplicationBase.kt      |   46 +
 .../structure/GrailsApplicationListener.java       |   12 +
 .../grails/structure/GrailsApplicationManager.java |  191 +
 .../GrailsApplicationManagerRefresher.java         |   82 +
 .../structure/GrailsApplicationProvider.java       |   31 +
 .../grails/structure/GrailsCommonClassNames.kt     |   72 +
 .../grails/structure/GrailsSDKListener.java        |   13 +
 .../grails/structure/OldGrailsApplication.kt       |   15 +
 .../grails/structure/impl/Grails2Application.kt    |   34 +
 .../structure/impl/Grails3ApplicationBase.kt       |   43 +
 .../structure/impl/Grails3ApplicationProvider.kt   |   42 +
 .../impl/Grails3MultiModuleApplication.kt          |   43 +
 .../impl/Grails3SingleModuleApplication.kt         |   39 +
 .../structure/impl/GrailsModuleBasedApplication.kt |   48 +
 .../impl/OldGrailsModuleBasedApplication.kt        |   57 +
 .../impl/PlainGrailsApplicationProvider.java       |   38 +
 .../plugins/grails/structure/impl/util.kt          |    9 +
 .../sync/GrailsApplicationBackgroundTask.java      |   61 +
 .../sync/GrailsMvcStructureRefresher.java          |   45 +
 .../structure/sync/GrailsRunConfigurationTask.java |   54 +
 .../grails/structure/sync/GrailsSdkCheckTask.java  |   44 +
 .../structure/sync/GrailsStartupActivity.java      |   57 +
 src/org/jetbrains/plugins/grails/structure/util.kt |   15 +
 .../grails/tests/GrailsJUnitRecognizer.java        |   40 +
 .../grails/tests/GrailsTestMemberContributor.java  |  194 +
 .../plugins/grails/tests/GrailsTestUtils.java      |  231 +
 .../plugins/grails/tests/GrailsTestUtils.kt        |   25 +
 .../grails/tests/GrailsTestVariableEnhancer.java   |   55 +
 .../runner/GrailsTestConfigurationProducer.java    |  180 +
 .../grails/tests/runner/GrailsUrlProvider.java     |   75 +
 .../plugins/grails/tests/runner/byClass.kt         |   83 +
 .../plugins/grails/tests/runner/byDirectory.kt     |   94 +
 .../plugins/grails/tests/runner/oldUtils.kt        |   24 +
 .../grails/ui/EditorComboBoxWithHistory.java       |   99 +
 .../grails/ui/GrailsConfigureSDKDialog.form        |   47 +
 .../grails/ui/GrailsConfigureSDKDialog.java        |   93 +
 .../plugins/grails/ui/GrailsRunCommandDialog.form  |   68 +
 .../plugins/grails/ui/GrailsRunCommandDialog.java  |  150 +
 .../plugins/grails/ui/GrailsSDKHomeForm.form       |   46 +
 .../plugins/grails/ui/GrailsSDKHomeForm.java       |  167 +
 .../plugins/grails/util/DelegateReference.java     |   82 +
 .../plugins/grails/util/GrailsArtifact.java        |  463 ++
 .../util/GrailsArtifactTransformerUtils.java       |  118 +
 .../plugins/grails/util/GrailsCachedValue.java     |   41 +
 .../plugins/grails/util/GrailsFacetProvider.java   |   17 +
 .../plugins/grails/util/GrailsNameUtils.java       |  382 +
 .../plugins/grails/util/GrailsPatterns.java        |   60 +
 .../plugins/grails/util/GrailsPluginCondition.java |   25 +
 .../plugins/grails/util/GrailsPsiUtil.java         |  336 +
 .../jetbrains/plugins/grails/util/GrailsUtils.java |  997 +++
 .../grails/util/GrailsVersionCondition.java        |   25 +
 .../grails/util/GrailsWebFacetProvider.java        |   93 +
 .../plugins/grails/util/ModuleCachedValue.java     |   23 +
 .../util/PredefinedTypeReferenceEnhancer.java      |   20 +
 .../grails/util/ReferenceGutterIconRenderer.java   |   73 +
 .../plugins/grails/util/SafeReference.java         |   96 +
 .../plugins/grails/util/version/Range.java         |   86 +
 .../plugins/grails/util/version/Version.java       |   68 +
 .../plugins/grails/util/version/VersionImpl.java   |   47 +
 .../plugins/grails/util/version/versions.kt        |    9 +
 .../plugins/groovy/dsl/GrailsScriptProvider.java   |    6 +
 .../annotator/GrailsControllerAnnotator.java       |   64 +
 .../grails/annotator/GrailsDomainAnnotator.java    |  278 +
 .../annotator/GrailsForkSettingsNotificator.java   |   53 +
 .../GrailsSynchronizeStructureAnnotator.java       |  113 +
 .../groovy/mvc/ConsoleProcessDescriptor.java       |   13 +
 .../jetbrains/plugins/groovy/mvc/MvcCommand.java   |  153 +
 .../groovy/mvc/MvcModuleStructureSynchronizer.java |  370 +
 .../plugins/groovy/mvc/MvcModuleStructureUtil.java |  890 +++
 .../plugins/groovy/mvc/MvcPathMacros.java          |   39 +
 .../plugins/groovy/mvc/MvcProjectStructure.java    |   68 +
 .../plugins/groovy/mvc/MvcWatchedRootProvider.java |   52 +
 .../groovy/mvc/plugins/AvailablePluginsModel.java  |  132 +
 .../mvc/plugins/InstallUninstallPluginsDialog.java |  296 +
 .../groovy/mvc/plugins/MvcPluginColumnInfo.java    |  108 +
 .../groovy/mvc/plugins/MvcPluginDescriptor.java    |  114 +
 .../plugins/MvcPluginIsInstalledColumnInfo.java    |  107 +
 .../plugins/groovy/mvc/plugins/MvcPluginUtil.java  |  416 +
 .../groovy/mvc/plugins/MvcPluginsAction.java       |  139 +
 .../plugins/groovy/mvc/plugins/MvcPluginsMain.form |  216 +
 .../plugins/groovy/mvc/plugins/MvcPluginsMain.java |  556 ++
 .../groovy/mvc/plugins/MvcPluginsTable.java        |   99 +
 .../mvc/plugins/actions/AddCustomPluginAction.java |   50 +
 .../plugins/actions/ReloadMvcPluginListAction.java |   55 +
 .../plugins/groovy/mvc/plugins/proxySettings.kt    |   80 +
 .../mvc/util/MvcTargetDialogCompletionUtils.java   |  149 +
 .../plugins/groovy/grails/DoublePluginTest.java    |   35 +
 .../plugins/groovy/grails/GrEditorActionTest.java  |   36 +
 .../plugins/groovy/grails/GrailsActionsTest.java   |   33 +
 .../plugins/groovy/grails/GrailsCodecTest.java     |  111 +
 .../groovy/grails/GrailsConsoleFilterTest.java     |   63 +
 ...HighlightGrailsSpecifiedFieldsAsUnusedTest.java |  101 +
 .../plugins/groovy/grails/GrailsGantTest.java      |   23 +
 .../groovy/grails/GrailsHighlightingTest.java      |  173 +
 .../groovy/grails/GrailsLightVariableInfoTest.java |   21 +
 .../groovy/grails/GrailsLinkGeneratorTest.java     |  104 +
 .../groovy/grails/GrailsLogVariableTest.java       |   61 +
 .../groovy/grails/GrailsMavenImporterTest.kt       |  111 +
 .../groovy/grails/GrailsPageRendererTest.java      |   23 +
 .../groovy/grails/GrailsPluginClassTest.java       |   95 +
 .../groovy/grails/GrailsProjectStructureTest.java  |  467 ++
 .../grails/GrailsResolveInConfigFileTest.java      |   26 +
 .../plugins/groovy/grails/GrailsResolveTest.java   |  103 +
 .../grails/GrailsRunConfigurationProducerTest.java |  311 +
 .../grails/GrailsRunTargetCompletionTest.java      |   85 +
 .../groovy/grails/GspLiveTemplatesTest.java        |   23 +
 .../plugins/groovy/grails/JamInGroovyTest.java     |   37 +
 .../grails/VersionInDynamicMemberUtils14Test.java  |   10 +
 .../grails/VersionInDynamicMemberUtilsTest.java    |   26 +
 .../grails/action/DomainFieldIntentionsTest.kt     |  526 ++
 .../grails/action/GenerateViewByActionTest.java    |   91 +
 .../grails/action/GrailsGenerateAction2Test.java   |   96 +
 .../grails/action/GrailsGenerateActionTest.java    |  179 +
 .../ControllerReferenceCompletionTest.java         |   32 +
 .../DomainClassMethodsCompletionTest.java          |   49 +
 .../grails/completion/GrailsCompletionTest.java    |  256 +
 .../completion/GrailsNamedAttributeTest.java       |   56 +
 ...GrailsTestAutoCompletionHardReferencesTest.java |   22 +
 .../completion/GspTagReferenceCompletionTest.java  |   64 +
 .../grails/completion/ServiceCompletionTest.java   |   21 +
 .../TaglibClassMethodsCompletionTest.java          |   32 +
 .../config/GrailsBuildConfigResolveTest.java       |   56 +
 .../groovy/grails/config/GrailsConfigFileTest.java |  135 +
 .../grails/config/GrailsConfigSelectWordTest.java  |  111 +
 .../grails/config/GrailsConfigSlurperTest.java     |   35 +
 .../grails/config/GrailsConstraintGroupTest.java   |   51 +
 .../GrailsPluginDependencyCompletionTest.java      |  108 +
 .../groovy/grails/domain/GormApiFromJavaTest.java  |   75 +
 .../groovy/grails/domain/GormBinding14Test.java    |   11 +
 .../groovy/grails/domain/GormBindingTest.java      |   87 +
 .../grails/domain/GormDetachedCriteriaTest.java    |  182 +
 .../grails/domain/GormDomainConstructorTest.java   |   25 +
 .../grails/domain/GormDomainFinderMethodTest.java  |   39 +
 .../groovy/grails/domain/GormFetchModeTest.java    |   94 +
 .../domain/GormFinderMethodsCompletionTest.java    |   73 +
 .../GormGetPersistentValueReturnTypeTest.java      |   19 +
 .../grails/domain/GormHasManyRelationTypeTest.java |   44 +
 .../groovy/grails/domain/GormMappingSortTest.java  |   64 +
 .../grails/domain/GormMultiDefinePropertyTest.java |   34 +
 ...amedArgumentForHibernateMappingBuilderTest.java |   28 +
 .../grails/domain/GormPersistentFacetTest.java     |   85 +
 .../grails/domain/GormPropertyAccessTest.java      |   19 +
 .../groovy/grails/domain/GormStandaloneTest.java   |  175 +
 .../grails/domain/GormSyntheticFieldTest.java      |  151 +
 .../domain/GormUniqueConstraintValueTest.java      |  100 +
 .../grails/domain/GrailsAddToMethodTest.java       |  195 +
 .../grails/domain/GrailsCriteriaBuilder14Test.java |   11 +
 .../grails/domain/GrailsCriteriaBuilderTest.java   |  566 ++
 .../GrailsCriteriaBuilderToManyRelationTest.java   |  165 +
 .../domain/GrailsCriteriaProjections14Test.java    |   11 +
 .../domain/GrailsCriteriaProjectionsTest.java      |  236 +
 .../grails/domain/GrailsDomainConstraintTest.java  |  364 +
 .../domain/GrailsDomainFinderMethodsTest.java      |  219 +
 .../domain/GrailsDomainNamedAttribute14Test.java   |   11 +
 .../domain/GrailsDomainNamedAttributeTest.java     |   81 +
 .../GrailsDomainPropertiesFromParentTest.java      |   31 +
 .../grails/domain/GrailsDomainPropertiesTest.java  |   65 +
 .../domain/GrailsFindWhereParameterTest.java       |   32 +
 .../grails/domain/GrailsGormGetIdMethodTest.java   |   97 +
 .../grails/domain/GrailsInjectRegexTest.java       |   22 +
 .../grails/domain/GrailsNamedQuery14Test.java      |   11 +
 .../groovy/grails/domain/GrailsNamedQueryTest.java |  216 +
 .../domain/GrailsRenameDomainPropertyTest.java     |   40 +
 .../grails/domain/GrailsRenameDomainTest.java      |   47 +
 .../grails/domain/GrailsTransientPropertyTest.java |  130 +
 .../grails/domain/GrormEmbeddedFieldTest.java      |   39 +
 .../NamedArgumentsInFindOrCreateMethodTest.java    |   57 +
 .../grails/gradle/GradleGrailsImportingTest.java   |   69 +
 .../grails/gsp/GspActionToViewNavigation2Test.java |   39 +
 .../grails/gsp/GspActionToViewNavigationTest.java  |   39 +
 .../grails/gsp/GspAttributeFromJavadocTest.java    |   46 +
 .../groovy/grails/gsp/GspBuiltInTagTest.java       |   50 +
 .../groovy/grails/gsp/GspCharsetReferenceTest.java |   24 +
 .../gsp/GspClearCompletionBehaviourTest.java       |   24 +
 .../grails/gsp/GspCompletionEmptyTagTest.java      |   44 +
 .../GspCompletionInNonexistingNamespaceTest.java   |   17 +
 .../groovy/grails/gsp/GspContentTagTest.java       |   56 +
 .../grails/gsp/GspControllerReferenceTest.java     |   54 +
 .../grails/gsp/GspCreateTemplateQuickFixTest.java  |   61 +
 .../grails/gsp/GspCreateVariableQuickFixTest.java  |   61 +
 .../plugins/groovy/grails/gsp/GspCssTest.java      |   91 +
 .../grails/gsp/GspDirectiveDefaultCodecTest.java   |   15 +
 ...mentManipulatorForOuterLanguageElementTest.java |   39 +
 .../groovy/grails/gsp/GspFormatterTest.java        |  204 +
 .../groovy/grails/gsp/GspGroovyInspectionTest.java |   14 +
 .../groovy/grails/gsp/GspHighlightingTest.java     |   30 +
 .../groovy/grails/gsp/GspHtmlAttributesTest.java   |   53 +
 .../groovy/grails/gsp/GspIDEA68710Test.java        |   17 +
 .../plugins/groovy/grails/gsp/GspImportTest.java   |  113 +
 .../groovy/grails/gsp/GspJavascriptTest.java       |  196 +
 .../groovy/grails/gsp/GspJsFormatterTest.java      |   40 +
 .../grails/gsp/GspLanguageInjectionTest.java       |   81 +
 .../groovy/grails/gsp/GspLayoutTagTest.java        |   75 +
 .../groovy/grails/gsp/GspNamedMappingTest.java     |   58 +
 .../gsp/GspNamespacePrefixIsNotIdentifierTest.java |   42 +
 .../grails/gsp/GspNamespacePriorityTest.java       |   52 +
 .../grails/gsp/GspPropertyResolvePriorityTest.java |   48 +
 .../groovy/grails/gsp/GspResolveIndexesTest.java   |  285 +
 .../gsp/GspResolveVariablesFromModelTest.java      |  373 +
 .../groovy/grails/gsp/GspSdkTagCompletionTest.java |   23 +
 .../groovy/grails/gsp/GspSelectWordTest.java       |   98 +
 .../groovy/grails/gsp/GspTagDocumentationTest.java |   55 +
 .../grails/gsp/GspThisAndSuperReferenceTest.java   |   18 +
 .../plugins/groovy/grails/gsp/GspTypingTest.java   |  212 +
 .../groovy/grails/gsp/GstringAttributeTest.java    |   77 +
 .../grails/i18n/GrailsGroovyI18nIntentionTest.kt   |   93 +
 .../groovy/grails/i18n/GrailsI18nInspectionTest.kt |  159 +
 .../groovy/grails/i18n/GspI18nIntentionTest.kt     |  106 +
 .../GrailsMethodMayBeStaticInspectionTest.java     |   28 +
 .../groovy/grails/parser/GspGroovyParserTest.java  |   49 +
 .../groovy/grails/parser/GspHtmlParserTest.java    |   34 +
 .../groovy/grails/parser/GspParserTest.java        |   40 +
 .../groovy/grails/parser/GspParsingTestCase.java   |   24 +
 .../pluginSupport/GrailsBuildTestDataTest.java     |   91 +
 .../pluginSupport/GrailsResourcePluginTest.java    |  214 +
 .../grails/pluginSupport/GrailsSearchableTest.java |  336 +
 .../pluginSupport/GrailsShiroPluginTest.java       |  111 +
 .../grails/pluginSupport/GrailsWebflowTest.java    |  236 +
 .../pluginSupport/SpockMethodReturnTypeTest.java   |   48 +
 .../reference/GrailsBeanNameConventionTest.java    |  102 +
 .../groovy/grails/reference/GrailsFilterTest.java  |  171 +
 .../groovy/grails/reference/GrailsJobTest.java     |   59 +
 .../grails/reference/GrailsViewReferenceTest.java  |  173 +
 .../reference/bootstrap/GrailsBootStrapTest.java   |  147 +
 .../controller/Grails14MimePluginTest.java         |   48 +
 .../reference/controller/GrailsAction14Test.java   |   69 +
 .../controller/GrailsActionWithExtensionTest.java  |   95 +
 .../controller/GrailsController14Test.java         |   52 +
 .../GrailsControllerActionReferenceTest.java       |  239 +
 .../GrailsControllerAllowedMethodsTest.java        |   79 +
 ...ilsControllerAmbiguousMethodInspectionTest.java |   39 +
 .../GrailsControllerLayoutReferenceTest.java       |   47 +
 .../controller/GrailsControllerRenameTest.java     |   35 +
 .../GrailsControllerRespondMethodTest.java         |   22 +
 .../controller/GrailsHttpRequestTest.java          |   29 +
 .../controller/GrailsHttpResponseTest.java         |   29 +
 .../reference/controller/GrailsMimePluginTest.java |   37 +
 .../reference/controller/GrailsNamespaceTest.java  |   31 +
 .../controller/GrailsRenderMethodTest.java         |   32 +
 .../reference/spring/GrailsSpringDSLTest.java      |  108 +
 .../spring/GrailsSpringIntegrationTest.java        |  505 ++
 .../taglib/GrailsTagLibNamedArgumentsTest.java     |   53 +
 .../reference/taglib/GrailsTaglib14Test.java       |   19 +
 .../taglib/GrailsTaglibParamTypeTest.java          |   25 +
 .../grails/reference/taglib/GspCustomTagTest.java  |  174 +
 .../GspResolveTagsFromSameNamespaceTest.java       |   33 +
 .../reference/urlMapping/GrailsUrlMappingTest.java |  217 +
 .../GrailsUrlMappingUnresolvedReferenceTest.java   |   72 +
 .../grails/tagSupport/GspActionAttributeTest.java  |   63 +
 .../grails/tagSupport/GspCodecAttributeTest.java   |   38 +
 .../tagSupport/GspControllerAttributeTest.java     |   67 +
 .../groovy/grails/tagSupport/GspFieldTagTest.java  |  220 +
 .../tagSupport/GspIncludeViewAttributeTest.java    |   44 +
 .../groovy/grails/tagSupport/GspJsSrcTest.java     |   19 +
 .../groovy/grails/tagSupport/GspMetaTagTest.java   |   51 +
 .../grails/tagSupport/GspPluginAttributeTest.java  |   63 +
 .../groovy/grails/tagSupport/GspRenderTagTest.java |  323 +
 .../grails/tagSupport/GspResourceTagTest.java      |  252 +
 .../groovy/grails/tagSupport/GspSelectTagTest.java |  141 +
 .../grails/tagSupport/GspUpdateAttributeTest.java  |   31 +
 .../grails/tests/GrailsMockAnnotationTest.java     |   29 +
 .../grails/tests/GrailsTestForAnnotationTest.java  |   82 +
 .../tests/GrailsTestMixinAnnotationTest.java       |   65 +
 .../groovy/grails/tests/GrailsTestUtilTest.java    |   43 +
 .../grails/tests/GrailsVariableEnhance14Test.java  |   10 +
 .../grails/tests/GrailsVariableEnhanceTest.java    |  102 +
 .../groovy/grails/v3/GrailsEnhancesTest.java       |   55 +
 .../groovy/grails/v3/GrailsProjectDescriptor.java  |   45 +
 .../grails/validation/GrailsImportFromTest.java    |  108 +
 .../validation/GrailsValidatedObjectTest.java      |  220 +
 testFramework/README.md                            |    2 +
 testFramework/build.gradle.kts                     |   67 +
 .../service/execution/TestUnknownSdkResolver.kt    |  123 +
 .../externalSystem/test/JavaCompileTestUtil.kt     |   91 +
 .../test/JavaExternalSystemImportingTestCase.kt    |   97 +
 .../util/ExternalSystemFileTestUtil.kt             |   25 +
 .../openapi/roots/ui/configuration/SdkTestCase.kt  |  327 +
 .../testFramework/eelJava/EelTestJdkProvider.kt    |   63 +
 .../platform/testFramework/eelJava/EelTestUtil.kt  |   43 +
 .../gradle/importing/GradleImportingTestCase.kt    |  546 ++
 .../importing/TestGradleBuildScripBuilder.kt       |  133 +
 .../importing/TestGradleSettingsScriptHelper.kt    |   17 +
 .../plugins/gradle/tooling/GradleJvmResolver.kt    |  124 +
 .../plugins/gradle/tooling/JavaVersionMatcher.kt   |   42 +
 .../gradle/tooling/JavaVersionRestriction.kt       |   42 +
 .../gradle/tooling/TargetJavaVersionWatcher.java   |   46 +
 .../plugins/gradle/tooling/VersionMatcherRule.java |   75 +
 .../tooling/annotation/TargetJavaVersion.java      |   36 +
 .../gradle/tooling/annotation/TargetVersions.java  |   30 +
 ...adleBrokenJvmSerialisationVersionRestriction.kt |   17 +
 .../jvm/GradleToolingApi9VersionRestriction.kt     |   15 +
 .../gradle/tooling/util/VersionMatcher.java        |   79 +
 .../plugins/gradle/util/testWrapperUtil.kt         |   57 +
 .../plugins/groovy/CompoundTestLibrary.java        |   25 +
 .../plugins/groovy/GroovyProjectDescriptors.java   |   61 +
 .../groovy/LibraryLightProjectDescriptor.java      |   25 +
 .../plugins/groovy/LightGroovyTestCase.java        |  269 +
 .../plugins/groovy/RepositoryTestLibrary.java      |   79 +
 .../org/jetbrains/plugins/groovy/TestLibrary.java  |   21 +
 .../groovy/completion/CompletionTestBase.java      |   77 +
 .../plugins/groovy/grails/Grails14TestCase.java    |   11 +
 .../plugins/groovy/grails/GrailsTestCase.java      |  485 ++
 .../plugins/groovy/grails/GrailsTestUtil.java      |  168 +
 .../plugins/groovy/grails/HddGrailsTestCase.java   |  344 +
 .../grails/TestGrailsApplicationProvider.java      |   29 +
 .../groovy/lang/GroovyFormatterTestCase.java       |  172 +
 .../lang/highlighting/GrHighlightingTestBase.java  |   80 +
 .../jetbrains/plugins/groovy/util/TestUtils.java   |  278 +
 testdata/grails/actions/GspBlockComment.gsp        |   16 +
 testdata/grails/actions/GspBlockComment_after.gsp  |   16 +
 testdata/grails/actions/GspLineComment.gsp         |   16 +
 testdata/grails/actions/GspLineComment_after.gsp   |   16 +
 testdata/grails/actions/GspLineUncomment.gsp       |   16 +
 testdata/grails/actions/GspLineUncomment_after.gsp |   16 +
 .../AllFinderMethodsInDomainClass.groovy           |    8 +
 .../completion/AttributeNameFinishWithEq.gsp       |    1 +
 .../completion/AttributeNameFinishWithEq_after.gsp |    1 +
 .../ConstrainsPropertyInDomainClass.groovy         |    5 +
 ...nstrainsPropertyInDomainClassConstraints.groovy |    3 +
 .../ConstrainsPropertyInDomainClass_after.groovy   |    5 +
 .../completion/DomainClassDynamicMethods.groovy    |    5 +
 .../completion/DomainClassNonStaticMethods.groovy  |    9 +
 .../completion/DomainClassStaticProperties.groovy  |    3 +
 .../DomainClassStaticProperties_after.groovy       |    3 +
 .../completion/FindByMethodInDomainClass.groovy    |    8 +
 .../FindByMethodWithThreeArgsInDomainClass.groovy  |    8 +
 .../completion/FinderMethodsInDomainClass.groovy   |   12 +
 .../grails/completion/FinderMethodsInGsp.groovy    |    5 +
 testdata/grails/completion/FinderMethodsInGsp.gsp  |    1 +
 .../grails/completion/FindersWithBelongsTo.groovy  |   10 +
 .../grails/completion/FindersWithHasMany.groovy    |   10 +
 .../grails/completion/GspInjectedJavaScript.gsp    |   20 +
 .../completion/GspInjectedJavaScript_after.gsp     |   20 +
 testdata/grails/completion/JspTagCompletion.gsp    |    2 +
 .../grails/completion/JspTagCompletion_after.gsp   |    2 +
 .../completion/ModifiersInGspGroovyDeclaration.gsp |    2 +
 testdata/grails/completion/OverrideStandardTag.gsp |    1 +
 testdata/grails/completion/ServiceInjection.groovy |    7 +
 .../completion/ServiceInjection_after.groovy       |    7 +
 .../completion/UnpackagedDomainClassInGsp.groovy   |    3 +
 .../completion/UnpackagedDomainClassInGsp.gsp      |    1 +
 .../UnpackagedDomainClassInGsp_after.gsp           |    1 +
 testdata/grails/enterAction/dir.test               |    3 +
 testdata/grails/enterAction/simple_type.test       |    6 +
 testdata/grails/fmt.tld                            |  671 ++
 testdata/grails/formatter/comments/schulz1.test    |  101 +
 testdata/grails/formatter/comments/ven1.test       |   11 +
 testdata/grails/formatter/groovy/groovy.test       |   15 +
 testdata/grails/formatter/groovy/plain1.test       |    7 +
 testdata/grails/formatter/groovy/sim1.test         |   15 +
 testdata/grails/formatter/gsp/EA29587.test         |  296 +
 testdata/grails/formatter/gsp/gsp1.test            |    9 +
 testdata/grails/formatter/gsp/gsp2.test            |    9 +
 testdata/grails/formatter/gsp/gsp3.test            |   21 +
 testdata/grails/formatter/gsp/gsp8.test            |    9 +
 testdata/grails/formatter/gsp/gsp9.test            |   13 +
 .../grails/formatter/html/error/giga_werle.test    |  650 ++
 testdata/grails/formatter/html/error/nik1.test     |  134 +
 testdata/grails/formatter/html/error/show.test     |  128 +
 testdata/grails/formatter/html/error/werle1.test   |    3 +
 testdata/grails/formatter/html/error/werle30.test  |    3 +
 .../grails/formatter/html/inner/GRVY_1165.test     |   96 +
 .../grails/formatter/html/inner/GRVY_1165_1.test   |   15 +
 testdata/grails/formatter/html/inner/GRVY_876.test |   19 +
 testdata/grails/formatter/html/inner/inner2.test   |   21 +
 testdata/grails/formatter/html/inner/inner3.test   |   23 +
 .../grails/formatter/html/megabug/GRVY_1046.test   |  161 +
 .../grails/formatter/html/megabug/bigTest.test     |  508 ++
 testdata/grails/formatter/html/megabug/megap2.test |  907 +++
 testdata/grails/formatter/html/megabug/mmm3.test   |    5 +
 testdata/grails/formatter/html/megabug/peter.test  |   17 +
 testdata/grails/formatter/html/megabug/peter2.test |    5 +
 testdata/grails/formatter/html/megabug/peter3.test |  905 +++
 testdata/grails/formatter/html/megabug/range1.test |    3 +
 testdata/grails/formatter/html/nested/attr1.test   |   13 +
 testdata/grails/formatter/html/nested/gsp6.test    |   15 +
 testdata/grails/formatter/html/nested/gsp7.test    |   11 +
 .../grails/formatter/html/nested/peter7926.test    |  207 +
 .../grails/formatter/html/nested/peter_simple.test |   19 +
 testdata/grails/formatter/html/nested/range.test   |   13 +
 testdata/grails/formatter/html/nested/range2.test  |   23 +
 testdata/grails/formatter/html/nested/range4.test  |   17 +
 testdata/grails/formatter/html/nested/range5.test  |   21 +
 .../grails/formatter/html/simple/GRVY_1146.test    |   23 +
 .../formatter/html/simple/attributeSpaces.test     |   34 +
 testdata/grails/formatter/html/simple/gsp4.test    |    7 +
 testdata/grails/formatter/html/simple/gsp8.test    |    7 +
 testdata/grails/formatter/html/simple/htm1.test    |    9 +
 testdata/grails/formatter/html/simple/htm2.test    |   11 +
 testdata/grails/formatter/html/simple/range3.test  |   19 +
 testdata/grails/formatter/html/simple/sim2.test    |   19 +
 .../formatter/html/simple/tagNameSpaces.test       |   23 +
 testdata/grails/formatter/html/trash/bug1.test     |    9 +
 testdata/grails/formatter/html/trash/bug2.test     |   13 +
 testdata/grails/formatter/html/trash/bug3.test     |   11 +
 testdata/grails/formatter/html/trash/bug4.test     |   13 +
 testdata/grails/formatter/html/trash/bug5.test     |    9 +
 testdata/grails/formatter/html/trash/trash1.test   |   13 +
 testdata/grails/formatter/hyphenInTagName.test     |   29 +
 testdata/grails/formatter/js/t1.test               |   46 +
 testdata/grails/formatter/js/t2.test               |   42 +
 testdata/grails/formatter/spacing/groovy/gr1.test  |    7 +
 testdata/grails/formatter/spacing/groovy/gr2.test  |    7 +
 testdata/grails/formatter/spacing/htm3.test        |    3 +
 testdata/grails/formatter/spacing/spac2.test       |    3 +
 testdata/grails/formatter/spacing/spac3.test       |    5 +
 testdata/grails/formatter/spacing/tags2.test       |    3 +
 testdata/grails/gsp/TestController.groovy          |   67 +
 .../DomainFindersWithInheritedProperties.groovy    |   16 +
 .../FileReferenceWithGspInjections.gsp             |    8 +
 testdata/grails/highlighting/FinderMethods.groovy  |   22 +
 .../grails/highlighting/HasManyBelongsTo.groovy    |   29 +
 .../highlighting/JavascriptWithGspInjections.gsp   |   11 +
 testdata/grails/highlighting/JspTagInGsp.gsp       |    3 +
 testdata/grails/highlighting/MappedBy.groovy       |   11 +
 .../grails/highlighting/MappedByErrors1.groovy     |   11 +
 testdata/grails/highlighting/Properties.groovy     |   12 +
 .../grails/highlighting/UnknownArgsAttribute.gsp   |    1 +
 .../controllers/contr1Controller.test              |   20 +
 .../controllers/contr2Controller.test              |    9 +
 .../controllers/contr3Controller.test              |   14 +
 testdata/grails/oldCompletion/domain/dyn1.test     |    6 +
 testdata/grails/oldCompletion/domain/finder10.test |    3 +
 testdata/grails/oldCompletion/domain/finder11.test |    3 +
 testdata/grails/oldCompletion/domain/finder2.test  |    3 +
 testdata/grails/oldCompletion/domain/finder3.test  |    2 +
 testdata/grails/oldCompletion/domain/finder4.test  |    4 +
 testdata/grails/oldCompletion/domain/finder5.test  |    3 +
 testdata/grails/oldCompletion/domain/finder6.test  |    3 +
 testdata/grails/oldCompletion/domain/finder7.test  |   17 +
 testdata/grails/oldCompletion/domain/finder8.test  |    2 +
 .../grails/oldCompletion/domain/finder_full.test   |    2 +
 .../grails/oldCompletion/domain/list_order.test    |    9 +
 testdata/grails/oldCompletion/domain/not_ref.test  |    5 +
 .../grails/oldCompletion/domain/qulaified.test     |    6 +
 testdata/grails/oldCompletion/domain/static.test   |    3 +
 testdata/grails/oldCompletion/gsp/attr1.test       |    5 +
 testdata/grails/oldCompletion/gsp/custom1.test     |    6 +
 .../oldCompletion/gsp/customNamespacePrefix.test   |    4 +
 testdata/grails/oldCompletion/gsp/dirname.test     |    4 +
 testdata/grails/oldCompletion/gsp/endTagTest1.test |    8 +
 testdata/grails/oldCompletion/gsp/endTagTest2.test |    6 +
 testdata/grails/oldCompletion/gsp/endTagTest3.test |    2 +
 testdata/grails/oldCompletion/gsp/endTagTest4.test |    6 +
 testdata/grails/oldCompletion/gsp/endTagTest5.test |    3 +
 .../oldCompletion/gsp/excludeNotATagGroovy.test    |    2 +
 .../oldCompletion/gsp/excludeNotATagHTML.test      |    2 +
 testdata/grails/oldCompletion/gsp/g1.test          |    4 +
 testdata/grails/oldCompletion/gsp/get1.test        |   14 +
 testdata/grails/oldCompletion/gsp/groo1.test       |   15 +
 testdata/grails/oldCompletion/gsp/groo2.test       |    6 +
 testdata/grails/oldCompletion/gsp/groo3.test       |   13 +
 testdata/grails/oldCompletion/gsp/groo4.test       |   14 +
 testdata/grails/oldCompletion/gsp/groo5.test       |   18 +
 testdata/grails/oldCompletion/gsp/html1.test       |  191 +
 .../grails/oldCompletion/gsp/htmlWithDoctype.test  |   84 +
 testdata/grails/oldCompletion/gsp/link.test        |    6 +
 testdata/grails/oldCompletion/gsp/link2.test       |    6 +
 testdata/grails/oldCompletion/gsp/my1.test         |    6 +
 testdata/grails/oldCompletion/gsp/nonStatic.test   |    2 +
 .../grails/oldCompletion/gsp/packageTagLib1.test   |    6 +
 .../grails/oldCompletion/gsp/packageTagLib2.test   |    6 +
 testdata/grails/oldCompletion/gsp/tail1.test       |    4 +
 testdata/grails/oldCompletion/gsp/tail2.test       |    8 +
 testdata/grails/oldCompletion/gsp/tail3.test       |    6 +
 testdata/grails/oldCompletion/gsp/tail4.test       |    4 +
 testdata/grails/parser/gsp/dir/dir1.test           |   27 +
 testdata/grails/parser/gsp/hyphenInTagName.test    |   64 +
 testdata/grails/parser/gsp/inject/GRVY_943.test    |   34 +
 testdata/grails/parser/gsp/inject/escaped1.test    |   25 +
 testdata/grails/parser/gsp/simple/bubug1.test      |   36 +
 testdata/grails/parser/gsp/simple/clos1.test       |   24 +
 testdata/grails/parser/gsp/simple/clos2.test       |   34 +
 testdata/grails/parser/gsp/simple/common.test      |   57 +
 testdata/grails/parser/gsp/simple/form1.test       |   17 +
 testdata/grails/parser/gsp/simple/megap1.test      |   36 +
 testdata/grails/parser/gsp/simple/mmm1.test        |   28 +
 testdata/grails/parser/gsp/simple/peter2.test      |   14 +
 testdata/grails/parser/gsp/tags/act1.test          |   34 +
 testdata/grails/parser/gsp/tags/act2.test          |   27 +
 testdata/grails/parser/gsp/tags/gps9.test          |   27 +
 testdata/grails/parser/gsp/tags/orph1.test         |   24 +
 testdata/grails/parser/gsp/tags/tag3.test          |   19 +
 testdata/grails/parser/gsp/tags/tagWithSlash.test  |   45 +
 .../grails/parser/gsp/tags/uglyAttributeName.test  |   27 +
 .../grails/parser/gsp/tags/unendedAttrList.test    |  139 +
 testdata/grails/parser/gspGroovy/comments/com.test |    9 +
 .../grails/parser/gspGroovy/comments/comm1.test    |   43 +
 .../grails/parser/gspGroovy/comments/commm2.test   |    9 +
 .../grails/parser/gspGroovy/control/common.test    |  130 +
 testdata/grails/parser/gspGroovy/control/for1.test |   56 +
 testdata/grails/parser/gspGroovy/control/for2.test |   65 +
 .../grails/parser/gspGroovy/control/foreach1.test  |   36 +
 testdata/grails/parser/gspGroovy/control/if1.test  |   53 +
 testdata/grails/parser/gspGroovy/control/if2.test  |   47 +
 .../grails/parser/gspGroovy/control/swit1.test     |   81 +
 .../grails/parser/gspGroovy/control/swit2.test     |   53 +
 testdata/grails/parser/gspGroovy/custom/tag1.test  |   41 +
 testdata/grails/parser/gspGroovy/custom/tag2.test  |   29 +
 testdata/grails/parser/gspGroovy/custom/tag3.test  |   45 +
 .../grails/parser/gspGroovy/declarations/dec1.test |   51 +
 .../grails/parser/gspGroovy/declarations/dec2.test |   47 +
 .../grails/parser/gspGroovy/declarations/dec3.test |   56 +
 .../grails/parser/gspGroovy/declarations/dec4.test |   23 +
 testdata/grails/parser/gspGroovy/direct/dir1.test  |   17 +
 testdata/grails/parser/gspGroovy/errors/err1.test  |   65 +
 testdata/grails/parser/gspGroovy/errors/err2.test  |   24 +
 testdata/grails/parser/gspGroovy/errors/err3.test  |   45 +
 testdata/grails/parser/gspGroovy/errors/err4.test  |   41 +
 testdata/grails/parser/gspGroovy/simple/clos1.test |   39 +
 testdata/grails/parser/gspGroovy/simple/inj1.test  |   15 +
 testdata/grails/parser/gspGroovy/simple/inj2.test  |   28 +
 testdata/grails/parser/gspGroovy/simple/nl.test    |   25 +
 testdata/grails/parser/gspGroovy/simple/stat1.test |   51 +
 .../grails/parser/gspHtml/afterWhitespace.test     |   32 +
 testdata/grails/parser/gspHtml/alone.test          |   33 +
 testdata/grails/parser/gspHtml/common.test         |   58 +
 testdata/grails/parser/gspHtml/html1.test          |   21 +
 testdata/grails/parser/gspHtml/inject1.test        |   39 +
 testdata/grails/parser/gspHtml/mmm2.test           |  169 +
 testdata/grails/parser/gspHtml/mmm5.test           | 8313 ++++++++++++++++++++
 testdata/grails/parser/gspHtml/peter1.test         |   56 +
 testdata/grails/parser/gspHtml/peter2.test         |   49 +
 testdata/grails/parser/gspHtml/range_parse.test    |   39 +
 testdata/grails/parser/gspHtml/werle.test          |   35 +
 testdata/grails/parser/gspHtml/werle28.test        |   23 +
 testdata/grails/parser/gspHtml/werle29.test        |   27 +
 .../grails/pluginList/plugins-list-grailsCore.xml  |   93 +
 .../resolve/ResolvePropertiesFromBelongsTo.groovy  |    4 +
 .../resolve/ResolvePropertiesFromBelongsTo2.groovy |    4 +
 .../resolve/ResolvePropertiesFromHasMany.groovy    |    5 +
 .../resolve/ResolvePropertyFromMappedBy.groovy     |   11 +
 testdata/grails/resolve/StaticsOk.groovy           |    7 +
 testdata/grails/shiro/AccessControlBuilder.groovy  |   85 +
 .../mockDomainDir/grails-app/domain/Product.groovy |   21 +
 .../grails-app/domain/aaa/Book.groovy              |    8 +
 testdata/mockGrails11/conf/groovy-starter.conf     |   14 +
 testdata/mockGrails11/conf/webdefault.xml          |  411 +
 .../mockGrails11/dist/grails-bootstrap-1.1.jar     |  Bin 0 -> 59825 bytes
 testdata/mockGrails11/dist/grails-core-1.1.jar     |  Bin 0 -> 765928 bytes
 testdata/mockGrails11/dist/grails-core-1.3.1.jar   |  Bin 0 -> 267870 bytes
 testdata/mockGrails11/dist/grails-gorm-1.3.1.jar   |  Bin 0 -> 134980 bytes
 .../dist/grails-plugin-url-mappings-2.0.4.jar      |  Bin 0 -> 15579 bytes
 .../mockGrails11/dist/grails-scripts-1.3.7.jar     |  Bin 0 -> 71629 bytes
 testdata/mockGrails11/dist/grails-spring-1.3.1.jar |  Bin 0 -> 32529 bytes
 testdata/mockGrails11/dist/grails-test-1.3.1.jar   |  Bin 0 -> 94627 bytes
 testdata/mockGrails11/dist/grails-web-1.3.1.jar    |  Bin 0 -> 292243 bytes
 .../mockGrails11/dist/grails-webflow-1.3.7.jar     |  Bin 0 -> 63103 bytes
 testdata/mockGrails11/lib/groovy-all.jar           |  Bin 0 -> 293844 bytes
 .../mockGrails11/lib/hibernate-core-3.3.1.GA.jar   |  Bin 0 -> 50695 bytes
 testdata/mockGrails11/lib/junit-4.8.1.jar          |  Bin 0 -> 29406 bytes
 .../org.springframework.beans-3.0.3.RELEASE.jar    |  Bin 0 -> 21965 bytes
 .../org.springframework.context-3.0.3.RELEASE.jar  |  Bin 0 -> 81194 bytes
 .../lib/org.springframework.test-3.0.2.RELEASE.jar |  Bin 0 -> 85612 bytes
 testdata/mockGrails11/lib/servlet-api-2.5.jar      |  Bin 0 -> 36662 bytes
 .../lib/spock-grails-support-0.5-groovy-1.7.jar    |  Bin 0 -> 52135 bytes
 .../mockGrails11/lib/spring-core-3.0.3.RELEASE.jar |  Bin 0 -> 365217 bytes
 testdata/mockGrails11/scripts/Bootstrap.groovy     |   28 +
 testdata/mockGrails11/scripts/BugReport.groovy     |   55 +
 testdata/mockGrails11/scripts/Clean.groovy         |   27 +
 testdata/mockGrails11/scripts/Compile.groovy       |   27 +
 testdata/mockGrails11/scripts/Console.groovy       |  100 +
 testdata/mockGrails11/scripts/CreateApp_.groovy    |   27 +
 .../mockGrails11/scripts/CreateController.groovy   |   42 +
 .../mockGrails11/scripts/CreateDomainClass.groovy  |   36 +
 testdata/mockGrails11/scripts/CreateFilters.groovy |   39 +
 .../scripts/CreateIntegrationTest.groovy           |   35 +
 testdata/mockGrails11/scripts/CreatePlugin_.groovy |   28 +
 testdata/mockGrails11/scripts/CreateScript.groovy  |   34 +
 testdata/mockGrails11/scripts/CreateService.groovy |   39 +
 testdata/mockGrails11/scripts/CreateTagLib.groovy  |   37 +
 .../mockGrails11/scripts/CreateUnitTest.groovy     |   35 +
 testdata/mockGrails11/scripts/Doc.groovy           |   26 +
 testdata/mockGrails11/scripts/GenerateAll.groovy   |   49 +
 .../mockGrails11/scripts/GenerateController.groovy |   34 +
 testdata/mockGrails11/scripts/GenerateViews.groovy |   34 +
 testdata/mockGrails11/scripts/Help_.groovy         |  158 +
 testdata/mockGrails11/scripts/Init.groovy          |  171 +
 testdata/mockGrails11/scripts/InstallPlugin.groovy |   27 +
 .../mockGrails11/scripts/InstallTemplates.groovy   |   53 +
 testdata/mockGrails11/scripts/ListPlugins_.groovy  |   27 +
 testdata/mockGrails11/scripts/Package.groovy       |   29 +
 .../mockGrails11/scripts/PackagePlugin_.groovy     |   27 +
 testdata/mockGrails11/scripts/PluginInfo_.groovy   |   27 +
 testdata/mockGrails11/scripts/ReleasePlugin.groovy |  342 +
 testdata/mockGrails11/scripts/RunApp.groovy        |   36 +
 testdata/mockGrails11/scripts/RunWar.groovy        |   52 +
 testdata/mockGrails11/scripts/SchemaExport.groovy  |  125 +
 testdata/mockGrails11/scripts/SetProxy_.groovy     |   54 +
 testdata/mockGrails11/scripts/SetVersion.groovy    |   41 +
 testdata/mockGrails11/scripts/Shell.groovy         |   67 +
 testdata/mockGrails11/scripts/Stats.groovy         |   91 +
 testdata/mockGrails11/scripts/TestApp.groovy       |   67 +
 .../mockGrails11/scripts/UninstallPlugin.groovy    |   28 +
 testdata/mockGrails11/scripts/Upgrade.groovy       |  249 +
 testdata/mockGrails11/scripts/War.groovy           |   41 +
 .../mockGrails11/scripts/_GrailsArgParsing.groovy  |   54 +
 .../mockGrails11/scripts/_GrailsBootstrap.groovy   |  143 +
 .../mockGrails11/scripts/_GrailsClasspath.groovy   |  241 +
 testdata/mockGrails11/scripts/_GrailsClean.groovy  |   72 +
 .../mockGrails11/scripts/_GrailsCompile.groovy     |  146 +
 .../scripts/_GrailsCreateArtifacts.groovy          |  115 +
 .../scripts/_GrailsCreateProject.groovy            |  118 +
 testdata/mockGrails11/scripts/_GrailsDocs.groovy   |   91 +
 testdata/mockGrails11/scripts/_GrailsEvents.groovy |   65 +
 .../mockGrails11/scripts/_GrailsGenerate.groovy    |   93 +
 testdata/mockGrails11/scripts/_GrailsInit.groovy   |  186 +
 .../mockGrails11/scripts/_GrailsPackage.groovy     |  270 +
 .../mockGrails11/scripts/_GrailsPluginDev.groovy   |   95 +
 .../mockGrails11/scripts/_GrailsPlugins.groovy     |  398 +
 testdata/mockGrails11/scripts/_GrailsProxy.groovy  |   42 +
 testdata/mockGrails11/scripts/_GrailsRun.groovy    |  282 +
 .../mockGrails11/scripts/_GrailsSettings.groovy    |  272 +
 testdata/mockGrails11/scripts/_GrailsTest.groovy   |  368 +
 testdata/mockGrails11/scripts/_GrailsWar.groovy    |  353 +
 .../mockGrails11/scripts/_PackagePlugins.groovy    |   91 +
 .../scripts/_PluginDependencies.groovy             |  958 +++
 testdata/mockGrails11/scripts/log4j.properties     |    5 +
 .../mockGrails14/grails-bootstrap-2.0.0.M2.jar     |  Bin 0 -> 269630 bytes
 testdata/mockGrails14/grails-core-2.0.0.M1.jar     |  Bin 0 -> 356496 bytes
 .../grails-datastore-gorm-1.0.0.RC1.jar            |  Bin 0 -> 163878 bytes
 .../mockGrails14/grails-hibernate-2.0.0.M1.jar     |  Bin 0 -> 233327 bytes
 .../grails-plugin-controllers-1.4.0.M1.jar         |  Bin 0 -> 10557 bytes
 .../grails-plugin-converters-2.2.1.jar             |  Bin 0 -> 134318 bytes
 .../grails-plugin-gsp-2.0.0.RC1-sources.jar        |  Bin 0 -> 47099 bytes
 .../mockGrails14/grails-plugin-gsp-2.0.0.RC1.jar   |  Bin 0 -> 121101 bytes
 .../grails-plugin-mimetypes-1.4.0.M1.jar           |  Bin 0 -> 23907 bytes
 testdata/mockGrails14/grails-plugin-rest-2.3.0.jar |  Bin 0 -> 164753 bytes
 .../mockGrails14/grails-plugin-servlets-2.2.1.jar  |  Bin 0 -> 11816 bytes
 .../grails-plugin-testing-1.4.0.M1.jar             |  Bin 0 -> 184132 bytes
 testdata/mockGrails14/grails-spring-2.2.3.jar      |  Bin 0 -> 35645 bytes
 testdata/mockGrails14/grails-test-2.0.0.M2.jar     |  Bin 0 -> 147718 bytes
 testdata/mockGrails14/grails-web-2.0.0.M1.jar      |  Bin 0 -> 509061 bytes
 testdata/mockGrails14/spring-test-3.1.0.M2.jar     |  Bin 0 -> 200617 bytes
 .../mockTagLib/grails-app/taglib/MyTagLib.groovy   |    8 +
 .../grails-app/taglib/NonStaticTagLib.groovy       |    8 +
 .../grails-app/taglib/UnnamedTagLib.groovy         |    8 +
 .../grails-app/taglib/apackage/PppTagLib.groovy    |   10 +
 1330 files changed, 113608 insertions(+)

diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/LICENSE.txt
@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/build.gradle.kts b/build.gradle.kts
new file mode 100644
index 0000000..07a2c9b
--- /dev/null
+++ b/build.gradle.kts
@@ -0,0 +1,128 @@
+// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
+import org.jetbrains.intellij.platform.gradle.TestFrameworkType
+import org.jetbrains.intellij.platform.gradle.tasks.PrepareSandboxTask
+
+fun properties(key: String) = project.findProperty(key).toString()
+
+plugins {
+    id("java")
+    id("org.jetbrains.kotlin.jvm") version "2.3.0"
+    id("org.jetbrains.intellij.platform") version "2.11.0"
+}
+
+group = "org.intellij.grails"
+version = properties("pluginVersion")
+
+repositories {
+    mavenCentral()
+    intellijPlatform {
+        defaultRepositories()
+    }
+}
+
+dependencies {
+    intellijPlatform {
+        intellijIdea(properties("platformVersion"))
+
+        bundledPlugin("com.intellij.javaee")
+        bundledPlugin("com.intellij.persistence")
+        bundledPlugin("com.intellij.javaee.jpa")
+        bundledPlugin("com.intellij.jsp")
+        bundledPlugin("com.intellij.javaee.web")
+        bundledPlugin("org.intellij.groovy")
+        bundledPlugin("com.intellij.database")
+        bundledPlugin("com.intellij.spring")
+        bundledPlugin("org.jetbrains.plugins.gradle")
+        bundledPlugin("com.intellij.modules.ultimate")
+        bundledPlugin("com.intellij.microservices.jvm")
+        bundledPlugin("JavaScript")
+        bundledPlugin("com.intellij.css")
+
+
+        // plugin modules
+        pluginModule(project(":copyright"))
+        pluginModule(project(":coverage"))
+        pluginModule(project(":hibernate"))
+        pluginModule(project(":i18n"))
+        pluginModule(project(":langInjection"))
+        pluginModule(project(":maven"))
+
+
+        // additional plugins required for testing
+        testBundledPlugin("com.intellij.hibernate")
+        testBundledPlugin("HtmlTools")
+        testBundledPlugin("org.jetbrains.idea.maven")
+
+        testFramework(TestFrameworkType.Plugin.ExternalSystem)
+        testFramework(TestFrameworkType.Plugin.Java)
+        testFramework(TestFrameworkType.Plugin.Maven)
+        testFramework(TestFrameworkType.Platform)
+    }
+
+    testImplementation("junit:junit:4.13.2")
+    testImplementation("org.assertj:assertj-core:4.0.0-M1")
+    testImplementation(project(":testFramework"))
+    testCompileOnly(project(":i18n"))
+
+    implementation(project(":gradle-tooling"))
+    implementation(project(":grails-rt"))
+
+    // JPS/build
+    runtimeOnly(project(":grails-compiler-patch"))
+    runtimeOnly(project(":jps-plugin"))
+}
+
+kotlin {
+    jvmToolchain(21)
+}
+
+java.sourceSets["main"].java {
+    srcDir("src")
+    srcDir("gen")
+}
+
+java.sourceSets["main"].resources {
+    srcDir("resources")
+    srcDir("compatibilityResources")
+}
+
+// special handling for /standardDsls/ packaging, see PrepareSandboxTask below
+sourceSets {
+    main {
+        resources {
+            exclude("standardDsls/")
+        }
+    }
+}
+
+java.sourceSets["test"].java {
+    srcDir("test")
+}
+
+intellijPlatform {
+    pluginConfiguration {
+        ideaVersion {
+            sinceBuild = properties("pluginSinceBuild")
+        }
+
+        changeNotes = ""
+    }
+}
+
+tasks {
+    withType<JavaCompile> {
+        sourceCompatibility = properties("platformJavaVersion")
+        targetCompatibility = properties("platformJavaVersion")
+    }
+
+    test {
+        systemProperty("idea.home.path", properties("test.idea.home.path"))
+    }
+
+    // special handling for /standardDsls/ packaging
+    withType<PrepareSandboxTask> {
+        from(layout.projectDirectory.dir("resources/standardDsls")) {
+            into(pluginName.map { "$it/lib/standardDsls/"})
+        }
+    }
+}
diff --git a/compatibilityResources/icons/grails.png b/compatibilityResources/icons/grails.png
new file mode 100644
index 0000000..3a6c2c6
Binary files /dev/null and b/compatibilityResources/icons/grails.png differ
diff --git a/compatibilityResources/icons/grails@2x.png b/compatibilityResources/icons/grails@2x.png
new file mode 100644
index 0000000..c56b209
Binary files /dev/null and b/compatibilityResources/icons/grails@2x.png differ
diff --git a/compatibilityResources/icons/grails_13.png b/compatibilityResources/icons/grails_13.png
new file mode 100644
index 0000000..3c2e54a
Binary files /dev/null and b/compatibilityResources/icons/grails_13.png differ
diff --git a/compatibilityResources/icons/grails_13@2x.png b/compatibilityResources/icons/grails_13@2x.png
new file mode 100644
index 0000000..1370386
Binary files /dev/null and b/compatibilityResources/icons/grails_13@2x.png differ
diff --git a/compatibilityResources/icons/grails_app.png b/compatibilityResources/icons/grails_app.png
new file mode 100644
index 0000000..017bf63
Binary files /dev/null and b/compatibilityResources/icons/grails_app.png differ
diff --git a/compatibilityResources/icons/grails_app@2x.png b/compatibilityResources/icons/grails_app@2x.png
new file mode 100644
index 0000000..4a1dae5
Binary files /dev/null and b/compatibilityResources/icons/grails_app@2x.png differ
diff --git a/compatibilityResources/icons/grails_app@2x_dark.png b/compatibilityResources/icons/grails_app@2x_dark.png
new file mode 100644
index 0000000..0bd70b7
Binary files /dev/null and b/compatibilityResources/icons/grails_app@2x_dark.png differ
diff --git a/compatibilityResources/icons/grails_app_dark.png b/compatibilityResources/icons/grails_app_dark.png
new file mode 100644
index 0000000..8d9c8f2
Binary files /dev/null and b/compatibilityResources/icons/grails_app_dark.png differ
diff --git a/compatibilityResources/icons/grails_sdk.png b/compatibilityResources/icons/grails_sdk.png
new file mode 100644
index 0000000..9b7b783
Binary files /dev/null and b/compatibilityResources/icons/grails_sdk.png differ
diff --git a/compatibilityResources/icons/grails_sdk@2x.png b/compatibilityResources/icons/grails_sdk@2x.png
new file mode 100644
index 0000000..731d253
Binary files /dev/null and b/compatibilityResources/icons/grails_sdk@2x.png differ
diff --git a/compatibilityResources/icons/grails_test.png b/compatibilityResources/icons/grails_test.png
new file mode 100644
index 0000000..830e260
Binary files /dev/null and b/compatibilityResources/icons/grails_test.png differ
diff --git a/compatibilityResources/icons/grails_test@2x.png b/compatibilityResources/icons/grails_test@2x.png
new file mode 100644
index 0000000..d2ec854
Binary files /dev/null and b/compatibilityResources/icons/grails_test@2x.png differ
diff --git a/compatibilityResources/icons/gsp_logo.png b/compatibilityResources/icons/gsp_logo.png
new file mode 100644
index 0000000..c38ce30
Binary files /dev/null and b/compatibilityResources/icons/gsp_logo.png differ
diff --git a/compatibilityResources/icons/gsp_logo@2x.png b/compatibilityResources/icons/gsp_logo@2x.png
new file mode 100644
index 0000000..d30e6c6
Binary files /dev/null and b/compatibilityResources/icons/gsp_logo@2x.png differ
diff --git a/compatibilityResources/icons/taglib.png b/compatibilityResources/icons/taglib.png
new file mode 100644
index 0000000..8e5bbb7
Binary files /dev/null and b/compatibilityResources/icons/taglib.png differ
diff --git a/compatibilityResources/icons/taglib@2x.png b/compatibilityResources/icons/taglib@2x.png
new file mode 100644
index 0000000..814c2cf
Binary files /dev/null and b/compatibilityResources/icons/taglib@2x.png differ
diff --git a/copyright/build.gradle.kts b/copyright/build.gradle.kts
new file mode 100644
index 0000000..0fc172b
--- /dev/null
+++ b/copyright/build.gradle.kts
@@ -0,0 +1,43 @@
+// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
+
+fun properties(key: String) = project.findProperty(key).toString()
+
+plugins {
+    id("java")
+    id("org.jetbrains.intellij.platform.module")
+}
+
+group = "org.intellij.grails.copyright"
+
+repositories {
+    mavenCentral()
+    intellijPlatform {
+        defaultRepositories()
+    }
+}
+
+dependencies {
+    intellijPlatform {
+        intellijIdea(properties("platformVersion"))
+        bundledPlugin("com.intellij.copyright")
+        bundledPlugin("com.intellij.jsp")
+    }
+
+    compileOnly(project(":"))
+}
+
+java.sourceSets["main"].java {
+    srcDir("src")
+}
+
+java.sourceSets["main"].resources {
+    srcDir("resources")
+}
+
+tasks {
+        withType<JavaCompile> {
+        sourceCompatibility = properties("platformJavaVersion")
+        targetCompatibility = properties("platformJavaVersion")
+    }
+
+}
diff --git a/copyright/resources/intellij.groovy.grails.copyright.xml b/copyright/resources/intellij.groovy.grails.copyright.xml
new file mode 100644
index 0000000..7dd8853
--- /dev/null
+++ b/copyright/resources/intellij.groovy.grails.copyright.xml
@@ -0,0 +1,11 @@
+<!-- Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.-->
+<idea-plugin>
+
+  <dependencies>
+    <plugin id="com.intellij.copyright"/>
+  </dependencies>
+
+  <extensions defaultExtensionNs="com.intellij.copyright">
+    <updater filetype="GSP" implementationClass="com.intellij.groovy.grails.copyright.UpdateGspCopyrightsProvider"/>
+  </extensions>
+</idea-plugin>
\ No newline at end of file
diff --git a/copyright/src/com/intellij/groovy/grails/copyright/UpdateGspCopyrightsProvider.java b/copyright/src/com/intellij/groovy/grails/copyright/UpdateGspCopyrightsProvider.java
new file mode 100644
index 0000000..b96e481
--- /dev/null
+++ b/copyright/src/com/intellij/groovy/grails/copyright/UpdateGspCopyrightsProvider.java
@@ -0,0 +1,25 @@
+// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
+
+package com.intellij.groovy.grails.copyright;
+
+import com.intellij.jsp.copyright.UpdateJspFileCopyright;
+import com.intellij.openapi.fileTypes.FileType;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.maddyhome.idea.copyright.CopyrightProfile;
+import com.maddyhome.idea.copyright.psi.UpdateCopyright;
+import com.maddyhome.idea.copyright.psi.UpdateCopyrightsProvider;
+import org.jetbrains.plugins.grails.fileType.GspFileType;
+
+final class UpdateGspCopyrightsProvider extends UpdateCopyrightsProvider {
+    @Override
+    public UpdateCopyright createInstance(Project project, Module module, VirtualFile file, FileType base, CopyrightProfile options) {
+        return new UpdateJspFileCopyright(project, module, file, options) {
+            @Override
+            protected boolean accept() {
+                return getFile().getFileType() == GspFileType.GSP_FILE_TYPE;
+            }
+        };
+    }
+}
\ No newline at end of file
diff --git a/coverage/build.gradle.kts b/coverage/build.gradle.kts
new file mode 100644
index 0000000..ad1bbec
--- /dev/null
+++ b/coverage/build.gradle.kts
@@ -0,0 +1,46 @@
+// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
+
+fun properties(key: String) = project.findProperty(key).toString()
+
+plugins {
+    id("java")
+    id("org.jetbrains.kotlin.jvm") version "2.3.0"
+    id("org.jetbrains.intellij.platform.module")
+}
+
+group = "org.intellij.grails.coverage"
+
+kotlin {
+    jvmToolchain(21)
+}
+
+repositories {
+    mavenCentral()
+    intellijPlatform {
+        defaultRepositories()
+    }
+}
+
+dependencies {
+    intellijPlatform {
+        intellijIdea(properties("platformVersion"))
+        bundledPlugin("Coverage")
+    }
+    compileOnly(project(":"))
+}
+
+java.sourceSets["main"].java {
+    srcDir("src")
+}
+
+java.sourceSets["main"].resources {
+    srcDir("resources")
+}
+
+tasks {
+        withType<JavaCompile> {
+        sourceCompatibility = properties("platformJavaVersion")
+        targetCompatibility = properties("platformJavaVersion")
+    }
+
+}
diff --git a/coverage/resources/intellij.groovy.grails.coverage.xml b/coverage/resources/intellij.groovy.grails.coverage.xml
new file mode 100644
index 0000000..11681a6
--- /dev/null
+++ b/coverage/resources/intellij.groovy.grails.coverage.xml
@@ -0,0 +1,11 @@
+<!-- Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.-->
+<idea-plugin>
+
+  <dependencies>
+    <plugin id="com.intellij.modules.coverage"/>
+  </dependencies>
+
+  <extensions defaultExtensionNs="com.intellij">
+    <javaCoverageEngineExtension implementation="com.intellij.groovy.grails.coverage.GrailsCoverageExtension"/>
+  </extensions>
+</idea-plugin>
\ No newline at end of file
diff --git a/coverage/src/com/intellij/groovy/grails/coverage/GrailsCoverageExtension.kt b/coverage/src/com/intellij/groovy/grails/coverage/GrailsCoverageExtension.kt
new file mode 100644
index 0000000..3e3c012
--- /dev/null
+++ b/coverage/src/com/intellij/groovy/grails/coverage/GrailsCoverageExtension.kt
@@ -0,0 +1,14 @@
+// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
+
+package com.intellij.groovy.grails.coverage
+
+import com.intellij.coverage.JavaCoverageEngineExtension
+import com.intellij.execution.configurations.RunConfigurationBase
+import org.jetbrains.plugins.grails.runner.GrailsRunConfiguration
+import org.jetbrains.plugins.grails.runner.canPassVmArgs
+
+internal class GrailsCoverageExtension : JavaCoverageEngineExtension() {
+  override fun isApplicableTo(conf: RunConfigurationBase<*>?): Boolean {
+    return conf is GrailsRunConfiguration && conf.grailsApplicationNullable.canPassVmArgs()
+  }
+}
\ No newline at end of file
diff --git a/gen/org/jetbrains/plugins/grails/GroovyMvcIcons.java b/gen/org/jetbrains/plugins/grails/GroovyMvcIcons.java
new file mode 100644
index 0000000..a7c4c56
--- /dev/null
+++ b/gen/org/jetbrains/plugins/grails/GroovyMvcIcons.java
@@ -0,0 +1,26 @@
+// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
+package org.jetbrains.plugins.grails;
+
+import com.intellij.ui.IconManager;
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.*;
+
+/**
+ * NOTE THIS FILE IS AUTO-GENERATED
+ * DO NOT EDIT IT BY HAND, run "Generate icon classes" configuration instead
+ */
+public final class GroovyMvcIcons {
+  private static @NotNull Icon load(@NotNull String path, int cacheKey, int flags) {
+    return IconManager.getInstance().loadRasterizedIcon(path, GroovyMvcIcons.class.getClassLoader(), cacheKey, flags);
+  }
+  /** 16x16 */ public static final @NotNull Icon Action_method = load("icons/action_method.svg", 1289043343, 0);
+  /** 16x16 */ public static final @NotNull Icon Grails = load("icons/grails.svg", -1483802237, 0);
+  /** 13x13 */ public static final @NotNull Icon Grails_13 = load("icons/grails_13.svg", 1191246384, 2);
+  /** 16x16 */ public static final @NotNull Icon Grails_app = load("icons/grails_app.svg", 1928800982, 0);
+  /** 16x16 */ public static final @NotNull Icon Grails_sdk = load("icons/grails_sdk.svg", 2018310341, 0);
+  /** 16x16 */ public static final @NotNull Icon Grails_test = load("icons/grails_test.svg", 20123500, 0);
+  /** 16x16 */ public static final @NotNull Icon Groovy_mvc_plugin = load("icons/groovy_mvc_plugin.svg", -1041397689, 0);
+  /** 16x16 */ public static final @NotNull Icon Gsp_logo = load("icons/gsp_logo.svg", 1092319254, 0);
+  /** 16x16 */ public static final @NotNull Icon Taglib = load("icons/taglib.svg", 528989771, 0);
+}
diff --git a/gen/org/jetbrains/plugins/grails/lang/gsp/lexer/core/_GspDirectiveLexer.java b/gen/org/jetbrains/plugins/grails/lang/gsp/lexer/core/_GspDirectiveLexer.java
new file mode 100644
index 0000000..c9415d6
--- /dev/null
+++ b/gen/org/jetbrains/plugins/grails/lang/gsp/lexer/core/_GspDirectiveLexer.java
@@ -0,0 +1,795 @@
+// Generated by JFlex 1.9.1 http://jflex.de/  (tweaked for IntelliJ platform)
+// source: gsp_directive.flex
+
+// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
+
+package org.jetbrains.plugins.grails.lang.gsp.lexer.core;
+
+import com.intellij.lexer.FlexLexer;
+import com.intellij.psi.tree.IElementType;
+import java.util.*;
+import java.lang.reflect.Field;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.plugins.grails.lang.gsp.lexer.*;
+
+import static com.intellij.psi.xml.XmlTokenType.*;
+
+
+public class _GspDirectiveLexer implements FlexLexer, GspTokenTypesEx {
+
+  /** This character denotes the end of file */
+  public static final int YYEOF = -1;
+
+  /** initial size of the lookahead buffer */
+  private static final int ZZ_BUFFERSIZE = 16384;
+
+  /** lexical states */
+  public static final int YYINITIAL = 0;
+  public static final int ATTR_LIST = 2;
+  public static final int ATTR = 4;
+  public static final int ATTR_VALUE = 6;
+  public static final int ATTR_VALUE_DQ = 8;
+  public static final int ATTR_VALUE_SQ = 10;
+  public static final int BAD = 12;
+
+  /**
+   * ZZ_LEXSTATE[l] is the state in the DFA for the lexical state l
+   * ZZ_LEXSTATE[l+1] is the state in the DFA for the lexical state l
+   *                  at the beginning of a line
+   * l is of the form l = 2*k, k a non negative integer
+   */
+  private static final int ZZ_LEXSTATE[] = {
+     0,  0,  1,  1,  2,  2,  3,  3,  4,  4,  5,  5,  6, 6
+  };
+
+  /**
+   * Top-level table for translating characters to character classes
+   */
+  private static final int [] ZZ_CMAP_TOP = zzUnpackcmap_top();
+
+  private static final String ZZ_CMAP_TOP_PACKED_0 =
+    "\1\0\1\u0100\1\u0200\1\u0300\1\u0400\1\u0500\1\u0600\1\u0700"+
+    "\1\u0800\1\u0900\1\u0a00\1\u0b00\1\u0c00\1\u0d00\1\u0e00\1\u0f00"+
+    "\1\u1000\1\u0100\1\u1100\1\u1200\1\u1300\1\u0100\1\u1400\1\u1500"+
+    "\1\u1600\1\u1700\1\u1800\1\u1900\1\u1a00\1\u1b00\1\u0100\1\u1c00"+
+    "\1\u1d00\1\u1e00\12\u1f00\1\u2000\1\u2100\1\u2200\1\u1f00\1\u2300"+
+    "\1\u2400\2\u1f00\31\u0100\1\u1b00\121\u0100\1\u2500\4\u0100\1\u2600"+
+    "\1\u0100\1\u2700\1\u2800\1\u2900\1\u2a00\1\u2b00\1\u2c00\53\u0100"+
+    "\1\u2d00\41\u1f00\1\u0100\1\u2e00\1\u2f00\1\u0100\1\u3000\1\u3100"+
+    "\1\u3200\1\u3300\1\u3400\1\u3500\1\u3600\1\u3700\1\u3800\1\u0100"+
+    "\1\u3900\1\u3a00\1\u3b00\1\u3c00\1\u3d00\1\u3e00\1\u3f00\1\u4000"+
+    "\1\u4100\1\u4200\1\u4300\1\u4400\1\u4500\1\u4600\1\u4700\1\u4800"+
+    "\1\u4900\1\u4a00\1\u4b00\1\u4c00\1\u1f00\1\u4d00\1\u4e00\1\u4f00"+
+    "\1\u5000\3\u0100\1\u5100\1\u5200\1\u5300\12\u1f00\4\u0100\1\u5400"+
+    "\17\u1f00\2\u0100\1\u5500\41\u1f00\2\u0100\1\u5600\1\u5700\2\u1f00"+
+    "\1\u5800\1\u5900\27\u0100\1\u5a00\4\u0100\1\u5b00\1\u5c00\42\u1f00"+
+    "\1\u0100\1\u5d00\1\u5e00\11\u1f00\1\u5f00\27\u1f00\1\u6000\1\u6100"+
+    "\1\u6200\1\u6300\11\u1f00\1\u6400\1\u6500\5\u1f00\1\u6600\1\u6700"+
+    "\2\u1f00\1\u6800\1\u1f00\1\u6900\21\u1f00\246\u0100\1\u6a00\20\u0100"+
+    "\1\u6b00\1\u6c00\25\u0100\1\u6d00\34\u0100\1\u6e00\14\u1f00\2\u0100"+
+    "\1\u6f00\5\u1f00\23\u0100\1\u7000\u0dec\u1f00";
+
+  private static int [] zzUnpackcmap_top() {
+    int [] result = new int[4352];
+    int offset = 0;
+    offset = zzUnpackcmap_top(ZZ_CMAP_TOP_PACKED_0, offset, result);
+    return result;
+  }
+
+  private static int zzUnpackcmap_top(String packed, int offset, int [] result) {
+    int i = 0;       /* index in packed string  */
+    int j = offset;  /* index in unpacked array */
+    int l = packed.length();
+    while (i < l) {
+      int count = packed.charAt(i++);
+      int value = packed.charAt(i++);
+      do result[j++] = value; while (--count > 0);
+    }
+    return j;
+  }
+
+
+  /**
+   * Second-level tables for translating characters to character classes
+   */
+  private static final int [] ZZ_CMAP_BLOCKS = zzUnpackcmap_blocks();
+
+  private static final String ZZ_CMAP_BLOCKS_PACKED_0 =
+    "\11\0\1\1\1\2\1\0\1\1\1\3\22\0\1\1"+
+    "\1\0\1\4\2\0\1\5\1\0\1\6\10\0\12\7"+
+    "\2\0\1\10\1\11\1\12\1\0\1\13\32\14\4\0"+
+    "\1\14\1\0\32\14\1\15\1\0\1\16\44\0\4\14"+
+    "\4\0\1\14\12\0\1\14\4\0\1\14\5\0\27\14"+
+    "\1\0\37\14\1\0\u01ca\14\4\0\14\14\16\0\5\14"+
+    "\7\0\1\14\1\0\1\14\201\0\5\14\1\0\2\14"+
+    "\2\0\4\14\1\0\1\14\6\0\1\14\1\0\3\14"+
+    "\1\0\1\14\1\0\24\14\1\0\123\14\1\0\213\14"+
+    "\10\0\246\14\1\0\46\14\2\0\1\14\6\0\51\14"+
+    "\6\0\1\14\100\0\33\14\4\0\4\14\30\0\1\14"+
+    "\24\0\53\14\43\0\2\14\1\0\143\14\1\0\1\14"+
+    "\17\0\2\14\7\0\2\14\12\0\3\14\2\0\1\14"+
+    "\20\0\1\14\1\0\36\14\35\0\131\14\13\0\1\14"+
+    "\30\0\41\14\11\0\2\14\4\0\1\14\3\0\30\14"+
+    "\4\0\1\14\11\0\1\14\3\0\1\14\27\0\31\14"+
+    "\7\0\13\14\65\0\25\14\1\0\22\14\74\0\66\14"+
+    "\3\0\1\14\22\0\1\14\7\0\12\14\17\0\20\14"+
+    "\4\0\10\14\2\0\2\14\2\0\26\14\1\0\7\14"+
+    "\1\0\1\14\3\0\4\14\3\0\1\14\20\0\1\14"+
+    "\15\0\2\14\1\0\3\14\16\0\4\14\7\0\2\14"+
+    "\10\0\6\14\4\0\2\14\2\0\26\14\1\0\7\14"+
+    "\1\0\2\14\1\0\2\14\1\0\2\14\37\0\4\14"+
+    "\1\0\1\14\23\0\3\14\20\0\11\14\1\0\3\14"+
+    "\1\0\26\14\1\0\7\14\1\0\2\14\1\0\5\14"+
+    "\3\0\1\14\22\0\1\14\17\0\2\14\17\0\1\14"+
+    "\7\0\1\14\13\0\10\14\2\0\2\14\2\0\26\14"+
+    "\1\0\7\14\1\0\2\14\1\0\5\14\3\0\1\14"+
+    "\36\0\2\14\1\0\3\14\17\0\1\14\21\0\1\14"+
+    "\1\0\6\14\3\0\3\14\1\0\4\14\3\0\2\14"+
+    "\1\0\1\14\1\0\2\14\3\0\2\14\3\0\3\14"+
+    "\3\0\14\14\26\0\1\14\50\0\1\14\13\0\10\14"+
+    "\1\0\3\14\1\0\27\14\1\0\20\14\3\0\1\14"+
+    "\32\0\3\14\5\0\2\14\36\0\1\14\4\0\10\14"+
+    "\1\0\3\14\1\0\27\14\1\0\12\14\1\0\5\14"+
+    "\3\0\1\14\40\0\1\14\1\0\2\14\17\0\2\14"+
+    "\21\0\11\14\1\0\3\14\1\0\51\14\2\0\1\14"+
+    "\20\0\1\14\5\0\3\14\10\0\3\14\30\0\6\14"+
+    "\5\0\22\14\3\0\30\14\1\0\11\14\1\0\1\14"+
+    "\2\0\7\14\72\0\60\14\1\0\2\14\13\0\10\14"+
+    "\72\0\2\14\1\0\1\14\1\0\5\14\1\0\30\14"+
+    "\1\0\1\14\1\0\12\14\1\0\2\14\11\0\1\14"+
+    "\2\0\5\14\1\0\1\14\25\0\4\14\40\0\1\14"+
+    "\77\0\10\14\1\0\44\14\33\0\5\14\163\0\53\14"+
+    "\24\0\1\14\20\0\6\14\4\0\4\14\3\0\1\14"+
+    "\3\0\2\14\7\0\3\14\4\0\15\14\14\0\1\14"+
+    "\21\0\46\14\1\0\1\14\5\0\1\14\2\0\53\14"+
+    "\1\0\115\14\1\0\4\14\2\0\7\14\1\0\1\14"+
+    "\1\0\4\14\2\0\51\14\1\0\4\14\2\0\41\14"+
+    "\1\0\4\14\2\0\7\14\1\0\1\14\1\0\4\14"+
+    "\2\0\17\14\1\0\71\14\1\0\4\14\2\0\103\14"+
+    "\45\0\20\14\20\0\126\14\2\0\6\14\3\0\u016c\14"+
+    "\2\0\21\14\1\0\32\14\5\0\113\14\3\0\13\14"+
+    "\7\0\15\14\1\0\4\14\16\0\22\14\16\0\22\14"+
+    "\16\0\15\14\1\0\3\14\17\0\64\14\43\0\1\14"+
+    "\3\0\2\14\103\0\131\14\7\0\5\14\2\0\42\14"+
+    "\1\0\1\14\5\0\106\14\12\0\37\14\61\0\36\14"+
+    "\2\0\5\14\13\0\54\14\4\0\32\14\66\0\27\14"+
+    "\11\0\65\14\122\0\1\14\135\0\57\14\21\0\7\14"+
+    "\67\0\36\14\15\0\2\14\12\0\54\14\32\0\44\14"+
+    "\51\0\3\14\12\0\44\14\2\0\11\14\7\0\53\14"+
+    "\2\0\3\14\51\0\4\14\1\0\6\14\1\0\2\14"+
+    "\3\0\1\14\5\0\300\14\100\0\26\14\2\0\6\14"+
+    "\2\0\46\14\2\0\6\14\2\0\10\14\1\0\1\14"+
+    "\1\0\1\14\1\0\1\14\1\0\37\14\2\0\65\14"+
+    "\1\0\7\14\1\0\1\14\3\0\3\14\1\0\7\14"+
+    "\3\0\4\14\2\0\6\14\4\0\15\14\5\0\3\14"+
+    "\1\0\7\14\102\0\2\14\23\0\1\14\34\0\1\14"+
+    "\15\0\1\14\20\0\15\14\3\0\40\14\102\0\1\14"+
+    "\4\0\1\14\2\0\12\14\1\0\1\14\3\0\5\14"+
+    "\6\0\1\14\1\0\1\14\1\0\1\14\1\0\4\14"+
+    "\1\0\13\14\2\0\4\14\5\0\5\14\4\0\1\14"+
+    "\21\0\51\14\u0177\0\57\14\1\0\57\14\1\0\205\14"+
+    "\6\0\4\14\3\0\2\14\14\0\46\14\1\0\1\14"+
+    "\5\0\1\14\2\0\70\14\7\0\1\14\20\0\27\14"+
+    "\11\0\7\14\1\0\7\14\1\0\7\14\1\0\7\14"+
+    "\1\0\7\14\1\0\7\14\1\0\7\14\1\0\7\14"+
+    "\120\0\1\14\325\0\3\14\31\0\11\14\7\0\5\14"+
+    "\2\0\5\14\4\0\126\14\6\0\3\14\1\0\132\14"+
+    "\1\0\4\14\5\0\53\14\1\0\136\14\21\0\40\14"+
+    "\60\0\u010d\14\3\0\215\14\103\0\56\14\2\0\15\14"+
+    "\3\0\20\14\12\0\2\14\24\0\57\14\20\0\37\14"+
+    "\2\0\120\14\47\0\11\14\2\0\147\14\2\0\65\14"+
+    "\2\0\11\14\52\0\15\14\1\0\3\14\1\0\4\14"+
+    "\1\0\27\14\25\0\1\14\7\0\64\14\16\0\62\14"+
+    "\76\0\6\14\3\0\1\14\1\0\2\14\13\0\34\14"+
+    "\12\0\27\14\31\0\35\14\7\0\57\14\34\0\1\14"+
+    "\20\0\5\14\1\0\12\14\12\0\5\14\1\0\51\14"+
+    "\27\0\3\14\1\0\10\14\24\0\27\14\3\0\1\14"+
+    "\3\0\62\14\1\0\1\14\3\0\2\14\2\0\5\14"+
+    "\2\0\1\14\1\0\1\14\30\0\3\14\2\0\13\14"+
+    "\7\0\3\14\14\0\6\14\2\0\6\14\2\0\6\14"+
+    "\11\0\7\14\1\0\7\14\1\0\53\14\1\0\16\14"+
+    "\6\0\163\14\35\0\244\14\14\0\27\14\4\0\61\14"+
+    "\4\0\156\14\2\0\152\14\46\0\7\14\14\0\5\14"+
+    "\5\0\1\14\1\0\12\14\1\0\15\14\1\0\5\14"+
+    "\1\0\1\14\1\0\2\14\1\0\2\14\1\0\154\14"+
+    "\41\0\153\14\22\0\100\14\2\0\66\14\50\0\15\14"+
+    "\66\0\2\14\30\0\3\14\31\0\1\14\6\0\5\14"+
+    "\1\0\207\14\7\0\1\14\34\0\32\14\4\0\1\14"+
+    "\1\0\32\14\13\0\131\14\3\0\6\14\2\0\6\14"+
+    "\2\0\6\14\2\0\3\14\3\0\2\14\3\0\2\14"+
+    "\31\0\14\14\1\0\32\14\1\0\23\14\1\0\2\14"+
+    "\1\0\17\14\2\0\16\14\42\0\173\14\105\0\65\14"+
+    "\u010b\0\35\14\3\0\61\14\57\0\40\14\15\0\36\14"+
+    "\5\0\46\14\12\0\36\14\2\0\44\14\4\0\10\14"+
+    "\1\0\5\14\52\0\236\14\22\0\44\14\4\0\44\14"+
+    "\4\0\50\14\10\0\64\14\234\0\67\14\11\0\26\14"+
+    "\12\0\10\14\230\0\6\14\2\0\1\14\1\0\54\14"+
+    "\1\0\2\14\3\0\1\14\2\0\27\14\12\0\27\14"+
+    "\11\0\37\14\101\0\23\14\1\0\2\14\12\0\26\14"+
+    "\12\0\32\14\106\0\70\14\6\0\2\14\100\0\1\14"+
+    "\17\0\4\14\1\0\3\14\1\0\35\14\52\0\35\14"+
+    "\3\0\35\14\43\0\10\14\1\0\34\14\33\0\66\14"+
+    "\12\0\26\14\12\0\23\14\15\0\22\14\156\0\111\14"+
+    "\67\0\63\14\15\0\63\14\15\0\44\14\u015c\0\52\14"+
+    "\6\0\2\14\116\0\35\14\12\0\1\14\10\0\26\14"+
+    "\152\0\25\14\33\0\27\14\14\0\65\14\113\0\55\14"+
+    "\40\0\31\14\32\0\44\14\35\0\1\14\2\0\1\14"+
+    "\10\0\43\14\3\0\1\14\14\0\60\14\16\0\4\14"+
+    "\25\0\1\14\1\0\1\14\43\0\22\14\1\0\31\14"+
+    "\124\0\7\14\1\0\1\14\1\0\4\14\1\0\17\14"+
+    "\1\0\12\14\7\0\57\14\46\0\10\14\2\0\2\14"+
+    "\2\0\26\14\1\0\7\14\1\0\2\14\1\0\5\14"+
+    "\3\0\1\14\22\0\1\14\14\0\5\14\236\0\65\14"+
+    "\22\0\4\14\24\0\3\14\36\0\60\14\24\0\2\14"+
+    "\1\0\1\14\270\0\57\14\51\0\4\14\44\0\60\14"+
+    "\24\0\1\14\73\0\53\14\15\0\1\14\107\0\33\14"+
+    "\345\0\54\14\164\0\100\14\37\0\10\14\2\0\1\14"+
+    "\2\0\10\14\1\0\2\14\1\0\30\14\17\0\1\14"+
+    "\1\0\1\14\136\0\10\14\2\0\47\14\20\0\1\14"+
+    "\1\0\1\14\34\0\1\14\12\0\50\14\7\0\1\14"+
+    "\25\0\1\14\13\0\56\14\23\0\1\14\42\0\71\14"+
+    "\7\0\11\14\1\0\45\14\21\0\1\14\61\0\36\14"+
+    "\160\0\7\14\1\0\2\14\1\0\46\14\25\0\1\14"+
+    "\31\0\6\14\1\0\2\14\1\0\40\14\16\0\1\14"+
+    "\u0147\0\23\14\275\0\1\14\54\0\4\14\37\0\232\14"+
+    "\146\0\157\14\21\0\304\14\274\0\57\14\321\0\107\14"+
+    "\271\0\71\14\7\0\37\14\161\0\36\14\22\0\60\14"+
+    "\20\0\4\14\37\0\25\14\5\0\23\14\260\0\100\14"+
+    "\200\0\113\14\5\0\1\14\102\0\15\14\100\0\2\14"+
+    "\1\0\1\14\34\0\370\14\10\0\326\14\52\0\11\14"+
+    "\367\0\37\14\61\0\3\14\21\0\4\14\10\0\u018c\14"+
+    "\4\0\153\14\5\0\15\14\3\0\11\14\7\0\12\14"+
+    "\146\0\125\14\1\0\107\14\1\0\2\14\2\0\1\14"+
+    "\2\0\2\14\2\0\4\14\1\0\14\14\1\0\1\14"+
+    "\1\0\7\14\1\0\101\14\1\0\4\14\2\0\10\14"+
+    "\1\0\7\14\1\0\34\14\1\0\4\14\1\0\5\14"+
+    "\1\0\1\14\3\0\7\14\1\0\u0154\14\2\0\31\14"+
+    "\1\0\31\14\1\0\37\14\1\0\31\14\1\0\37\14"+
+    "\1\0\31\14\1\0\37\14\1\0\31\14\1\0\37\14"+
+    "\1\0\31\14\1\0\10\14\64\0\55\14\12\0\7\14"+
+    "\20\0\1\14\u0171\0\54\14\23\0\306\14\73\0\104\14"+
+    "\7\0\1\14\u0164\0\1\14\117\0\4\14\1\0\33\14"+
+    "\1\0\2\14\1\0\1\14\2\0\1\14\1\0\12\14"+
+    "\1\0\4\14\1\0\1\14\1\0\1\14\6\0\1\14"+
+    "\4\0\1\14\1\0\1\14\1\0\1\14\1\0\3\14"+
+    "\1\0\2\14\1\0\1\14\2\0\1\14\1\0\1\14"+
+    "\1\0\1\14\1\0\1\14\1\0\1\14\1\0\2\14"+
+    "\1\0\1\14\2\0\4\14\1\0\7\14\1\0\4\14"+
+    "\1\0\4\14\1\0\1\14\1\0\12\14\1\0\21\14"+
+    "\5\0\3\14\1\0\5\14\1\0\21\14\104\0\336\14"+
+    "\42\0\65\14\13\0\336\14\2\0\u0182\14\16\0\u0131\14"+
+    "\37\0\36\14\342\0\113\14\265\0";
+
+  private static int [] zzUnpackcmap_blocks() {
+    int [] result = new int[28928];
+    int offset = 0;
+    offset = zzUnpackcmap_blocks(ZZ_CMAP_BLOCKS_PACKED_0, offset, result);
+    return result;
+  }
+
+  private static int zzUnpackcmap_blocks(String packed, int offset, int [] result) {
+    int i = 0;       /* index in packed string  */
+    int j = offset;  /* index in unpacked array */
+    int l = packed.length();
+    while (i < l) {
+      int count = packed.charAt(i++);
+      int value = packed.charAt(i++);
+      do result[j++] = value; while (--count > 0);
+    }
+    return j;
+  }
+
+  /**
+   * Translates DFA states to action switch labels.
+   */
+  private static final int [] ZZ_ACTION = zzUnpackAction();
+
+  private static final String ZZ_ACTION_PACKED_0 =
+    "\7\0\1\1\1\2\3\1\1\3\1\4\1\5\2\6"+
+    "\1\7\1\10\1\11\2\6\1\12\2\6\1\13\1\0"+
+    "\1\14\1\0\1\15\1\0\1\16";
+
+  private static int [] zzUnpackAction() {
+    int [] result = new int[32];
+    int offset = 0;
+    offset = zzUnpackAction(ZZ_ACTION_PACKED_0, offset, result);
+    return result;
+  }
+
+  private static int zzUnpackAction(String packed, int offset, int [] result) {
+    int i = 0;       /* index in packed string  */
+    int j = offset;  /* index in unpacked array */
+    int l = packed.length();
+    while (i < l) {
+      int count = packed.charAt(i++);
+      int value = packed.charAt(i++);
+      do result[j++] = value; while (--count > 0);
+    }
+    return j;
+  }
+
+
+  /**
+   * Translates a state to a row index in the transition table
+   */
+  private static final int [] ZZ_ROWMAP = zzUnpackRowMap();
+
+  private static final String ZZ_ROWMAP_PACKED_0 =
+    "\0\0\0\17\0\36\0\55\0\74\0\113\0\132\0\151"+
+    "\0\170\0\207\0\226\0\245\0\264\0\151\0\303\0\151"+
+    "\0\207\0\151\0\151\0\151\0\322\0\341\0\151\0\360"+
+    "\0\377\0\151\0\u010e\0\151\0\322\0\151\0\360\0\151";
+
+  private static int [] zzUnpackRowMap() {
+    int [] result = new int[32];
+    int offset = 0;
+    offset = zzUnpackRowMap(ZZ_ROWMAP_PACKED_0, offset, result);
+    return result;
+  }
+
+  private static int zzUnpackRowMap(String packed, int offset, int [] result) {
+    int i = 0;  /* index in packed string  */
+    int j = offset;  /* index in unpacked array */
+    int l = packed.length() - 1;
+    while (i < l) {
+      int high = packed.charAt(i++) << 16;
+      result[j++] = high | packed.charAt(i++);
+    }
+    return j;
+  }
+
+  /**
+   * The transition table of the DFA
+   */
+  private static final int [] ZZ_TRANS = zzUnpacktrans();
+
+  private static final String ZZ_TRANS_PACKED_0 =
+    "\1\10\3\11\1\10\1\12\2\10\1\13\2\10\1\14"+
+    "\1\15\1\10\1\16\1\10\3\11\1\10\1\12\6\10"+
+    "\1\17\1\10\1\16\1\20\3\11\1\20\1\21\3\20"+
+    "\1\22\4\20\1\16\1\20\3\11\1\23\1\20\1\24"+
+    "\10\20\3\25\1\26\1\27\12\25\3\30\1\31\2\30"+
+    "\1\27\10\30\17\10\20\0\3\11\25\0\1\32\11\0"+
+    "\1\33\26\0\1\34\10\0\1\15\4\0\1\15\11\0"+
+    "\1\17\4\0\1\17\2\0\4\35\1\36\14\35\1\25"+
+    "\1\35\1\36\12\35\6\37\1\36\12\37\1\30\3\37"+
+    "\1\36\10\37\13\0\1\40\3\0";
+
+  private static int [] zzUnpacktrans() {
+    int [] result = new int[285];
+    int offset = 0;
+    offset = zzUnpacktrans(ZZ_TRANS_PACKED_0, offset, result);
+    return result;
+  }
+
+  private static int zzUnpacktrans(String packed, int offset, int [] result) {
+    int i = 0;       /* index in packed string  */
+    int j = offset;  /* index in unpacked array */
+    int l = packed.length();
+    while (i < l) {
+      int count = packed.charAt(i++);
+      int value = packed.charAt(i++);
+      value--;
+      do result[j++] = value; while (--count > 0);
+    }
+    return j;
+  }
+
+
+  /* error codes */
+  private static final int ZZ_UNKNOWN_ERROR = 0;
+  private static final int ZZ_NO_MATCH = 1;
+  private static final int ZZ_PUSHBACK_2BIG = 2;
+
+  /* error messages for the codes above */
+  private static final String[] ZZ_ERROR_MSG = {
+    "Unknown internal scanner error",
+    "Error: could not match input",
+    "Error: pushback value was too large"
+  };
+
+  /**
+   * ZZ_ATTRIBUTE[aState] contains the attributes of state {@code aState}
+   */
+  private static final int [] ZZ_ATTRIBUTE = zzUnpackAttribute();
+
+  private static final String ZZ_ATTRIBUTE_PACKED_0 =
+    "\7\0\1\11\5\1\1\11\1\1\1\11\1\1\3\11"+
+    "\2\1\1\11\2\1\1\11\1\0\1\11\1\0\1\11"+
+    "\1\0\1\11";
+
+  private static int [] zzUnpackAttribute() {
+    int [] result = new int[32];
+    int offset = 0;
+    offset = zzUnpackAttribute(ZZ_ATTRIBUTE_PACKED_0, offset, result);
+    return result;
+  }
+
+  private static int zzUnpackAttribute(String packed, int offset, int [] result) {
+    int i = 0;       /* index in packed string  */
+    int j = offset;  /* index in unpacked array */
+    int l = packed.length();
+    while (i < l) {
+      int count = packed.charAt(i++);
+      int value = packed.charAt(i++);
+      do result[j++] = value; while (--count > 0);
+    }
+    return j;
+  }
+
+  /** the input device */
+  private java.io.Reader zzReader;
+
+  /** the current state of the DFA */
+  private int zzState;
+
+  /** the current lexical state */
+  private int zzLexicalState = YYINITIAL;
+
+  /** this buffer contains the current text to be matched and is
+      the source of the yytext() string */
+  private CharSequence zzBuffer = "";
+
+  /** the textposition at the last accepting state */
+  private int zzMarkedPos;
+
+  /** the current text position in the buffer */
+  private int zzCurrentPos;
+
+  /** startRead marks the beginning of the yytext() string in the buffer */
+  private int zzStartRead;
+
+  /** endRead marks the last character in the buffer, that has been read
+      from input */
+  private int zzEndRead;
+
+  /** zzAtEOF == true <=> the scanner is at the EOF */
+  private boolean zzAtEOF;
+
+  /** Number of newlines encountered up to the start of the matched text. */
+  @SuppressWarnings("unused")
+  private int yyline;
+
+  /** Number of characters from the last newline up to the start of the matched text. */
+  @SuppressWarnings("unused")
+  protected int yycolumn;
+
+  /** Number of characters up to the start of the matched text. */
+  @SuppressWarnings("unused")
+  private long yychar;
+
+  /** Whether the scanner is currently at the beginning of a line. */
+  @SuppressWarnings("unused")
+  private boolean zzAtBOL = true;
+
+  /** Whether the user-EOF-code has already been executed. */
+  @SuppressWarnings("unused")
+  private boolean zzEOFDone;
+
+  /* user code: */
+
+
+
+
+  /**
+   * Creates a new scanner
+   *
+   * @param   in  the java.io.Reader to read input from.
+   */
+  public _GspDirectiveLexer(java.io.Reader in) {
+    this.zzReader = in;
+  }
+
+
+  /** Returns the maximum size of the scanner buffer, which limits the size of tokens. */
+  private int zzMaxBufferLen() {
+    return Integer.MAX_VALUE;
+  }
+
+  /**  Whether the scanner buffer can grow to accommodate a larger token. */
+  private boolean zzCanGrow() {
+    return true;
+  }
+
+  /**
+   * Translates raw input code points to DFA table row
+   */
+  private static int zzCMap(int input) {
+    int offset = input & 255;
+    return offset == input ? ZZ_CMAP_BLOCKS[offset] : ZZ_CMAP_BLOCKS[ZZ_CMAP_TOP[input >> 8] | offset];
+  }
+
+  public final int getTokenStart() {
+    return zzStartRead;
+  }
+
+  public final int getTokenEnd() {
+    return getTokenStart() + yylength();
+  }
+
+  public void reset(CharSequence buffer, int start, int end, int initialState) {
+    zzBuffer = buffer;
+    zzCurrentPos = zzMarkedPos = zzStartRead = start;
+    zzAtEOF  = false;
+    zzAtBOL = true;
+    zzEndRead = end;
+    yybegin(initialState);
+  }
+
+  /**
+   * Refills the input buffer.
+   *
+   * @return      {@code false}, iff there was new input.
+   *
+   * @exception   java.io.IOException  if any I/O-Error occurs
+   */
+  private boolean zzRefill() throws java.io.IOException {
+    return true;
+  }
+
+
+  /**
+   * Returns the current lexical state.
+   */
+  public final int yystate() {
+    return zzLexicalState;
+  }
+
+
+  /**
+   * Enters a new lexical state
+   *
+   * @param newState the new lexical state
+   */
+  public final void yybegin(int newState) {
+    zzLexicalState = newState;
+  }
+
+
+  /**
+   * Returns the text matched by the current regular expression.
+   */
+  public final CharSequence yytext() {
+    return zzBuffer.subSequence(zzStartRead, zzMarkedPos);
+  }
+
+
+  /**
+   * Returns the character at position {@code pos} from the
+   * matched text.
+   *
+   * It is equivalent to yytext().charAt(pos), but faster
+   *
+   * @param pos the position of the character to fetch.
+   *            A value from 0 to yylength()-1.
+   *
+   * @return the character at position pos
+   */
+  public final char yycharat(int pos) {
+    return zzBuffer.charAt(zzStartRead+pos);
+  }
+
+
+  /**
+   * Returns the length of the matched text region.
+   */
+  public final int yylength() {
+    return zzMarkedPos-zzStartRead;
+  }
+
+
+  /**
+   * Reports an error that occurred while scanning.
+   *
+   * In a wellformed scanner (no or only correct usage of
+   * yypushback(int) and a match-all fallback rule) this method
+   * will only be called with things that "Can't Possibly Happen".
+   * If this method is called, something is seriously wrong
+   * (e.g. a JFlex bug producing a faulty scanner etc.).
+   *
+   * Usual syntax/scanner level error handling should be done
+   * in error fallback rules.
+   *
+   * @param   errorCode  the code of the errormessage to display
+   */
+  private void zzScanError(int errorCode) {
+    String message;
+    try {
+      message = ZZ_ERROR_MSG[errorCode];
+    }
+    catch (ArrayIndexOutOfBoundsException e) {
+      message = ZZ_ERROR_MSG[ZZ_UNKNOWN_ERROR];
+    }
+
+    throw new Error(message);
+  }
+
+
+  /**
+   * Pushes the specified amount of characters back into the input stream.
+   *
+   * They will be read again by then next call of the scanning method
+   *
+   * @param number  the number of characters to be read again.
+   *                This number must not be greater than yylength()!
+   */
+  public void yypushback(int number)  {
+    if ( number > yylength() )
+      zzScanError(ZZ_PUSHBACK_2BIG);
+
+    zzMarkedPos -= number;
+  }
+
+
+  /**
+   * Resumes scanning until the next regular expression is matched,
+   * the end of input is encountered or an I/O-Error occurs.
+   *
+   * @return      the next token
+   * @exception   java.io.IOException  if any I/O-Error occurs
+   */
+  public IElementType advance() throws java.io.IOException
+  {
+    int zzInput;
+    int zzAction;
+
+    // cached fields:
+    int zzCurrentPosL;
+    int zzMarkedPosL;
+    int zzEndReadL = zzEndRead;
+    CharSequence zzBufferL = zzBuffer;
+
+    int [] zzTransL = ZZ_TRANS;
+    int [] zzRowMapL = ZZ_ROWMAP;
+    int [] zzAttrL = ZZ_ATTRIBUTE;
+
+    while (true) {
+      zzMarkedPosL = zzMarkedPos;
+
+      zzAction = -1;
+
+      zzCurrentPosL = zzCurrentPos = zzStartRead = zzMarkedPosL;
+
+      zzState = ZZ_LEXSTATE[zzLexicalState];
+
+      // set up zzAction for empty match case:
+      int zzAttributes = zzAttrL[zzState];
+      if ( (zzAttributes & 1) == 1 ) {
+        zzAction = zzState;
+      }
+
+
+      zzForAction: {
+        while (true) {
+
+          if (zzCurrentPosL < zzEndReadL) {
+            zzInput = Character.codePointAt(zzBufferL, zzCurrentPosL);
+            zzCurrentPosL += Character.charCount(zzInput);
+          }
+          else if (zzAtEOF) {
+            zzInput = YYEOF;
+            break zzForAction;
+          }
+          else {
+            // store back cached positions
+            zzCurrentPos  = zzCurrentPosL;
+            zzMarkedPos   = zzMarkedPosL;
+            boolean eof = zzRefill();
+            // get translated positions and possibly new buffer
+            zzCurrentPosL  = zzCurrentPos;
+            zzMarkedPosL   = zzMarkedPos;
+            zzBufferL      = zzBuffer;
+            zzEndReadL     = zzEndRead;
+            if (eof) {
+              zzInput = YYEOF;
+              break zzForAction;
+            }
+            else {
+              zzInput = Character.codePointAt(zzBufferL, zzCurrentPosL);
+              zzCurrentPosL += Character.charCount(zzInput);
+            }
+          }
+          int zzNext = zzTransL[ zzRowMapL[zzState] + zzCMap(zzInput) ];
+          if (zzNext == -1) break zzForAction;
+          zzState = zzNext;
+
+          zzAttributes = zzAttrL[zzState];
+          if ( (zzAttributes & 1) == 1 ) {
+            zzAction = zzState;
+            zzMarkedPosL = zzCurrentPosL;
+            if ( (zzAttributes & 8) == 8 ) break zzForAction;
+          }
+
+        }
+      }
+
+      // store back cached position
+      zzMarkedPos = zzMarkedPosL;
+
+      if (zzInput == YYEOF && zzStartRead == zzCurrentPos) {
+        zzAtEOF = true;
+        return null;
+      }
+      else {
+        switch (zzAction < 0 ? zzAction : ZZ_ACTION[zzAction]) {
+          case 1:
+            { return GSP_BAD_CHARACTER;
+            }
+          // fall through
+          case 15: break;
+          case 2:
+            { return XML_WHITE_SPACE;
+            }
+          // fall through
+          case 16: break;
+          case 3:
+            { yybegin(ATTR_LIST);
+                                                  return XML_TAG_NAME;
+            }
+          // fall through
+          case 17: break;
+          case 4:
+            { yybegin(YYINITIAL); return GDIRECT_END;
+            }
+          // fall through
+          case 18: break;
+          case 5:
+            { yybegin(ATTR);
+                                                  return XML_NAME;
+            }
+          // fall through
+          case 19: break;
+          case 6:
+            { yybegin(ATTR_LIST); return GSP_BAD_CHARACTER;
+            }
+          // fall through
+          case 20: break;
+          case 7:
+            { yybegin(ATTR_VALUE); return XML_EQ;
+            }
+          // fall through
+          case 21: break;
+          case 8:
+            { yybegin(ATTR_VALUE_DQ); return XML_ATTRIBUTE_VALUE_START_DELIMITER;
+            }
+          // fall through
+          case 22: break;
+          case 9:
+            { yybegin(ATTR_VALUE_SQ); return XML_ATTRIBUTE_VALUE_START_DELIMITER;
+            }
+          // fall through
+          case 23: break;
+          case 10:
+            { yybegin(ATTR_LIST); return XML_ATTRIBUTE_VALUE_END_DELIMITER;
+            }
+          // fall through
+          case 24: break;
+          case 11:
+            { yybegin(YYINITIAL); return JDIRECT_END;
+            }
+          // fall through
+          case 25: break;
+          case 12:
+            { return GDIRECT_BEGIN;
+            }
+          // fall through
+          case 26: break;
+          case 13:
+            { yypushback(1); return XML_ATTRIBUTE_VALUE_TOKEN;
+            }
+          // fall through
+          case 27: break;
+          case 14:
+            { return JDIRECT_BEGIN;
+            }
+          // fall through
+          case 28: break;
+          default:
+            zzScanError(ZZ_NO_MATCH);
+          }
+      }
+    }
+  }
+
+
+}
diff --git a/gen/org/jetbrains/plugins/grails/lang/gsp/lexer/core/_GspLexer.java b/gen/org/jetbrains/plugins/grails/lang/gsp/lexer/core/_GspLexer.java
new file mode 100644
index 0000000..947b05f
--- /dev/null
+++ b/gen/org/jetbrains/plugins/grails/lang/gsp/lexer/core/_GspLexer.java
@@ -0,0 +1,1190 @@
+// Generated by JFlex 1.9.1 http://jflex.de/  (tweaked for IntelliJ platform)
+// source: gsp.flex
+
+// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
+
+package org.jetbrains.plugins.grails.lang.gsp.lexer.core;
+
+import com.intellij.lexer.FlexLexer;
+import com.intellij.psi.tree.IElementType;
+import java.util.*;
+import java.lang.reflect.Field;
+import org.jetbrains.annotations.NotNull;
+import com.intellij.openapi.util.text.StringUtil;
+import org.jetbrains.plugins.grails.lang.gsp.lexer.*;
+
+
+public class _GspLexer implements FlexLexer, GspTokenTypesEx {
+
+  /** This character denotes the end of file */
+  public static final int YYEOF = -1;
+
+  /** initial size of the lookahead buffer */
+  private static final int ZZ_BUFFERSIZE = 16384;
+
+  /** lexical states */
+  public static final int YYINITIAL = 0;
+  public static final int INJECTION_START = 2;
+  public static final int IN_J_SCRIPT = 4;
+  public static final int IN_JSCRIPT_END = 6;
+  public static final int IN_J_DIRECT = 8;
+  public static final int IN_J_DECLAR = 10;
+  public static final int IN_J_EXPR = 12;
+  public static final int IN_GEXPR = 14;
+  public static final int IN_GSCRIPT = 16;
+  public static final int IN_GDIRECT = 18;
+  public static final int IN_GDECLAR = 20;
+  public static final int IN_GTAG_START = 22;
+  public static final int IN_GTAG_END = 24;
+  public static final int IN_GTAG_END_AFTER_NAME = 26;
+  public static final int ATTR_LIST = 28;
+  public static final int ATTR = 30;
+  public static final int ATTR_VALUE = 32;
+  public static final int GEXPR_IN_VALUE_DQ = 34;
+  public static final int GEXPR_IN_VALUE_SQ = 36;
+  public static final int ATTR_VALUE_OR_MAP_DQ = 38;
+  public static final int ATTR_VALUE_OR_MAP_SQ = 40;
+  public static final int ATTR_VALUE_GSTRING_SQ = 42;
+  public static final int ATTR_VALUE_GSTRING_DQ = 44;
+  public static final int ATTR_VALUE_END = 46;
+  public static final int ATTR_VALUE_DQ_NOT_MAP = 48;
+  public static final int ATTR_VALUE_SQ_NOT_MAP = 50;
+  public static final int ATTR_GROOVY_START_DQ = 52;
+  public static final int ATTR_GROOVY_START_SQ = 54;
+  public static final int GSP_COMMENT = 56;
+  public static final int JSP_COMMENT = 58;
+
+  /**
+   * ZZ_LEXSTATE[l] is the state in the DFA for the lexical state l
+   * ZZ_LEXSTATE[l+1] is the state in the DFA for the lexical state l
+   *                  at the beginning of a line
+   * l is of the form l = 2*k, k a non negative integer
+   */
+  private static final int ZZ_LEXSTATE[] = {
+     0,  0,  1,  1,  2,  2,  3,  3,  4,  4,  5,  5,  6,  6,  7,  7, 
+     8,  8,  9,  9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 
+    16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 23, 
+    24, 24, 25, 25, 26, 26, 27, 27, 28, 28, 29, 29
+  };
+
+  /**
+   * Top-level table for translating characters to character classes
+   */
+  private static final int [] ZZ_CMAP_TOP = zzUnpackcmap_top();
+
+  private static final String ZZ_CMAP_TOP_PACKED_0 =
+    "\1\0\1\u0100\1\u0200\1\u0300\1\u0400\1\u0500\1\u0600\1\u0700"+
+    "\1\u0800\1\u0900\1\u0a00\1\u0b00\1\u0c00\1\u0d00\1\u0e00\1\u0f00"+
+    "\1\u1000\1\u0100\1\u1100\1\u1200\1\u1300\1\u0100\1\u1400\1\u1500"+
+    "\1\u1600\1\u1700\1\u1800\1\u1900\1\u1a00\1\u1b00\1\u0100\1\u1c00"+
+    "\1\u1d00\1\u1e00\12\u1f00\1\u2000\1\u2100\1\u2200\1\u1f00\1\u2300"+
+    "\1\u2400\2\u1f00\31\u0100\1\u1b00\121\u0100\1\u2500\4\u0100\1\u2600"+
+    "\1\u0100\1\u2700\1\u2800\1\u2900\1\u2a00\1\u2b00\1\u2c00\53\u0100"+
+    "\1\u2d00\41\u1f00\1\u0100\1\u2e00\1\u2f00\1\u0100\1\u3000\1\u3100"+
+    "\1\u3200\1\u3300\1\u3400\1\u3500\1\u3600\1\u3700\1\u3800\1\u0100"+
+    "\1\u3900\1\u3a00\1\u3b00\1\u3c00\1\u3d00\1\u3e00\1\u3f00\1\u4000"+
+    "\1\u4100\1\u4200\1\u4300\1\u4400\1\u4500\1\u4600\1\u4700\1\u4800"+
+    "\1\u4900\1\u4a00\1\u4b00\1\u4c00\1\u1f00\1\u4d00\1\u4e00\1\u4f00"+
+    "\1\u5000\3\u0100\1\u5100\1\u5200\1\u5300\12\u1f00\4\u0100\1\u5400"+
+    "\17\u1f00\2\u0100\1\u5500\41\u1f00\2\u0100\1\u5600\1\u5700\2\u1f00"+
+    "\1\u5800\1\u5900\27\u0100\1\u5a00\4\u0100\1\u5b00\1\u5c00\42\u1f00"+
+    "\1\u0100\1\u5d00\1\u5e00\11\u1f00\1\u5f00\27\u1f00\1\u6000\1\u6100"+
+    "\1\u6200\1\u6300\11\u1f00\1\u6400\1\u6500\5\u1f00\1\u6600\1\u6700"+
+    "\2\u1f00\1\u6800\1\u1f00\1\u6900\21\u1f00\246\u0100\1\u6a00\20\u0100"+
+    "\1\u6b00\1\u6c00\25\u0100\1\u6d00\34\u0100\1\u6e00\14\u1f00\2\u0100"+
+    "\1\u6f00\5\u1f00\23\u0100\1\u7000\u0dec\u1f00";
+
+  private static int [] zzUnpackcmap_top() {
+    int [] result = new int[4352];
+    int offset = 0;
+    offset = zzUnpackcmap_top(ZZ_CMAP_TOP_PACKED_0, offset, result);
+    return result;
+  }
+
+  private static int zzUnpackcmap_top(String packed, int offset, int [] result) {
+    int i = 0;       /* index in packed string  */
+    int j = offset;  /* index in unpacked array */
+    int l = packed.length();
+    while (i < l) {
+      int count = packed.charAt(i++);
+      int value = packed.charAt(i++);
+      do result[j++] = value; while (--count > 0);
+    }
+    return j;
+  }
+
+
+  /**
+   * Second-level tables for translating characters to character classes
+   */
+  private static final int [] ZZ_CMAP_BLOCKS = zzUnpackcmap_blocks();
+
+  private static final String ZZ_CMAP_BLOCKS_PACKED_0 =
+    "\11\0\2\1\1\0\1\2\1\1\22\0\1\1\1\3"+
+    "\1\4\1\0\1\5\1\6\1\0\1\7\5\0\1\10"+
+    "\1\0\1\11\12\12\1\13\1\0\1\14\1\15\1\16"+
+    "\1\0\1\17\32\20\1\21\1\22\1\23\1\0\1\24"+
+    "\1\0\32\20\1\25\1\0\1\26\44\0\4\27\4\0"+
+    "\1\27\12\0\1\27\4\0\1\27\5\0\27\27\1\0"+
+    "\37\27\1\0\u01ca\27\4\0\14\27\16\0\5\27\7\0"+
+    "\1\27\1\0\1\27\201\0\5\27\1\0\2\27\2\0"+
+    "\4\27\1\0\1\27\6\0\1\27\1\0\3\27\1\0"+
+    "\1\27\1\0\24\27\1\0\123\27\1\0\213\27\10\0"+
+    "\246\27\1\0\46\27\2\0\1\27\6\0\51\27\6\0"+
+    "\1\27\100\0\33\27\4\0\4\27\30\0\1\27\24\0"+
+    "\53\27\43\0\2\27\1\0\143\27\1\0\1\27\17\0"+
+    "\2\27\7\0\2\27\12\0\3\27\2\0\1\27\20\0"+
+    "\1\27\1\0\36\27\35\0\131\27\13\0\1\27\30\0"+
+    "\41\27\11\0\2\27\4\0\1\27\3\0\30\27\4\0"+
+    "\1\27\11\0\1\27\3\0\1\27\27\0\31\27\7\0"+
+    "\13\27\65\0\25\27\1\0\22\27\74\0\66\27\3\0"+
+    "\1\27\22\0\1\27\7\0\12\27\17\0\20\27\4\0"+
+    "\10\27\2\0\2\27\2\0\26\27\1\0\7\27\1\0"+
+    "\1\27\3\0\4\27\3\0\1\27\20\0\1\27\15\0"+
+    "\2\27\1\0\3\27\16\0\4\27\7\0\2\27\10\0"+
+    "\6\27\4\0\2\27\2\0\26\27\1\0\7\27\1\0"+
+    "\2\27\1\0\2\27\1\0\2\27\37\0\4\27\1\0"+
+    "\1\27\23\0\3\27\20\0\11\27\1\0\3\27\1\0"+
+    "\26\27\1\0\7\27\1\0\2\27\1\0\5\27\3\0"+
+    "\1\27\22\0\1\27\17\0\2\27\17\0\1\27\7\0"+
+    "\1\27\13\0\10\27\2\0\2\27\2\0\26\27\1\0"+
+    "\7\27\1\0\2\27\1\0\5\27\3\0\1\27\36\0"+
+    "\2\27\1\0\3\27\17\0\1\27\21\0\1\27\1\0"+
+    "\6\27\3\0\3\27\1\0\4\27\3\0\2\27\1\0"+
+    "\1\27\1\0\2\27\3\0\2\27\3\0\3\27\3\0"+
+    "\14\27\26\0\1\27\50\0\1\27\13\0\10\27\1\0"+
+    "\3\27\1\0\27\27\1\0\20\27\3\0\1\27\32\0"+
+    "\3\27\5\0\2\27\36\0\1\27\4\0\10\27\1\0"+
+    "\3\27\1\0\27\27\1\0\12\27\1\0\5\27\3\0"+
+    "\1\27\40\0\1\27\1\0\2\27\17\0\2\27\21\0"+
+    "\11\27\1\0\3\27\1\0\51\27\2\0\1\27\20\0"+
+    "\1\27\5\0\3\27\10\0\3\27\30\0\6\27\5\0"+
+    "\22\27\3\0\30\27\1\0\11\27\1\0\1\27\2\0"+
+    "\7\27\72\0\60\27\1\0\2\27\13\0\10\27\72\0"+
+    "\2\27\1\0\1\27\1\0\5\27\1\0\30\27\1\0"+
+    "\1\27\1\0\12\27\1\0\2\27\11\0\1\27\2\0"+
+    "\5\27\1\0\1\27\25\0\4\27\40\0\1\27\77\0"+
+    "\10\27\1\0\44\27\33\0\5\27\163\0\53\27\24\0"+
+    "\1\27\20\0\6\27\4\0\4\27\3\0\1\27\3\0"+
+    "\2\27\7\0\3\27\4\0\15\27\14\0\1\27\21\0"+
+    "\46\27\1\0\1\27\5\0\1\27\2\0\53\27\1\0"+
+    "\115\27\1\0\4\27\2\0\7\27\1\0\1\27\1\0"+
+    "\4\27\2\0\51\27\1\0\4\27\2\0\41\27\1\0"+
+    "\4\27\2\0\7\27\1\0\1\27\1\0\4\27\2\0"+
+    "\17\27\1\0\71\27\1\0\4\27\2\0\103\27\45\0"+
+    "\20\27\20\0\126\27\2\0\6\27\3\0\u016c\27\2\0"+
+    "\21\27\1\0\32\27\5\0\113\27\3\0\13\27\7\0"+
+    "\15\27\1\0\4\27\16\0\22\27\16\0\22\27\16\0"+
+    "\15\27\1\0\3\27\17\0\64\27\43\0\1\27\3\0"+
+    "\2\27\103\0\131\27\7\0\5\27\2\0\42\27\1\0"+
+    "\1\27\5\0\106\27\12\0\37\27\61\0\36\27\2\0"+
+    "\5\27\13\0\54\27\4\0\32\27\66\0\27\27\11\0"+
+    "\65\27\122\0\1\27\135\0\57\27\21\0\7\27\67\0"+
+    "\36\27\15\0\2\27\12\0\54\27\32\0\44\27\51\0"+
+    "\3\27\12\0\44\27\2\0\11\27\7\0\53\27\2\0"+
+    "\3\27\51\0\4\27\1\0\6\27\1\0\2\27\3\0"+
+    "\1\27\5\0\300\27\100\0\26\27\2\0\6\27\2\0"+
+    "\46\27\2\0\6\27\2\0\10\27\1\0\1\27\1\0"+
+    "\1\27\1\0\1\27\1\0\37\27\2\0\65\27\1\0"+
+    "\7\27\1\0\1\27\3\0\3\27\1\0\7\27\3\0"+
+    "\4\27\2\0\6\27\4\0\15\27\5\0\3\27\1\0"+
+    "\7\27\102\0\2\27\23\0\1\27\34\0\1\27\15\0"+
+    "\1\27\20\0\15\27\3\0\40\27\102\0\1\27\4\0"+
+    "\1\27\2\0\12\27\1\0\1\27\3\0\5\27\6\0"+
+    "\1\27\1\0\1\27\1\0\1\27\1\0\4\27\1\0"+
+    "\13\27\2\0\4\27\5\0\5\27\4\0\1\27\21\0"+
+    "\51\27\u0177\0\57\27\1\0\57\27\1\0\205\27\6\0"+
+    "\4\27\3\0\2\27\14\0\46\27\1\0\1\27\5\0"+
+    "\1\27\2\0\70\27\7\0\1\27\20\0\27\27\11\0"+
+    "\7\27\1\0\7\27\1\0\7\27\1\0\7\27\1\0"+
+    "\7\27\1\0\7\27\1\0\7\27\1\0\7\27\120\0"+
+    "\1\27\325\0\3\27\31\0\11\27\7\0\5\27\2\0"+
+    "\5\27\4\0\126\27\6\0\3\27\1\0\132\27\1\0"+
+    "\4\27\5\0\53\27\1\0\136\27\21\0\40\27\60\0"+
+    "\u010d\27\3\0\215\27\103\0\56\27\2\0\15\27\3\0"+
+    "\20\27\12\0\2\27\24\0\57\27\20\0\37\27\2\0"+
+    "\120\27\47\0\11\27\2\0\147\27\2\0\65\27\2\0"+
+    "\11\27\52\0\15\27\1\0\3\27\1\0\4\27\1\0"+
+    "\27\27\25\0\1\27\7\0\64\27\16\0\62\27\76\0"+
+    "\6\27\3\0\1\27\1\0\2\27\13\0\34\27\12\0"+
+    "\27\27\31\0\35\27\7\0\57\27\34\0\1\27\20\0"+
+    "\5\27\1\0\12\27\12\0\5\27\1\0\51\27\27\0"+
+    "\3\27\1\0\10\27\24\0\27\27\3\0\1\27\3\0"+
+    "\62\27\1\0\1\27\3\0\2\27\2\0\5\27\2\0"+
+    "\1\27\1\0\1\27\30\0\3\27\2\0\13\27\7\0"+
+    "\3\27\14\0\6\27\2\0\6\27\2\0\6\27\11\0"+
+    "\7\27\1\0\7\27\1\0\53\27\1\0\16\27\6\0"+
+    "\163\27\35\0\244\27\14\0\27\27\4\0\61\27\4\0"+
+    "\156\27\2\0\152\27\46\0\7\27\14\0\5\27\5\0"+
+    "\1\27\1\0\12\27\1\0\15\27\1\0\5\27\1\0"+
+    "\1\27\1\0\2\27\1\0\2\27\1\0\154\27\41\0"+
+    "\153\27\22\0\100\27\2\0\66\27\50\0\15\27\66\0"+
+    "\2\27\30\0\3\27\31\0\1\27\6\0\5\27\1\0"+
+    "\207\27\7\0\1\27\34\0\32\27\4\0\1\27\1\0"+
+    "\32\27\13\0\131\27\3\0\6\27\2\0\6\27\2\0"+
+    "\6\27\2\0\3\27\3\0\2\27\3\0\2\27\31\0"+
+    "\14\27\1\0\32\27\1\0\23\27\1\0\2\27\1\0"+
+    "\17\27\2\0\16\27\42\0\173\27\105\0\65\27\u010b\0"+
+    "\35\27\3\0\61\27\57\0\40\27\15\0\36\27\5\0"+
+    "\46\27\12\0\36\27\2\0\44\27\4\0\10\27\1\0"+
+    "\5\27\52\0\236\27\22\0\44\27\4\0\44\27\4\0"+
+    "\50\27\10\0\64\27\234\0\67\27\11\0\26\27\12\0"+
+    "\10\27\230\0\6\27\2\0\1\27\1\0\54\27\1\0"+
+    "\2\27\3\0\1\27\2\0\27\27\12\0\27\27\11\0"+
+    "\37\27\101\0\23\27\1\0\2\27\12\0\26\27\12\0"+
+    "\32\27\106\0\70\27\6\0\2\27\100\0\1\27\17\0"+
+    "\4\27\1\0\3\27\1\0\35\27\52\0\35\27\3\0"+
+    "\35\27\43\0\10\27\1\0\34\27\33\0\66\27\12\0"+
+    "\26\27\12\0\23\27\15\0\22\27\156\0\111\27\67\0"+
+    "\63\27\15\0\63\27\15\0\44\27\u015c\0\52\27\6\0"+
+    "\2\27\116\0\35\27\12\0\1\27\10\0\26\27\152\0"+
+    "\25\27\33\0\27\27\14\0\65\27\113\0\55\27\40\0"+
+    "\31\27\32\0\44\27\35\0\1\27\2\0\1\27\10\0"+
+    "\43\27\3\0\1\27\14\0\60\27\16\0\4\27\25\0"+
+    "\1\27\1\0\1\27\43\0\22\27\1\0\31\27\124\0"+
+    "\7\27\1\0\1\27\1\0\4\27\1\0\17\27\1\0"+
+    "\12\27\7\0\57\27\46\0\10\27\2\0\2\27\2\0"+
+    "\26\27\1\0\7\27\1\0\2\27\1\0\5\27\3\0"+
+    "\1\27\22\0\1\27\14\0\5\27\236\0\65\27\22\0"+
+    "\4\27\24\0\3\27\36\0\60\27\24\0\2\27\1\0"+
+    "\1\27\270\0\57\27\51\0\4\27\44\0\60\27\24\0"+
+    "\1\27\73\0\53\27\15\0\1\27\107\0\33\27\345\0"+
+    "\54\27\164\0\100\27\37\0\10\27\2\0\1\27\2\0"+
+    "\10\27\1\0\2\27\1\0\30\27\17\0\1\27\1\0"+
+    "\1\27\136\0\10\27\2\0\47\27\20\0\1\27\1\0"+
+    "\1\27\34\0\1\27\12\0\50\27\7\0\1\27\25\0"+
+    "\1\27\13\0\56\27\23\0\1\27\42\0\71\27\7\0"+
+    "\11\27\1\0\45\27\21\0\1\27\61\0\36\27\160\0"+
+    "\7\27\1\0\2\27\1\0\46\27\25\0\1\27\31\0"+
+    "\6\27\1\0\2\27\1\0\40\27\16\0\1\27\u0147\0"+
+    "\23\27\275\0\1\27\54\0\4\27\37\0\232\27\146\0"+
+    "\157\27\21\0\304\27\274\0\57\27\321\0\107\27\271\0"+
+    "\71\27\7\0\37\27\161\0\36\27\22\0\60\27\20\0"+
+    "\4\27\37\0\25\27\5\0\23\27\260\0\100\27\200\0"+
+    "\113\27\5\0\1\27\102\0\15\27\100\0\2\27\1\0"+
+    "\1\27\34\0\370\27\10\0\326\27\52\0\11\27\367\0"+
+    "\37\27\61\0\3\27\21\0\4\27\10\0\u018c\27\4\0"+
+    "\153\27\5\0\15\27\3\0\11\27\7\0\12\27\146\0"+
+    "\125\27\1\0\107\27\1\0\2\27\2\0\1\27\2\0"+
+    "\2\27\2\0\4\27\1\0\14\27\1\0\1\27\1\0"+
+    "\7\27\1\0\101\27\1\0\4\27\2\0\10\27\1\0"+
+    "\7\27\1\0\34\27\1\0\4\27\1\0\5\27\1\0"+
+    "\1\27\3\0\7\27\1\0\u0154\27\2\0\31\27\1\0"+
+    "\31\27\1\0\37\27\1\0\31\27\1\0\37\27\1\0"+
+    "\31\27\1\0\37\27\1\0\31\27\1\0\37\27\1\0"+
+    "\31\27\1\0\10\27\64\0\55\27\12\0\7\27\20\0"+
+    "\1\27\u0171\0\54\27\23\0\306\27\73\0\104\27\7\0"+
+    "\1\27\u0164\0\1\27\117\0\4\27\1\0\33\27\1\0"+
+    "\2\27\1\0\1\27\2\0\1\27\1\0\12\27\1\0"+
+    "\4\27\1\0\1\27\1\0\1\27\6\0\1\27\4\0"+
+    "\1\27\1\0\1\27\1\0\1\27\1\0\3\27\1\0"+
+    "\2\27\1\0\1\27\2\0\1\27\1\0\1\27\1\0"+
+    "\1\27\1\0\1\27\1\0\1\27\1\0\2\27\1\0"+
+    "\1\27\2\0\4\27\1\0\7\27\1\0\4\27\1\0"+
+    "\4\27\1\0\1\27\1\0\12\27\1\0\21\27\5\0"+
+    "\3\27\1\0\5\27\1\0\21\27\104\0\336\27\42\0"+
+    "\65\27\13\0\336\27\2\0\u0182\27\16\0\u0131\27\37\0"+
+    "\36\27\342\0\113\27\265\0";
+
+  private static int [] zzUnpackcmap_blocks() {
+    int [] result = new int[28928];
+    int offset = 0;
+    offset = zzUnpackcmap_blocks(ZZ_CMAP_BLOCKS_PACKED_0, offset, result);
+    return result;
+  }
+
+  private static int zzUnpackcmap_blocks(String packed, int offset, int [] result) {
+    int i = 0;       /* index in packed string  */
+    int j = offset;  /* index in unpacked array */
+    int l = packed.length();
+    while (i < l) {
+      int count = packed.charAt(i++);
+      int value = packed.charAt(i++);
+      do result[j++] = value; while (--count > 0);
+    }
+    return j;
+  }
+
+  /**
+   * Translates DFA states to action switch labels.
+   */
+  private static final int [] ZZ_ACTION = zzUnpackAction();
+
+  private static final String ZZ_ACTION_PACKED_0 =
+    "\36\0\5\1\5\0\2\2\2\3\2\4\2\5\1\6"+
+    "\1\7\1\2\1\10\1\4\3\11\1\12\1\13\1\14"+
+    "\1\12\2\15\1\16\1\17\2\20\1\21\1\22\1\23"+
+    "\1\24\1\22\1\25\1\26\1\27\1\30\1\31\2\27"+
+    "\1\32\1\33\2\32\1\34\1\35\1\36\1\37\2\40"+
+    "\4\41\2\42\2\43\3\44\2\0\1\45\1\46\1\47"+
+    "\1\50\1\51\1\52\1\53\1\54\1\55\1\56\1\0"+
+    "\1\57\1\0\1\60\1\61\1\16\1\62\2\0\1\63"+
+    "\3\0\1\64\6\0\1\65\1\66\1\67\1\70\1\57"+
+    "\1\71\2\0\1\72\1\73\1\74\1\75\1\76\1\77";
+
+  private static int [] zzUnpackAction() {
+    int [] result = new int[145];
+    int offset = 0;
+    offset = zzUnpackAction(ZZ_ACTION_PACKED_0, offset, result);
+    return result;
+  }
+
+  private static int zzUnpackAction(String packed, int offset, int [] result) {
+    int i = 0;       /* index in packed string  */
+    int j = offset;  /* index in unpacked array */
+    int l = packed.length();
+    while (i < l) {
+      int count = packed.charAt(i++);
+      int value = packed.charAt(i++);
+      do result[j++] = value; while (--count > 0);
+    }
+    return j;
+  }
+
+
+  /**
+   * Translates a state to a row index in the transition table
+   */
+  private static final int [] ZZ_ROWMAP = zzUnpackRowMap();
+
+  private static final String ZZ_ROWMAP_PACKED_0 =
+    "\0\0\0\30\0\60\0\110\0\140\0\170\0\220\0\250"+
+    "\0\300\0\330\0\360\0\u0108\0\u0120\0\u0138\0\u0150\0\u0168"+
+    "\0\u0180\0\u0198\0\u01b0\0\u01c8\0\u01e0\0\u01f8\0\u0210\0\u0228"+
+    "\0\u0240\0\u0258\0\u0270\0\u0288\0\u02a0\0\u02b8\0\110\0\u02d0"+
+    "\0\u02e8\0\u0300\0\u0318\0\u0330\0\u0348\0\u0360\0\u0378\0\u0390"+
+    "\0\110\0\u03a8\0\110\0\u03c0\0\110\0\u03d8\0\110\0\u03f0"+
+    "\0\110\0\110\0\u0408\0\110\0\u0420\0\110\0\u0438\0\u0450"+
+    "\0\u0468\0\110\0\u0480\0\u0498\0\110\0\u04b0\0\u04c8\0\110"+
+    "\0\110\0\u04b0\0\110\0\110\0\110\0\110\0\u04b0\0\110"+
+    "\0\110\0\110\0\110\0\u04e0\0\u04f8\0\u0510\0\110\0\u0528"+
+    "\0\u0540\0\u0558\0\110\0\u0570\0\110\0\u0588\0\110\0\u05a0"+
+    "\0\u05b8\0\u04e0\0\u05d0\0\u0528\0\110\0\u05e8\0\110\0\u0600"+
+    "\0\110\0\u0618\0\u0630\0\u0648\0\u0660\0\110\0\110\0\110"+
+    "\0\u0678\0\110\0\110\0\110\0\110\0\110\0\110\0\u0438"+
+    "\0\u0690\0\u0450\0\u06a8\0\110\0\110\0\110\0\u04f8\0\u06c0"+
+    "\0\110\0\u0540\0\u06d8\0\u05b8\0\110\0\u05d0\0\u06f0\0\u0708"+
+    "\0\u0720\0\u0738\0\u0750\0\110\0\110\0\110\0\110\0\u0768"+
+    "\0\110\0\u0780\0\u0798\0\110\0\110\0\110\0\110\0\110"+
+    "\0\110";
+
+  private static int [] zzUnpackRowMap() {
+    int [] result = new int[145];
+    int offset = 0;
+    offset = zzUnpackRowMap(ZZ_ROWMAP_PACKED_0, offset, result);
+    return result;
+  }
+
+  private static int zzUnpackRowMap(String packed, int offset, int [] result) {
+    int i = 0;  /* index in packed string  */
+    int j = offset;  /* index in unpacked array */
+    int l = packed.length() - 1;
+    while (i < l) {
+      int high = packed.charAt(i++) << 16;
+      result[j++] = high | packed.charAt(i++);
+    }
+    return j;
+  }
+
+  /**
+   * The transition table of the DFA
+   */
+  private static final int [] ZZ_TRANS = zzUnpacktrans();
+
+  private static final String ZZ_TRANS_PACKED_0 =
+    "\3\37\1\40\1\37\1\40\1\41\5\37\1\42\2\37"+
+    "\1\40\2\37\1\43\5\37\3\0\1\44\1\0\1\45"+
+    "\1\46\5\0\1\47\2\0\1\50\10\0\6\51\1\52"+
+    "\21\51\30\0\6\53\1\54\21\53\6\55\1\56\21\55"+
+    "\6\57\1\60\46\57\1\61\1\62\1\57\26\51\1\63"+
+    "\1\51\26\53\1\64\1\53\26\55\1\65\1\55\20\66"+
+    "\1\67\27\66\1\70\10\66\2\71\13\66\1\72\11\66"+
+    "\1\73\1\71\1\74\1\73\2\75\1\73\1\75\1\73"+
+    "\1\76\1\73\1\75\1\77\1\75\1\100\3\73\1\75"+
+    "\5\73\1\101\2\71\6\101\1\102\2\101\1\77\1\103"+
+    "\1\100\11\101\1\104\2\71\1\104\1\105\2\104\1\106"+
+    "\1\104\1\107\2\104\1\77\1\104\1\100\11\104\25\57"+
+    "\1\61\1\110\26\57\1\61\1\111\1\57\4\112\1\113"+
+    "\1\114\13\112\1\115\1\116\5\112\5\117\1\120\1\117"+
+    "\1\113\11\117\1\121\1\122\5\117\20\123\1\124\3\123"+
+    "\1\124\2\123\1\124\20\125\1\126\3\125\1\126\2\125"+
+    "\1\126\30\113\4\127\1\113\1\114\14\127\1\130\12\127"+
+    "\1\120\1\127\1\113\12\127\1\130\5\127\4\131\1\113"+
+    "\1\132\22\131\5\133\1\134\1\133\1\113\20\133\10\135"+
+    "\1\136\17\135\10\137\1\140\17\137\25\0\1\141\27\0"+
+    "\1\142\10\0\1\143\2\0\1\144\6\0\1\145\14\0"+
+    "\1\37\47\0\1\146\27\0\1\147\27\0\1\150\10\0"+
+    "\1\151\46\0\1\152\20\0\1\153\27\0\1\64\27\0"+
+    "\1\154\27\0\1\155\17\0\1\156\24\0\1\157\36\0"+
+    "\1\160\1\161\4\0\1\160\3\0\1\160\15\0\1\162"+
+    "\1\163\4\0\1\162\3\0\1\162\4\0\2\71\25\0"+
+    "\1\73\1\0\2\73\2\0\1\73\1\0\1\73\1\0"+
+    "\1\73\4\0\3\73\1\0\6\73\1\71\1\74\1\73"+
+    "\2\0\1\73\1\0\1\73\1\0\1\73\4\0\3\73"+
+    "\1\0\5\73\16\0\1\164\22\0\1\165\43\0\1\166"+
+    "\2\0\4\167\1\0\16\167\1\170\4\167\5\0\1\112"+
+    "\47\0\1\171\2\0\7\172\1\0\13\172\1\173\4\172"+
+    "\5\0\1\117\34\0\1\124\5\0\1\124\3\0\1\124"+
+    "\2\0\1\124\12\0\1\126\5\0\1\126\3\0\1\126"+
+    "\2\0\1\126\5\0\1\127\22\0\4\174\1\175\23\174"+
+    "\7\176\1\175\20\176\10\0\1\177\27\0\1\200\27\0"+
+    "\1\201\27\0\1\202\37\0\1\203\21\0\1\145\1\204"+
+    "\4\0\1\145\3\0\1\145\6\0\1\205\11\0\1\206"+
+    "\1\0\1\207\20\0\1\161\1\210\1\161\5\0\1\161"+
+    "\3\0\1\161\13\0\3\163\5\0\1\163\3\0\1\163"+
+    "\3\0\4\167\1\211\16\167\1\170\4\167\7\172\1\211"+
+    "\13\172\1\173\4\172\26\0\1\212\7\0\1\213\31\0"+
+    "\1\214\27\0\1\215\31\0\1\203\1\216\4\0\1\203"+
+    "\3\0\1\203\13\0\1\161\1\210\1\161\3\0\1\217"+
+    "\1\0\1\161\3\0\1\161\11\0\1\220\37\0\1\221"+
+    "\11\0";
+
+  private static int [] zzUnpacktrans() {
+    int [] result = new int[1968];
+    int offset = 0;
+    offset = zzUnpacktrans(ZZ_TRANS_PACKED_0, offset, result);
+    return result;
+  }
+
+  private static int zzUnpacktrans(String packed, int offset, int [] result) {
+    int i = 0;       /* index in packed string  */
+    int j = offset;  /* index in unpacked array */
+    int l = packed.length();
+    while (i < l) {
+      int count = packed.charAt(i++);
+      int value = packed.charAt(i++);
+      value--;
+      do result[j++] = value; while (--count > 0);
+    }
+    return j;
+  }
+
+
+  /* error codes */
+  private static final int ZZ_UNKNOWN_ERROR = 0;
+  private static final int ZZ_NO_MATCH = 1;
+  private static final int ZZ_PUSHBACK_2BIG = 2;
+
+  /* error messages for the codes above */
+  private static final String[] ZZ_ERROR_MSG = {
+    "Unknown internal scanner error",
+    "Error: could not match input",
+    "Error: pushback value was too large"
+  };
+
+  /**
+   * ZZ_ATTRIBUTE[aState] contains the attributes of state {@code aState}
+   */
+  private static final int [] ZZ_ATTRIBUTE = zzUnpackAttribute();
+
+  private static final String ZZ_ATTRIBUTE_PACKED_0 =
+    "\3\0\1\10\32\0\1\11\4\1\5\0\1\11\1\1"+
+    "\1\11\1\1\1\11\1\1\1\11\1\1\2\11\1\1"+
+    "\1\11\1\1\1\11\3\1\1\11\2\1\1\11\2\1"+
+    "\2\11\1\1\4\11\1\1\4\11\3\1\1\11\3\1"+
+    "\1\11\1\1\1\11\1\1\1\11\5\1\1\11\1\1"+
+    "\1\11\1\1\1\11\2\1\2\0\3\11\1\1\6\11"+
+    "\1\0\1\1\1\0\1\1\3\11\2\0\1\11\3\0"+
+    "\1\11\6\0\4\11\1\1\1\11\2\0\6\11";
+
+  private static int [] zzUnpackAttribute() {
+    int [] result = new int[145];
+    int offset = 0;
+    offset = zzUnpackAttribute(ZZ_ATTRIBUTE_PACKED_0, offset, result);
+    return result;
+  }
+
+  private static int zzUnpackAttribute(String packed, int offset, int [] result) {
+    int i = 0;       /* index in packed string  */
+    int j = offset;  /* index in unpacked array */
+    int l = packed.length();
+    while (i < l) {
+      int count = packed.charAt(i++);
+      int value = packed.charAt(i++);
+      do result[j++] = value; while (--count > 0);
+    }
+    return j;
+  }
+
+  /** the input device */
+  private java.io.Reader zzReader;
+
+  /** the current state of the DFA */
+  private int zzState;
+
+  /** the current lexical state */
+  private int zzLexicalState = YYINITIAL;
+
+  /** this buffer contains the current text to be matched and is
+      the source of the yytext() string */
+  private CharSequence zzBuffer = "";
+
+  /** the textposition at the last accepting state */
+  private int zzMarkedPos;
+
+  /** the current text position in the buffer */
+  private int zzCurrentPos;
+
+  /** startRead marks the beginning of the yytext() string in the buffer */
+  private int zzStartRead;
+
+  /** endRead marks the last character in the buffer, that has been read
+      from input */
+  private int zzEndRead;
+
+  /** zzAtEOF == true <=> the scanner is at the EOF */
+  private boolean zzAtEOF;
+
+  /** Number of newlines encountered up to the start of the matched text. */
+  @SuppressWarnings("unused")
+  private int yyline;
+
+  /** Number of characters from the last newline up to the start of the matched text. */
+  @SuppressWarnings("unused")
+  protected int yycolumn;
+
+  /** Number of characters up to the start of the matched text. */
+  @SuppressWarnings("unused")
+  private long yychar;
+
+  /** Whether the scanner is currently at the beginning of a line. */
+  @SuppressWarnings("unused")
+  private boolean zzAtBOL = true;
+
+  /** Whether the user-EOF-code has already been executed. */
+  @SuppressWarnings("unused")
+  private boolean zzEOFDone;
+
+  /* user code: */
+  private int curlyCount = 0;
+
+  private CharSequence tagName;
+  private CharSequence attrName;
+
+  public boolean isGroovyAttribute() {
+    if (attrName != null && "expr".contentEquals(attrName)) {
+      if (tagName != null && ("g:collect".contentEquals(tagName) || "g:findAll".contentEquals(tagName))) {
+        return true;
+      }
+    }
+
+    return false;
+  }
+
+
+  /**
+   * Creates a new scanner
+   *
+   * @param   in  the java.io.Reader to read input from.
+   */
+  public _GspLexer(java.io.Reader in) {
+    this.zzReader = in;
+  }
+
+
+  /** Returns the maximum size of the scanner buffer, which limits the size of tokens. */
+  private int zzMaxBufferLen() {
+    return Integer.MAX_VALUE;
+  }
+
+  /**  Whether the scanner buffer can grow to accommodate a larger token. */
+  private boolean zzCanGrow() {
+    return true;
+  }
+
+  /**
+   * Translates raw input code points to DFA table row
+   */
+  private static int zzCMap(int input) {
+    int offset = input & 255;
+    return offset == input ? ZZ_CMAP_BLOCKS[offset] : ZZ_CMAP_BLOCKS[ZZ_CMAP_TOP[input >> 8] | offset];
+  }
+
+  public final int getTokenStart() {
+    return zzStartRead;
+  }
+
+  public final int getTokenEnd() {
+    return getTokenStart() + yylength();
+  }
+
+  public void reset(CharSequence buffer, int start, int end, int initialState) {
+    zzBuffer = buffer;
+    zzCurrentPos = zzMarkedPos = zzStartRead = start;
+    zzAtEOF  = false;
+    zzAtBOL = true;
+    zzEndRead = end;
+    yybegin(initialState);
+  }
+
+  /**
+   * Refills the input buffer.
+   *
+   * @return      {@code false}, iff there was new input.
+   *
+   * @exception   java.io.IOException  if any I/O-Error occurs
+   */
+  private boolean zzRefill() throws java.io.IOException {
+    return true;
+  }
+
+
+  /**
+   * Returns the current lexical state.
+   */
+  public final int yystate() {
+    return zzLexicalState;
+  }
+
+
+  /**
+   * Enters a new lexical state
+   *
+   * @param newState the new lexical state
+   */
+  public final void yybegin(int newState) {
+    zzLexicalState = newState;
+  }
+
+
+  /**
+   * Returns the text matched by the current regular expression.
+   */
+  public final CharSequence yytext() {
+    return zzBuffer.subSequence(zzStartRead, zzMarkedPos);
+  }
+
+
+  /**
+   * Returns the character at position {@code pos} from the
+   * matched text.
+   *
+   * It is equivalent to yytext().charAt(pos), but faster
+   *
+   * @param pos the position of the character to fetch.
+   *            A value from 0 to yylength()-1.
+   *
+   * @return the character at position pos
+   */
+  public final char yycharat(int pos) {
+    return zzBuffer.charAt(zzStartRead+pos);
+  }
+
+
+  /**
+   * Returns the length of the matched text region.
+   */
+  public final int yylength() {
+    return zzMarkedPos-zzStartRead;
+  }
+
+
+  /**
+   * Reports an error that occurred while scanning.
+   *
+   * In a wellformed scanner (no or only correct usage of
+   * yypushback(int) and a match-all fallback rule) this method
+   * will only be called with things that "Can't Possibly Happen".
+   * If this method is called, something is seriously wrong
+   * (e.g. a JFlex bug producing a faulty scanner etc.).
+   *
+   * Usual syntax/scanner level error handling should be done
+   * in error fallback rules.
+   *
+   * @param   errorCode  the code of the errormessage to display
+   */
+  private void zzScanError(int errorCode) {
+    String message;
+    try {
+      message = ZZ_ERROR_MSG[errorCode];
+    }
+    catch (ArrayIndexOutOfBoundsException e) {
+      message = ZZ_ERROR_MSG[ZZ_UNKNOWN_ERROR];
+    }
+
+    throw new Error(message);
+  }
+
+
+  /**
+   * Pushes the specified amount of characters back into the input stream.
+   *
+   * They will be read again by then next call of the scanning method
+   *
+   * @param number  the number of characters to be read again.
+   *                This number must not be greater than yylength()!
+   */
+  public void yypushback(int number)  {
+    if ( number > yylength() )
+      zzScanError(ZZ_PUSHBACK_2BIG);
+
+    zzMarkedPos -= number;
+  }
+
+
+  /**
+   * Resumes scanning until the next regular expression is matched,
+   * the end of input is encountered or an I/O-Error occurs.
+   *
+   * @return      the next token
+   * @exception   java.io.IOException  if any I/O-Error occurs
+   */
+  public IElementType advance() throws java.io.IOException
+  {
+    int zzInput;
+    int zzAction;
+
+    // cached fields:
+    int zzCurrentPosL;
+    int zzMarkedPosL;
+    int zzEndReadL = zzEndRead;
+    CharSequence zzBufferL = zzBuffer;
+
+    int [] zzTransL = ZZ_TRANS;
+    int [] zzRowMapL = ZZ_ROWMAP;
+    int [] zzAttrL = ZZ_ATTRIBUTE;
+
+    while (true) {
+      zzMarkedPosL = zzMarkedPos;
+
+      zzAction = -1;
+
+      zzCurrentPosL = zzCurrentPos = zzStartRead = zzMarkedPosL;
+
+      zzState = ZZ_LEXSTATE[zzLexicalState];
+
+      // set up zzAction for empty match case:
+      int zzAttributes = zzAttrL[zzState];
+      if ( (zzAttributes & 1) == 1 ) {
+        zzAction = zzState;
+      }
+
+
+      zzForAction: {
+        while (true) {
+
+          if (zzCurrentPosL < zzEndReadL) {
+            zzInput = Character.codePointAt(zzBufferL, zzCurrentPosL);
+            zzCurrentPosL += Character.charCount(zzInput);
+          }
+          else if (zzAtEOF) {
+            zzInput = YYEOF;
+            break zzForAction;
+          }
+          else {
+            // store back cached positions
+            zzCurrentPos  = zzCurrentPosL;
+            zzMarkedPos   = zzMarkedPosL;
+            boolean eof = zzRefill();
+            // get translated positions and possibly new buffer
+            zzCurrentPosL  = zzCurrentPos;
+            zzMarkedPosL   = zzMarkedPos;
+            zzBufferL      = zzBuffer;
+            zzEndReadL     = zzEndRead;
+            if (eof) {
+              zzInput = YYEOF;
+              break zzForAction;
+            }
+            else {
+              zzInput = Character.codePointAt(zzBufferL, zzCurrentPosL);
+              zzCurrentPosL += Character.charCount(zzInput);
+            }
+          }
+          int zzNext = zzTransL[ zzRowMapL[zzState] + zzCMap(zzInput) ];
+          if (zzNext == -1) break zzForAction;
+          zzState = zzNext;
+
+          zzAttributes = zzAttrL[zzState];
+          if ( (zzAttributes & 1) == 1 ) {
+            zzAction = zzState;
+            zzMarkedPosL = zzCurrentPosL;
+            if ( (zzAttributes & 8) == 8 ) break zzForAction;
+          }
+
+        }
+      }
+
+      // store back cached position
+      zzMarkedPos = zzMarkedPosL;
+
+      if (zzInput == YYEOF && zzStartRead == zzCurrentPos) {
+        zzAtEOF = true;
+        return null;
+      }
+      else {
+        switch (zzAction < 0 ? zzAction : ZZ_ACTION[zzAction]) {
+          case 1:
+            { return GSP_TEMPLATE_DATA;
+            }
+          // fall through
+          case 64: break;
+          case 2:
+            { return GROOVY_CODE;
+            }
+          // fall through
+          case 65: break;
+          case 3:
+            { return GSP_DIRECTIVE;
+            }
+          // fall through
+          case 66: break;
+          case 4:
+            { return GROOVY_DECLARATION;
+            }
+          // fall through
+          case 67: break;
+          case 5:
+            { return GROOVY_EXPR_CODE;
+            }
+          // fall through
+          case 68: break;
+          case 6:
+            { curlyCount++; return GROOVY_EXPR_CODE;
+            }
+          // fall through
+          case 69: break;
+          case 7:
+            { if (curlyCount > 0) {
+                                                 curlyCount--;
+                                                 return GROOVY_EXPR_CODE;
+                                               } else {
+                                                curlyCount = 0;
+                                                yybegin(YYINITIAL);
+                                                return GEXPR_END;
+                                               }
+            }
+          // fall through
+          case 70: break;
+          case 8:
+            { yybegin(YYINITIAL); return GSP_DIRECTIVE;
+            }
+          // fall through
+          case 71: break;
+          case 9:
+            { yybegin(YYINITIAL);
+                                               return GSP_BAD_CHARACTER;
+            }
+          // fall through
+          case 72: break;
+          case 10:
+            { return GSP_WHITE_SPACE;
+            }
+          // fall through
+          case 73: break;
+          case 11:
+            { yybegin(YYINITIAL);
+                                               return GTAG_TAG_END;
+            }
+          // fall through
+          case 74: break;
+          case 12:
+            { yybegin(ATTR);
+                                                  attrName = yytext();
+                                                  return GSP_ATTR_NAME;
+            }
+          // fall through
+          case 75: break;
+          case 13:
+            { return GSP_BAD_CHARACTER;
+            }
+          // fall through
+          case 76: break;
+          case 14:
+            { yypushback(yytext().length()); yybegin(YYINITIAL);
+            }
+          // fall through
+          case 77: break;
+          case 15:
+            { yybegin(YYINITIAL);
+                                                  return GTAG_TAG_END;
+            }
+          // fall through
+          case 78: break;
+          case 16:
+            { yypushback(yytext().length()); yybegin(ATTR_LIST);
+            }
+          // fall through
+          case 79: break;
+          case 17:
+            { yybegin(ATTR_VALUE); return GSP_EQ;
+            }
+          // fall through
+          case 80: break;
+          case 18:
+            { yypushback(yytext().length());  yybegin(ATTR_LIST);
+            }
+          // fall through
+          case 81: break;
+          case 19:
+            { yybegin(isGroovyAttribute() ? ATTR_GROOVY_START_DQ : ATTR_VALUE_OR_MAP_DQ);
+                                                  return GSP_ATTR_VALUE_START_DELIMITER;
+            }
+          // fall through
+          case 82: break;
+          case 20:
+            { yybegin(isGroovyAttribute() ? ATTR_GROOVY_START_SQ : ATTR_VALUE_OR_MAP_SQ);
+                                                  return GSP_ATTR_VALUE_START_DELIMITER;
+            }
+          // fall through
+          case 83: break;
+          case 21:
+            { if (curlyCount > 0) {
+                                                    curlyCount--;
+                                                    return GROOVY_EXPR_CODE;
+                                                  } else {
+                                                    curlyCount = 0;
+                                                    yybegin(ATTR_VALUE_DQ_NOT_MAP);
+                                                    return GEXPR_END;
+                                                  }
+            }
+          // fall through
+          case 84: break;
+          case 22:
+            { if (curlyCount > 0) {
+                                                    curlyCount--;
+                                                    return GROOVY_EXPR_CODE;
+                                                 } else {
+                                                   curlyCount = 0;
+                                                   yybegin(ATTR_VALUE_SQ_NOT_MAP);
+                                                   return GEXPR_END;
+                                                 }
+            }
+          // fall through
+          case 85: break;
+          case 23:
+            { yybegin(ATTR_VALUE_DQ_NOT_MAP); return GSP_ATTRIBUTE_VALUE_TOKEN;
+            }
+          // fall through
+          case 86: break;
+          case 24:
+            { yybegin(ATTR_LIST); return GSP_ATTR_VALUE_END_DELIMITER;
+            }
+          // fall through
+          case 87: break;
+          case 25:
+            { yybegin(ATTR_VALUE_GSTRING_DQ); return GSTRING_DOLLAR;
+            }
+          // fall through
+          case 88: break;
+          case 26:
+            { yybegin(ATTR_VALUE_SQ_NOT_MAP); return GSP_ATTRIBUTE_VALUE_TOKEN;
+            }
+          // fall through
+          case 89: break;
+          case 27:
+            { yybegin(ATTR_VALUE_GSTRING_SQ); return GSTRING_DOLLAR;
+            }
+          // fall through
+          case 90: break;
+          case 28:
+            { yypushback(1); yybegin(ATTR_VALUE_SQ_NOT_MAP); return GSP_ATTRIBUTE_VALUE_TOKEN;
+            }
+          // fall through
+          case 91: break;
+          case 29:
+            { yybegin(ATTR_VALUE_SQ_NOT_MAP); return GROOVY_ATTR_VALUE;
+            }
+          // fall through
+          case 92: break;
+          case 30:
+            { yypushback(1); yybegin(ATTR_VALUE_DQ_NOT_MAP); return GSP_ATTRIBUTE_VALUE_TOKEN;
+            }
+          // fall through
+          case 93: break;
+          case 31:
+            { yybegin(ATTR_VALUE_DQ_NOT_MAP); return GROOVY_ATTR_VALUE;
+            }
+          // fall through
+          case 94: break;
+          case 32:
+            { return GSP_ATTRIBUTE_VALUE_TOKEN;
+            }
+          // fall through
+          case 95: break;
+          case 33:
+            { yybegin(ATTR_LIST); return GSP_BAD_CHARACTER;
+            }
+          // fall through
+          case 96: break;
+          case 34:
+            { return GSP_STYLE_COMMENT;
+            }
+          // fall through
+          case 97: break;
+          case 35:
+            { return JSP_STYLE_COMMENT;
+            }
+          // fall through
+          case 98: break;
+          case 36:
+            { yybegin(INJECTION_START);
+                                               yypushback(2);
+            }
+          // fall through
+          case 99: break;
+          case 37:
+            { yybegin (IN_GDECLAR);
+                                               return GDECLAR_BEGIN;
+            }
+          // fall through
+          case 100: break;
+          case 38:
+            { curlyCount = 0;
+                                               yybegin (IN_GEXPR);
+                                               return GEXPR_BEGIN;
+            }
+          // fall through
+          case 101: break;
+          case 39:
+            { yybegin (IN_GSCRIPT);
+                                               return GSCRIPT_BEGIN;
+            }
+          // fall through
+          case 102: break;
+          case 40:
+            { yybegin (IN_J_SCRIPT);
+                                               return JSCRIPT_BEGIN;
+            }
+          // fall through
+          case 103: break;
+          case 41:
+            { yypushback(2);
+                                               yybegin (IN_GDIRECT);
+            }
+          // fall through
+          case 104: break;
+          case 42:
+            { yybegin(YYINITIAL); return JSCRIPT_END;
+            }
+          // fall through
+          case 105: break;
+          case 43:
+            { yybegin(YYINITIAL); return JDECLAR_END;
+            }
+          // fall through
+          case 106: break;
+          case 44:
+            { yybegin(YYINITIAL); return JEXPR_END;
+            }
+          // fall through
+          case 107: break;
+          case 45:
+            { yybegin (YYINITIAL); return GSCRIPT_END;
+            }
+          // fall through
+          case 108: break;
+          case 46:
+            { yybegin(YYINITIAL); return GDECLAR_END;
+            }
+          // fall through
+          case 109: break;
+          case 47:
+            { yybegin(ATTR_LIST);
+                                                tagName = yytext();
+                                                return GSP_TAG_NAME;
+            }
+          // fall through
+          case 110: break;
+          case 48:
+            { yybegin(IN_GTAG_END_AFTER_NAME);
+                                               return GSP_TAG_NAME;
+            }
+          // fall through
+          case 111: break;
+          case 49:
+            { yybegin(YYINITIAL);
+                                                  return GTAG_START_TAG_END;
+            }
+          // fall through
+          case 112: break;
+          case 50:
+            { curlyCount = 0; yybegin(GEXPR_IN_VALUE_DQ); return GEXPR_BEGIN;
+            }
+          // fall through
+          case 113: break;
+          case 51:
+            { curlyCount = 0; yybegin(GEXPR_IN_VALUE_SQ); return GEXPR_BEGIN;
+            }
+          // fall through
+          case 114: break;
+          case 52:
+            { yypushback(1); yybegin(ATTR_VALUE_END); return GROOVY_ATTR_VALUE;
+            }
+          // fall through
+          case 115: break;
+          case 53:
+            { yypushback(yytext().length()-1);
+                                              yybegin(IN_GTAG_START);
+                                              return(GTAG_START_TAG_START);
+            }
+          // fall through
+          case 116: break;
+          case 54:
+            { yybegin (IN_J_DECLAR);
+                                               return JDECLAR_BEGIN;
+            }
+          // fall through
+          case 117: break;
+          case 55:
+            { yybegin (IN_J_EXPR);
+                                               return JEXPR_BEGIN;
+            }
+          // fall through
+          case 118: break;
+          case 56:
+            { yypushback(3) ;
+                                               yybegin (IN_J_DIRECT);
+            }
+          // fall through
+          case 119: break;
+          case 57:
+            { yypushback(1); yybegin(ATTR_VALUE_END); return GSP_MAP_ATTR_VALUE;
+            }
+          // fall through
+          case 120: break;
+          case 58:
+            { yypushback(4);
+                                              yybegin(GSP_COMMENT);
+            }
+          // fall through
+          case 121: break;
+          case 59:
+            { yypushback(4);
+                                              yybegin(JSP_COMMENT);
+            }
+          // fall through
+          case 122: break;
+          case 60:
+            { yypushback(yytext().length()-2);
+                                              yybegin(IN_GTAG_END);
+                                              return(GTAG_END_TAG_START);
+            }
+          // fall through
+          case 123: break;
+          case 61:
+            { yypushback(2);
+                                                tagName = yytext();
+                                                yybegin(ATTR_LIST);
+                                                return GSP_TAG_NAME;
+            }
+          // fall through
+          case 124: break;
+          case 62:
+            { yybegin(YYINITIAL); return GSP_STYLE_COMMENT;
+            }
+          // fall through
+          case 125: break;
+          case 63:
+            { yybegin(YYINITIAL); return JSP_STYLE_COMMENT;
+            }
+          // fall through
+          case 126: break;
+          default:
+            zzScanError(ZZ_NO_MATCH);
+          }
+      }
+    }
+  }
+
+
+}
diff --git a/gradle-tooling/build.gradle.kts b/gradle-tooling/build.gradle.kts
new file mode 100644
index 0000000..0596767
--- /dev/null
+++ b/gradle-tooling/build.gradle.kts
@@ -0,0 +1,40 @@
+// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
+fun properties(key: String) = project.findProperty(key).toString()
+
+plugins {
+    id("java")
+    id("org.jetbrains.intellij.platform.module")
+}
+
+group = "org.intellij.grails.gradle.tooling"
+
+repositories {
+    mavenCentral()
+    intellijPlatform {
+        defaultRepositories()
+    }
+}
+
+dependencies {
+    intellijPlatform {
+        intellijIdea(properties("platformVersion"))
+        bundledPlugin("org.jetbrains.plugins.gradle")
+    }
+}
+
+java.sourceSets["main"].java {
+    srcDir("src")
+}
+
+java.sourceSets["main"].resources {
+    srcDir("resources")
+}
+
+tasks {
+    // Set the JVM compatibility versions
+    withType<JavaCompile> {
+        sourceCompatibility = "8"
+        targetCompatibility = "8"
+    }
+
+}
diff --git a/gradle-tooling/resources/META-INF/services/org.jetbrains.plugins.gradle.tooling.ModelBuilderService b/gradle-tooling/resources/META-INF/services/org.jetbrains.plugins.gradle.tooling.ModelBuilderService
new file mode 100644
index 0000000..5cb13e8
--- /dev/null
+++ b/gradle-tooling/resources/META-INF/services/org.jetbrains.plugins.gradle.tooling.ModelBuilderService
@@ -0,0 +1,2 @@
+# Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
+org.jetbrains.plugins.grails.gradle.tooling.builder.GrailsModuleModelBuilderImpl
diff --git a/gradle-tooling/src/org/jetbrains/plugins/grails/gradle/tooling/builder/GrailsModule.java b/gradle-tooling/src/org/jetbrains/plugins/grails/gradle/tooling/builder/GrailsModule.java
new file mode 100644
index 0000000..c26ff0d
--- /dev/null
+++ b/gradle-tooling/src/org/jetbrains/plugins/grails/gradle/tooling/builder/GrailsModule.java
@@ -0,0 +1,20 @@
+// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
+package org.jetbrains.plugins.grails.gradle.tooling.builder;
+
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.Serializable;
+import java.util.List;
+
+public interface GrailsModule extends Serializable {
+
+  @NotNull
+  String getGrailsVersion();
+
+  @NotNull
+  String getGrailsPluginId();
+
+  @Nullable
+  List<String> getShellUrls();
+}
diff --git a/gradle-tooling/src/org/jetbrains/plugins/grails/gradle/tooling/builder/GrailsModuleImpl.java b/gradle-tooling/src/org/jetbrains/plugins/grails/gradle/tooling/builder/GrailsModuleImpl.java
new file mode 100644
index 0000000..a162d78
--- /dev/null
+++ b/gradle-tooling/src/org/jetbrains/plugins/grails/gradle/tooling/builder/GrailsModuleImpl.java
@@ -0,0 +1,46 @@
+// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
+package org.jetbrains.plugins.grails.gradle.tooling.builder;
+
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.List;
+
+/**
+ * @author Vladislav.Soroka
+ */
+public class GrailsModuleImpl implements GrailsModule {
+
+  private static final long serialVersionUID = 1L;
+
+  private final @NotNull String myGrailsVersion;
+
+  private final @NotNull String myGrailsPluginId;
+
+  private final @Nullable List<String> myShellUrls;
+
+  public GrailsModuleImpl(@NotNull String grailsVersion, @NotNull String grailsPluginId) {
+    this(grailsVersion, grailsPluginId, null);
+  }
+
+  public GrailsModuleImpl(@NotNull String grailsVersion, @NotNull String grailsPluginId, @Nullable List<String> urls) {
+    myGrailsVersion = grailsVersion;
+    myGrailsPluginId = grailsPluginId;
+    myShellUrls = urls;
+  }
+
+  @Override
+  public @NotNull String getGrailsVersion() {
+    return myGrailsVersion;
+  }
+
+  @Override
+  public @NotNull String getGrailsPluginId() {
+    return myGrailsPluginId;
+  }
+
+  @Override
+  public @Nullable List<String> getShellUrls() {
+    return myShellUrls;
+  }
+}
diff --git a/gradle-tooling/src/org/jetbrains/plugins/grails/gradle/tooling/builder/GrailsModuleModelBuilderImpl.java b/gradle-tooling/src/org/jetbrains/plugins/grails/gradle/tooling/builder/GrailsModuleModelBuilderImpl.java
new file mode 100644
index 0000000..bf76e97
--- /dev/null
+++ b/gradle-tooling/src/org/jetbrains/plugins/grails/gradle/tooling/builder/GrailsModuleModelBuilderImpl.java
@@ -0,0 +1,137 @@
+// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
+
+package org.jetbrains.plugins.grails.gradle.tooling.builder;
+
+import com.intellij.gradle.toolingExtension.util.GradleVersionUtil;
+import org.gradle.api.Project;
+import org.gradle.api.artifacts.Configuration;
+import org.gradle.api.internal.artifacts.dependencies.DefaultExternalModuleDependency;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.plugins.gradle.tooling.Message;
+import org.jetbrains.plugins.gradle.tooling.ModelBuilderContext;
+import org.jetbrains.plugins.gradle.tooling.ModelBuilderService;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * @author Vladislav.Soroka
+ */
+@SuppressWarnings("SSBasedInspection")
+public class GrailsModuleModelBuilderImpl implements ModelBuilderService {
+  @Override
+  public boolean canBuild(String modelName) {
+    return GrailsModule.class.getName().equals(modelName);
+  }
+
+  @Override
+  public Object buildAll(String modelName, Project project) {
+    Context context = Context.from(project);
+    if (context == null) return null;
+
+    GrailsVersionInfo grailsVersionInfo = context.myGrailsVersionInfo;
+    DefaultExternalModuleDependency dependency = new DefaultExternalModuleDependency(grailsVersionInfo.gradleDependencyGroup, grailsVersionInfo.shellArtifactId, null);
+    Configuration configuration = getConfiguration(project);
+    configuration.getDependencies().add(dependency);
+
+    String version = (String) project.getProperties().get("grailsVersion");
+    if (version == null || version.isEmpty()) {
+      version = configuration.getResolvedConfiguration().getFirstLevelModuleDependencies()
+        .stream()
+        .filter(dep -> grailsVersionInfo.gradleDependencyGroup.equals(dep.getModuleGroup())
+                       && grailsVersionInfo.shellArtifactId.equals(dep.getModuleName()))
+        .findFirst()
+        .map(dep -> dep.getModuleVersion())
+        .orElse(null);
+    }
+
+    List<String> paths = configuration.resolve().stream().map(file -> file.getAbsolutePath()).collect(Collectors.toList());
+    return (version != null && !version.isEmpty()) ? new GrailsModuleImpl(version, context.grailsPluginCoordinates, paths) : null;
+  }
+
+  private static Configuration getConfiguration(Project project) {
+    if (GradleVersionUtil.isCurrentGradleNewerThan("7.0")) {
+      Configuration configuration = project.getConfigurations().getByName("implementation").copy(dependency -> false);
+      configuration.setCanBeResolved(true);
+      return configuration;
+    } else {
+      return project.getConfigurations().getByName("compile").copy(dependency -> false);
+    }
+  }
+
+  @Override
+  public void reportErrorMessage(
+    @NotNull String modelName,
+    @NotNull Project project,
+    @NotNull ModelBuilderContext context,
+    @NotNull Exception exception
+  ) {
+    context.getMessageReporter().createMessage()
+      .withGroup(this)
+      .withKind(Message.Kind.WARNING)
+      .withTitle("Grails import errors")
+      .withText("Unable to build Grails project configuration")
+      .withException(exception)
+      .reportMessage(project);
+  }
+
+  private static class Context {
+    /**
+     * Array of Grails gradle plugins (see <a href="https://grails.github.io/grails-doc/latest/guide/single.html#gradlePlugins">Grails plugins for Gradle</a>).
+     * If any is present, we make assumption that this is Grails project.
+     *
+     */
+    private static final String[] GRAILS_PLUGIN_NAME_ARRAY = {
+      "grails-core",
+      "grails-plugin",
+      "grails-web",
+      "grails-gsp",
+      "grails-doc",
+    };
+
+
+    private final @NotNull GrailsModuleModelBuilderImpl.GrailsVersionInfo myGrailsVersionInfo;
+    private final @NotNull String grailsPluginCoordinates;
+
+
+    private Context(@NotNull GrailsModuleModelBuilderImpl.GrailsVersionInfo version, @NotNull String plugin) {
+      myGrailsVersionInfo = version;
+      grailsPluginCoordinates = plugin;
+    }
+
+    private static @Nullable Context from(@NotNull Project project) {
+      for (GrailsVersionInfo version : GrailsVersionInfo.values()) {
+        for (String pluginName : GRAILS_PLUGIN_NAME_ARRAY) {
+          String pluginCoordinates = version.getPluginCoordinates(pluginName);
+          if (project.getPlugins().hasPlugin(pluginCoordinates)) {
+            return new Context(version, pluginCoordinates);
+          }
+        }
+      }
+      return null;
+    }
+  }
+
+  /**
+   * Cordinates parts of Grails may differ from version to version. This enum stores the possible options.
+   */
+  private enum GrailsVersionInfo {
+    GRAILS_3("org.grails", "org.grails", "grails-shell"),
+    GRAILS_7("org.apache.grails.gradle", "org.apache.grails", "grails-shell-cli");
+
+    private final String gradlePluginGroup;
+    private final String gradleDependencyGroup;
+    private final String shellArtifactId;
+
+    GrailsVersionInfo(String gradlePluginGroup, String gradleDependencyGroup, String shellArtifactId) {
+      this.gradlePluginGroup = gradlePluginGroup;
+      this.gradleDependencyGroup = gradleDependencyGroup;
+      this.shellArtifactId = shellArtifactId;
+    }
+
+    private String getPluginCoordinates(String pluginName) {
+      return gradlePluginGroup + "." + pluginName;
+    }
+  }
+}
diff --git a/gradle.properties b/gradle.properties
new file mode 100644
index 0000000..5856c8d
--- /dev/null
+++ b/gradle.properties
@@ -0,0 +1,14 @@
+platformVersion=2025.3.1
+platformJavaVersion=21
+pluginSinceBuild=253
+pluginVersion=253.0.0
+
+kotlin.stdlib.default.dependency=false
+
+# https://plugins.jetbrains.com/docs/intellij/testing-faq.html#how-to-test-a-jvm-language
+# set path to local intellij-community project
+test.idea.home.path=/Users/yann/idea-ultimate/ultimate/community
+
+# Gradle config
+org.gradle.configuration-cache=true
+org.gradle.caching=true
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..61285a6
Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..37f78a6
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,7 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-9.3.1-bin.zip
+networkTimeout=10000
+validateDistributionUrl=true
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/gradlew b/gradlew
new file mode 100755
index 0000000..adff685
--- /dev/null
+++ b/gradlew
@@ -0,0 +1,248 @@
+#!/bin/sh
+
+#
+# Copyright © 2015 the original authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+
+##############################################################################
+#
+#   Gradle start up script for POSIX generated by Gradle.
+#
+#   Important for running:
+#
+#   (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
+#       noncompliant, but you have some other compliant shell such as ksh or
+#       bash, then to run this script, type that shell name before the whole
+#       command line, like:
+#
+#           ksh Gradle
+#
+#       Busybox and similar reduced shells will NOT work, because this script
+#       requires all of these POSIX shell features:
+#         * functions;
+#         * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
+#           «${var#prefix}», «${var%suffix}», and «$( cmd )»;
+#         * compound commands having a testable exit status, especially «case»;
+#         * various built-in commands including «command», «set», and «ulimit».
+#
+#   Important for patching:
+#
+#   (2) This script targets any POSIX shell, so it avoids extensions provided
+#       by Bash, Ksh, etc; in particular arrays are avoided.
+#
+#       The "traditional" practice of packing multiple parameters into a
+#       space-separated string is a well documented source of bugs and security
+#       problems, so this is (mostly) avoided, by progressively accumulating
+#       options in "$@", and eventually passing that to Java.
+#
+#       Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
+#       and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
+#       see the in-line comments for details.
+#
+#       There are tweaks for specific operating systems such as AIX, CygWin,
+#       Darwin, MinGW, and NonStop.
+#
+#   (3) This script is generated from the Groovy template
+#       https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
+#       within the Gradle project.
+#
+#       You can find Gradle at https://github.com/gradle/gradle/.
+#
+##############################################################################
+
+# Attempt to set APP_HOME
+
+# Resolve links: $0 may be a link
+app_path=$0
+
+# Need this for daisy-chained symlinks.
+while
+    APP_HOME=${app_path%"${app_path##*/}"}  # leaves a trailing /; empty if no leading path
+    [ -h "$app_path" ]
+do
+    ls=$( ls -ld "$app_path" )
+    link=${ls#*' -> '}
+    case $link in             #(
+      /*)   app_path=$link ;; #(
+      *)    app_path=$APP_HOME$link ;;
+    esac
+done
+
+# This is normally unused
+# shellcheck disable=SC2034
+APP_BASE_NAME=${0##*/}
+# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
+APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD=maximum
+
+warn () {
+    echo "$*"
+} >&2
+
+die () {
+    echo
+    echo "$*"
+    echo
+    exit 1
+} >&2
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "$( uname )" in                #(
+  CYGWIN* )         cygwin=true  ;; #(
+  Darwin* )         darwin=true  ;; #(
+  MSYS* | MINGW* )  msys=true    ;; #(
+  NONSTOP* )        nonstop=true ;;
+esac
+
+
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+        # IBM's JDK on AIX uses strange locations for the executables
+        JAVACMD=$JAVA_HOME/jre/sh/java
+    else
+        JAVACMD=$JAVA_HOME/bin/java
+    fi
+    if [ ! -x "$JAVACMD" ] ; then
+        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+    fi
+else
+    JAVACMD=java
+    if ! command -v java >/dev/null 2>&1
+    then
+        die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+    fi
+fi
+
+# Increase the maximum file descriptors if we can.
+if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
+    case $MAX_FD in #(
+      max*)
+        # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
+        # shellcheck disable=SC2039,SC3045
+        MAX_FD=$( ulimit -H -n ) ||
+            warn "Could not query maximum file descriptor limit"
+    esac
+    case $MAX_FD in  #(
+      '' | soft) :;; #(
+      *)
+        # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
+        # shellcheck disable=SC2039,SC3045
+        ulimit -n "$MAX_FD" ||
+            warn "Could not set maximum file descriptor limit to $MAX_FD"
+    esac
+fi
+
+# Collect all arguments for the java command, stacking in reverse order:
+#   * args from the command line
+#   * the main class name
+#   * -classpath
+#   * -D...appname settings
+#   * --module-path (only if needed)
+#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if "$cygwin" || "$msys" ; then
+    APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
+
+    JAVACMD=$( cygpath --unix "$JAVACMD" )
+
+    # Now convert the arguments - kludge to limit ourselves to /bin/sh
+    for arg do
+        if
+            case $arg in                                #(
+              -*)   false ;;                            # don't mess with options #(
+              /?*)  t=${arg#/} t=/${t%%/*}              # looks like a POSIX filepath
+                    [ -e "$t" ] ;;                      #(
+              *)    false ;;
+            esac
+        then
+            arg=$( cygpath --path --ignore --mixed "$arg" )
+        fi
+        # Roll the args list around exactly as many times as the number of
+        # args, so each arg winds up back in the position where it started, but
+        # possibly modified.
+        #
+        # NB: a `for` loop captures its iteration list before it begins, so
+        # changing the positional parameters here affects neither the number of
+        # iterations, nor the values presented in `arg`.
+        shift                   # remove old arg
+        set -- "$@" "$arg"      # push replacement arg
+    done
+fi
+
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Collect all arguments for the java command:
+#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
+#     and any embedded shellness will be escaped.
+#   * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
+#     treated as '${Hostname}' itself on the command line.
+
+set -- \
+        "-Dorg.gradle.appname=$APP_BASE_NAME" \
+        -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
+        "$@"
+
+# Stop when "xargs" is not available.
+if ! command -v xargs >/dev/null 2>&1
+then
+    die "xargs is not available"
+fi
+
+# Use "xargs" to parse quoted args.
+#
+# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
+#
+# In Bash we could simply go:
+#
+#   readarray ARGS < <( xargs -n1 <<<"$var" ) &&
+#   set -- "${ARGS[@]}" "$@"
+#
+# but POSIX shell has neither arrays nor command substitution, so instead we
+# post-process each arg (as a line of input to sed) to backslash-escape any
+# character that might be a shell metacharacter, then use eval to reverse
+# that process (while maintaining the separation between arguments), and wrap
+# the whole thing up as a single "set" statement.
+#
+# This will of course break if any of these variables contains a newline or
+# an unmatched quote.
+#
+
+eval "set -- $(
+        printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
+        xargs -n1 |
+        sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
+        tr '\n' ' '
+    )" '"$@"'
+
+exec "$JAVACMD" "$@"
diff --git a/gradlew.bat b/gradlew.bat
new file mode 100644
index 0000000..c4bdd3a
--- /dev/null
+++ b/gradlew.bat
@@ -0,0 +1,93 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem      https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+@rem SPDX-License-Identifier: Apache-2.0
+@rem
+
+@if "%DEBUG%"=="" @echo off
+@rem ##########################################################################
+@rem
+@rem  Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%"=="" set DIRNAME=.
+@rem This is normally unused
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if %ERRORLEVEL% equ 0 goto execute
+
+echo. 1>&2
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
+echo. 1>&2
+echo Please set the JAVA_HOME variable in your environment to match the 1>&2
+echo location of your Java installation. 1>&2
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto execute
+
+echo. 1>&2
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
+echo. 1>&2
+echo Please set the JAVA_HOME variable in your environment to match the 1>&2
+echo location of your Java installation. 1>&2
+
+goto fail
+
+:execute
+@rem Setup the command line
+
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
+
+:end
+@rem End local scope for the variables with windows NT shell
+if %ERRORLEVEL% equ 0 goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+set EXIT_CODE=%ERRORLEVEL%
+if %EXIT_CODE% equ 0 set EXIT_CODE=1
+if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
+exit /b %EXIT_CODE%
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/grails-compiler-patch/build.gradle.kts b/grails-compiler-patch/build.gradle.kts
new file mode 100644
index 0000000..7796ba7
--- /dev/null
+++ b/grails-compiler-patch/build.gradle.kts
@@ -0,0 +1,42 @@
+// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
+fun properties(key: String) = project.findProperty(key).toString()
+
+plugins {
+    id("java")
+    id("org.jetbrains.intellij.platform.module")
+}
+
+group = "org.intellij.grails.compiler.patch"
+
+repositories {
+    mavenCentral()
+    intellijPlatform {
+        defaultRepositories()
+    }
+}
+
+dependencies {
+    intellijPlatform {
+        intellijIdea(properties("platformVersion"))
+        bundledPlugin("org.intellij.groovy")
+    }
+
+    compileOnly("org.grails:grails-core:1.2.0")
+}
+
+java.sourceSets["main"].java {
+    srcDir("src")
+}
+
+java.sourceSets["main"].resources {
+    srcDir("resources")
+}
+
+tasks {
+    // Set the JVM compatibility versions
+    withType<JavaCompile> {
+        sourceCompatibility = "8"
+        targetCompatibility = "8"
+    }
+
+}
diff --git a/grails-compiler-patch/resources/log4j.properties b/grails-compiler-patch/resources/log4j.properties
new file mode 100644
index 0000000..d5eefe3
--- /dev/null
+++ b/grails-compiler-patch/resources/log4j.properties
@@ -0,0 +1,10 @@
+# Set root logger level to DEBUG and its only appender to A1.
+# TRACE,DEBUG,INFO,WARN,ERROR,FATAL
+log4j.rootLogger=ERROR, A1
+
+# A1 is set to be a ConsoleAppender.
+log4j.appender.A1=org.apache.log4j.ConsoleAppender
+
+# A1 uses PatternLayout.
+log4j.appender.A1.layout=org.apache.log4j.PatternLayout
+log4j.appender.A1.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n
diff --git a/grails-compiler-patch/src/org/jetbrains/groovy/grails/compiler/EmptyGrailsAwarePatcher.java b/grails-compiler-patch/src/org/jetbrains/groovy/grails/compiler/EmptyGrailsAwarePatcher.java
new file mode 100644
index 0000000..fa5c6f2
--- /dev/null
+++ b/grails-compiler-patch/src/org/jetbrains/groovy/grails/compiler/EmptyGrailsAwarePatcher.java
@@ -0,0 +1,31 @@
+// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
+
+package org.jetbrains.groovy.grails.compiler;
+
+import groovy.lang.GroovyResourceLoader;
+import org.codehaus.groovy.control.CompilationUnit;
+import org.codehaus.groovy.control.Phases;
+import org.codehaus.groovy.grails.compiler.injection.GrailsAwareInjectionOperation;
+import org.jetbrains.groovy.compiler.rt.CompilationUnitPatcher;
+
+import java.io.File;
+import java.lang.reflect.Constructor;
+
+public class EmptyGrailsAwarePatcher extends CompilationUnitPatcher {
+
+  @Override
+  public void patchCompilationUnit(CompilationUnit compilationUnit, GroovyResourceLoader resourceLoader, File[] srcFiles) {
+    Object instance;
+
+    try {
+      Constructor c = GrailsAwareInjectionOperation.class.getConstructor();
+      instance = c.newInstance();
+    }
+    catch (Exception ee) {
+      throw new RuntimeException(ee);
+    }
+
+    compilationUnit.addPhaseOperation((GrailsAwareInjectionOperation)instance, Phases.CANONICALIZATION);
+  }
+
+}
diff --git a/grails-compiler-patch/src/org/jetbrains/groovy/grails/compiler/Grails2_0_JUnitPatcher.java b/grails-compiler-patch/src/org/jetbrains/groovy/grails/compiler/Grails2_0_JUnitPatcher.java
new file mode 100644
index 0000000..965b7d6
--- /dev/null
+++ b/grails-compiler-patch/src/org/jetbrains/groovy/grails/compiler/Grails2_0_JUnitPatcher.java
@@ -0,0 +1,116 @@
+// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
+
+package org.jetbrains.groovy.grails.compiler;
+
+import groovy.lang.GroovyResourceLoader;
+import groovyjarjarasm.asm.Opcodes;
+import org.codehaus.groovy.ast.AnnotatedNode;
+import org.codehaus.groovy.ast.AnnotationNode;
+import org.codehaus.groovy.ast.ClassHelper;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.MethodNode;
+import org.codehaus.groovy.ast.MixinNode;
+import org.codehaus.groovy.classgen.GeneratorContext;
+import org.codehaus.groovy.control.CompilationFailedException;
+import org.codehaus.groovy.control.CompilationUnit;
+import org.codehaus.groovy.control.Phases;
+import org.codehaus.groovy.control.SourceUnit;
+import org.jetbrains.groovy.compiler.rt.CompilationUnitPatcher;
+
+import java.io.File;
+import java.lang.annotation.Annotation;
+import java.util.List;
+
+public class Grails2_0_JUnitPatcher extends CompilationUnitPatcher {
+  @Override
+  public void patchCompilationUnit(CompilationUnit compilationUnit, GroovyResourceLoader resourceLoader, File[] srcFiles) {
+    compilationUnit.addPhaseOperation(new CompilationUnit.PrimaryClassNodeOperation() {
+      @Override
+      public void call(SourceUnit source, GeneratorContext context, ClassNode classNode) throws CompilationFailedException {
+        if (hasTestForAnnotation(classNode)) {
+          source.getAST().addStaticStarImport(null, ClassHelper.make("org.junit.Assert"));
+        }
+      }
+
+      @Override
+      public void doPhaseOperation(CompilationUnit unit) throws CompilationFailedException {
+        super.doPhaseOperation(unit);
+      }
+    }, Phases.CONVERSION);
+
+    compilationUnit.addPhaseOperation(new CompilationUnit.PrimaryClassNodeOperation() {
+      @Override
+      public void call(SourceUnit source, GeneratorContext context, ClassNode classNode) throws CompilationFailedException {
+        if (hasTestForAnnotation(classNode)) {
+          List<MethodNode> methods = classNode.getMethods();
+
+          for (MethodNode method : methods) {
+            if (!GrailsJUnitPatcher.isTestMethod(method)) continue;
+            if (hasAnnotation(method, "org.junit.Test")) continue;
+
+            if ("java.lang.Object".equals(method.getReturnType().getName())) {
+              if (GrailsJUnitPatcher.isReturnNonVoid(method)) continue;
+              method.setReturnType(ClassHelper.VOID_TYPE);
+            }
+            else {
+              if (!ClassHelper.VOID_TYPE.equals(method.getReturnType())) continue;
+            }
+
+            AnnotationNode annotationNode = new AnnotationNode(new ClassNode("org.junit.Test",
+                                                                             Opcodes.ACC_PUBLIC |
+                                                                             Opcodes.ACC_INTERFACE |
+                                                                             Opcodes.ACC_ABSTRACT
+                                                                             |
+                                                                             Opcodes.ACC_ANNOTATION,
+                                                                             ClassHelper.OBJECT_TYPE,
+                                                                             new ClassNode[]{ClassHelper.make(Annotation.class)},
+                                                                             MixinNode.EMPTY_ARRAY
+            ));
+            annotationNode.setRuntimeRetention(true);
+            annotationNode.setAllowedTargets(AnnotationNode.METHOD_TARGET);
+
+            method.addAnnotation(annotationNode);
+          }
+        }
+      }
+
+      @Override
+      public void doPhaseOperation(CompilationUnit unit) throws CompilationFailedException {
+        super.doPhaseOperation(unit);
+      }
+    }, Phases.INSTRUCTION_SELECTION);
+  }
+
+  public static boolean hasAnnotation(AnnotatedNode node, String annotationName) {
+    List<AnnotationNode> annotations = node.getAnnotations();
+    for (AnnotationNode annotationNode : annotations) {
+      if (annotationName.equals(annotationNode.getClassNode().getName())) {
+        return true;
+      }
+    }
+
+    return false;
+  }
+
+  public static boolean hasTestForAnnotation(ClassNode classNode) {
+    List<AnnotationNode> annotations = classNode.getAnnotations();
+    for (AnnotationNode annotationNode : annotations) {
+      String annotationName = annotationNode.getClassNode().getName();
+
+      if ("grails.test.mixin.TestFor".equals(annotationName)
+          || "TestFor".equals(annotationName)
+          || "grails.test.mixin.TestMixin".equals(annotationName)
+          || "TestMixin".equals(annotationName)
+          || "grails.test.mixin.Mock".equals(annotationName)
+          || "Mock".equals(annotationName)
+          || "grails.buildtestdata.mixin.Build".equals(annotationName)
+          || "Build".equals(annotationName)
+        ) {
+        return true;
+      }
+    }
+
+    return false;
+  }
+
+}
diff --git a/grails-compiler-patch/src/org/jetbrains/groovy/grails/compiler/GrailsCompilerRtMarker.java b/grails-compiler-patch/src/org/jetbrains/groovy/grails/compiler/GrailsCompilerRtMarker.java
new file mode 100644
index 0000000..2c160c0
--- /dev/null
+++ b/grails-compiler-patch/src/org/jetbrains/groovy/grails/compiler/GrailsCompilerRtMarker.java
@@ -0,0 +1,7 @@
+// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
+
+package org.jetbrains.groovy.grails.compiler;
+
+public class GrailsCompilerRtMarker {
+
+}
diff --git a/grails-compiler-patch/src/org/jetbrains/groovy/grails/compiler/GrailsDomainClassPatcher.java b/grails-compiler-patch/src/org/jetbrains/groovy/grails/compiler/GrailsDomainClassPatcher.java
new file mode 100644
index 0000000..2298fe8
--- /dev/null
+++ b/grails-compiler-patch/src/org/jetbrains/groovy/grails/compiler/GrailsDomainClassPatcher.java
@@ -0,0 +1,26 @@
+// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
+
+package org.jetbrains.groovy.grails.compiler;
+
+import groovy.lang.GroovyResourceLoader;
+import org.codehaus.groovy.control.CompilationUnit;
+import org.codehaus.groovy.control.Phases;
+import org.codehaus.groovy.grails.compiler.injection.ClassInjector;
+import org.codehaus.groovy.grails.compiler.injection.DefaultGrailsDomainClassInjector;
+import org.codehaus.groovy.grails.compiler.injection.GrailsAwareInjectionOperation;
+import org.jetbrains.groovy.compiler.rt.CompilationUnitPatcher;
+
+import java.io.File;
+
+/**
+ * @noinspection UnusedDeclaration
+ */
+public class GrailsDomainClassPatcher extends CompilationUnitPatcher {
+
+  @Override
+  public void patchCompilationUnit(CompilationUnit compilationUnit, GroovyResourceLoader resourceLoader, File[] srcFiles) {
+    final DefaultGrailsDomainClassInjector injector = new DefaultGrailsDomainClassInjector();
+    compilationUnit.addPhaseOperation(new GrailsAwareInjectionOperation(resourceLoader, new ClassInjector[]{injector}), Phases.CONVERSION);
+  }
+
+}
\ No newline at end of file
diff --git a/grails-compiler-patch/src/org/jetbrains/groovy/grails/compiler/GrailsJUnitPatcher.java b/grails-compiler-patch/src/org/jetbrains/groovy/grails/compiler/GrailsJUnitPatcher.java
new file mode 100644
index 0000000..6cf8688
--- /dev/null
+++ b/grails-compiler-patch/src/org/jetbrains/groovy/grails/compiler/GrailsJUnitPatcher.java
@@ -0,0 +1,149 @@
+// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
+
+package org.jetbrains.groovy.grails.compiler;
+
+import groovy.lang.ExpandoMetaClass;
+import groovy.lang.GroovyResourceLoader;
+import org.codehaus.groovy.ast.ClassHelper;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.CodeVisitorSupport;
+import org.codehaus.groovy.ast.MethodNode;
+import org.codehaus.groovy.ast.expr.ArgumentListExpression;
+import org.codehaus.groovy.ast.expr.ConstantExpression;
+import org.codehaus.groovy.ast.expr.StaticMethodCallExpression;
+import org.codehaus.groovy.ast.stmt.ExpressionStatement;
+import org.codehaus.groovy.ast.stmt.ReturnStatement;
+import org.codehaus.groovy.ast.stmt.Statement;
+import org.codehaus.groovy.classgen.GeneratorContext;
+import org.codehaus.groovy.control.CompilationUnit;
+import org.codehaus.groovy.control.Phases;
+import org.codehaus.groovy.control.SourceUnit;
+import org.codehaus.groovy.grails.compiler.injection.ClassInjector;
+import org.codehaus.groovy.grails.compiler.injection.GrailsAwareInjectionOperation;
+import org.jetbrains.groovy.compiler.rt.CompilationUnitPatcher;
+
+import java.io.File;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Modifier;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Pattern;
+
+/**
+ * @noinspection UnusedDeclaration
+ */
+public class GrailsJUnitPatcher extends CompilationUnitPatcher {
+
+  @Override
+  public void patchCompilationUnit(CompilationUnit compilationUnit, GroovyResourceLoader resourceLoader, File[] srcFiles) {
+    Object instance;
+
+    ClassInjector[] injectors = {new GrailsJUnitInjector()};
+
+    try {
+      try {
+        // for Grails 1.4 call new GrailsAwareInjectionOperation(new ClassInjector[]{...})
+        Constructor c = GrailsAwareInjectionOperation.class.getConstructor(ClassInjector[].class);
+        instance = c.newInstance(new Object[]{injectors});
+      }
+      catch (NoSuchMethodException e) {
+        // for Grails 1.3 call new GrailsAwareInjectionOperation(resourceLoader, new ClassInjector[]{...})
+        Constructor c = GrailsAwareInjectionOperation.class.getConstructor(GroovyResourceLoader.class, ClassInjector[].class);
+        instance = c.newInstance(resourceLoader, injectors);
+      }
+    }
+    catch (Exception ee) {
+      throw new RuntimeException(ee);
+    }
+
+    compilationUnit.addPhaseOperation((GrailsAwareInjectionOperation)instance, Phases.INSTRUCTION_SELECTION);
+  }
+
+  private static class GrailsJUnitInjector implements ClassInjector {
+
+    private static final Pattern URL_PATTERN = Pattern.compile(".+/test/(.+/)?.+Tests?\\.groovy");
+
+    @Override
+    public void performInjection(SourceUnit sourceUnit, GeneratorContext generatorContext, ClassNode classNode) {
+      addEnableGlobally(classNode);
+
+      if (isExtendsGrailsUnitTestCace(classNode)) {
+        patchReturnType(classNode);
+      }
+    }
+
+    private static void addEnableGlobally(ClassNode classNode) {
+      List<Statement> initializer = new ArrayList<>();
+      initializer.add(new ExpressionStatement(new StaticMethodCallExpression(new ClassNode(ExpandoMetaClass.class),
+                                                                             "enableGlobally",
+                                                                             new ArgumentListExpression())));
+      initializer.add(new ExpressionStatement(new StaticMethodCallExpression(new ClassNode(System.class),
+                                                                             "setProperty",
+                                                                             new ArgumentListExpression(
+                                                                               new ConstantExpression("net.sf.ehcache.skipUpdateCheck"),
+                                                                               new ConstantExpression("true")))));
+      classNode.addStaticInitializerStatements(initializer, true);
+    }
+
+    private static void patchReturnType(ClassNode classNode) {
+      List<MethodNode> methods = classNode.getMethods();
+
+      for (MethodNode method : methods) {
+        if (!isTestMethod(method)) continue;
+
+        if (!"java.lang.Object".equals(method.getReturnType().getName())) continue;
+
+        if (isReturnNonVoid(method)) continue;
+
+        method.setReturnType(ClassHelper.VOID_TYPE);
+      }
+    }
+
+    private static boolean isExtendsGrailsUnitTestCace(ClassNode classNode) {
+      for (ClassNode parent = classNode.getSuperClass(); parent != null; parent = parent.getSuperClass()) {
+        if ("grails.test.GrailsUnitTestCase".equals(parent.getName())) {
+          return true;
+        }
+      }
+
+      return false;
+    }
+
+    @Override
+    public void performInjection(SourceUnit sourceUnit, ClassNode classNode) {
+      performInjection(sourceUnit, null, classNode);
+    }
+
+    @Override
+    public boolean shouldInject(URL url) {
+      if (url == null) return false;
+
+      return URL_PATTERN.matcher(url.getFile()).find();
+    }
+  }
+
+  public static boolean isReturnNonVoid(MethodNode method) {
+    Statement code = method.getCode();
+    if (code == null) return false;
+
+    final boolean[] res = new boolean[1];
+
+    code.visit(new CodeVisitorSupport() {
+      @Override
+      public void visitReturnStatement(ReturnStatement statement) {
+        if (!statement.isReturningNullOrVoid()) {
+          res[0] = true;
+        }
+      }
+    });
+
+    return res[0];
+  }
+
+  public static boolean isTestMethod(MethodNode methodNode) {
+    return methodNode.getParameters().length == 0
+           && methodNode.getName().startsWith("test")
+           && (methodNode.getModifiers() & (Modifier.ABSTRACT | Modifier.PRIVATE | Modifier.PROTECTED)) == 0;
+  }
+}
\ No newline at end of file
diff --git a/grails-rt/build.gradle.kts b/grails-rt/build.gradle.kts
new file mode 100644
index 0000000..a9c032d
--- /dev/null
+++ b/grails-rt/build.gradle.kts
@@ -0,0 +1,30 @@
+// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
+plugins {
+    id("java")
+}
+
+group = "org.intellij.grails.rt"
+
+repositories {
+    mavenCentral()
+}
+
+dependencies {
+    compileOnly("org.grails:grails-bootstrap:1.2.0")
+}
+
+java.sourceSets["main"].java {
+    srcDir("src")
+}
+java.sourceSets["main"].resources {
+    srcDir("resources")
+}
+
+tasks {
+    // Set the JVM compatibility versions
+    withType<JavaCompile> {
+        sourceCompatibility = "8"
+        targetCompatibility = "8"
+    }
+
+}
diff --git a/grails-rt/resources/META-INF/MANIFEST.MF b/grails-rt/resources/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..bfa57e9
--- /dev/null
+++ b/grails-rt/resources/META-INF/MANIFEST.MF
@@ -0,0 +1,2 @@
+Premain-Class: org.jetbrains.groovy.grails.rt.Agent
+Can-Redefine-Classes: true
diff --git a/grails-rt/resources/META-INF/commands/intellij-command-proxy.groovy b/grails-rt/resources/META-INF/commands/intellij-command-proxy.groovy
new file mode 100644
index 0000000..954ef64
--- /dev/null
+++ b/grails-rt/resources/META-INF/commands/intellij-command-proxy.groovy
@@ -0,0 +1,20 @@
+import org.grails.cli.gradle.GradleInvoker
+
+String initScriptPath = System.getenv("INTELLIJ_GRADLE_INIT_SCRIPT")
+String[] allArgs = commandLine.remainingArgs
+String commandToRun = grails.util.GrailsNameUtils.getNameFromScript(allArgs.head())
+String[] commandArguments = allArgs.tail() + commandLine.systemProperties.collect { key, value ->
+  "-D${key}=$value".toString()
+}
+
+if (initScriptPath) {
+  MetaMethod original = GradleInvoker.metaClass.getMetaMethod("invokeMethod", [String.class, Object.class] as Object[])
+  GradleInvoker.metaClass.invokeMethod = { String name, Object args ->
+    def argsList = args as List<Object>
+    argsList.add('--init-script')
+    argsList.add(initScriptPath)
+    original.invoke(delegate, [name, argsList] as Object[])
+  }
+}
+
+"$commandToRun"(*commandArguments)
diff --git a/grails-rt/resources/idea-injected-traits.groovy b/grails-rt/resources/idea-injected-traits.groovy
new file mode 100644
index 0000000..a9c922d
--- /dev/null
+++ b/grails-rt/resources/idea-injected-traits.groovy
@@ -0,0 +1,28 @@
+import grails.compiler.traits.TraitInjector
+import org.grails.core.io.support.GrailsFactoriesLoader
+
+def MARKER = '--------------------------------------'
+
+def injectors = GrailsFactoriesLoader.loadFactories(
+  TraitInjector.class, Thread.currentThread().contextClassLoader
+)
+
+try {
+  injectors = org.grails.compiler.injection.TraitInjectionSupport.resolveTraitInjectors(injectors)
+}
+catch (Throwable ignored) {
+}
+
+println()
+println()
+println()
+println MARKER
+injectors.each {
+  Class traitClass = it.trait
+  println traitClass.name
+  println it.artefactTypes.length
+  it.artefactTypes.each {
+    println it
+  }
+}
+println MARKER
diff --git a/grails-rt/src/org/jetbrains/groovy/grails/rt/AddAgentJarToClassPathTransformer.java b/grails-rt/src/org/jetbrains/groovy/grails/rt/AddAgentJarToClassPathTransformer.java
new file mode 100644
index 0000000..3cca7d0
--- /dev/null
+++ b/grails-rt/src/org/jetbrains/groovy/grails/rt/AddAgentJarToClassPathTransformer.java
@@ -0,0 +1,47 @@
+package org.jetbrains.groovy.grails.rt;
+
+import java.lang.instrument.ClassFileTransformer;
+import java.lang.instrument.IllegalClassFormatException;
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.security.ProtectionDomain;
+import java.util.HashSet;
+import java.util.Set;
+
+public final class AddAgentJarToClassPathTransformer implements ClassFileTransformer {
+  private final Set<ClassLoader> oldClassLoaders = new HashSet<>();
+
+  @Override
+  public byte[] transform(ClassLoader loader,
+                          String className,
+                          Class classBeingRedefined,
+                          ProtectionDomain protectionDomain,
+                          byte[] classfileBuffer) throws IllegalClassFormatException {
+    if (!AgentUtils.isGrailsClassLoader(loader)) {
+      return null;
+    }
+
+    synchronized (oldClassLoaders) {
+      if (!oldClassLoaders.add(loader)) {
+        return null;
+      }
+
+      String agentClassUrl = Agent.class.getClassLoader().getResource(Agent.class.getName().replace('.', '/') + ".class").toString();
+      int jarEnd = agentClassUrl.indexOf('!');
+      if (!agentClassUrl.startsWith("jar:") || jarEnd == -1) {
+        return null;
+      }
+
+      String jarPath = agentClassUrl.substring("jar:".length(), jarEnd);
+      try {
+        Method addUrlMethod = loader.getClass().getMethod("addURL", URL.class);
+        addUrlMethod.invoke(loader, new URL(jarPath));
+      }
+      catch (Exception e) {
+        System.out.println("Failed to add IDE test listener, some IDE features will be disabled");
+        e.printStackTrace();
+      }
+    }
+    return null;
+  }
+}
diff --git a/grails-rt/src/org/jetbrains/groovy/grails/rt/Agent.java b/grails-rt/src/org/jetbrains/groovy/grails/rt/Agent.java
new file mode 100644
index 0000000..ca63a6d
--- /dev/null
+++ b/grails-rt/src/org/jetbrains/groovy/grails/rt/Agent.java
@@ -0,0 +1,26 @@
+package org.jetbrains.groovy.grails.rt;
+
+import java.lang.instrument.Instrumentation;
+
+public final class Agent {
+
+  private static boolean initialized;
+
+  public static final String DEBUG_KIND_FILE = "idea.grails.kind.file";
+
+  public static void premain(String options, Instrumentation inst) {
+    // Handle duplicate agents
+    if (initialized) {
+      return;
+    }
+    initialized = true;
+
+    inst.addTransformer(new AddAgentJarToClassPathTransformer());
+
+    String fileName = System.getProperty(DEBUG_KIND_FILE);
+    if (fileName != null) {
+      inst.addTransformer(new ForkListenerTransformer(fileName));
+    }
+  }
+
+}
diff --git a/grails-rt/src/org/jetbrains/groovy/grails/rt/AgentUtils.java b/grails-rt/src/org/jetbrains/groovy/grails/rt/AgentUtils.java
new file mode 100644
index 0000000..1c5879f
--- /dev/null
+++ b/grails-rt/src/org/jetbrains/groovy/grails/rt/AgentUtils.java
@@ -0,0 +1,11 @@
+package org.jetbrains.groovy.grails.rt;
+
+public final class AgentUtils {
+
+  public static boolean isGrailsClassLoader(ClassLoader loader) {
+    String loaderName = loader.getClass().getName();
+    return loaderName.equals("org.codehaus.groovy.grails.cli.support.GrailsRootLoader")
+           || loaderName.equals("org.grails.launcher.RootLoader");
+  }
+
+}
diff --git a/grails-rt/src/org/jetbrains/groovy/grails/rt/ForkListenerTransformer.java b/grails-rt/src/org/jetbrains/groovy/grails/rt/ForkListenerTransformer.java
new file mode 100644
index 0000000..641d916
--- /dev/null
+++ b/grails-rt/src/org/jetbrains/groovy/grails/rt/ForkListenerTransformer.java
@@ -0,0 +1,52 @@
+package org.jetbrains.groovy.grails.rt;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.lang.instrument.ClassFileTransformer;
+import java.lang.instrument.IllegalClassFormatException;
+import java.security.ProtectionDomain;
+
+public class ForkListenerTransformer implements ClassFileTransformer {
+
+  private boolean done;
+
+  private final String fileName;
+
+  public ForkListenerTransformer(String fileName) {
+    this.fileName = fileName;
+  }
+
+  @Override
+  public byte[] transform(ClassLoader loader,
+                          String className,
+                          Class classBeingRedefined,
+                          ProtectionDomain protectionDomain,
+                          byte[] classfileBuffer) throws IllegalClassFormatException {
+    if (done) return null;
+
+    if (AgentUtils.isGrailsClassLoader(loader)) {
+      writeToFile(fileName, new byte[]{1});
+      done = true;
+    }
+    else {
+      if ("org/grails/maven/plugin/tools/ForkedGrailsRuntime".equals(className)) {
+        writeToFile(fileName, new byte[]{1, 2, 3});
+        done = true;
+      }
+    }
+
+    return null;
+  }
+
+  private static void writeToFile(String fileName, byte[] content) {
+    try {
+      try (FileOutputStream outputStream = new FileOutputStream(fileName)) {
+        outputStream.write(content);
+      }
+    }
+    catch (IOException ignored) {
+
+    }
+  }
+
+}
diff --git a/grails-rt/src/org/jetbrains/groovy/grails/rt/GrailsIdeaTestListener.java b/grails-rt/src/org/jetbrains/groovy/grails/rt/GrailsIdeaTestListener.java
new file mode 100644
index 0000000..450da4b
--- /dev/null
+++ b/grails-rt/src/org/jetbrains/groovy/grails/rt/GrailsIdeaTestListener.java
@@ -0,0 +1,98 @@
+// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
+
+package org.jetbrains.groovy.grails.rt;
+
+import grails.build.GrailsBuildListener;
+
+import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author Maxim.Medvedev
+ * @noinspection UseOfSystemOutOrSystemErr,UnusedDeclaration
+ */
+public class GrailsIdeaTestListener implements GrailsBuildListener {
+  private final Map<String, Long> myProperties = new HashMap<>();
+  private String myClassName;
+
+  private PrintStream out;
+
+  @Override
+  public void receiveGrailsBuildEvent(String s, Object[] objects) {
+    if (out == null) {
+      out = System.out; // Save System.out. They change it during test running.
+    }
+
+    if ("TestCaseEnd".equals(s)) {
+      out.println(objects[1]);
+      testSuiteFinished((String)objects[0]);
+    }
+
+    else if ("TestCaseStart".equals(s)) {
+      myClassName = (String)objects[0];
+      testSuiteStarted(myClassName);
+    }
+
+    else if ("TestStart".equals(s)) {
+      testStarted((String)objects[0]);
+    }
+
+    else if ("TestEnd".equals(s)) {
+      testFinished((String)objects[0]);
+    }
+
+    else if ("TestFailure".equals(s)) {
+      Object failure = objects[1];
+      String message = failure instanceof Throwable ?
+                       replaceEscapedSymbols(((Throwable)failure).getMessage()) :
+                       String.valueOf(failure);
+      String details = failure instanceof Throwable ? getStackTrace((Throwable)failure) : "none";
+      String error = failure instanceof AssertionError || failure == null ? "error='true'" : "";
+      out.println("\n##teamcity[testFailed name='" + replaceEscapedSymbols(((String)objects[0])) +
+                  "' message='" + message +
+                  "' details='" + details +
+                  "' " + error + "]");
+    }
+  }
+
+  private static String replaceEscapedSymbols(String s) {
+    if (s == null) return null;
+    return s.replaceAll("[\\|'\\[\\]]", "\\|$0").
+      replaceAll("\n", "|n").
+      replaceAll("\r", "|r");
+  }
+
+  static String getStackTrace(Throwable e) {
+    StringWriter writer = new StringWriter();
+    e.printStackTrace(new PrintWriter(writer));
+    return replaceEscapedSymbols(writer.getBuffer().toString());
+  }
+
+  private void testFinished(String testName) {
+    long duration = System.currentTimeMillis() - myProperties.get(testName).longValue();
+    out.println("\n##teamcity[testFinished name='" + replaceEscapedSymbols(testName) + "' duration='" + duration + "']");
+  }
+
+  private void testStarted(String testName) {
+    String testLocation = replaceEscapedSymbols(myClassName + '.' + testName);
+    out.println("\n##teamcity[testStarted name='" +
+                replaceEscapedSymbols(testName) +
+                "' captureStandardOutput='false' locationHint='grails://methodName::" +
+                testLocation +
+                "']");
+    myProperties.put(testName, Long.valueOf(System.currentTimeMillis()));
+  }
+
+  private void testSuiteStarted(String name) {
+    out.println("\n##teamcity[testSuiteStarted name='" + name + "' locationHint='grails://className::" + name + "']");
+    myProperties.put(name, Long.valueOf(System.currentTimeMillis()));
+  }
+
+  private void testSuiteFinished(String name) {
+    long duration = System.currentTimeMillis() - myProperties.get(name).longValue();
+    out.println("\n##teamcity[testSuiteFinished name='" + name + "' duration='" + duration + "']");
+  }
+}
diff --git a/grails-rt/src/org/jetbrains/groovy/grails/rt/GrailsRtMarker.java b/grails-rt/src/org/jetbrains/groovy/grails/rt/GrailsRtMarker.java
new file mode 100644
index 0000000..ec97941
--- /dev/null
+++ b/grails-rt/src/org/jetbrains/groovy/grails/rt/GrailsRtMarker.java
@@ -0,0 +1,5 @@
+package org.jetbrains.groovy.grails.rt;
+
+public class GrailsRtMarker {
+  
+}
diff --git a/hibernate/build.gradle.kts b/hibernate/build.gradle.kts
new file mode 100644
index 0000000..6eafb57
--- /dev/null
+++ b/hibernate/build.gradle.kts
@@ -0,0 +1,41 @@
+// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
+fun properties(key: String) = project.findProperty(key).toString()
+
+plugins {
+    id("java")
+    id("org.jetbrains.intellij.platform.module")
+}
+
+group = "org.intellij.grails.hibernate"
+
+repositories {
+    mavenCentral()
+    intellijPlatform {
+        defaultRepositories()
+    }
+}
+
+dependencies {
+    intellijPlatform {
+        intellijIdea(properties("platformVersion"))
+        bundledPlugin("com.intellij.hibernate")
+    }
+
+    compileOnly(project(":"))
+}
+
+java.sourceSets["main"].java {
+    srcDir("src")
+}
+
+java.sourceSets["main"].resources {
+    srcDir("resources")
+}
+
+tasks {
+        withType<JavaCompile> {
+        sourceCompatibility = properties("platformJavaVersion")
+        targetCompatibility = properties("platformJavaVersion")
+    }
+
+}
diff --git a/hibernate/resources/intellij.groovy.grails.hibernate.xml b/hibernate/resources/intellij.groovy.grails.hibernate.xml
new file mode 100644
index 0000000..f63994e
--- /dev/null
+++ b/hibernate/resources/intellij.groovy.grails.hibernate.xml
@@ -0,0 +1,15 @@
+<!-- Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.-->
+<idea-plugin>
+
+  <dependencies>
+    <plugin id="com.intellij.hibernate"/>
+  </dependencies>
+
+  <extensions defaultExtensionNs="com.intellij">
+    <persistence.packagesProvider implementation="com.intellij.groovy.grails.hibernate.GormSessionFactoryContributor"/>
+  </extensions>
+
+  <extensions defaultExtensionNs="org.intellij.grails">
+    <facetProvider implementation="com.intellij.groovy.grails.hibernate.GormHibernateFacetProvider"/>
+  </extensions>
+</idea-plugin>
diff --git a/hibernate/src/com/intellij/groovy/grails/hibernate/GormHibernateFacetProvider.java b/hibernate/src/com/intellij/groovy/grails/hibernate/GormHibernateFacetProvider.java
new file mode 100644
index 0000000..2ce8d31
--- /dev/null
+++ b/hibernate/src/com/intellij/groovy/grails/hibernate/GormHibernateFacetProvider.java
@@ -0,0 +1,59 @@
+// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
+
+package com.intellij.groovy.grails.hibernate;
+
+import com.intellij.facet.ModifiableFacetModel;
+import com.intellij.hibernate.facet.HibernateFacet;
+import com.intellij.hibernate.facet.HibernateFacetType;
+import com.intellij.hibernate.model.HibernateDescriptorsConstants;
+import com.intellij.jpa.facet.JpaFacet;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.util.Consumer;
+import com.intellij.util.descriptors.ConfigFileInfo;
+import com.intellij.util.descriptors.ConfigFileInfoSet;
+import org.jetbrains.plugins.grails.util.GrailsFacetProvider;
+import org.jetbrains.plugins.grails.util.GrailsUtils;
+
+import java.util.Collection;
+
+final class GormHibernateFacetProvider implements GrailsFacetProvider {
+
+  @Override
+  public void addFacets(Collection<Consumer<ModifiableFacetModel>> actions,
+                        final Module module,
+                        Collection<VirtualFile> roots) {
+    actions.add(model -> {
+      final Collection<HibernateFacet> hibernateFacets = model.getFacetsByType(HibernateFacet.ID);
+      final Collection<JpaFacet> jpaFacets = model.getFacetsByType(JpaFacet.ID);
+      if (!hibernateFacets.isEmpty() || !jpaFacets.isEmpty()) return;
+      HibernateFacetType facetType = HibernateFacetType.getInstance();
+      HibernateFacet facet = facetType.createFacet(module, facetType.getPresentableName(), facetType.createDefaultConfiguration(), null);
+
+      VirtualFile confDirectory = GrailsUtils.findConfDirectory(module);
+      if (confDirectory != null) {
+        VirtualFile hibernateDir = confDirectory.findChild("hibernate");
+        if (hibernateDir != null) {
+          ConfigFileInfoSet cfg = facet.getConfiguration().getDescriptorsConfiguration();
+
+          VirtualFile hibernateCfgXml = hibernateDir.findChild("hibernate.cfg.xml");
+          if (hibernateCfgXml != null) {
+            cfg.addConfigFile(
+              new ConfigFileInfo(HibernateDescriptorsConstants.HIBERNATE_CONFIGURATION_META_DATA, hibernateCfgXml.getUrl()));
+          }
+
+          //VirtualFile[] children = hibernateDir.getChildren();
+          //if (children != null) {
+          //  for (VirtualFile child : children) {
+          //    if (!child.isDirectory() && child.getName().endsWith(".hbm.xml")) {
+          //
+          //    }
+          //  }
+          //}
+        }
+      }
+
+      model.addFacet(facet);
+    });
+  }
+}
diff --git a/hibernate/src/com/intellij/groovy/grails/hibernate/GormSessionFactory.java b/hibernate/src/com/intellij/groovy/grails/hibernate/GormSessionFactory.java
new file mode 100644
index 0000000..6f21055
--- /dev/null
+++ b/hibernate/src/com/intellij/groovy/grails/hibernate/GormSessionFactory.java
@@ -0,0 +1,168 @@
+// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
+
+package com.intellij.groovy.grails.hibernate;
+
+import com.intellij.hibernate.model.HibernateConstants;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.util.UserDataHolderBase;
+import com.intellij.persistence.model.PersistenceListener;
+import com.intellij.persistence.model.PersistenceMappings;
+import com.intellij.persistence.model.PersistencePackage;
+import com.intellij.persistence.model.helpers.PersistenceUnitModelHelper;
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiFile;
+import com.intellij.psi.PsiManager;
+import com.intellij.psi.PsiPackage;
+import com.intellij.psi.xml.XmlTag;
+import com.intellij.util.xml.GenericValue;
+import com.intellij.util.xml.ReadOnlyGenericValue;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.plugins.grails.references.domain.persistent.GormPersistenceMapping;
+import org.jetbrains.plugins.groovy.mvc.MvcModuleStructureSynchronizer;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Properties;
+
+public class GormSessionFactory extends UserDataHolderBase implements PersistencePackage, PersistenceUnitModelHelper {
+
+  private static final GenericValue<String> NAME = ReadOnlyGenericValue.getInstance("Gorm");
+
+  private final Module myModule;
+
+  private final GormPersistenceMapping myPersistenceMapping;
+
+  public GormSessionFactory(Module module) {
+    myModule = module;
+    myPersistenceMapping = new GormPersistenceMapping(module);
+  }
+
+  @Override
+  public GenericValue<String> getName() {
+    return NAME;
+  }
+
+  @Override
+  public PersistenceUnitModelHelper getModelHelper() {
+    return this;
+  }
+
+  @Override
+  public boolean isValid() {
+    return !myModule.isDisposed();
+  }
+
+  @Override
+  public @Nullable XmlTag getXmlTag() {
+    return null;
+  }
+
+  @Override
+  public PsiManager getPsiManager() {
+    return PsiManager.getInstance(myModule.getProject());
+  }
+
+  @Override
+  public Module getModule() {
+    return myModule;
+  }
+
+  @Override
+  public PsiElement getIdentifyingPsiElement() {
+    return null;
+  }
+
+  @Override
+  public PsiFile getContainingFile() {
+    return null;
+  }
+
+  @Override
+  public GenericValue<Boolean> getExcludeUnlistedClasses() {
+    return ReadOnlyGenericValue.getInstance(Boolean.TRUE);
+  }
+
+  @Override
+  public @Nullable String getPersistenceProviderName() {
+    return HibernateConstants.PERSISTENCE_PROVIDER_CLASS;
+  }
+
+  @Override
+  public @Nullable PersistenceMappings getAdditionalMapping() {
+    return myPersistenceMapping;
+  }
+
+  @Override
+  public @NotNull <V extends PersistenceMappings> List<? extends GenericValue<V>> getMappingFiles(Class<V> mappingsClass) {
+    return Collections.emptyList();
+  }
+
+  @Override
+  public @NotNull List<? extends PersistenceListener> getPersistentListeners() {
+    return Collections.emptyList();
+  }
+
+  @Override
+  public @NotNull List<? extends GenericValue<PsiFile>> getJarFiles() {
+    return Collections.emptyList();
+  }
+
+  @Override
+  public @NotNull List<? extends GenericValue<PsiClass>> getClasses() {
+    return Collections.emptyList();
+    //Collection<? extends GrClassDefinition> domains = GrailsArtifact.DOMAIN.getInstances(myModule).values();
+    //
+    //List<GenericValue<PsiClass>> res = new ArrayList<GenericValue<PsiClass>>(domains.size());
+    //
+    //for (GrClassDefinition definition : domains) {
+    //  res.add(ReadOnlyGenericValue.<PsiClass>getInstance(definition));
+    //}
+    //
+    //return res;
+  }
+
+  @Override
+  public @NotNull List<? extends GenericValue<PsiPackage>> getPackages() {
+    return Collections.emptyList();
+
+    //Set<String> packageNames = new HashSet<String>();
+    //for (GrClassDefinition definition : GrailsArtifact.DOMAIN.getInstances(myModule).values()) {
+    //  String qualifiedName = definition.getQualifiedName();
+    //  if (qualifiedName != null) {
+    //    packageNames.add(StringUtil.getPackageName(qualifiedName));
+    //  }
+    //}
+    //
+    //List<GenericValue<PsiPackage>> res = new ArrayList<GenericValue<PsiPackage>>(packageNames.size());
+    //
+    //JavaPsiFacade facade = JavaPsiFacade.getInstance(myModule.getProject());
+    //
+    //for (String packageName : packageNames) {
+    //  PsiPackage aPackage = facade.findPackage(packageName);
+    //  if (aPackage != null) {
+    //    res.add(ReadOnlyGenericValue.getInstance(aPackage));
+    //  }
+    //}
+    //
+    //return res;
+  }
+
+  @Override
+  public GenericValue<String> getDataSourceName() {
+    return null;
+  }
+
+  @Override
+  public @NotNull Properties getPersistenceUnitProperties() {
+    return new Properties();
+  }
+
+  @Override
+  public Collection<Object> getCacheDependencies() {
+    MvcModuleStructureSynchronizer synchronizer = MvcModuleStructureSynchronizer.getInstance(myModule.getProject());
+    return Collections.singletonList(synchronizer.getFileAndRootsModificationTracker());
+  }
+}
diff --git a/hibernate/src/com/intellij/groovy/grails/hibernate/GormSessionFactoryContributor.java b/hibernate/src/com/intellij/groovy/grails/hibernate/GormSessionFactoryContributor.java
new file mode 100644
index 0000000..fdf72db
--- /dev/null
+++ b/hibernate/src/com/intellij/groovy/grails/hibernate/GormSessionFactoryContributor.java
@@ -0,0 +1,28 @@
+// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
+
+package com.intellij.groovy.grails.hibernate;
+
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.util.Key;
+import com.intellij.persistence.extensions.PersistencePackagesProvider;
+import com.intellij.persistence.facet.PersistenceFacet;
+import com.intellij.persistence.model.PersistencePackage;
+import com.intellij.util.ConcurrencyUtil;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.plugins.grails.config.GrailsStructure;
+
+import java.util.Collections;
+import java.util.List;
+
+final class GormSessionFactoryContributor implements PersistencePackagesProvider {
+  private static final Key<List<PersistencePackage>> SESSION_FACTORY_KEY = Key.create("GormSessionFactoryContributor.SESSION_FACTORY_KEY");
+
+  @Override
+  public @NotNull List<PersistencePackage> getPersistencePackages(PersistenceFacet facet) {
+    Module module = facet.getModule();
+    GrailsStructure instance = GrailsStructure.getInstance(module);
+    if (instance == null) return Collections.emptyList();
+
+    return ConcurrencyUtil.computeIfAbsent(module, SESSION_FACTORY_KEY, () -> Collections.singletonList(new GormSessionFactory(module)));
+  }
+}
diff --git a/i18n/build.gradle.kts b/i18n/build.gradle.kts
new file mode 100644
index 0000000..28021d8
--- /dev/null
+++ b/i18n/build.gradle.kts
@@ -0,0 +1,47 @@
+// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
+fun properties(key: String) = project.findProperty(key).toString()
+
+plugins {
+    id("java")
+    id("org.jetbrains.kotlin.jvm") version "2.3.0"
+    id("org.jetbrains.intellij.platform.module")
+}
+
+group = "org.intellij.grails.i18n"
+
+repositories {
+    mavenCentral()
+    intellijPlatform {
+        defaultRepositories()
+    }
+}
+
+dependencies {
+    intellijPlatform {
+        intellijIdea(properties("platformVersion"))
+        bundledPlugin("org.intellij.groovy")
+        bundledPlugin("com.intellij.java-i18n")
+    }
+
+    compileOnly(project(":"))
+}
+
+kotlin {
+    jvmToolchain(21)
+}
+
+java.sourceSets["main"].java {
+    srcDir("src")
+}
+
+java.sourceSets["main"].resources {
+    srcDir("resources")
+}
+
+tasks {
+        withType<JavaCompile> {
+        sourceCompatibility = properties("platformJavaVersion")
+        targetCompatibility = properties("platformJavaVersion")
+    }
+
+}
diff --git a/i18n/resources/fileTemplates/code/I18nized GSP Expression with injections.gsp.ft b/i18n/resources/fileTemplates/code/I18nized GSP Expression with injections.gsp.ft
new file mode 100644
index 0000000..7cdd13e
--- /dev/null
+++ b/i18n/resources/fileTemplates/code/I18nized GSP Expression with injections.gsp.ft	
@@ -0,0 +1 @@
+<g:message code="${PROPERTY_KEY}" args="[${ARGS_KEY}]" />
\ No newline at end of file
diff --git a/i18n/resources/fileTemplates/code/I18nized GSP Expression with injections.gsp.html b/i18n/resources/fileTemplates/code/I18nized GSP Expression with injections.gsp.html
new file mode 100644
index 0000000..472848d
--- /dev/null
+++ b/i18n/resources/fileTemplates/code/I18nized GSP Expression with injections.gsp.html	
@@ -0,0 +1,58 @@
+<html>
+<body>
+
+<table width="100%" border="0" cellpadding="5" cellspacing="0" style="border-collapse: collapse" bordercolor="#111111">
+  <tr>
+    <td colspan="3"><font face="verdana" size="-1">
+    This is a built-in template used for replace text in <b>GSP</b>,
+    e.g.
+      <br>
+      &nbsp;&nbsp;&nbsp;&nbsp;
+        &lt;<span style="font-weight:bold;">html</span>&gt;
+        &lt;<span style="font-weight:bold;">body</span>&gt;
+      <font face="verdana">Hello ${'world'}!</font>
+      &lt;<span style="font-weight:bold;">/body</span>&gt;
+      &lt;<span style="font-weight:bold;">/html</span>&gt;
+      <br>
+    with internationalized GSP custom tag usage,
+      e.g.
+      <br>
+      &nbsp;&nbsp;&nbsp;&nbsp;
+      &lt;<span style="font-weight:bold;">html</span>&gt;
+      &lt;<span style="font-weight:bold;">body</span>&gt;
+      <font face="verdana">&lt;g:message code="HelloKey" args="['world']"/&gt;</font>
+    &lt;<span style="font-weight:bold;">/body</span>&gt;
+    &lt;<span style="font-weight:bold;">/html</span>&gt;
+    .
+    <p>
+    The template is editable. Along with expressions allowed in GSP context, you can also use the predefined variables that
+    will be then expanded into the corresponding values.</font>
+    </td>
+  </tr>
+</table>
+<table width="100%" border="0" cellpadding="5" cellspacing="0" style="border-collapse: collapse" bordercolor="#111111">
+  <tr>
+    <td colspan="3"><font face="verdana" size="-1">Predefined variables will take the following values:</font></td>
+  </tr>
+  <tr>
+    <td valign="top"><nobr><font face="verdana" size="-2"><b>${PROPERTY_KEY}</b></font></nobr></td>
+    <td width="10">&nbsp;</td>
+    <td width="100%" valign="top"><font face="verdana" size="-1">Property key name which is specified in the corresponding properties file.
+    </font></td>
+  </tr>
+  <tr>
+    <td valign="top"><nobr><font face="verdana" size="-2"><b>${PROPERTY_VALUE}</b></font></nobr></td>
+    <td width="10">&nbsp;</td>
+    <td width="100%" valign="top"><font face="verdana" size="-1">Property value which is specified in the corresponding properties file.
+      This is an original GSP text selection value.
+    </font></td>
+  </tr>
+  <tr>
+    <td valign="top"><nobr><font face="verdana" size="-2"><b>${ARGS_KEY}</b></font></nobr></td>
+    <td width="10">&nbsp;</td>
+    <td width="100%" valign="top"><font face="verdana" size="-1">Arguments</font></td>
+  </tr>
+
+</table>
+</body>
+</html>
\ No newline at end of file
diff --git a/i18n/resources/fileTemplates/code/I18nized GSP Expression.gsp.ft b/i18n/resources/fileTemplates/code/I18nized GSP Expression.gsp.ft
new file mode 100644
index 0000000..54b94c2
--- /dev/null
+++ b/i18n/resources/fileTemplates/code/I18nized GSP Expression.gsp.ft	
@@ -0,0 +1 @@
+<g:message code="${PROPERTY_KEY}" />
\ No newline at end of file
diff --git a/i18n/resources/fileTemplates/code/I18nized GSP Expression.gsp.html b/i18n/resources/fileTemplates/code/I18nized GSP Expression.gsp.html
new file mode 100644
index 0000000..dfb30d0
--- /dev/null
+++ b/i18n/resources/fileTemplates/code/I18nized GSP Expression.gsp.html	
@@ -0,0 +1,53 @@
+<html>
+<body>
+
+<table width="100%" border="0" cellpadding="5" cellspacing="0" style="border-collapse: collapse" bordercolor="#111111">
+  <tr>
+    <td colspan="3"><font face="verdana" size="-1">
+    This is a built-in template used for replace text in <b>GSP</b>,
+    e.g.
+      <br>
+      &nbsp;&nbsp;&nbsp;&nbsp;
+        &lt;<span style="font-weight:bold;">html</span>&gt;
+        &lt;<span style="font-weight:bold;">body</span>&gt;
+      <font face="verdana">Hello!</font>
+      &lt;<span style="font-weight:bold;">/body</span>&gt;
+      &lt;<span style="font-weight:bold;">/html</span>&gt;
+      <br>
+    with internationalized GSP custom tag usage,
+      e.g.
+      <br>
+      &nbsp;&nbsp;&nbsp;&nbsp;
+      &lt;<span style="font-weight:bold;">html</span>&gt;
+      &lt;<span style="font-weight:bold;">body</span>&gt;
+      <font face="verdana">&lt;g:message code="HelloKey"/&gt;</font>
+    &lt;<span style="font-weight:bold;">/body</span>&gt;
+    &lt;<span style="font-weight:bold;">/html</span>&gt;
+    .
+    <p>
+    The template is editable. Along with expressions allowed in GSP context, you can also use the predefined variables that
+    will be then expanded into the corresponding values.</font>
+    </td>
+  </tr>
+</table>
+<table width="100%" border="0" cellpadding="5" cellspacing="0" style="border-collapse: collapse" bordercolor="#111111">
+  <tr>
+    <td colspan="3"><font face="verdana" size="-1">Predefined variables will take the following values:</font></td>
+  </tr>
+  <tr>
+    <td valign="top"><nobr><font face="verdana" size="-2"><b>${PROPERTY_KEY}</b></font></nobr></td>
+    <td width="10">&nbsp;</td>
+    <td width="100%" valign="top"><font face="verdana" size="-1">Property key name which is specified in the corresponding properties file.
+    </font></td>
+  </tr>
+  <tr>
+    <td valign="top"><nobr><font face="verdana" size="-2"><b>${PROPERTY_VALUE}</b></font></nobr></td>
+    <td width="10">&nbsp;</td>
+    <td width="100%" valign="top"><font face="verdana" size="-1">Property value which is specified in the corresponding properties file.
+      This is an original GSP text selection value.
+    </font></td>
+  </tr>
+
+</table>
+</body>
+</html>
\ No newline at end of file
diff --git a/i18n/resources/fileTemplates/code/I18nized Groovy string with injections.gsp.ft b/i18n/resources/fileTemplates/code/I18nized Groovy string with injections.gsp.ft
new file mode 100644
index 0000000..f37d4c6
--- /dev/null
+++ b/i18n/resources/fileTemplates/code/I18nized Groovy string with injections.gsp.ft	
@@ -0,0 +1 @@
+message(code:"${PROPERTY_KEY}", args:[${ARGS_KEY}])
\ No newline at end of file
diff --git a/i18n/resources/fileTemplates/code/I18nized Groovy string with injections.gsp.html b/i18n/resources/fileTemplates/code/I18nized Groovy string with injections.gsp.html
new file mode 100644
index 0000000..472848d
--- /dev/null
+++ b/i18n/resources/fileTemplates/code/I18nized Groovy string with injections.gsp.html	
@@ -0,0 +1,58 @@
+<html>
+<body>
+
+<table width="100%" border="0" cellpadding="5" cellspacing="0" style="border-collapse: collapse" bordercolor="#111111">
+  <tr>
+    <td colspan="3"><font face="verdana" size="-1">
+    This is a built-in template used for replace text in <b>GSP</b>,
+    e.g.
+      <br>
+      &nbsp;&nbsp;&nbsp;&nbsp;
+        &lt;<span style="font-weight:bold;">html</span>&gt;
+        &lt;<span style="font-weight:bold;">body</span>&gt;
+      <font face="verdana">Hello ${'world'}!</font>
+      &lt;<span style="font-weight:bold;">/body</span>&gt;
+      &lt;<span style="font-weight:bold;">/html</span>&gt;
+      <br>
+    with internationalized GSP custom tag usage,
+      e.g.
+      <br>
+      &nbsp;&nbsp;&nbsp;&nbsp;
+      &lt;<span style="font-weight:bold;">html</span>&gt;
+      &lt;<span style="font-weight:bold;">body</span>&gt;
+      <font face="verdana">&lt;g:message code="HelloKey" args="['world']"/&gt;</font>
+    &lt;<span style="font-weight:bold;">/body</span>&gt;
+    &lt;<span style="font-weight:bold;">/html</span>&gt;
+    .
+    <p>
+    The template is editable. Along with expressions allowed in GSP context, you can also use the predefined variables that
+    will be then expanded into the corresponding values.</font>
+    </td>
+  </tr>
+</table>
+<table width="100%" border="0" cellpadding="5" cellspacing="0" style="border-collapse: collapse" bordercolor="#111111">
+  <tr>
+    <td colspan="3"><font face="verdana" size="-1">Predefined variables will take the following values:</font></td>
+  </tr>
+  <tr>
+    <td valign="top"><nobr><font face="verdana" size="-2"><b>${PROPERTY_KEY}</b></font></nobr></td>
+    <td width="10">&nbsp;</td>
+    <td width="100%" valign="top"><font face="verdana" size="-1">Property key name which is specified in the corresponding properties file.
+    </font></td>
+  </tr>
+  <tr>
+    <td valign="top"><nobr><font face="verdana" size="-2"><b>${PROPERTY_VALUE}</b></font></nobr></td>
+    <td width="10">&nbsp;</td>
+    <td width="100%" valign="top"><font face="verdana" size="-1">Property value which is specified in the corresponding properties file.
+      This is an original GSP text selection value.
+    </font></td>
+  </tr>
+  <tr>
+    <td valign="top"><nobr><font face="verdana" size="-2"><b>${ARGS_KEY}</b></font></nobr></td>
+    <td width="10">&nbsp;</td>
+    <td width="100%" valign="top"><font face="verdana" size="-1">Arguments</font></td>
+  </tr>
+
+</table>
+</body>
+</html>
\ No newline at end of file
diff --git a/i18n/resources/fileTemplates/code/I18nized Groovy string.gsp.ft b/i18n/resources/fileTemplates/code/I18nized Groovy string.gsp.ft
new file mode 100644
index 0000000..340a99b
--- /dev/null
+++ b/i18n/resources/fileTemplates/code/I18nized Groovy string.gsp.ft	
@@ -0,0 +1 @@
+message(code:"${PROPERTY_KEY}")
\ No newline at end of file
diff --git a/i18n/resources/fileTemplates/code/I18nized Groovy string.gsp.html b/i18n/resources/fileTemplates/code/I18nized Groovy string.gsp.html
new file mode 100644
index 0000000..dfb30d0
--- /dev/null
+++ b/i18n/resources/fileTemplates/code/I18nized Groovy string.gsp.html	
@@ -0,0 +1,53 @@
+<html>
+<body>
+
+<table width="100%" border="0" cellpadding="5" cellspacing="0" style="border-collapse: collapse" bordercolor="#111111">
+  <tr>
+    <td colspan="3"><font face="verdana" size="-1">
+    This is a built-in template used for replace text in <b>GSP</b>,
+    e.g.
+      <br>
+      &nbsp;&nbsp;&nbsp;&nbsp;
+        &lt;<span style="font-weight:bold;">html</span>&gt;
+        &lt;<span style="font-weight:bold;">body</span>&gt;
+      <font face="verdana">Hello!</font>
+      &lt;<span style="font-weight:bold;">/body</span>&gt;
+      &lt;<span style="font-weight:bold;">/html</span>&gt;
+      <br>
+    with internationalized GSP custom tag usage,
+      e.g.
+      <br>
+      &nbsp;&nbsp;&nbsp;&nbsp;
+      &lt;<span style="font-weight:bold;">html</span>&gt;
+      &lt;<span style="font-weight:bold;">body</span>&gt;
+      <font face="verdana">&lt;g:message code="HelloKey"/&gt;</font>
+    &lt;<span style="font-weight:bold;">/body</span>&gt;
+    &lt;<span style="font-weight:bold;">/html</span>&gt;
+    .
+    <p>
+    The template is editable. Along with expressions allowed in GSP context, you can also use the predefined variables that
+    will be then expanded into the corresponding values.</font>
+    </td>
+  </tr>
+</table>
+<table width="100%" border="0" cellpadding="5" cellspacing="0" style="border-collapse: collapse" bordercolor="#111111">
+  <tr>
+    <td colspan="3"><font face="verdana" size="-1">Predefined variables will take the following values:</font></td>
+  </tr>
+  <tr>
+    <td valign="top"><nobr><font face="verdana" size="-2"><b>${PROPERTY_KEY}</b></font></nobr></td>
+    <td width="10">&nbsp;</td>
+    <td width="100%" valign="top"><font face="verdana" size="-1">Property key name which is specified in the corresponding properties file.
+    </font></td>
+  </tr>
+  <tr>
+    <td valign="top"><nobr><font face="verdana" size="-2"><b>${PROPERTY_VALUE}</b></font></nobr></td>
+    <td width="10">&nbsp;</td>
+    <td width="100%" valign="top"><font face="verdana" size="-1">Property value which is specified in the corresponding properties file.
+      This is an original GSP text selection value.
+    </font></td>
+  </tr>
+
+</table>
+</body>
+</html>
\ No newline at end of file
diff --git a/i18n/resources/inspectionDescriptions/InvalidI18nProperty.html b/i18n/resources/inspectionDescriptions/InvalidI18nProperty.html
new file mode 100644
index 0000000..dcbf0c7
--- /dev/null
+++ b/i18n/resources/inspectionDescriptions/InvalidI18nProperty.html
@@ -0,0 +1,5 @@
+<html>
+<body>
+Reports unresolved properties from <code>*.property</code> files.
+</body>
+</html>
diff --git a/i18n/resources/inspectionDescriptions/metaInformation.json b/i18n/resources/inspectionDescriptions/metaInformation.json
new file mode 100644
index 0000000..36a9c97
--- /dev/null
+++ b/i18n/resources/inspectionDescriptions/metaInformation.json
@@ -0,0 +1,8 @@
+{
+  "inspections": [
+    {
+      "id": "InvalidI18nProperty",
+      "codeQualityCategory": "Sanity"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/i18n/resources/intellij.groovy.grails.i18n.xml b/i18n/resources/intellij.groovy.grails.i18n.xml
new file mode 100644
index 0000000..f2d676d
--- /dev/null
+++ b/i18n/resources/intellij.groovy.grails.i18n.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<!-- Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.-->
+<idea-plugin>
+
+  <resource-bundle>messages.GrailsBundle</resource-bundle>
+
+  <dependencies>
+    <plugin id="com.intellij.java-i18n"/>
+  </dependencies>
+
+  <extensions defaultExtensionNs="com.intellij.java-i18n">
+    <i18nizeHandlerProvider implementation="com.intellij.groovy.grails.i18n.GrailsI18nizeProvider"/>
+  </extensions>
+
+  <extensions defaultExtensionNs="com.intellij">
+    <psi.referenceContributor language="GSP" implementation="com.intellij.groovy.grails.i18n.GrailsI18nReferenceContributor"/>
+
+    <localInspection language="Groovy" enabledByDefault="true" level="ERROR"
+                     groupBundle="messages.GrailsBundle" groupPathKey="inspections.groupPath" groupKey="inspections.groupName"
+                     bundle="messages.GrailsBundle" key="inspection.invalid.i.18.n.property.display.name"
+                     shortName="InvalidI18nProperty" implementationClass="com.intellij.groovy.grails.i18n.GrailsI18nInspection"/>
+
+    <intentionAction>
+      <className>com.intellij.groovy.grails.i18n.GspI18nIntention</className>
+      <categoryKey>library.name</categoryKey>
+    </intentionAction>
+
+  </extensions>
+
+</idea-plugin>
diff --git a/i18n/resources/intentionDescriptions/GspI18nIntention/after.gsp.template b/i18n/resources/intentionDescriptions/GspI18nIntention/after.gsp.template
new file mode 100644
index 0000000..10d505c
--- /dev/null
+++ b/i18n/resources/intentionDescriptions/GspI18nIntention/after.gsp.template
@@ -0,0 +1,8 @@
+<div class="panelBody">
+    <h1><spot><g:message code="application.status" /></spot></h1>
+    <ul>
+        <li>App version: <g:meta name="app.version"></g:meta></li>
+        <li>Grails version: <g:meta name="app.grails.version"></g:meta></li>
+        <li>Groovy version: ${org.codehaus.groovy.runtime.InvokerHelper.getVersion()}</li>
+    </ul>
+</div>
diff --git a/i18n/resources/intentionDescriptions/GspI18nIntention/before.gsp.template b/i18n/resources/intentionDescriptions/GspI18nIntention/before.gsp.template
new file mode 100644
index 0000000..f15a26b
--- /dev/null
+++ b/i18n/resources/intentionDescriptions/GspI18nIntention/before.gsp.template
@@ -0,0 +1,8 @@
+<div class="panelBody">
+    <h1><spot>Application Status</spot></h1>
+    <ul>
+        <li>App version: <g:meta name="app.version"></g:meta></li>
+        <li>Grails version: <g:meta name="app.grails.version"></g:meta></li>
+        <li>Groovy version: ${org.codehaus.groovy.runtime.InvokerHelper.getVersion()}</li>
+    </ul>
+</div>
diff --git a/i18n/resources/intentionDescriptions/GspI18nIntention/description.html b/i18n/resources/intentionDescriptions/GspI18nIntention/description.html
new file mode 100644
index 0000000..4052a2e
--- /dev/null
+++ b/i18n/resources/intentionDescriptions/GspI18nIntention/description.html
@@ -0,0 +1,5 @@
+<html>
+<body>
+Extracts selected text from a GSP file to <code>message.properties</code>.
+</body>
+</html>
diff --git a/i18n/src/com/intellij/groovy/grails/i18n/GrailsI18nGroovyQuickFixHandler.java b/i18n/src/com/intellij/groovy/grails/i18n/GrailsI18nGroovyQuickFixHandler.java
new file mode 100644
index 0000000..29f5211
--- /dev/null
+++ b/i18n/src/com/intellij/groovy/grails/i18n/GrailsI18nGroovyQuickFixHandler.java
@@ -0,0 +1,175 @@
+// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
+
+package com.intellij.groovy.grails.i18n;
+
+import com.intellij.codeInspection.i18n.I18nQuickFixHandler;
+import com.intellij.codeInspection.i18n.I18nizeAction;
+import com.intellij.codeInspection.i18n.JavaI18nUtil;
+import com.intellij.codeInspection.i18n.JavaI18nizeQuickFixDialog;
+import com.intellij.lang.properties.psi.PropertiesFile;
+import com.intellij.lang.properties.psi.PropertyCreationHandler;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.TextRange;
+import com.intellij.openapi.util.Trinity;
+import com.intellij.psi.ElementManipulators;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiFile;
+import com.intellij.psi.impl.PsiImplUtil;
+import com.intellij.util.IncorrectOperationException;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.annotations.VisibleForTesting;
+import org.jetbrains.plugins.groovy.GroovyLanguage;
+import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes;
+import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock;
+import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression;
+import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.literals.GrStringContent;
+import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.literals.GrStringInjection;
+import org.jetbrains.plugins.groovy.lang.psi.api.statements.params.GrParameterList;
+import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.literals.GrLiteralImpl;
+import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.literals.GrStringImpl;
+import org.jetbrains.uast.UExpression;
+
+import java.util.Collection;
+
+@ApiStatus.Internal
+public final class GrailsI18nGroovyQuickFixHandler implements I18nQuickFixHandler<UExpression> {
+
+  private static final Logger LOG = Logger.getInstance(GrailsI18nGroovyQuickFixHandler.class);
+
+  public static final GrailsI18nGroovyQuickFixHandler INSTANCE = new GrailsI18nGroovyQuickFixHandler();
+
+  @Override
+  public void checkApplicability(PsiFile psiFile, Editor editor) throws IncorrectOperationException {
+    if (calculatePropertyValue(editor, psiFile) == null) {
+      LOG.error("Method checkApplicability() must not be called if file is not applicable, GrailsI18nizeProvider must check applicable " +
+                "before returning GrailsI18nGroovyQuickFixHandler");
+      throw new IncorrectOperationException("Failed to extract i18n property");
+    }
+  }
+
+  private static TextRange rangeInnerString(@NotNull PsiElement element) {
+    return ElementManipulators.getValueTextRange(element).shiftRight(element.getTextOffset());
+  }
+
+  @VisibleForTesting
+  public static @Nullable Trinity<String, String, PsiElement> calculatePropertyValue(Editor editor, PsiFile psiFile) {
+    TextRange selectedRange = JavaI18nUtil.getSelectedRange(editor, psiFile);
+    if (selectedRange == null) return null;
+
+    PsiElement elementAt = psiFile.getViewProvider().findElementAt(selectedRange.getStartOffset());
+    if (elementAt == null || elementAt.getLanguage() != GroovyLanguage.INSTANCE) {
+      return null;
+    }
+
+    elementAt = elementAt.getParent();
+    if (elementAt instanceof GrStringContent) {
+      elementAt = elementAt.getParent();
+    }
+
+    if (elementAt instanceof GrLiteralImpl) {
+      if (!selectedRange.equals(elementAt.getTextRange()) && !selectedRange.equals(rangeInnerString(elementAt))) {
+        return null;
+      }
+
+      Object value = ((GrLiteralImpl)elementAt).getValue();
+      if (!(value instanceof String)) return null;
+
+      return Trinity.create((String)value, "", elementAt);
+    }
+
+    if (elementAt.getClass() == GrStringImpl.class) { // Don't use instanceof, because GrRegexImpl is instance of GrStringImpl.
+      if (editor.getSelectionModel().hasSelection()) {
+        if (!selectedRange.equals(elementAt.getTextRange()) && !selectedRange.equals(rangeInnerString(elementAt))) return null;
+      }
+
+      GrStringImpl grString = (GrStringImpl)elementAt;
+
+      StringBuilder propertyValueText = new StringBuilder();
+      StringBuilder argsText = new StringBuilder();
+      int injectionCount = 0;
+      
+      for (PsiElement e = grString.getFirstChild(); e != null; e = e.getNextSibling()) {
+        if (PsiImplUtil.isLeafElementOfType(e, GroovyTokenTypes.mGSTRING_CONTENT) || e instanceof GrStringContent) {
+          propertyValueText.append(e.getText());
+        }
+        else if (e instanceof GrStringInjection) {
+          propertyValueText.append('{').append(injectionCount++).append('}');
+  
+          if (!argsText.isEmpty()) {
+            argsText.append(", ");
+          }
+
+          PsiElement expression = ((GrStringInjection)e).getExpression();
+          if (expression == null) {
+            GrClosableBlock closableBlock = ((GrStringInjection)e).getClosableBlock();
+            if (closableBlock == null || closableBlock.hasParametersSection()) return null;
+
+            PsiElement rBrace = closableBlock.getRBrace();
+            if (rBrace == null) return null;
+            
+            expression = rBrace.getPrevSibling();
+            if (!(expression instanceof GrExpression) || !(expression.getPrevSibling() instanceof GrParameterList)) return null;
+          }
+
+          argsText.append(expression.getText().trim());
+        }
+      }
+      
+      return Trinity.create(propertyValueText.toString(), argsText.toString(), elementAt);
+    }
+
+    return null;
+  }
+
+  //public static boolean isApplicable(PsiFile psiFile, Editor editor) {
+  //
+  //}
+
+  @Override
+  public void performI18nization(PsiFile psiFile,
+                                 Editor editor,
+                                 UExpression literalExpression,
+                                 Collection<PropertiesFile> propertiesFiles,
+                                 String key,
+                                 String value,
+                                 String i18nizedText,
+                                 UExpression[] parameters,
+                                 PropertyCreationHandler propertyCreationHandler) throws IncorrectOperationException {
+
+    Project project = psiFile.getProject();
+
+    Trinity<String, String, PsiElement> trinity = calculatePropertyValue(editor, psiFile);
+    assert trinity != null;
+    TextRange textRange = trinity.third.getTextRange();
+
+    propertyCreationHandler.createProperty(project, propertiesFiles, key, value, parameters);
+    editor.getDocument().replaceString(textRange.getStartOffset(), textRange.getEndOffset(), i18nizedText);
+  }
+
+  @Override
+  public UExpression getEnclosingLiteral(PsiFile file, Editor editor) {
+    return I18nizeAction.getEnclosingStringLiteral(file, editor);
+  }
+
+  @Override
+  public JavaI18nizeQuickFixDialog<UExpression> createDialog(Project project, Editor editor, PsiFile psiFile) {
+    final Trinity<String, String, PsiElement> trinity = calculatePropertyValue(editor, psiFile);
+    if (trinity == null) return null;
+
+    return new GrailsI18nizeQuickFixDialog(project, psiFile, trinity.first) {
+      @Override
+      protected String getArgs() {
+        return trinity.second;
+      }
+
+      @Override
+      protected @NotNull String getTemplateName() {
+        return !trinity.second.isEmpty() ? "I18nized Groovy string with injections.gsp" : "I18nized Groovy string.gsp";
+      }
+    };
+  }
+}
diff --git a/i18n/src/com/intellij/groovy/grails/i18n/GrailsI18nInspection.java b/i18n/src/com/intellij/groovy/grails/i18n/GrailsI18nInspection.java
new file mode 100644
index 0000000..83a16a5
--- /dev/null
+++ b/i18n/src/com/intellij/groovy/grails/i18n/GrailsI18nInspection.java
@@ -0,0 +1,162 @@
+// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
+
+package com.intellij.groovy.grails.i18n;
+
+import com.intellij.codeInspection.InspectionProfile;
+import com.intellij.codeInspection.LocalQuickFix;
+import com.intellij.codeInspection.ProblemDescriptor;
+import com.intellij.codeInspection.ProblemHighlightType;
+import com.intellij.codeInspection.ProblemsHolder;
+import com.intellij.codeInspection.options.OptPane;
+import com.intellij.lang.properties.PrefixBasedPropertyReference;
+import com.intellij.lang.properties.references.PropertyReference;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Key;
+import com.intellij.profile.codeInspection.InspectionProjectProfileManager;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiReference;
+import com.intellij.psi.xml.XmlAttribute;
+import com.intellij.psi.xml.XmlAttributeValue;
+import com.intellij.util.ArrayUtil;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.plugins.grails.GrailsBundle;
+import org.jetbrains.plugins.grails.inspections.GspAndGroovyInspection;
+import org.jetbrains.plugins.grails.lang.gsp.psi.gsp.api.gtag.GspAttribute;
+import org.jetbrains.plugins.grails.lang.gsp.resolve.taglib.TagLibNamespaceDescriptor;
+import org.jetbrains.plugins.grails.references.common.GroovyGspAttributeWrapper;
+import org.jetbrains.plugins.grails.references.common.GspAttributeWrapper;
+import org.jetbrains.plugins.grails.references.common.XmlGspAttributeWrapper;
+import org.jetbrains.plugins.groovy.lang.psi.GroovyRecursiveElementVisitor;
+import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrNamedArgument;
+import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrCall;
+import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression;
+import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrMethodCall;
+import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression;
+import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.literals.GrLiteral;
+import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil;
+
+import java.text.MessageFormat;
+
+import static com.intellij.codeInspection.options.OptPane.checkbox;
+import static com.intellij.codeInspection.options.OptPane.pane;
+
+@ApiStatus.Internal
+public final class GrailsI18nInspection extends GspAndGroovyInspection {
+
+  private static final String SHORT_NAME = "InvalidI18nProperty";
+  private static final Key<GrailsI18nInspection> SHORT_NAME_KEY = Key.create(SHORT_NAME);
+
+  public boolean ignoreTagsWithDefault;
+
+  @Override
+  public @NotNull String getShortName() {
+    return SHORT_NAME;
+  }
+
+  @Override
+  public @NotNull OptPane getOptionsPane() {
+    return pane(
+      checkbox("ignoreTagsWithDefault", GrailsBundle.message("label.text.ignore.if.default.value.specified")));
+  }
+
+  @Override
+  protected GroovyRecursiveElementVisitor createGroovyFileVisitor(final @NotNull ProblemsHolder holder, final boolean isOnTheFly) {
+    return new GroovyRecursiveElementVisitor() {
+      @Override
+      public void visitNamedArgument(@NotNull GrNamedArgument argument) {
+        GrExpression expression = argument.getExpression();
+        if (expression instanceof GrLiteral) {
+          if (((GrLiteral)expression).isString()) {
+            GrCall call = PsiUtil.getCallByNamedParameter(argument);
+            if (call instanceof GrMethodCall) {
+              GrExpression invokedExpression = ((GrMethodCall)call).getInvokedExpression();
+              if (invokedExpression instanceof GrReferenceExpression) {
+                PsiElement resolve = ((GrReferenceExpression)invokedExpression).resolve();
+                if (resolve instanceof TagLibNamespaceDescriptor.GspTagMethod) {
+                  
+                  GspAttributeWrapper attr = new GroovyGspAttributeWrapper(argument, (TagLibNamespaceDescriptor.GspTagMethod)resolve);
+                  Boolean refType = GrailsI18nPropertyReferenceProvider.getTypeOfReference(attr);
+
+                  if (refType != null && (!refType || !ignoreTagsWithDefault)) {
+                    for (PsiReference reference : expression.getReferences()) {
+                      if (reference instanceof PropertyReference) {
+                        checkReference(holder, refType, isOnTheFly, (PropertyReference)reference);
+                        break;
+                      }
+                    }
+                  }
+                }
+              }
+            }
+          }
+
+          return;
+        }
+
+        super.visitNamedArgument(argument);
+      }
+    };
+  }
+
+  private static void checkReference(ProblemsHolder holder, boolean isSoft, boolean isOnTheFly, @NotNull PropertyReference reference) {
+    if (!(reference instanceof PrefixBasedPropertyReference) ||
+        !((PrefixBasedPropertyReference)reference).isDynamicPrefix()) {
+      
+      if (reference.multiResolve(false).length == 0) {
+        String pattern = reference.getUnresolvedMessagePattern();
+        String message = pattern.contains("{0}") ? MessageFormat.format(pattern, reference.getCanonicalText()) : pattern;
+
+        LocalQuickFix[] quickFixes;
+        if (!isOnTheFly) {
+          quickFixes = LocalQuickFix.EMPTY_ARRAY;
+        }
+        else {
+          quickFixes = reference.getQuickFixes();
+
+          if (isSoft) {
+            quickFixes = ArrayUtil.append(quickFixes, new LocalQuickFix() {
+
+              @Override
+              public @NotNull String getFamilyName() {
+                return GrailsBundle.message("quick.fix.don.t.check.message.code");
+              }
+
+              @Override
+              public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
+                final PsiElement element = descriptor.getPsiElement();
+
+                InspectionProfile profile = InspectionProjectProfileManager.getInstance(project).getCurrentProfile();
+                profile.modifyToolSettings(SHORT_NAME_KEY, element, tool -> tool.ignoreTagsWithDefault = true);
+              }
+            });
+          }
+        }
+
+        holder.registerProblemForReference(reference, ProblemHighlightType.LIKE_UNKNOWN_SYMBOL, message, quickFixes);
+      }
+    }
+  }
+
+  @Override
+  protected GspElementVisitor createGspElementVisitor() {
+    return new GspElementVisitor() {
+      @Override
+      public void visitXmlAttribute(final @NotNull XmlAttribute attribute) {
+        if (!(attribute instanceof GspAttribute)) return;
+
+        Boolean refType = GrailsI18nPropertyReferenceProvider.getTypeOfReference(new XmlGspAttributeWrapper((GspAttribute)attribute));
+
+        if (refType != null && (!refType || !ignoreTagsWithDefault)) {
+          final XmlAttributeValue attributeValue = attribute.getValueElement();
+          if (attributeValue == null) return;
+
+          PropertyReference reference = GrailsI18nPropertyReferenceProvider.getReferenceByElement(attributeValue);
+          if (reference == null) return;
+
+          checkReference(getProblemHolder(), refType, isOnTheFly(), reference);
+        }
+      }
+    };
+  }
+}
diff --git a/i18n/src/com/intellij/groovy/grails/i18n/GrailsI18nPropertyReferenceProvider.java b/i18n/src/com/intellij/groovy/grails/i18n/GrailsI18nPropertyReferenceProvider.java
new file mode 100644
index 0000000..847e5fa
--- /dev/null
+++ b/i18n/src/com/intellij/groovy/grails/i18n/GrailsI18nPropertyReferenceProvider.java
@@ -0,0 +1,111 @@
+// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
+
+package com.intellij.groovy.grails.i18n;
+
+import com.intellij.lang.properties.psi.PropertiesFile;
+import com.intellij.lang.properties.references.PropertyReference;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.patterns.ElementPattern;
+import com.intellij.patterns.InitialPatternCondition;
+import com.intellij.patterns.PsiElementPattern;
+import com.intellij.patterns.XmlAttributeValuePattern;
+import com.intellij.psi.PsiDirectory;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiFile;
+import com.intellij.psi.PsiReference;
+import com.intellij.psi.PsiReferenceProvider;
+import com.intellij.psi.xml.XmlAttributeValue;
+import com.intellij.util.ProcessingContext;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.plugins.grails.lang.gsp.psi.gsp.api.gtag.GspAttribute;
+import org.jetbrains.plugins.grails.references.common.GspAttributeWrapper;
+import org.jetbrains.plugins.grails.references.common.GspTagWrapper;
+import org.jetbrains.plugins.grails.references.common.XmlGspAttributeWrapper;
+import org.jetbrains.plugins.grails.util.GrailsPatterns;
+import org.jetbrains.plugins.grails.util.GrailsPsiUtil;
+import org.jetbrains.plugins.grails.util.GrailsUtils;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+
+final class GrailsI18nPropertyReferenceProvider extends PsiReferenceProvider {
+
+  public static final ElementPattern<GspAttribute> ATTRIBUTE_PATTERN =
+    new PsiElementPattern.Capture<GspAttribute>(new InitialPatternCondition<GspAttribute>(GspAttribute.class) {
+      @Override
+      public boolean accepts(@Nullable Object o, ProcessingContext context) {
+        if (!(o instanceof GspAttribute attr)) return false;
+
+        return getTypeOfReference(new XmlGspAttributeWrapper(attr)) != null;
+      }
+    }) {
+    };
+
+  public static final XmlAttributeValuePattern ATTRIBUTE_VALUE_PATTERN = GrailsPatterns.gspAttributeValue(ATTRIBUTE_PATTERN);
+
+  /**
+   * @return Returns Boolean.TRUE if soft reference, Boolean.FALSE if non-soft reference, null if reference should not be injected.
+   */
+  static @Nullable Boolean getTypeOfReference(GspAttributeWrapper attr) {
+    String attributeName = attr.getName();
+
+    if ("code".equals(attributeName)) {
+      GspTagWrapper tag = attr.getTag();
+      String tagName = tag.getTagName();
+      if ("g:message".equals(tagName)) {
+        return tag.hasAttribute("default");
+      }
+      if ("tooltip:tip".equals(tagName)) {
+        return true;
+      }
+    }
+    else if ("titleKey".equals(attributeName)) {
+      GspTagWrapper tag = attr.getTag();
+      if ("g:sortableColumn".equals(tag.getTagName())) {
+        return tag.hasAttribute("title");
+      }
+    }
+
+    return null;
+  }
+
+  static @Nullable PropertyReference getReferenceByElement(@NotNull XmlAttributeValue value) {
+    if (!GrailsPsiUtil.isSimpleAttribute(value)) return null;
+
+    return new PropertyReference(value.getValue(), value, "messages", true) {
+      @Override
+      protected List<PropertiesFile> retrievePropertyFilesByBundleName(String bundleName, PsiElement element) {
+        VirtualFile i18nFile = GrailsUtils.findI18nDirectory(element);
+        if (i18nFile == null) return super.retrievePropertyFilesByBundleName(bundleName, element);
+
+        PsiDirectory i18nPsiDir = getElement().getManager().findDirectory(i18nFile);
+        if (i18nPsiDir == null) return super.retrievePropertyFilesByBundleName(bundleName, element);
+
+        List<PropertiesFile> res = new ArrayList<>();
+
+        for (PsiFile file : i18nPsiDir.getFiles()) {
+          if (file instanceof PropertiesFile) {
+            res.add((PropertiesFile)file);
+          }
+        }
+
+        if (res.isEmpty()) return super.retrievePropertyFilesByBundleName(bundleName, element);
+
+        res.sort(Comparator.comparing(PropertiesFile::getName));
+
+        return res;
+      }
+    };
+  }
+
+  @Override
+  public PsiReference @NotNull [] getReferencesByElement(@NotNull PsiElement element,
+                                                         @NotNull ProcessingContext context) {
+    PsiReference reference = getReferenceByElement((XmlAttributeValue)element);
+    if (reference == null) return PsiReference.EMPTY_ARRAY;
+
+    return new PsiReference[]{reference};
+  }
+}
diff --git a/i18n/src/com/intellij/groovy/grails/i18n/GrailsI18nQuickFixHandler.java b/i18n/src/com/intellij/groovy/grails/i18n/GrailsI18nQuickFixHandler.java
new file mode 100644
index 0000000..ced3e31
--- /dev/null
+++ b/i18n/src/com/intellij/groovy/grails/i18n/GrailsI18nQuickFixHandler.java
@@ -0,0 +1,249 @@
+// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
+
+package com.intellij.groovy.grails.i18n;
+
+import com.intellij.codeInspection.i18n.I18nQuickFixHandler;
+import com.intellij.codeInspection.i18n.I18nizeAction;
+import com.intellij.codeInspection.i18n.JavaI18nUtil;
+import com.intellij.codeInspection.i18n.JavaI18nizeQuickFixDialog;
+import com.intellij.lang.html.HTMLLanguage;
+import com.intellij.lang.properties.psi.PropertiesFile;
+import com.intellij.lang.properties.psi.PropertyCreationHandler;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Couple;
+import com.intellij.openapi.util.TextRange;
+import com.intellij.psi.FileViewProvider;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiFile;
+import com.intellij.psi.PsiRecursiveElementVisitor;
+import com.intellij.psi.impl.PsiImplUtil;
+import com.intellij.psi.impl.source.tree.LeafPsiElement;
+import com.intellij.psi.templateLanguages.OuterLanguageElement;
+import com.intellij.psi.util.PsiTreeUtil;
+import com.intellij.psi.xml.XmlAttribute;
+import com.intellij.psi.xml.XmlAttributeValue;
+import com.intellij.psi.xml.XmlTag;
+import com.intellij.util.IncorrectOperationException;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.plugins.grails.lang.gsp.GspLanguage;
+import org.jetbrains.plugins.grails.lang.gsp.lexer.core.GspTokenTypes;
+import org.jetbrains.plugins.grails.lang.gsp.psi.groovy.api.GspOuterGroovyElement;
+import org.jetbrains.plugins.grails.lang.gsp.psi.gsp.api.GspExpressionTag;
+import org.jetbrains.uast.UExpression;
+
+import java.util.Collection;
+
+@ApiStatus.Internal
+public final class GrailsI18nQuickFixHandler implements I18nQuickFixHandler<UExpression> {
+
+  public static final GrailsI18nQuickFixHandler INSTANCE = new GrailsI18nQuickFixHandler();
+
+  private GrailsI18nQuickFixHandler() {
+  }
+
+  @Override
+  public void checkApplicability(PsiFile psiFile, Editor editor) throws IncorrectOperationException {
+    final FileViewProvider viewProvider = psiFile.getViewProvider();
+
+    final PsiFile gspHtmlFile = viewProvider.getPsi(HTMLLanguage.INSTANCE);
+    assert gspHtmlFile != null;
+
+    final TextRange selectedRange = JavaI18nUtil.getSelectedRange(editor, psiFile);
+    assert selectedRange != null;
+    // must contain no or balanced tags only
+    // must not contain scriptlets or custom tags
+    gspHtmlFile.accept(new PsiRecursiveElementVisitor() {
+
+      private static void throwError() {
+        throw new IncorrectOperationException("""
+                                                Please select GSP text to I18nize.
+                                                Make sure you have not selected any scriptlets, custom tags or other foreign languages elements.
+                                                Also, HTML tags inside selection must be balanced.""");
+      }
+
+      @Override
+      public void visitElement(@NotNull PsiElement element) {
+        TextRange elementRange = element.getTextRange();
+        if (elementRange.intersectsStrict(selectedRange)) {
+          if (isInapplicableOuterElement(element, elementRange, selectedRange, viewProvider)) {
+            throwError();
+          }
+
+          if (element instanceof XmlTag) {
+            if (!selectedRange.contains(elementRange)) {
+              if (!elementRange.contains(selectedRange)) {
+                throwError();
+              }
+
+              XmlTag xmlTag = (XmlTag)element;
+
+              if (!xmlTag.getValue().getTextRange().contains(selectedRange) && !isAttributeValueSelected(xmlTag, selectedRange)) {
+                throwError();
+              }
+            }
+          }
+        }
+
+        super.visitElement(element);
+      }
+    });
+  }
+
+  private static boolean isAttributeValueSelected(XmlTag xmlTag, TextRange selectedRange) {
+    for (XmlAttribute attribute : xmlTag.getAttributes()) {
+      XmlAttributeValue valueElement = attribute.getValueElement();
+      if (valueElement != null && valueElement.getTextRange().contains(selectedRange)) {
+        if (valueElement.getValueTextRange().contains(selectedRange)) { // getValueTextRange() is a slow method, check by getTextRange().contains(selectedRange) before.
+          return true;
+        }
+      }
+    }
+
+    return false;
+  }
+
+  private static boolean isInapplicableOuterElement(PsiElement element,
+                                                    TextRange elementRange,
+                                                    TextRange selectedRange,
+                                                    FileViewProvider viewProvider) {
+    if (element instanceof OuterLanguageElement) {
+      PsiElement gspElement = viewProvider.findElementAt(element.getTextOffset(), GspLanguage.INSTANCE);
+      if (gspElement == null) return true;
+
+      PsiElement exprTag = gspElement.getParent();
+      if (!(exprTag instanceof GspExpressionTag)) return true;
+
+      if (!exprTag.getTextRange().equals(elementRange)) return false;
+
+      return selectedRange.contains(elementRange);
+    }
+
+    return false;
+  }
+
+  @Override
+  public void performI18nization(PsiFile psiFile,
+                                 Editor editor,
+                                 UExpression literalExpression,
+                                 Collection<PropertiesFile> propertiesFiles,
+                                 String key,
+                                 String value,
+                                 String i18nizedText,
+                                 UExpression[] parameters,
+                                 PropertyCreationHandler propertyCreationHandler) throws IncorrectOperationException {
+    Project project = psiFile.getProject();
+    TextRange selectedText = JavaI18nUtil.getSelectedRange(editor, psiFile);
+    if (selectedText == null) return;
+    propertyCreationHandler.createProperty(project, propertiesFiles, key, value, parameters);
+    editor.getDocument().replaceString(selectedText.getStartOffset(), selectedText.getEndOffset(), i18nizedText);
+  }
+
+  @Override
+  public UExpression getEnclosingLiteral(PsiFile file, Editor editor) {
+    return I18nizeAction.getEnclosingStringLiteral(file, editor);
+  }
+
+  public static @Nullable Couple<String> calculatePropertyValue(Editor editor, PsiFile psiFile) {
+    TextRange selectedRange = JavaI18nUtil.getSelectedRange(editor, psiFile);
+    if (selectedRange == null) return null;
+
+    StringBuilder propertyValueText = new StringBuilder();
+    final StringBuilder argsText = new StringBuilder();
+
+    int injectionCount = 0;
+
+    PsiElement e = psiFile.getViewProvider().findElementAt(selectedRange.getStartOffset(), HTMLLanguage.INSTANCE);
+
+    while (e != null && e.getTextOffset() < selectedRange.getEndOffset()) {
+      if (e instanceof OuterLanguageElement) {
+        PsiElement gspElement = psiFile.getViewProvider().findElementAt(e.getTextOffset(), GspLanguage.INSTANCE);
+        if (gspElement == null) return null;
+
+        PsiElement exprTag = gspElement.getParent();
+        if (!(exprTag instanceof GspExpressionTag)) return null;
+
+        TextRange exprTagTextRange = exprTag.getTextRange();
+        if (!selectedRange.contains(exprTagTextRange)) return null;
+
+        propertyValueText.append('{').append(injectionCount++).append('}');
+
+        if (!argsText.isEmpty()) {
+          argsText.append(", ");
+        }
+
+        PsiElement firstChild = exprTag.getFirstChild();
+        if (!PsiImplUtil.isLeafElementOfType(firstChild, GspTokenTypes.JEXPR_BEGIN) &&
+            !PsiImplUtil.isLeafElementOfType(firstChild, GspTokenTypes.GEXPR_BEGIN)) return null;
+        assert firstChild != null;
+
+        PsiElement expr = firstChild.getNextSibling();
+
+        if (!(expr instanceof GspOuterGroovyElement)) return null;
+
+        PsiElement closedElement = expr.getNextSibling();
+
+        if (closedElement == null || closedElement != exprTag.getLastChild()) return null;
+
+        argsText.append(expr.getText().trim());
+
+        e = psiFile.getViewProvider().findElementAt(exprTagTextRange.getEndOffset(), HTMLLanguage.INSTANCE);
+      }
+      else {
+        CharSequence chars = ((LeafPsiElement)e).getChars();
+        int start = e.getTextOffset();
+
+        propertyValueText.append(chars, Math.max(selectedRange.getStartOffset(), start) - start,
+                                 Math.min(start + chars.length(), selectedRange.getEndOffset()) - start);
+        e = PsiTreeUtil.nextLeaf(e);
+      }
+    }
+
+    return Couple.of(propertyValueText.toString(), argsText.toString());
+  }
+
+  @Override
+  public JavaI18nizeQuickFixDialog<UExpression> createDialog(final Project project, final Editor editor, final PsiFile psiFile) {
+    final Couple<String> pair = calculatePropertyValue(editor, psiFile);
+    if (pair == null) return null;
+
+    TextRange selectedRange = JavaI18nUtil.getSelectedRange(editor, psiFile);
+    assert selectedRange != null;
+    PsiElement e = psiFile.getViewProvider().findElementAt(selectedRange.getStartOffset(), HTMLLanguage.INSTANCE);
+    XmlAttributeValue attributeValue = PsiTreeUtil.getParentOfType(e, XmlAttributeValue.class);
+
+    if (attributeValue != null) {
+      return new GrailsI18nizeQuickFixDialog(project, psiFile, pair.first) {
+        @Override
+        protected String getArgs() {
+          return pair.second;
+        }
+
+        @Override
+        public @NotNull String getI18nizedText() {
+          String nizedText = super.getI18nizedText();
+          return "${" + nizedText + "}";
+        }
+
+        @Override
+        protected @NotNull String getTemplateName() {
+          return !pair.second.isEmpty() ? "I18nized Groovy string with injections.gsp" : "I18nized Groovy string.gsp";
+        }
+      };
+    }
+
+    return new GrailsI18nizeQuickFixDialog(project, psiFile, pair.first) {
+      @Override
+      protected String getArgs() {
+        return pair.second;
+      }
+
+      @Override
+      protected @NotNull String getTemplateName() {
+        return !pair.second.isEmpty() ? "I18nized GSP Expression with injections.gsp" : "I18nized GSP Expression.gsp";
+      }
+    };
+  }
+}
diff --git a/i18n/src/com/intellij/groovy/grails/i18n/GrailsI18nReferenceContributor.java b/i18n/src/com/intellij/groovy/grails/i18n/GrailsI18nReferenceContributor.java
new file mode 100644
index 0000000..46042b2
--- /dev/null
+++ b/i18n/src/com/intellij/groovy/grails/i18n/GrailsI18nReferenceContributor.java
@@ -0,0 +1,16 @@
+// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
+
+package com.intellij.groovy.grails.i18n;
+
+import com.intellij.psi.PsiReferenceContributor;
+import com.intellij.psi.PsiReferenceRegistrar;
+import org.jetbrains.annotations.NotNull;
+
+final class GrailsI18nReferenceContributor extends PsiReferenceContributor {
+
+  @Override
+  public void registerReferenceProviders(@NotNull PsiReferenceRegistrar registrar) {
+    registrar.registerReferenceProvider(GrailsI18nPropertyReferenceProvider.ATTRIBUTE_VALUE_PATTERN,
+                                        new GrailsI18nPropertyReferenceProvider());
+  }
+}
diff --git a/i18n/src/com/intellij/groovy/grails/i18n/GrailsI18nizeProvider.java b/i18n/src/com/intellij/groovy/grails/i18n/GrailsI18nizeProvider.java
new file mode 100644
index 0000000..c0711c7
--- /dev/null
+++ b/i18n/src/com/intellij/groovy/grails/i18n/GrailsI18nizeProvider.java
@@ -0,0 +1,52 @@
+// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
+
+package com.intellij.groovy.grails.i18n;
+
+import com.intellij.codeInspection.i18n.I18nQuickFixHandler;
+import com.intellij.codeInspection.i18n.I18nizeHandlerProvider;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.util.TextRange;
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiFile;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.plugins.grails.lang.gsp.psi.gsp.api.GspFile;
+import org.jetbrains.plugins.grails.util.GrailsArtifact;
+import org.jetbrains.plugins.groovy.GroovyLanguage;
+import org.jetbrains.plugins.groovy.lang.psi.GroovyFile;
+import org.jetbrains.plugins.groovy.lang.psi.GroovyFileBase;
+
+final class GrailsI18nizeProvider extends I18nizeHandlerProvider {
+
+  @Override
+  public I18nQuickFixHandler<?> getHandler(@NotNull PsiFile psiFile, @NotNull Editor editor, @NotNull TextRange range) {
+    if (psiFile instanceof GspFile) {
+      PsiElement elementAt = psiFile.getViewProvider().findElementAt(range.getStartOffset());
+      if (elementAt != null && elementAt.getLanguage() == GroovyLanguage.INSTANCE) {
+        if (GrailsI18nGroovyQuickFixHandler.calculatePropertyValue(editor, psiFile) != null) {
+          return GrailsI18nGroovyQuickFixHandler.INSTANCE;
+        }
+
+        return null;
+      }
+
+      return GrailsI18nQuickFixHandler.INSTANCE;
+    }
+
+    if (psiFile instanceof GroovyFile && isApplicableGroovyFile((GroovyFile)psiFile)) {
+      return GrailsI18nGroovyQuickFixHandler.INSTANCE;
+    }
+
+    return null;
+  }
+
+  public static boolean isApplicableGroovyFile(@NotNull GroovyFileBase groovyFile) {
+    PsiClass[] classes = groovyFile.getClasses();
+    if (classes.length == 1) {
+      PsiClass aClass = classes[0];
+      return GrailsArtifact.CONTROLLER.isInstance(aClass) || GrailsArtifact.TAGLIB.isInstance(aClass);
+    }
+
+    return false;
+  }
+}
diff --git a/i18n/src/com/intellij/groovy/grails/i18n/GrailsI18nizeQuickFixDialog.java b/i18n/src/com/intellij/groovy/grails/i18n/GrailsI18nizeQuickFixDialog.java
new file mode 100644
index 0000000..0268a09
--- /dev/null
+++ b/i18n/src/com/intellij/groovy/grails/i18n/GrailsI18nizeQuickFixDialog.java
@@ -0,0 +1,62 @@
+// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
+
+package com.intellij.groovy.grails.i18n;
+
+import com.intellij.codeInspection.i18n.JavaI18nizeQuickFixDialog;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.psi.PsiFile;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.annotations.TestOnly;
+import org.jetbrains.plugins.grails.util.GrailsUtils;
+import org.jetbrains.uast.UExpression;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+abstract class GrailsI18nizeQuickFixDialog extends JavaI18nizeQuickFixDialog<UExpression> {
+
+  GrailsI18nizeQuickFixDialog(@NotNull Project project,
+                              final @NotNull PsiFile context,
+                              @NotNull String defaultPropertyValue) {
+    super(project, context, null, defaultPropertyValue, null, false, true);
+  }
+
+  protected abstract @Nullable String getArgs();
+
+  @Override
+  protected void addAdditionalAttributes(Map<String, String> attributes) {
+    attributes.put("ARGS_KEY", getArgs());
+    attributes.put(PROPERTY_VALUE_ATTR, myDefaultPropertyValue);
+  }
+
+  @TestOnly
+  public String getDefaultPropertyValue() {
+    return myDefaultPropertyValue;
+  }
+
+  @Override
+  protected List<String> defaultSuggestPropertiesFiles() {
+    VirtualFile i18nFile = GrailsUtils.findI18nDirectory(myContext);
+    if (i18nFile == null) return super.defaultSuggestPropertiesFiles();
+
+    List<String> res = new ArrayList<>();
+
+    for (VirtualFile virtualFile : i18nFile.getChildren()) {
+      if (virtualFile.getName().endsWith(".properties")) {
+        res.add(virtualFile.getPath());
+      }
+    }
+
+    Collections.sort(res);
+
+    return res;
+  }
+
+  @Override
+  protected abstract @NotNull String getTemplateName();
+
+}
diff --git a/i18n/src/com/intellij/groovy/grails/i18n/GspI18nIntention.java b/i18n/src/com/intellij/groovy/grails/i18n/GspI18nIntention.java
new file mode 100644
index 0000000..2d5d3ab
--- /dev/null
+++ b/i18n/src/com/intellij/groovy/grails/i18n/GspI18nIntention.java
@@ -0,0 +1,116 @@
+// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
+
+package com.intellij.groovy.grails.i18n;
+
+import com.intellij.codeInsight.intention.IntentionAction;
+import com.intellij.codeInspection.i18n.I18nizeAction;
+import com.intellij.codeInspection.i18n.JavaI18nUtil;
+import com.intellij.lang.html.HTMLLanguage;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.editor.SelectionModel;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.TextRange;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiFile;
+import com.intellij.psi.templateLanguages.OuterLanguageElement;
+import com.intellij.psi.util.PsiTreeUtil;
+import com.intellij.util.IncorrectOperationException;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.plugins.grails.GrailsBundle;
+import org.jetbrains.plugins.grails.lang.gsp.GspFileViewProvider;
+import org.jetbrains.plugins.grails.lang.gsp.GspLanguage;
+import org.jetbrains.plugins.grails.lang.gsp.psi.gsp.api.GspExpressionTag;
+import org.jetbrains.plugins.grails.lang.gsp.psi.gsp.api.GspFile;
+import org.jetbrains.plugins.grails.lang.gsp.psi.html.impl.GspHtmlFileImpl;
+import org.jetbrains.plugins.groovy.lang.psi.GroovyFileBase;
+
+final class GspI18nIntention implements IntentionAction {
+
+  @Override
+  public @NotNull String getText() {
+    return GrailsBundle.message("intention.text.extract.selected.text.to.message.properties");
+  }
+
+  @Override
+  public @NotNull String getFamilyName() {
+    return getText();
+  }
+
+  private static boolean isGroovyStringLiteral(Editor editor, PsiFile file) {
+    if (file instanceof GspFile) {
+      file = ((GspFile)file).getGroovyLanguageRoot();
+    }
+
+    if (file instanceof GroovyFileBase) {
+      if (file.getViewProvider() instanceof GspFileViewProvider || GrailsI18nizeProvider.isApplicableGroovyFile((GroovyFileBase)file)) {
+        if (GrailsI18nGroovyQuickFixHandler.calculatePropertyValue(editor, file) != null) {
+          return true;
+        }
+      }
+    }
+
+    return false;
+  }
+
+  @Override
+  public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile psiFile) {
+    if (isGroovyStringLiteral(editor, psiFile)) {
+      return true;
+    }
+
+    if (!(psiFile instanceof GspFile || psiFile instanceof GspHtmlFileImpl)) return false;
+
+    SelectionModel selectionModel = editor.getSelectionModel();
+    if (!selectionModel.hasSelection()) return false;
+
+    final TextRange selectedRange = JavaI18nUtil.getSelectedRange(editor, psiFile);
+    if (selectedRange == null) return false;
+
+    PsiElement e = psiFile.getViewProvider().findElementAt(selectedRange.getStartOffset(), HTMLLanguage.INSTANCE);
+    do {
+      if (e == null) return false;
+
+      if (e instanceof OuterLanguageElement) {
+        PsiElement gspElement = psiFile.getViewProvider().findElementAt(e.getTextOffset(), GspLanguage.INSTANCE);
+        if (gspElement == null) return false;
+
+        PsiElement exprTag = gspElement.getParent();
+        if (!(exprTag instanceof GspExpressionTag)) return false;
+
+        TextRange exprTagTextRange = exprTag.getTextRange();
+
+        if (!selectedRange.contains(exprTagTextRange)) return false;
+        if (selectedRange.getEndOffset() == exprTagTextRange.getEndOffset()) break;
+
+        e = psiFile.getViewProvider().findElementAt(exprTagTextRange.getEndOffset(), HTMLLanguage.INSTANCE);
+      }
+      else {
+        if (e.getTextOffset() + e.getTextLength() >= selectedRange.getEndOffset()) break;
+        e = PsiTreeUtil.nextLeaf(e);
+      }
+    } while (true);
+
+    return true;
+  }
+
+  @Override
+  public void invoke(@NotNull Project project, Editor editor, PsiFile psiFile) throws IncorrectOperationException {
+    if (isGroovyStringLiteral(editor, psiFile)) {
+      I18nizeAction.doI18nSelectedString(project, editor, psiFile, GrailsI18nGroovyQuickFixHandler.INSTANCE);
+      return;
+    }
+
+    if (psiFile instanceof GspFile) {
+      psiFile = psiFile.getViewProvider().getPsi(HTMLLanguage.INSTANCE);
+    }
+    assert psiFile instanceof GspHtmlFileImpl;
+    PsiFile gspFile = psiFile.getViewProvider().getPsi(GspLanguage.INSTANCE);
+    assert gspFile != null;
+    I18nizeAction.doI18nSelectedString(project, editor, gspFile, GrailsI18nQuickFixHandler.INSTANCE);
+  }
+
+  @Override
+  public boolean startInWriteAction() {
+    return true;
+  }
+}
diff --git a/jps-plugin/build.gradle.kts b/jps-plugin/build.gradle.kts
new file mode 100644
index 0000000..72b4525
--- /dev/null
+++ b/jps-plugin/build.gradle.kts
@@ -0,0 +1,42 @@
+// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
+fun properties(key: String) = project.findProperty(key).toString()
+
+plugins {
+    id("java")
+    id("org.jetbrains.intellij.platform.module")
+}
+
+group = "org.intellij.grails.jps"
+
+repositories {
+    mavenCentral()
+    intellijPlatform {
+        defaultRepositories()
+    }
+}
+
+dependencies {
+    intellijPlatform {
+        intellijIdea(properties("platformVersion"))
+        bundledPlugin("org.intellij.groovy")
+    }
+
+    compileOnly(project(":grails-compiler-patch"))
+}
+
+java.sourceSets["main"].java {
+    srcDir("src")
+}
+
+java.sourceSets["main"].resources {
+    srcDir("resources")
+}
+
+tasks {
+    // Set the JVM compatibility versions
+    withType<JavaCompile> {
+        sourceCompatibility = "11"
+        targetCompatibility = "11"
+    }
+
+}
diff --git a/jps-plugin/resources/META-INF/services/org.jetbrains.jps.incremental.groovy.GroovyBuilderExtension b/jps-plugin/resources/META-INF/services/org.jetbrains.jps.incremental.groovy.GroovyBuilderExtension
new file mode 100644
index 0000000..65d671f
--- /dev/null
+++ b/jps-plugin/resources/META-INF/services/org.jetbrains.jps.incremental.groovy.GroovyBuilderExtension
@@ -0,0 +1 @@
+org.jetbrains.jps.grails.builder.GrailsBuilderExtension
\ No newline at end of file
diff --git a/jps-plugin/src/org/jetbrains/jps/grails/builder/GrailsBuilderExtension.java b/jps-plugin/src/org/jetbrains/jps/grails/builder/GrailsBuilderExtension.java
new file mode 100644
index 0000000..4a9cfd9
--- /dev/null
+++ b/jps-plugin/src/org/jetbrains/jps/grails/builder/GrailsBuilderExtension.java
@@ -0,0 +1,98 @@
+// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
+
+package org.jetbrains.jps.grails.builder;
+
+import com.intellij.openapi.application.PathManager;
+import com.intellij.util.text.VersionComparatorUtil;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.groovy.grails.compiler.GrailsCompilerRtMarker;
+import org.jetbrains.jps.ModuleChunk;
+import org.jetbrains.jps.incremental.CompileContext;
+import org.jetbrains.jps.incremental.groovy.GroovyBuilderExtension;
+import org.jetbrains.jps.model.library.JpsLibrary;
+import org.jetbrains.jps.model.library.JpsOrderRootType;
+import org.jetbrains.jps.model.module.JpsDependencyElement;
+import org.jetbrains.jps.model.module.JpsLibraryDependency;
+import org.jetbrains.jps.model.module.JpsModule;
+
+import java.io.File;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public final class GrailsBuilderExtension implements GroovyBuilderExtension {
+  public static final Pattern CORE_JAR_PATTERN = Pattern.compile("grails-core-(\\d[^-]*(?:-SNAPSHOT)?)\\.jar");
+
+  @Override
+  public @NotNull Collection<String> getCompilationClassPath(@NotNull CompileContext context, @NotNull ModuleChunk chunk) {
+    for (JpsModule module : chunk.getModules()) {
+      if (shouldInjectGrails(module)) {
+        return Collections.singleton(PathManager.getJarPathForClass(GrailsCompilerRtMarker.class));
+      }
+    }
+
+    return Collections.emptyList();
+  }
+
+  private static boolean shouldInjectGrails(@NotNull JpsModule module) {
+    final String version = getGrailsVersion(module);
+    return shouldInjectGrails(version);
+  }
+
+  @Contract("null -> false")
+  private static boolean shouldInjectGrails(@Nullable String version) {
+    return version != null && VersionComparatorUtil.compare(version, "3.0") < 0;
+  }
+
+  private static @Nullable String getGrailsVersion(@NotNull JpsModule module) {
+    String foundVersion = null;
+
+    for (JpsDependencyElement dependencyElement : module.getDependenciesList().getDependencies()) {
+      if (!(dependencyElement instanceof JpsLibraryDependency)) continue;
+      final JpsLibrary library = ((JpsLibraryDependency)dependencyElement).getLibrary();
+      if (library == null) continue;
+
+      for (File file : library.getFiles(JpsOrderRootType.COMPILED)) {
+        final Matcher matcher = CORE_JAR_PATTERN.matcher(file.getName());
+        if (!matcher.matches()) continue;
+        if (foundVersion == null) {
+          foundVersion = matcher.group(1);
+        }
+        else if (VersionComparatorUtil.compare(foundVersion, matcher.group(1)) != 0) {
+          // if another version is present in classpath
+          // return null to avoid compilation issues
+          return null;
+        }
+      }
+    }
+
+    return foundVersion;
+  }
+
+  @Override
+  public @NotNull Collection<String> getCompilationUnitPatchers(@NotNull CompileContext context, @NotNull ModuleChunk chunk) {
+    Set<String> res = new HashSet<>();
+
+    for (JpsModule module : chunk.getModules()) {
+      String grailsVersion = getGrailsVersion(module);
+      if (!shouldInjectGrails(grailsVersion)) continue;
+
+      res.add("org.jetbrains.groovy.grails.compiler.GrailsJUnitPatcher");
+
+      if (grailsVersion.compareTo("1.2") < 0) {
+        res.add("org.jetbrains.groovy.grails.compiler.GrailsDomainClassPatcher");
+      }
+      else if (grailsVersion.compareTo("2.0") >= 0) {
+        res.add("org.jetbrains.groovy.grails.compiler.EmptyGrailsAwarePatcher");
+        res.add("org.jetbrains.groovy.grails.compiler.Grails2_0_JUnitPatcher");
+      }
+    }
+
+    return res;
+  }
+}
diff --git a/langInjection/build.gradle.kts b/langInjection/build.gradle.kts
new file mode 100644
index 0000000..ac3a7e4
--- /dev/null
+++ b/langInjection/build.gradle.kts
@@ -0,0 +1,35 @@
+// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
+fun properties(key: String) = project.findProperty(key).toString()
+
+plugins {
+    id("java")
+    id("org.jetbrains.intellij.platform.module")
+}
+
+group = "org.intellij.grails.langInjection"
+
+repositories {
+    mavenCentral()
+    intellijPlatform {
+        defaultRepositories()
+    }
+}
+
+dependencies {
+    intellijPlatform {
+        intellijIdea(properties("platformVersion"))
+        bundledModule("intellij.platform.langInjection")
+    }
+}
+
+java.sourceSets["main"].resources {
+    srcDir("resources")
+}
+
+tasks {
+        withType<JavaCompile> {
+        sourceCompatibility = properties("platformJavaVersion")
+        targetCompatibility = properties("platformJavaVersion")
+    }
+
+}
diff --git a/langInjection/resources/META-INF/languageInjections.xml b/langInjection/resources/META-INF/languageInjections.xml
new file mode 100644
index 0000000..eeee747
--- /dev/null
+++ b/langInjection/resources/META-INF/languageInjections.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<component name="LanguageInjectionConfiguration">
+  <injection language="HQL" injector-id="groovy">
+    <display-name>Grails HQL injection</display-name>
+    <place><![CDATA[groovyElement().methodCallParameter(0, grLightMethod("Gorm:DomainDescriptor:DynamicMethod").withName(string().oneOf("find", "findAll", "executeQuery", "executeUpdate")))]]></place>
+  </injection>
+</component>
diff --git a/langInjection/resources/intellij.groovy.grails.langInjection.xml b/langInjection/resources/intellij.groovy.grails.langInjection.xml
new file mode 100644
index 0000000..465dc6e
--- /dev/null
+++ b/langInjection/resources/intellij.groovy.grails.langInjection.xml
@@ -0,0 +1,11 @@
+<!-- Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.-->
+<idea-plugin>
+
+  <dependencies>
+    <module name="intellij.platform.langInjection"/>
+  </dependencies>
+
+  <extensions defaultExtensionNs="org.intellij.intelliLang">
+    <injectionConfig config="META-INF/languageInjections.xml"/>
+  </extensions>
+</idea-plugin>
\ No newline at end of file
diff --git a/maven/build.gradle.kts b/maven/build.gradle.kts
new file mode 100644
index 0000000..50b4fa9
--- /dev/null
+++ b/maven/build.gradle.kts
@@ -0,0 +1,47 @@
+// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
+fun properties(key: String) = project.findProperty(key).toString()
+
+plugins {
+    id("java")
+    id("org.jetbrains.kotlin.jvm") version "2.3.0"
+    id("org.jetbrains.intellij.platform.module")
+}
+
+group = "org.intellij.grails.maven"
+
+kotlin {
+    jvmToolchain(21)
+}
+
+repositories {
+    mavenCentral()
+    intellijPlatform {
+        defaultRepositories()
+    }
+}
+
+dependencies {
+    intellijPlatform {
+        intellijIdea(properties("platformVersion"))
+        bundledPlugin("org.jetbrains.idea.maven")
+    }
+
+    compileOnly(project(":grails-rt"))
+    compileOnly(project(":"))
+}
+
+java.sourceSets["main"].java {
+    srcDir("src")
+}
+
+java.sourceSets["main"].resources {
+    srcDir("resources")
+}
+
+tasks {
+        withType<JavaCompile> {
+        sourceCompatibility = properties("platformJavaVersion")
+        targetCompatibility = properties("platformJavaVersion")
+    }
+
+}
diff --git a/maven/resources/intellij.groovy.grails.maven.xml b/maven/resources/intellij.groovy.grails.maven.xml
new file mode 100644
index 0000000..3270325
--- /dev/null
+++ b/maven/resources/intellij.groovy.grails.maven.xml
@@ -0,0 +1,18 @@
+<!-- Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.-->
+<idea-plugin>
+
+  <dependencies>
+    <plugin id="org.jetbrains.idea.maven"/>
+  </dependencies>
+
+  <extensions defaultExtensionNs="org">
+    <intellij.grails.commandExecutor implementation="com.intellij.groovy.grails.maven.MavenCommandExecutor" order="FIRST"/>
+    <jetbrains.idea.maven.importer implementation="com.intellij.groovy.grails.maven.GrailsMavenImporter" order="after web"/>
+    <intellij.grails.applicationProvider implementation="com.intellij.groovy.grails.maven.GrailsMavenApplicationProvider" order="first"/>
+    <intellij.grails.viewNodeProvider implementation="com.intellij.groovy.grails.maven.MavenNodeProvider"/>
+  </extensions>
+
+  <extensions defaultExtensionNs="com.intellij">
+    <programRunner implementation="com.intellij.groovy.grails.maven.GrailsMavenDebuggerRunner" order="before grailsDebuggerRunner"/>
+  </extensions>
+</idea-plugin>
diff --git a/maven/src/com/intellij/groovy/grails/maven/GrailsMavenApplication.java b/maven/src/com/intellij/groovy/grails/maven/GrailsMavenApplication.java
new file mode 100644
index 0000000..a79fda5
--- /dev/null
+++ b/maven/src/com/intellij/groovy/grails/maven/GrailsMavenApplication.java
@@ -0,0 +1,47 @@
+// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
+
+package com.intellij.groovy.grails.maven;
+
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.idea.maven.model.MavenId;
+import org.jetbrains.plugins.grails.structure.impl.OldGrailsModuleBasedApplication;
+
+final class GrailsMavenApplication extends OldGrailsModuleBasedApplication {
+
+  private final @NotNull MavenId myMavenId;
+
+  GrailsMavenApplication(@NotNull Module module, @NotNull VirtualFile root, @NotNull MavenId id) {
+    super(module, root);
+    myMavenId = id;
+  }
+
+  @Override
+  public @NotNull String getName() {
+    return StringUtil.notNullize(myMavenId.getArtifactId());
+  }
+
+  @Override
+  public @Nullable String getAppVersion() {
+    return myMavenId.getVersion();
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+    if (!super.equals(o)) return false;
+
+    GrailsMavenApplication that = (GrailsMavenApplication)o;
+
+    return myMavenId.equals(that.myMavenId);
+  }
+
+  @Override
+  public int hashCode() {
+    return 31 * super.hashCode() + myMavenId.hashCode();
+  }
+}
diff --git a/maven/src/com/intellij/groovy/grails/maven/GrailsMavenApplicationProvider.java b/maven/src/com/intellij/groovy/grails/maven/GrailsMavenApplicationProvider.java
new file mode 100644
index 0000000..7ea5225
--- /dev/null
+++ b/maven/src/com/intellij/groovy/grails/maven/GrailsMavenApplicationProvider.java
@@ -0,0 +1,30 @@
+// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
+
+package com.intellij.groovy.grails.maven;
+
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.ProjectRootManager;
+import com.intellij.openapi.vfs.VirtualFile;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.idea.maven.project.MavenProject;
+import org.jetbrains.idea.maven.project.MavenProjectsManager;
+import org.jetbrains.plugins.grails.structure.GrailsApplication;
+import org.jetbrains.plugins.grails.structure.GrailsApplicationProvider;
+
+final class GrailsMavenApplicationProvider extends GrailsApplicationProvider {
+
+  @Override
+  public @Nullable GrailsApplication createApplication(@NotNull Project project, @NotNull VirtualFile root) {
+    final Module module = ProjectRootManager.getInstance(project).getFileIndex().getModuleForFile(root);
+    if (module == null) return null;
+    final MavenProject mavenProject = MavenProjectsManager.getInstance(project).findProject(module);
+    if (mavenProject == null
+        || !mavenProject.getDirectoryFile().equals(root)
+        || mavenProject.findPlugin("org.grails", "grails-maven-plugin") == null) {
+      return null;
+    }
+    return new GrailsMavenApplication(module, root, mavenProject.getMavenId());
+  }
+}
diff --git a/maven/src/com/intellij/groovy/grails/maven/GrailsMavenDebuggerRunner.java b/maven/src/com/intellij/groovy/grails/maven/GrailsMavenDebuggerRunner.java
new file mode 100644
index 0000000..72d23a2
--- /dev/null
+++ b/maven/src/com/intellij/groovy/grails/maven/GrailsMavenDebuggerRunner.java
@@ -0,0 +1,201 @@
+// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
+
+package com.intellij.groovy.grails.maven;
+
+import com.intellij.debugger.engine.DebuggerUtils;
+import com.intellij.debugger.impl.GenericDebuggerRunner;
+import com.intellij.debugger.impl.RemoteConnectionBuilder;
+import com.intellij.debugger.settings.DebuggerSettings;
+import com.intellij.execution.ExecutionException;
+import com.intellij.execution.configurations.JavaCommandLine;
+import com.intellij.execution.configurations.JavaParameters;
+import com.intellij.execution.configurations.RemoteConnection;
+import com.intellij.execution.configurations.RunProfile;
+import com.intellij.execution.configurations.RunProfileState;
+import com.intellij.execution.executors.DefaultDebugExecutor;
+import com.intellij.execution.process.ProcessHandler;
+import com.intellij.execution.runners.ExecutionEnvironment;
+import com.intellij.execution.runners.JavaProgramPatcher;
+import com.intellij.execution.ui.RunContentDescriptor;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.groovy.grails.rt.Agent;
+import org.jetbrains.plugins.grails.GrailsBundle;
+import org.jetbrains.plugins.grails.runner.GrailsRunConfiguration;
+import org.jetbrains.plugins.grails.runner.impl.GrailsCommandLineState;
+import org.jetbrains.plugins.grails.util.GrailsUtils;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.ServerSocket;
+import java.net.Socket;
+
+import static org.jetbrains.plugins.grails.runner.util.GrailsExecutionUtils.addAgentJar;
+
+final class GrailsMavenDebuggerRunner extends GenericDebuggerRunner {
+  @Override
+  public boolean canRun(@NotNull String executorId, @NotNull RunProfile profile) {
+    if (profile instanceof GrailsRunConfiguration && DefaultDebugExecutor.EXECUTOR_ID.equals(executorId)) {
+      return ((GrailsRunConfiguration)profile).getGrailsApplicationNullable() instanceof GrailsMavenApplication;
+    }
+    return false;
+  }
+
+  @Override
+  public @NotNull String getRunnerId() {
+    return getClass().getSimpleName();
+  }
+
+  @Override
+  protected RunContentDescriptor createContentDescriptor(@NotNull RunProfileState state,
+                                                         @NotNull ExecutionEnvironment environment) throws ExecutionException {
+    if (!(state instanceof GrailsCommandLineState) || !(((GrailsCommandLineState)state).getExecutor() instanceof MavenCommandExecutor)) {
+      return super.createContentDescriptor(state, environment);
+    }
+
+    File tmpFile;
+    try {
+      tmpFile = File.createTempFile("grailsStartFlag", "");
+    }
+    catch (IOException e) {
+      throw new ExecutionException(GrailsBundle.message("dialog.message.failed.to.create.temp.file"), e);
+    }
+
+    tmpFile.deleteOnExit();
+
+    JavaParameters parameters = ((JavaCommandLine)state).getJavaParameters();
+
+    addAgentJar(parameters);
+
+    // It's needed for maven project, see AbstractGrailsMojo
+    GrailsUtils.addSystemPropertyIfNotExists(parameters.getVMParametersList(), "-DforkDebug=true");
+
+    JavaProgramPatcher.runCustomPatchers(parameters, environment.getExecutor(), environment.getRunProfile());
+    RemoteConnection connection = new RemoteConnectionBuilder(false, DebuggerSettings.SOCKET_TRANSPORT, "")
+      .asyncAgent(true)
+      .project(environment.getProject())
+      .create(parameters);
+
+    parameters.getVMParametersList().addProperty(Agent.DEBUG_KIND_FILE, tmpFile.getAbsolutePath());
+
+    RemoteConnection c2 = new RemoteConnection(true, "127.0.0.1", DebuggerUtils.getInstance().findAvailableDebugAddress(true), false);
+
+    ServerSocket serverSocket;
+
+    try {
+      serverSocket = new ServerSocket(Integer.parseInt(c2.getDebuggerAddress()));
+    }
+    catch (IOException e) {
+      throw new ExecutionException(GrailsBundle.message("dialog.message.failed.to.open.server.socket"), e);
+    }
+
+    try {
+      RunContentDescriptor descriptor = attachVirtualMachine(state, environment, c2, true);
+      try {
+        SocketThread socketThread = new SocketThread(serverSocket.accept(),
+                                                     tmpFile,
+                                                     descriptor.getProcessHandler(),
+                                                     Integer.parseInt(connection.getDebuggerAddress()));
+        Thread t = new Thread(socketThread, "grails socket thread");
+        t.setDaemon(true);
+        t.start();
+      }
+      catch (IOException e) {
+        throw new ExecutionException(GrailsBundle.message("dialog.message.failed.to.open.server.socket"), e);
+      }
+
+      return descriptor;
+    }
+    finally {
+      try {
+        serverSocket.close();
+      }
+      catch (IOException ignored) {
+
+      }
+    }
+  }
+
+  private static final class SocketThread implements Runnable {
+
+    private final Socket mySocket;
+    private final File myFile;
+    private final ProcessHandler myProcessHandler;
+    private final int myMavenProcessPort;
+
+    private SocketThread(Socket socket, File file, ProcessHandler processHandler, int mavenProcessPort) {
+      mySocket = socket;
+      myFile = file;
+      myProcessHandler = processHandler;
+      myMavenProcessPort = mavenProcessPort;
+    }
+
+    @Override
+    public void run() {
+      try {
+        int debugPort;
+
+        while (true) {
+          if (myProcessHandler.isProcessTerminated()) {
+            return;
+          }
+
+          long length = myFile.length();
+          if (length != 0) {
+            myFile.delete();
+
+            if (length == 1) {
+              // Application will execute in started maven process
+              debugPort = myMavenProcessPort;
+            }
+            else {
+              // Application will execute in child process
+              debugPort = 5005; // port 5005 is hardcoded in
+              Thread.sleep(1000); // Wait for child process start.
+            }
+
+            break;
+          }
+
+          Thread.sleep(300);
+        }
+
+        try (Socket debuggedProcessSocket = new Socket("127.0.0.1", debugPort)) {
+          Thread t = new Thread(() -> {
+            try {
+              copyStream(mySocket.getInputStream(), debuggedProcessSocket.getOutputStream());
+            }
+            catch (IOException ignored) {
+
+            }
+          }, "copy stream");
+          t.setDaemon(true);
+          t.start();
+
+          copyStream(debuggedProcessSocket.getInputStream(), mySocket.getOutputStream());
+        }
+      }
+      catch (IOException | InterruptedException ignored) {
+
+      }
+      finally {
+        try {
+          mySocket.close();
+        }
+        catch (IOException ignored) {
+
+        }
+      }
+    }
+
+    private static void copyStream(InputStream in, OutputStream out) throws IOException {
+      byte[] buffer = new byte[4 * 1024];
+
+      int length;
+      while ((length = in.read(buffer)) != -1) {
+        out.write(buffer, 0, length);
+      }
+    }
+  }
+}
diff --git a/maven/src/com/intellij/groovy/grails/maven/GrailsMavenImporter.java b/maven/src/com/intellij/groovy/grails/maven/GrailsMavenImporter.java
new file mode 100644
index 0000000..6c0d78a
--- /dev/null
+++ b/maven/src/com/intellij/groovy/grails/maven/GrailsMavenImporter.java
@@ -0,0 +1,67 @@
+// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
+
+package com.intellij.groovy.grails.maven;
+
+import com.intellij.facet.ModifiableFacetModel;
+import com.intellij.openapi.externalSystem.service.project.IdeModifiableModelsProvider;
+import com.intellij.openapi.module.Module;
+import com.intellij.util.Consumer;
+import com.intellij.util.PairConsumer;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.idea.maven.importing.MavenImporter;
+import org.jetbrains.idea.maven.importing.MavenRootModelAdapter;
+import org.jetbrains.idea.maven.project.MavenProject;
+import org.jetbrains.idea.maven.project.MavenProjectChanges;
+import org.jetbrains.idea.maven.project.MavenProjectsProcessorTask;
+import org.jetbrains.idea.maven.project.MavenProjectsTree;
+import org.jetbrains.jps.model.module.JpsModuleSourceRootType;
+import org.jetbrains.plugins.grails.config.GrailsFramework;
+import org.jetbrains.plugins.grails.util.GrailsFacetProvider;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+final class GrailsMavenImporter extends MavenImporter {
+  private static final Set<String> IMPORTED_ARTIFACT_GROUPS = Set.of("javax.servlet", "org.grails");
+
+  GrailsMavenImporter() {
+    super("org.grails", "grails-maven-plugin");
+  }
+
+  @Override
+  public void process(@NotNull IdeModifiableModelsProvider modifiableModelsProvider,
+                      @NotNull Module module,
+                      @NotNull MavenRootModelAdapter rootModel,
+                      @NotNull MavenProjectsTree mavenModel,
+                      @NotNull MavenProject mavenProject,
+                      @NotNull MavenProjectChanges changes,
+                      @NotNull Map<MavenProject, String> mavenProjectToModuleName,
+                      @NotNull List<MavenProjectsProcessorTask> postTasks) {
+
+    final List<Consumer<ModifiableFacetModel>> actions = new ArrayList<>();
+    for (GrailsFacetProvider provider : GrailsFacetProvider.EP_NAME.getExtensions()) {
+      provider.addFacets(actions, module, Collections.singletonList(mavenProject.getDirectoryFile()));
+    }
+
+    final ModifiableFacetModel model = modifiableModelsProvider.getModifiableFacetModel(module);
+    for (Consumer<ModifiableFacetModel> action : actions) {
+      action.consume(model);
+    }
+  }
+
+  @Override
+  public void collectSourceRoots(MavenProject mavenProject, PairConsumer<String, JpsModuleSourceRootType<?>> result) {
+    for (Map.Entry<JpsModuleSourceRootType<?>, Collection<String>> entry : GrailsFramework.GRAILS_SOURCE_FOLDERS.entrySet()) {
+      JpsModuleSourceRootType<?> type = entry.getKey();
+
+      for (String path : entry.getValue()) {
+        result.consume(path, type);
+      }
+    }
+  }
+
+}
diff --git a/maven/src/com/intellij/groovy/grails/maven/MavenCommandExecutor.java b/maven/src/com/intellij/groovy/grails/maven/MavenCommandExecutor.java
new file mode 100644
index 0000000..f720f04
--- /dev/null
+++ b/maven/src/com/intellij/groovy/grails/maven/MavenCommandExecutor.java
@@ -0,0 +1,217 @@
+// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
+
+package com.intellij.groovy.grails.maven;
+
+import com.intellij.execution.ExecutionException;
+import com.intellij.execution.configurations.ConfigurationFactory;
+import com.intellij.execution.configurations.JavaParameters;
+import com.intellij.execution.configurations.ParametersList;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.options.ConfigurationException;
+import com.intellij.openapi.options.SettingsEditor;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Disposer;
+import com.intellij.openapi.util.Key;
+import com.intellij.openapi.util.NlsContexts.TabTitle;
+import com.intellij.openapi.util.Pair;
+import com.intellij.util.PathUtil;
+import com.intellij.util.containers.ContainerUtil;
+import com.intellij.util.xmlb.XmlSerializer;
+import org.jdom.Element;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.idea.maven.execution.MavenExternalParameters;
+import org.jetbrains.idea.maven.execution.MavenRunConfiguration;
+import org.jetbrains.idea.maven.execution.MavenRunConfigurationType;
+import org.jetbrains.idea.maven.execution.MavenRunner;
+import org.jetbrains.idea.maven.execution.MavenRunnerParameters;
+import org.jetbrains.idea.maven.execution.MavenRunnerSettings;
+import org.jetbrains.idea.maven.importing.MavenPomPathModuleService;
+import org.jetbrains.idea.maven.model.MavenExplicitProfiles;
+import org.jetbrains.idea.maven.project.MavenGeneralSettings;
+import org.jetbrains.idea.maven.project.MavenGeneralSettingsEditor;
+import org.jetbrains.idea.maven.project.MavenProjectsManager;
+import org.jetbrains.plugins.grails.GrailsBundle;
+import org.jetbrains.plugins.grails.runner.GrailsCommandLineExecutor;
+import org.jetbrains.plugins.grails.runner.GrailsRunConfiguration;
+import org.jetbrains.plugins.grails.runner.GrailsRunConfigurationExtension;
+import org.jetbrains.plugins.grails.runner.util.GrailsExecutionUtils;
+import org.jetbrains.plugins.grails.structure.GrailsApplication;
+import org.jetbrains.plugins.grails.util.GrailsUtils;
+import org.jetbrains.plugins.grails.util.version.Version;
+import org.jetbrains.plugins.groovy.mvc.MvcCommand;
+
+import javax.swing.JComponent;
+import java.util.Map;
+
+import static org.jetbrains.plugins.grails.runner.util.GrailsExecutionUtils.addCommonJvmOptions;
+
+final class MavenCommandExecutor extends GrailsCommandLineExecutor implements GrailsRunConfigurationExtension<MavenGeneralSettings> {
+
+  public static final Key<MavenGeneralSettings> DATA_KEY = Key.create(MavenCommandExecutor.class.getName() + " data key");
+
+  private static final Map<String, String> ourCommandGoals = GrailsUtils.createMap(
+    "run-app", "grails:run-app",
+    "test-app", "grails:test-app",
+    "war", "grails:war");
+
+  @Override
+  public boolean isApplicable(@NotNull GrailsApplication grailsApplication) {
+    return grailsApplication instanceof GrailsMavenApplication;
+  }
+
+  @Override
+  public @NotNull JavaParameters createJavaParameters(@NotNull GrailsApplication grailsApplication,
+                                                      @NotNull MvcCommand command,
+                                                      @Nullable MavenGeneralSettings generalSettings) throws ExecutionException {
+    if (!(grailsApplication instanceof GrailsMavenApplication)) {
+      throw new ExecutionException(GrailsBundle.message("dialog.message.maven.cannot.execute", grailsApplication.getClass()));
+    }
+    final Project project = grailsApplication.getProject();
+    final MavenProjectsManager projectsManager = MavenProjectsManager.getInstance(project);
+
+    final MavenRunnerSettings runnerSettings = MavenRunner.getInstance(project).getSettings().clone();
+
+    runnerSettings.setVmOptions(command.getVmOptions());
+
+    String parameterStr = null;
+    if (!command.getArgs().isEmpty()) {
+      parameterStr = ParametersList.join(command.getArgs());
+    }
+
+    String goal = null;
+
+    String mappedGoal = ourCommandGoals.get(command.getCommand());
+
+    if ("test-app".equals(command.getCommand())) {
+      final Version grailsVersion = grailsApplication.getGrailsVersion();
+
+      if (grailsVersion.compareToString("2.1.0") < 0) {
+        mappedGoal = null; // We should run Grails tests using 'mvn -Dcommand=test-app -Dargs=<TestClassName> grails:exec' instead of
+        // "mvn grails:test-app -Dgrails.cli.args=<TestClassName>" for Grails < 2.1.0
+        // see https://youtrack.jetbrains.com/issue/IDEA-105206
+      }
+    }
+
+    if (mappedGoal != null) {
+      goal = mappedGoal;
+
+      if (parameterStr != null) {
+        ParametersList vmOptionList = new ParametersList();
+        vmOptionList.addParametersString(runnerSettings.getVmOptions());
+        vmOptionList.addProperty("grails.cli.args", parameterStr);
+
+        runnerSettings.setVmOptions(vmOptionList.getParametersString());
+      }
+    }
+    else if (command.getCommand() != null) {
+      goal = "grails:exec";
+
+      runnerSettings.getMavenProperties().put("command", command.getCommand());
+
+      if (parameterStr != null) {
+        runnerSettings.getMavenProperties().put("args", parameterStr);
+      }
+    }
+
+    if (!command.getProperties().isEmpty() || command.getEnv() != null) {
+      ParametersList vmOptionList = new ParametersList();
+
+      vmOptionList.addParametersString(runnerSettings.getVmOptions());
+      vmOptionList.addAll(command.getProperties());
+
+      if (command.getEnv() != null) {
+        vmOptionList.add("-Dgrails.env=" + command.getEnv());
+      }
+
+      runnerSettings.setVmOptions(vmOptionList.getParametersString());
+    }
+
+    final MavenExplicitProfiles explicitProfiles = projectsManager.getExplicitProfiles();
+
+    Module module = ((GrailsMavenApplication)grailsApplication).getModule();
+    String pomFileUrl = MavenPomPathModuleService.getInstance(module).getPomFileUrl();
+
+    final MavenRunnerParameters runnerParameters = new MavenRunnerParameters(
+      true,
+      grailsApplication.getRoot().getPath(),
+      pomFileUrl == null ? null : PathUtil.getFileName(pomFileUrl),
+      ContainerUtil.createMaybeSingletonList(goal),
+      explicitProfiles.getEnabledProfiles(),
+      explicitProfiles.getDisabledProfiles()
+    );
+
+    final JavaParameters res = MavenExternalParameters.createJavaParameters(
+      project, runnerParameters, generalSettings, runnerSettings, null
+    );
+    addCommonJvmOptions(res);
+    return res;
+  }
+
+  @Override
+  public @NotNull JavaParameters createJavaParameters(@NotNull GrailsApplication grailsApplication,
+                                                      @NotNull MvcCommand command) throws ExecutionException {
+    return createJavaParameters(grailsApplication, command, null);
+  }
+
+  @Override
+  public void addListener(@NotNull JavaParameters params, @NotNull String listener) {
+    super.addListener(params, listener);
+    GrailsExecutionUtils.addAgentJar(params);
+  }
+
+  @Override
+  public @NotNull Key<MavenGeneralSettings> getKey() {
+    return DATA_KEY;
+  }
+
+  @Override
+  public @NotNull Pair<@TabTitle String, SettingsEditor<GrailsRunConfiguration>> createSettingsEditor(@NotNull Project project) {
+    return new Pair<>(GrailsBundle.message("tab.title.maven.settings"), new GrailsMavenSettingsEditor(project));
+  }
+
+  @Override
+  public @Nullable MavenGeneralSettings readAdditionalConfiguration(@NotNull Element element) {
+    Element e = element.getChild(MavenGeneralSettings.class.getSimpleName());
+    return e != null ? XmlSerializer.deserialize(e, MavenGeneralSettings.class) : null;
+  }
+
+  @Override
+  public void writeAdditionalConfiguration(@NotNull MavenGeneralSettings cfg, @NotNull Element element) {
+    element.addContent(XmlSerializer.serialize(cfg));
+  }
+
+  public static final class GrailsMavenSettingsEditor extends SettingsEditor<GrailsRunConfiguration> {
+
+    private final MavenGeneralSettingsEditor myDelegate;
+
+    private GrailsMavenSettingsEditor(Project project) {
+      myDelegate = new MavenGeneralSettingsEditor(project);
+      Disposer.register(this, myDelegate);
+    }
+
+    @Override
+    protected void resetEditorFrom(@NotNull GrailsRunConfiguration s) {
+      myDelegate.resetFrom(getConfiguration(s));
+    }
+
+    @Override
+    protected void applyEditorTo(@NotNull GrailsRunConfiguration s) throws ConfigurationException {
+      final MavenRunConfiguration mavenRunConfiguration = getConfiguration(s);
+      myDelegate.applyTo(mavenRunConfiguration);
+      s.putUserData(DATA_KEY, mavenRunConfiguration.getGeneralSettings());
+    }
+
+    @Override
+    protected @NotNull JComponent createEditor() {
+      return myDelegate.getComponent();
+    }
+
+    private static @NotNull MavenRunConfiguration getConfiguration(GrailsRunConfiguration s) {
+      ConfigurationFactory factory = MavenRunConfigurationType.getInstance().getConfigurationFactories()[0];
+      MavenRunConfiguration mavenRunConfiguration = (MavenRunConfiguration)factory.createTemplateConfiguration(s.getProject());
+      mavenRunConfiguration.setGeneralSettings(s.getUserData(DATA_KEY));
+      return mavenRunConfiguration;
+    }
+  }
+}
diff --git a/maven/src/com/intellij/groovy/grails/maven/MavenNodeProvider.kt b/maven/src/com/intellij/groovy/grails/maven/MavenNodeProvider.kt
new file mode 100644
index 0000000..6d90e05
--- /dev/null
+++ b/maven/src/com/intellij/groovy/grails/maven/MavenNodeProvider.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package com.intellij.groovy.grails.maven
+
+import com.intellij.ide.projectView.ViewSettings
+import com.intellij.ide.projectView.impl.nodes.PsiFileNode
+import com.intellij.ide.util.treeView.AbstractTreeNode
+import com.intellij.psi.PsiManager
+import org.jetbrains.idea.maven.model.MavenConstants
+import org.jetbrains.plugins.grails.projectView.api.GrailsSingleNodeProvider
+import org.jetbrains.plugins.grails.structure.GrailsApplication
+
+internal class MavenNodeProvider : GrailsSingleNodeProvider() {
+
+  override fun createNode(application: GrailsApplication, settings: ViewSettings): AbstractTreeNode<*>? {
+    if (application !is GrailsMavenApplication) return null
+    val project = application.project
+    return application.root.findChild(MavenConstants.POM_XML)
+      ?.let { PsiManager.getInstance(project).findFile(it) }
+      ?.let { PsiFileNode(project, it, settings) }
+  }
+}
\ No newline at end of file
diff --git a/resources/IdeaPrintProjectSettings.groovy b/resources/IdeaPrintProjectSettings.groovy
new file mode 100644
index 0000000..a5460eb
--- /dev/null
+++ b/resources/IdeaPrintProjectSettings.groovy
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
+ */
+import grails.util.BuildSettings
+import grails.util.BuildSettingsHolder
+
+// This constants were copied from org.jetbrains.plugins.grails.config.PrintGrailsSettingsConstants
+final String COMPILE = "Compile"
+final String RUNTIME = "Runtime"
+final String TESTS = "Test"
+final String PROVIDED = "Provided"
+final String BUILD = "Build"
+
+final String CUSTOM_PLUGIN_PREFIX = "grails.plugin.location."
+final String DEBUG_RUN_FORK = "grails.project.fork.run"
+final String DEBUG_TEST_FORK = "grails.project.fork.test"
+
+final String SETTINGS_START_MARKER = "---=== IDEA Grails build settings ===---"
+final String SETTINGS_END_MARKER = "---=== End IDEA Grails build settings ===---"
+
+// --- === Copied from BuildConfig.groovy === ---
+final String WORK_DIR = "grails.work.dir"
+final String PROJECT_WORK_DIR = "grails.project.work.dir"
+final String PLUGINS_DIR = "grails.project.plugins.dir"
+final String GLOBAL_PLUGINS_DIR = "grails.global.plugins.dir"
+
+includeTargets << grailsScript("_GrailsInit")
+
+target('default': "Print project settings") {
+  depends(resolveDependencies)
+  BuildSettings settings = BuildSettingsHolder.settings
+
+  def forkSettings
+  try {
+    forkSettings = settings.forkSettings
+  }
+  catch (Exception ignored) {
+    forkSettings = [:]
+  }
+  Properties properties = new LinkedProperties()
+
+  addProperty(properties, WORK_DIR, settings.getGrailsWorkDir())
+  addProperty(properties, PROJECT_WORK_DIR, settings.getProjectWorkDir())
+  addProperty(properties, PLUGINS_DIR, settings.getProjectPluginsDir())
+  addProperty(properties, GLOBAL_PLUGINS_DIR, settings.getGlobalPluginsDir())
+  addProperty(properties, DEBUG_RUN_FORK, forkSettings.run as boolean)
+  addProperty(properties, DEBUG_TEST_FORK, forkSettings.test as boolean)
+
+  Map flatten = settings.getConfig().flatten()
+  for (Iterator itr = flatten.entrySet().iterator(); itr.hasNext();) {
+    Map.Entry entry = (Map.Entry)itr.next()
+
+    Object value = entry.getValue()
+    if (value instanceof String || value instanceof GString || value instanceof File) {
+      String key = (String)entry.getKey()
+      if (key.startsWith(CUSTOM_PLUGIN_PREFIX)) {
+        properties.setProperty(key, value.toString())
+      }
+    }
+  }
+
+  storeDependencies(COMPILE, properties, settings.getCompileDependencies())
+  storeDependencies(RUNTIME, properties, settings.getRuntimeDependencies())
+  storeDependencies(TESTS, properties, settings.getTestDependencies())
+  storeDependencies(PROVIDED, properties, settings.getProvidedDependencies())
+  storeDependencies(BUILD, properties, settings.getBuildDependencies())
+
+  println()
+  println(SETTINGS_START_MARKER)
+
+  properties.store(System.out, "")
+
+  println(SETTINGS_END_MARKER)
+}
+
+private void addProperty(Properties properties, String name, Object value) {
+  if (value instanceof String || value instanceof GString || value instanceof File || value instanceof Boolean) {
+    properties.setProperty(name, value.toString())
+  }
+}
+
+private void storeDependencies(String env, Properties properties, List dependencies) {
+  for (int i = 0; i < dependencies.size(); i++) {
+    File file = (File)dependencies.get(i)
+    properties.setProperty(env + '.' + i, file.getPath())
+  }
+}
+
+
+class LinkedProperties extends Properties {
+
+  private final List list = new ArrayList()
+
+  /**
+   * @noinspection UseOfPropertiesAsHashtable
+   */
+  synchronized Object put(Object key, Object value) {
+    list.add(key)
+    return super.put(key, value)
+  }
+
+  synchronized Enumeration keys() {
+    return new ListEnumeration(list, this)
+  }
+}
+
+class ListEnumeration implements Enumeration {
+
+  private final List list
+  private final Properties properties
+  private int index = 0
+
+  ListEnumeration(List list, Properties properties) {
+    this.list = list
+    this.properties = properties
+  }
+
+  boolean hasMoreElements() {
+    while (index < list.size() && !properties.containsKey(list.get(index))) index++
+    return index < list.size()
+  }
+
+  Object nextElement() {
+    while (index < list.size() && !properties.containsKey(list.get(index))) index++
+    return list.get(index++)
+  }
+}
diff --git a/resources/META-INF/grails-css-integration.xml b/resources/META-INF/grails-css-integration.xml
new file mode 100644
index 0000000..a454b5b
--- /dev/null
+++ b/resources/META-INF/grails-css-integration.xml
@@ -0,0 +1,6 @@
+<idea-plugin>
+  <extensions defaultExtensionNs="com.intellij">
+    <psi.referenceContributor language="GSP"
+                              implementation="org.jetbrains.plugins.grails.lang.gsp.psi.gsp.impl.gtag.reference.contributor.GspCssClassReferenceContributor"/>
+  </extensions>
+</idea-plugin>
diff --git a/resources/META-INF/grails-js-integration.xml b/resources/META-INF/grails-js-integration.xml
new file mode 100644
index 0000000..d46c80f
--- /dev/null
+++ b/resources/META-INF/grails-js-integration.xml
@@ -0,0 +1,5 @@
+<idea-plugin>
+    <extensions defaultExtensionNs="com.intellij">
+      <multiHostInjector implementation="org.jetbrains.plugins.grails.addins.js.GrailsJavaScriptInjector"/>
+    </extensions>
+</idea-plugin>
diff --git a/resources/META-INF/grails-method-descriptors.xml b/resources/META-INF/grails-method-descriptors.xml
new file mode 100644
index 0000000..7136ff0
--- /dev/null
+++ b/resources/META-INF/grails-method-descriptors.xml
@@ -0,0 +1,237 @@
+<idea-plugin>
+  <extensions defaultExtensionNs="org.intellij.groovy">
+    <methodDescriptor lightMethodKey="ControllerMembersProvider_controller_method" name="render">
+      <namedArgument name="view" referenceProvider="org.jetbrains.plugins.grails.references.common.GrailsViewFileReferenceSet$Provider"/>
+      <namedArgument name="plugin" referenceProvider="org.jetbrains.plugins.grails.references.common.PluginReference$Provider"/>
+      <namedArgument name="contextPath" referenceProvider="org.jetbrains.plugins.grails.references.common.ContextPathReferenceProvider"/>
+      <namedArgument name="template"
+                     referenceProvider="org.jetbrains.plugins.grails.references.controller.ControllerRenderTemplateRefProvider"/>
+      <namedArgument name="encoding" referenceProvider="org.jetbrains.plugins.grails.references.common.GrailsEncodingReferenceProvider"/>
+      <namedArgument name="contentType" referenceProvider="org.jetbrains.plugins.grails.references.common.ContentTypeReferenceProvider"/>
+    </methodDescriptor>
+
+    <methodDescriptor lightMethodKey="Grails:GSP_TAG"
+                      namedArgsProvider="org.jetbrains.plugins.grails.lang.gsp.resolve.taglib.GrailsTaglibNamedArgumentProvider"/>
+
+    <methodDescriptor lightMethodKey="Grails:Constraint:method"
+                      namedArgsProvider="org.jetbrains.plugins.grails.references.constraints.GrailsConstraintNamedArgumentProvider">
+      <namedArgument name="shared"
+                     referenceProvider="org.jetbrains.plugins.grails.references.constraints.GrailsConstraintsSharedReferenceProvider"/>
+    </methodDescriptor>
+
+    <methodDescriptor lightMethodKey="Grails:ConstraintGroup:method"
+                      namedArgsProvider="org.jetbrains.plugins.grails.references.constraints.GrailsConstraintGroupNamedArgumentProvider"/>
+
+    <methodDescriptor lightMethodKey="Gorm:DomainDescriptor:addToMethod"
+                      namedArgsProvider="org.jetbrains.plugins.grails.references.domain.GormAddToNamedArgumentProvider"/>
+
+    <methodDescriptor lightMethodKey="GormMappingMethodProvider.gormMappingMethod">
+      <namedArgument name="sort"
+                     referenceProvider="org.jetbrains.plugins.grails.references.domain.GormMappingMethodProvider$SortAttributeReferenceProvider"/>
+      <namedArgument name="enumType" values="ordinal, string"/>
+    </methodDescriptor>
+
+    <!--suppress PluginXmlValidity -->
+    <classDescriptor class="org.codehaus.groovy.grails.plugins.web.api.ControllersApi">
+      <method name="redirect" namedArgs="url, uri, action, controller, id, fragment, params"/>
+      <method name="chain" namedArgs="uri, action, controller, id, model, params"/>
+      <method name="forward" namedArgs="action, controller, id, params"/>
+      <method name="render" namedArgs="text, builder, view, template, var, bean, model, collection, converter, plugin, status, contextPath">
+        <namedArgument name="layout"
+                       referenceProvider="org.jetbrains.plugins.grails.references.controller.ControllerLayoutReferenceProvider"/>
+      </method>
+    </classDescriptor>
+
+    <!--suppress PluginXmlValidity -->
+    <classDescriptor class="org.codehaus.groovy.grails.orm.hibernate.cfg.HibernateMappingBuilder">
+      <method name="cache" namedArgs="usage, include"/>
+      <method name="discriminator" namedArgs="value, column, type, insert, formula"/>
+      <method name="table" namedArgs="name, schema, catalog"/>
+      <method name="hibernateCustomUserType" namedArgs="type, class"/>
+      <method name="id" namedArgs="composite, compositeClass, generator, name, params, natural"/>
+    </classDescriptor>
+
+    <!--suppress PluginXmlValidity -->
+    <classDescriptor class="org.codehaus.groovy.grails.webflow.engine.builder.FlowInfoCapturer">
+      <method name="redirect" namedArgs="url, uri, action, controller, id, params"/>
+      <method name="subflow" namedArgs="action, controller"/>
+      <method name="render" namedArgs="view"/>
+    </classDescriptor>
+
+    <!--suppress PluginXmlValidity -->
+    <classDescriptor class="org.codehaus.groovy.grails.web.mapping.LinkGenerator">
+      <method name="link" namedArgs="controller, action, uri, url, base, absolute, contextPath, id, fragment, params, mapping, event"/>
+      <method name="resource" namedArgs="base, absolute">
+        <namedArgument name="plugin" referenceProvider="org.jetbrains.plugins.grails.references.common.PluginReference$Provider"/>
+        <namedArgument name="contextPath" referenceProvider="org.jetbrains.plugins.grails.references.common.ContextPathReferenceProvider"/>
+        <namedArgument name="dir" referenceProvider="org.jetbrains.plugins.grails.references.tagSupport.GspResourceDirAttributeSupport"/>
+        <namedArgument name="file" referenceProvider="org.jetbrains.plugins.grails.references.tagSupport.GspResourceFileAttributeSupport"/>
+      </method>
+    </classDescriptor>
+
+    <!--suppress PluginXmlValidity -->
+    <classDescriptor class="grails.gsp.PageRenderer">
+      <method name="render" namedArgs="model">
+        <namedArgument name="view" referenceProvider="org.jetbrains.plugins.grails.references.common.GrailsViewFileReferenceSet$Provider"/>
+        <namedArgument name="template"
+                       referenceProvider="org.jetbrains.plugins.grails.references.other.GrailsTemplateNamedArgumentReferenceProvider"/>
+      </method>
+      <method name="renderTo" namedArgs="model">
+        <namedArgument name="view" referenceProvider="org.jetbrains.plugins.grails.references.common.GrailsViewFileReferenceSet$Provider"/>
+        <namedArgument name="template"
+                       referenceProvider="org.jetbrains.plugins.grails.references.other.GrailsTemplateNamedArgumentReferenceProvider"/>
+      </method>
+    </classDescriptor>
+
+    <!--suppress PluginXmlValidity -->
+    <methodDescriptor class="org.codehaus.groovy.grails.plugins.web.api.ControllersMimeTypesApi"
+                      name="withFormat"
+                      returnType="java.util.Map"/>
+
+    <!--suppress PluginXmlValidity -->
+    <methodDescriptor class="org.grails.plugins.web.rest.api.ControllersRestApi"
+                      name="respond" namedArgs="status, model, formats">
+      <namedArgument name="view" referenceProvider="org.jetbrains.plugins.grails.references.common.GrailsViewFileReferenceSet$Provider"/>
+    </methodDescriptor>
+
+    <!--suppress PluginXmlValidity -->
+    <classDescriptor class="org.grails.datastore.gorm.GormInstanceApi">
+      <method name="flush" namedArgs="flush"/>
+      <method name="mutex" returnTypeCalculator="org.jetbrains.plugins.groovy.util.GroovyStdTypeCalculators$ClosureTypeExtractor"/>
+    </classDescriptor>
+
+    <methodDescriptor lightMethodKey="Gorm:DomainDescriptor:DynamicMethod"
+                      name="getPersistentValue"
+                      returnTypeCalculator="org.jetbrains.plugins.grails.references.domain.GormGetPersistentValueReturnTypeCalculator"/>
+
+    <!--suppress PluginXmlValidity -->
+    <methodDescriptor class="org.codehaus.groovy.grails.orm.hibernate.HibernateGormInstanceApi" name="getDirtyPropertyNames"
+                      returnType="java.util.List&lt;String&gt;"/>
+
+    <methodDescriptor lightMethodKey="Gorm:DomainDescriptor:DynamicMethod"
+                      name="list"
+                      namedArgs="max, offset, order, sort, ignoreCase, fetch"/>
+
+    <!--suppress PluginXmlValidity -->
+    <classDescriptor class="grails.gorm.DetachedCriteria">
+      <!-- See org.grails.datastore.gorm.finders.DynamicFinder#populateArgumentsForCriteria -->
+      <method name="list, get, count"
+              namedArgs="max, offset, order, sort"/>
+
+      <method name="updateAll"
+              namedArgsProvider="org.jetbrains.plugins.grails.references.domain.detachedCriteria.DetachedCriteriaPropertyNamedArgProvider"/>
+    </classDescriptor>
+
+    <methodDescriptor lightMethodKey="Gorm:DomainDescriptor:DynamicMethod"
+                      name="delete"
+                      namedArgs="flush"/>
+
+    <methodDescriptor lightMethodKey="Gorm:DomainDescriptor:DynamicMethod"
+                      name="merge"
+                      namedArgs="validate, failOnError, flush"/>
+
+    <methodDescriptor lightMethodKey="Gorm:DomainDescriptor:DynamicMethod"
+                      name="save"
+                      namedArgs="validate, flush, insert, failOnError"/>
+
+    <methodDescriptor lightMethodKey="Gorm:DomainDescriptor:DynamicMethod"
+                      name="createCriteria"
+                      returnType="grails.orm.HibernateCriteriaBuilder"/>
+
+    <methodDescriptor lightMethodKey="Gorm:DomainDescriptor:DynamicMethod"
+                      name="withSession, withTransaction, withNewTransaction, withNewSession, withDatastoreSession"
+                      returnTypeCalculator="org.jetbrains.plugins.groovy.util.GroovyStdTypeCalculators$ClosureTypeExtractor"/>
+
+    <methodDescriptor lightMethodKey="Gorm:DomainDescriptor:DynamicMethod"
+                      name="findAllWhere, findWhere, findOrCreateWhere, findOrSaveWhere"
+                      namedArgsProvider="org.jetbrains.plugins.grails.references.domain.GormPersistentPropertiesNamedArgProvider"/>
+
+    <methodDescriptor lightMethodKey="grails:plugins:buildTestData"
+                      name="build, buildLazy, buildWithoutSave"
+                      namedArgsProvider="org.jetbrains.plugins.grails.references.domain.GormPersistentPropertiesNamedArgProvider"/>
+
+    <!--suppress PluginXmlValidity -->
+    <classDescriptor class="SearchableService">
+      <method name="suggestQuery" returnType="java.util.String"/>
+      <method name="termFreqs" returnType="org.compass.core.CompassTermFreq[]"/>
+      <method name="countHits" returnType="java.lang.Integer"/>
+      <method name="search, moreLikeThis"
+              returnTypeCalculator="org.jetbrains.plugins.grails.pluginSupport.seachable.GrailsSearchableReturnTypeCalculator"/>
+    </classDescriptor>
+
+    <methodDescriptor lightMethodKey="grails:plugins:searchable:seachmethod"
+                      name="search, moreLikeThis"
+                      returnTypeCalculator="org.jetbrains.plugins.grails.pluginSupport.seachable.GrailsSearchableReturnTypeCalculator"/>
+
+    <methodDescriptor lightMethodKey="Gorm:DomainDescriptor:NamedQueryMethod"
+                      returnTypeCalculator="org.jetbrains.plugins.grails.references.domain.namedQuery.GormNamedQueryCallTypeCalculator"/>
+
+    <!--suppress PluginXmlValidity -->
+    <classDescriptor class="org.codehaus.groovy.grails.orm.hibernate.cfg.NamedCriteriaProxy">
+      <method returnTypeCalculator="org.jetbrains.plugins.grails.references.domain.namedQuery.NamedCriteriaProxyTypeCalculator"/>
+
+      <method name="findWhere, findAllWhere"
+              namedArgsProvider="org.jetbrains.plugins.grails.references.domain.namedQuery.NamedCriteriaProxyNamedArgumentProvider"/>
+    </classDescriptor>
+
+    <!--suppress PluginXmlValidity -->
+    <methodDescriptor class="org.grails.plugin.resource.module.ModuleBuilder" name="resource"
+                      namedArgs="url, exclude, bundle, disposition, attrs, id, linkOverride, wrapper"/>  <!-- See http://grails-plugins.github.com/grails-resources/guide/3.%20Declaring%20resources.html -->
+
+    <!--suppress PluginXmlValidity -->
+    <methodDescriptor class="org.codehaus.groovy.grails.orm.hibernate.cfg.HibernateMappingBuilder" name="sort"
+                      namedArgsProvider="org.jetbrains.plugins.grails.references.domain.GormSortNamedArgumentProvider"/>
+
+    <!--suppress PluginXmlValidity -->
+    <methodDescriptor class="org.codehaus.groovy.grails.orm.hibernate.cfg.GrailsHibernateUtil"
+                      name="unwrapIfProxy"
+                      returnTypeCalculator="org.jetbrains.plugins.groovy.util.GroovyStdTypeCalculators$TypeSameAsFirstArgument"/>
+
+    <!--suppress PluginXmlValidity -->
+    <classDescriptor class="org.apache.shiro.grails.AccessControlBuilder">
+      <method name="role, permission">
+        <namedArgument name="action, only"
+                       referenceProvider="org.jetbrains.plugins.grails.pluginSupport.shiro.GrailsShiroReferenceProvider"/>
+      </method>
+    </classDescriptor>
+    <!--suppress PluginXmlValidity -->
+    <classDescriptor class="org.jsecurity.grails.AccessControlBuilder">
+      <method name="role, permission">
+        <namedArgument name="action, only"
+                       referenceProvider="org.jetbrains.plugins.grails.pluginSupport.shiro.GrailsShiroReferenceProvider"/>
+      </method>
+    </classDescriptor>
+
+    <methodDescriptor lightMethodKey="grails:environments">
+      <closureArgument index="0" methodContributor="org.jetbrains.plugins.grails.config.GrailsEnvironmentClosureMemberContributor"/>
+    </methodDescriptor>
+
+    <methodDescriptor name="withFormat" lightMethodKey="ControllerMembersProvider_controller_method">
+      <closureArgument index="0"
+                       methodContributor="org.jetbrains.plugins.grails.references.controller.GrailsWithFormatClosureMethodContributor"/>
+    </methodDescriptor>
+
+    <!--suppress PluginXmlValidity -->
+    <methodDescriptor name="plugins" class="org.codehaus.groovy.grails.resolve.config.DependencyConfigurationConfigurer">
+      <closureArgument index="0" methodContributor="org.jetbrains.plugins.grails.config.GrailsPluginConfigMethodContributor"/>
+    </methodDescriptor>
+
+    <!--suppress PluginXmlValidity -->
+    <methodDescriptor class="grails.artefact.controller.support.ResponseRenderer" name="render"
+                      namedArgs="text, model, var, bean, collection, file, fileName, status, plugin, builder, contextPath">
+      <namedArgument name="view" referenceProvider="org.jetbrains.plugins.grails.references.common.GrailsViewFileReferenceSet$Provider"/>
+      <namedArgument name="template"
+                     referenceProvider="org.jetbrains.plugins.grails.references.controller.ControllerRenderTemplateRefProvider"/>
+      <namedArgument name="layout"
+                     referenceProvider="org.jetbrains.plugins.grails.references.controller.ControllerLayoutReferenceProvider"/>
+      <namedArgument name="contentType" referenceProvider="org.jetbrains.plugins.grails.references.common.ContentTypeReferenceProvider"/>
+      <namedArgument name="encoding" referenceProvider="org.jetbrains.plugins.grails.references.common.GrailsEncodingReferenceProvider"/>
+    </methodDescriptor>
+
+    <!--suppress PluginXmlValidity -->
+    <methodDescriptor class="grails.artefact.controller.RestResponder" name="respond"
+                      namedArgs="status, model, formats">
+      <namedArgument name="view" referenceProvider="org.jetbrains.plugins.grails.references.common.GrailsViewFileReferenceSet$Provider"/>
+    </methodDescriptor>
+  </extensions>
+</idea-plugin>
\ No newline at end of file
diff --git a/resources/META-INF/plugin.xml b/resources/META-INF/plugin.xml
new file mode 100644
index 0000000..0157e91
--- /dev/null
+++ b/resources/META-INF/plugin.xml
@@ -0,0 +1,498 @@
+<!-- Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.-->
+<idea-plugin xmlns:xi="http://www.w3.org/2001/XInclude" package="org.jetbrains.plugins.grails">
+  <id>org.intellij.grails</id>
+  <name>Grails</name>
+  <category>JVM Frameworks</category>
+  <description>
+    <![CDATA[
+   Provides integration with the <a href="https://grails.org">Grails</a> framework.
+   <ul>
+     <li>Import a generated Grails Application Forge project or create a new one directly from the IDE</li>
+     <li>Switch to the <b>Grails</b> view in the <b>Project</b> tool window</li>
+     <li>Edit Grails files with coding assistance features, such as code completion, navigation, inspections, refactoring, and much more</li>
+     <li>Use dedicated Grails run/debug configurations</li>
+   </ul>
+    ]]>
+  </description>
+  <vendor>JetBrains</vendor>
+
+  <dependencies>
+    <module name="intellij.javaee.platform"/>
+    <module name="intellij.javaee.platform.impl"/>
+    <plugin id="com.intellij.persistence"/>
+    <plugin id="com.intellij.javaee.jpa"/>
+    <plugin id="com.intellij.jsp"/>
+    <plugin id="com.intellij.javaee.el"/>
+    <plugin id="org.intellij.groovy"/>
+    <plugin id="com.intellij.properties"/>
+    <plugin id="com.intellij.database"/>
+
+    <module name="intellij.spring"/>
+    <module name="intellij.spring.core"/>
+
+    <plugin id="org.jetbrains.plugins.gradle"/>
+    <plugin id="com.intellij.modules.ultimate"/>
+    <plugin id="com.intellij.microservices.jvm"/>
+    <module name="intellij.properties.backend"/>
+    <module name="intellij.properties.backend.psi"/>
+    <module name="intellij.regexp"/>
+  </dependencies>
+
+  <content namespace="jetbrains">
+    <module name="intellij.groovy.grails.copyright"/>
+    <module name="intellij.groovy.grails.i18n"/>
+    <module name="intellij.groovy.grails.maven"/>
+    <module name="intellij.groovy.grails.hibernate"/>
+    <module name="intellij.groovy.grails.langInjection"/>
+    <module name="intellij.groovy.grails.coverage"/>
+  </content>
+
+  <depends optional="true" config-file="grails-js-integration.xml">JavaScript</depends>
+  <depends optional="true" config-file="grails-css-integration.xml">com.intellij.css</depends>
+
+  <resource-bundle>messages.GrailsBundle</resource-bundle>
+
+  <extensionPoints>
+    <extensionPoint name="facetProvider" dynamic="true" interface="org.jetbrains.plugins.grails.util.GrailsFacetProvider"/>
+    <extensionPoint name="applicationProvider" dynamic="true" interface="org.jetbrains.plugins.grails.structure.GrailsApplicationProvider"/>
+    <extensionPoint name="commandExecutor" dynamic="true" interface="org.jetbrains.plugins.grails.runner.GrailsCommandExecutor"/>
+    <extensionPoint name="commandProvider" dynamic="true" interface="org.jetbrains.plugins.grails.commands.GrailsCommandProvider"/>
+    <extensionPoint name="artefactHandler" dynamic="true" interface="org.jetbrains.plugins.grails.artefact.api.GrailsArtefactHandler"/>
+    <extensionPoint name="viewNodeProvider" dynamic="true" interface="org.jetbrains.plugins.grails.projectView.api.GrailsViewNodeProvider"/>
+  </extensionPoints>
+
+  <extensions defaultExtensionNs="org.jetbrains.plugins.gradle">
+    <projectResolve implementation="org.jetbrains.plugins.grails.gradle.GrailsProjectResolverExtension"/>
+    <taskManager implementation="org.jetbrains.plugins.grails.gradle.GrailsTaskManagerExtension"/>
+  </extensions>
+
+  <extensions defaultExtensionNs="org.intellij.grails">
+    <commandExecutor implementation="org.jetbrains.plugins.grails.runner.impl.Grails3GradleCommandExecutor"/>
+    <commandExecutor implementation="org.jetbrains.plugins.grails.runner.impl.GrailsInstallationCommandExecutor"/>
+    <commandExecutor implementation="org.jetbrains.plugins.grails.runner.impl.Grails3InstallationCommandExecutor"/>
+    <facetProvider implementation="org.jetbrains.plugins.grails.spring.GrailsSpringFacetProvider"/>
+    <facetProvider implementation="org.jetbrains.plugins.grails.util.GrailsWebFacetProvider"/> <!--j2ee plugin-->
+    <applicationProvider id="default" implementation="org.jetbrains.plugins.grails.structure.impl.PlainGrailsApplicationProvider"/>
+    <applicationProvider implementation="org.jetbrains.plugins.grails.structure.impl.Grails3ApplicationProvider"/>
+    <commandProvider implementation="org.jetbrains.plugins.grails.commands.OldGrailsCommandProvider"/>
+    <commandProvider implementation="org.jetbrains.plugins.grails.commands.Grails3CommandProvider"/>
+    <viewNodeProvider implementation="org.jetbrains.plugins.grails.projectView.impl.GrailsAppNodeProvider"/>
+    <viewNodeProvider implementation="org.jetbrains.plugins.grails.projectView.impl.OldGrailsNodeProvider"/>
+    <viewNodeProvider implementation="org.jetbrains.plugins.grails.projectView.impl.Grails3NodeProvider"/>
+  </extensions>
+
+  <extensions defaultExtensionNs="org.intellij.groovy">
+    <membersContributor implementation="org.jetbrains.plugins.grails.references.GrailsArtifactNonCodeMemberProcessor"/>
+    <membersContributor implementation="org.jetbrains.plugins.grails.references.CodecNonCodeMemberContributor"/>
+    <membersContributor implementation="org.jetbrains.plugins.grails.references.pluginClass.GrailsPluginClassMemberContributor"/>
+    <membersContributor implementation="org.jetbrains.plugins.grails.references.GrailsResourceClassMemberContributor"/>
+    <membersContributor implementation="org.jetbrains.plugins.grails.references.filter.GrailsFilterClosureMemberContributor"/>
+    <membersContributor implementation="org.jetbrains.plugins.grails.references.domain.criteria.CriteriaBuilderImplicitMemberContributor"/>
+    <membersContributor implementation="org.jetbrains.plugins.grails.config.BuildConfigMemberContributor"/>
+    <membersContributor implementation="org.jetbrains.plugins.grails.config.ConfigMemberContributor"/>
+    <membersContributor implementation="org.jetbrains.plugins.grails.spring.GrailsWebApplicationContextMembersContributor"/> <!-- Spring Support -->
+    <membersContributor implementation="org.jetbrains.plugins.grails.tests.GrailsTestMemberContributor"/>
+    <membersContributor implementation="org.jetbrains.plugins.grails.references.constraints.GrailsValidationMethodsContributor"/>
+    <membersContributor implementation="org.jetbrains.plugins.grails.pluginSupport.seachable.SearchableMemberMethodContributor"/>
+    <membersContributor implementation="org.jetbrains.plugins.grails.pluginSupport.webflow.WebFlowMethodContributor"/>
+    <membersContributor implementation="org.jetbrains.plugins.grails.references.domain.criteria.CriteriaClosureMemberContributor"/>
+    <membersContributor implementation="org.jetbrains.plugins.grails.references.domain.namedQuery.NamedCriteriaProxyMembersContributor"/>
+    <membersContributor implementation="org.jetbrains.plugins.grails.pluginSupport.resources.GrailsResourcesClosureMethodContributor"/>
+    <membersContributor implementation="org.jetbrains.plugins.grails.references.domain.detachedCriteria.DetachedCriteriaClosureMemberProvider"/>
+    <membersContributor implementation="org.jetbrains.plugins.grails.references.domain.detachedCriteria.DetachedCriteriaMemberProvider"/>
+    <membersContributor implementation="org.jetbrains.plugins.grails.references.controller.HttpRequestMemberContributor"/>
+    <membersContributor implementation="org.jetbrains.plugins.grails.references.controller.HttpResponseMemberContributor"/>
+    <membersContributor implementation="org.jetbrains.plugins.grails.spring.GrailsResourcesGroovyMemberContributor"/>
+    <astTransformationSupport implementation="org.jetbrains.plugins.grails.lang.Grails3TraitInjectorContributor"/>
+
+    <closureMissingMethodContributor implementation="org.jetbrains.plugins.grails.pluginSupport.webflow.WebFlowEventDefinitionMethodContributor"/>
+    <closureMissingMethodContributor implementation="org.jetbrains.plugins.grails.references.GrailsClosureMemberContributor"/>
+    <closureMissingMethodContributor implementation="org.jetbrains.plugins.grails.references.constraints.GrailsConstraintsClosureMemberProvider"/>
+    <closureMissingMethodContributor implementation="org.jetbrains.plugins.grails.references.urlMappings.UrlMappingConstraintClosureMemberProvider"/>
+    <closureMissingMethodContributor implementation="org.jetbrains.plugins.grails.references.constraints.GrailsConstraintsGroupClosureMemberProvider"/>
+
+    <closureMissingMethodContributor implementation="org.jetbrains.plugins.grails.config.BuildConfigClosureMemberContributor"/>
+
+    <methodComparator implementation="org.jetbrains.plugins.grails.references.controller.GspTagMethodComparator"/>
+    <overloadResolver implementation="org.jetbrains.plugins.grails.references.controller.GspTagMethodOverloadResolver"/>
+
+    <typeCalculator forClass="org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrMethodCall"
+                    implementationClass="org.jetbrains.plugins.grails.references.domain.criteria.CriteriaReturnTypeCalculator"/>
+
+    <groovySourceFolderDetector implementation="org.jetbrains.plugins.grails.actions.GrailsGroovySourceFolderDetector"/>
+
+    <referenceTypeEnhancer implementation="org.jetbrains.plugins.grails.pluginSupport.spock.GrailsSpockEnhancer"/>
+    <referenceTypeEnhancer implementation="org.jetbrains.plugins.grails.tests.GrailsTestVariableEnhancer"/>
+    <referenceTypeEnhancer implementation="org.jetbrains.plugins.grails.util.PredefinedTypeReferenceEnhancer"/>
+    <variableEnhancer implementation="org.jetbrains.plugins.grails.references.pluginClass.GrailsPluginParameterEnhancer"/>
+    <variableEnhancer implementation="org.jetbrains.plugins.grails.spring.InjectedSpringBeanProvider"/>
+    <variableEnhancer implementation="org.jetbrains.plugins.grails.references.taglib.TaglibClosureParamEnhancer"/>
+    <variableEnhancer implementation="org.jetbrains.plugins.grails.references.constraints.GrailsValidatorParamTypeEnhancer"/>
+
+    <scriptTypeDetector implementation="org.jetbrains.plugins.grails.GantFileDetector"/>
+
+    <mapContentProvider implementation="org.jetbrains.plugins.grails.GrailsFlashMapContentProvider"/>
+
+    <namedArgumentProvider implementation="org.jetbrains.plugins.grails.references.domain.criteria.CriteriaBuilderNamedArgumentProvider"/>
+    <namedArgumentProvider implementation="org.jetbrains.plugins.grails.references.urlMappings.UrlMappingNamedArgumentProvider"/>
+
+    <astTransformationSupport implementation="org.jetbrains.plugins.grails.references.domain.GormAstTransformationContributor"
+                              order="before defaultSupport"/>
+
+    <unresolvedHighlightFileFilter implementation="org.jetbrains.plugins.grails.config.GrailsConfigUnresolvedFileFilter"/>
+    <unresolvedHighlightFilter implementation="org.jetbrains.plugins.grails.references.urlMappings.UrlMappingUnresolvedHighlightingFilter"/>
+
+    <configSlurperSupport implementation="org.jetbrains.plugins.grails.config.BuildConfigSupport"/>
+    <configSlurperSupport implementation="org.jetbrains.plugins.grails.config.ConfigSupport"/>
+    <configSlurperSupport implementation="org.jetbrains.plugins.grails.config.DataSourcesSupport"/>
+
+    <methodMayBeStaticInspectionFilter implementation="org.jetbrains.plugins.grails.inspections.GrailsMethodMayBeStaticFilter" />
+    <astTransformationSupport implementation="org.jetbrains.plugins.grails.lang.DirtyCheckableContributor"/>
+    <astTransformationSupport implementation="org.jetbrains.plugins.grails.lang.GormTraitContributor"/>
+    <gdslScriptProvider implementation="org.jetbrains.plugins.groovy.dsl.GrailsScriptProvider"/>
+
+    <!--grails.rest.Linkable-->
+    <astTransformationSupport implementation="org.jetbrains.plugins.grails.feature.linkable.LinkableTransformationSupport"/>
+    <namedArgumentProvider implementation="org.jetbrains.plugins.grails.feature.linkable.LinkableNamedArgumentsProvider"/>
+
+    <delegatesToProvider
+        implementation="org.jetbrains.plugins.grails.references.domain.detachedCriteria.DetachedCriteriaDelegatesToProvider"/>
+  </extensions>
+
+  <extensions defaultExtensionNs="com.intellij">
+    <registryKey key="grails.advanced.mode" defaultValue="false"/>
+    <registryKey key="grails.simple.debug" defaultValue="false"
+                 description="Use simple debug strategy: pass --debug-jvm and then poll 5005. This switch works only for Grails &gt;= 3.1.5."/>
+    <registryKey key="grails.add.plain.output" defaultValue="true"
+                 description="Add --plain-output to Grails commands even it was not specified explicitly. This switch works only for Grails &gt;= 3.0"/>
+    <registryKey key="grails.create.run.configurations" defaultValue="true"
+                 description="Create run configurations on startup"/>
+    <registryKey key="grails.cli.project.wizard" defaultValue="false"
+                 description="Show new project Grails wizard"/>
+
+    <internalFileTemplate name="Groovy Server Page"/>
+    <externalProjectDataService implementation="org.jetbrains.plugins.grails.gradle.GrailsModuleGradleDataService"/>
+
+    <fileBasedIndex implementation="org.jetbrains.plugins.grails.lang.gsp.gspIndex.GspIncludeIndex" />
+
+    <referencesSearch implementation="org.jetbrains.plugins.grails.lang.gsp.search.JspPageReferenceSearcher"/> <!--j2ee plugin-->
+
+    <consoleFilterProvider implementation="org.jetbrains.plugins.grails.GrailsConsoleFilterProvider"/>
+
+    <library.presentationProvider implementation="org.jetbrains.plugins.grails.config.GrailsLibraryPresentationProvider"/>
+
+    <!--suppress PluginXmlValidity -->
+    <moduleBuilder builderClass="org.jetbrains.plugins.grails.config.GrailsModuleBuilder" order="before griffonModuleBuilder"/>
+    <moduleBuilder builderClass="org.jetbrains.plugins.grails.feature.forge.GrailsForgeModuleBuilder"/>
+    <projectStructureDetector implementation="org.jetbrains.plugins.grails.config.GrailsProjectStructureDetector" order="before groovyDetector"/>
+
+    <programRunner implementation="org.jetbrains.plugins.grails.runner.GrailsAppEngineDebuggerRunner"/>
+    <programRunner implementation="org.jetbrains.plugins.grails.runner.GrailsDebuggerRunner" id="grailsDebuggerRunner"/>
+
+    <editorNotificationProvider implementation="org.jetbrains.plugins.groovy.grails.annotator.GrailsForkSettingsNotificator"/>
+
+    <fileType name="GSP" language="GSP" extensions="gsp"
+              implementationClass="org.jetbrains.plugins.grails.fileType.GspFileType" fieldName="GSP_FILE_TYPE"/>
+    <fileEditorProvider implementation="org.jetbrains.plugins.grails.perspectives.DomainClassesRelationsEditorProvider"/>
+    <annotator language="Groovy" implementationClass="org.jetbrains.plugins.groovy.grails.annotator.GrailsDomainAnnotator"/>
+    <annotator language="Groovy" implementationClass="org.jetbrains.plugins.groovy.grails.annotator.GrailsSynchronizeStructureAnnotator"/>
+    <annotator language="Groovy" implementationClass="org.jetbrains.plugins.groovy.grails.annotator.GrailsControllerAnnotator"/>
+    <liveTemplateContext contextId="GSP" implementation="org.jetbrains.plugins.grails.GspTemplateContextType"/>
+
+    <lang.inspectionSuppressor language="GSP" implementationClass="org.jetbrains.plugins.grails.inspections.GspSuppressionProvider"/>
+
+    <completion.contributor language="Groovy" implementationClass="org.jetbrains.plugins.grails.GrailsCompletionContributor"/>
+    <completion.contributor language="Groovy" implementationClass="org.jetbrains.plugins.grails.references.controller.ControllerAllowedMethodCompletionProvider" order="before groovyBasic"/>
+    <completion.contributor language="Groovy" implementationClass="org.jetbrains.plugins.grails.config.GrailsPluginNameCompletionContributor"/>
+    <completion.contributor language="GSP" implementationClass="org.jetbrains.plugins.grails.GspCompletionContributor"/>
+    <completion.contributor language="HTML" implementationClass="org.jetbrains.plugins.grails.GspCompletionContributor"/>
+
+    <renamePsiElementProcessor implementation="org.jetbrains.plugins.grails.references.controller.ActionRenameProcessor" order="last"/>
+    <renamePsiElementProcessor implementation="org.jetbrains.plugins.grails.references.controller.ControllerRenameProcessor" order="last"/>
+    <renamePsiElementProcessor implementation="org.jetbrains.plugins.grails.references.GrailsArtifactRenameProcessor" order="last"/>
+    <renamePsiElementProcessor implementation="org.jetbrains.plugins.grails.references.domain.DomainFieldRenameProcessor"
+                               order="first, before groovyFieldRenameProcessor, before jpaAttributeRenameProcessor"/>
+
+    <attachSourcesProvider implementation="org.jetbrains.plugins.grails.GrailsAttachSourcesProvider" />
+
+    <xml.xmlTagRuleProvider implementation="org.jetbrains.plugins.grails.lang.gsp.psi.gsp.impl.gtag.GspTagRuleProvider" />
+    <xml.xmlExtension implementation="org.jetbrains.plugins.grails.lang.gsp.psi.html.impl.GspHtmlXmlExtension"
+                      id="gsp-html" order="after poly-symbols"/>
+
+    <localInspection language="GSP" enabledByDefault="true"
+                     groupPathKey="inspections.groupPath" groupKey="inspections.groupName" key="language.gsp"
+                     shortName="GspInspection" implementationClass="org.jetbrains.plugins.grails.lang.gsp.GspTagInspection"/>
+
+    <syntaxHighlighter key="GSP" implementationClass="org.jetbrains.plugins.grails.lang.gsp.highlighter.GspSyntaxHighlighter"/>
+    <editorHighlighterProvider filetype="GSP"
+                               implementationClass="org.jetbrains.plugins.grails.lang.gsp.highlighter.GspEditorHighlighterProvider"/>
+    <lang.braceMatcher language="GSP" implementationClass="org.jetbrains.plugins.grails.lang.gsp.highlighter.GspPairedBraceMatcher"/>
+    <braceMatcher filetype="GSP" implementationClass="org.jetbrains.plugins.grails.lang.gsp.highlighter.GspFileBraceMatcher"/>
+    <lang.commenter language="GSP" implementationClass="org.jetbrains.plugins.grails.lang.gsp.highlighter.GspCommenter"/>
+    <lang.foldingBuilder language="GSP" implementationClass="org.jetbrains.plugins.grails.lang.gsp.folding.GspFoldingBuilder"/>
+    <lang.formatter language="GSP" implementationClass="org.jetbrains.plugins.grails.lang.gsp.formatter.GspFormattingModelBuilder"/>
+    <lang.parserDefinition language="GSP" implementationClass="org.jetbrains.plugins.grails.lang.gsp.parsing.gsp.GspParserDefinition"/>
+    <lang.fileViewProviderFactory language="GSP" implementationClass="org.jetbrains.plugins.grails.lang.gsp.GspFileviewProviderFactory"/>
+    <codeStyleSettingsProvider implementation="org.jetbrains.plugins.grails.lang.gsp.formatter.settings.GspCodeStyleSettingsProvider"/>
+    <langCodeStyleSettingsProvider implementation="org.jetbrains.plugins.grails.lang.gsp.formatter.settings.GspLanguageCodeStyleSettingsProvider"/>
+
+    <psi.referenceContributor language="Groovy" implementation="org.jetbrains.plugins.grails.GrailsGroovyCodeReferenceContributor"/>
+
+    <psi.referenceContributor language="HTML" implementation="org.jetbrains.plugins.grails.GrailsHtmlReferenceContributor"/>
+    <psi.referenceContributor language="GSP" implementation="org.jetbrains.plugins.grails.GrailsGspReferenceContributor"/>
+
+    <referencesSearch implementation="org.jetbrains.plugins.grails.lang.gsp.search.GspPageReferenceSearcher"/>
+    <referencesSearch implementation="org.jetbrains.plugins.grails.lang.gsp.search.GspControllerReferenceSearcher"/>
+    <referencesSearch implementation="org.jetbrains.plugins.grails.references.common.ArtifactFieldReferenceSearcher"/>
+    <methodReferencesSearch implementation="org.jetbrains.plugins.grails.references.common.ArtifactPropertyGetterReferenceSearcher"/>
+    <referencesSearch implementation="org.jetbrains.plugins.grails.lang.gsp.resolve.taglib.BuiltInTagReferenceSearcher"/>
+
+    <methodReferencesSearch implementation="org.jetbrains.plugins.grails.pluginSupport.resources.GrailsResourceReferenceSearcher"/>
+
+    <debugger.codeFragmentFactory implementation="org.jetbrains.plugins.grails.lang.gsp.debug.GspCodeFragmentFactory"/>
+
+    <problemHighlightFilter implementation="org.jetbrains.plugins.grails.TemplateHighlightErrorFilter"/>
+
+    <lang.findUsagesProvider language="GSP" implementationClass="org.jetbrains.plugins.grails.GspFindUsagesProvider"/>
+
+    <lang.unwrapDescriptor language="GSP" implementationClass="com.intellij.lang.xml.XmlUnwrapDescriptor"/>
+
+    <gotoClassContributor implementation="org.jetbrains.plugins.grails.actions.GrailsGoToClassContributor"/>
+
+    <codeInsight.parameterInfo language="GSP"
+                               implementationClass="org.jetbrains.plugins.groovy.lang.parameterInfo.GroovyParameterInfoHandler"/>
+
+    <enterHandlerDelegate implementation="org.jetbrains.plugins.grails.lang.gsp.editor.actions.GspEnterHandler"/>
+    <typedHandler implementation="org.jetbrains.plugins.grails.lang.gsp.editor.actions.GspTypedHandler" order="last" />
+    <braceMatcher filetype="GSP" implementationClass="com.intellij.xml.impl.XmlBraceMatcher"/>
+
+    <configurationType implementation="org.jetbrains.plugins.grails.runner.GrailsRunConfigurationType"/>
+
+    <runConfigurationProducer implementation="org.jetbrains.plugins.grails.runner.GrailsScriptRunConfigurationProducer"/>
+    <runConfigurationProducer implementation="org.jetbrains.plugins.grails.tests.runner.GrailsTestConfigurationProducer"/>
+
+    <projectService serviceInterface="org.jetbrains.plugins.grails.lang.gsp.psi.GspPsiElementFactory"
+                    serviceImplementation="org.jetbrains.plugins.grails.lang.gsp.psi.GspPsiElementFactoryImpl"/>
+
+    <xml.fileNSInfoProvider implementation="org.jetbrains.plugins.grails.lang.gsp.completion.GspXmlFileNSInfoProvider"/>
+    <xml.elementDescriptorProvider implementation="org.jetbrains.plugins.grails.lang.gsp.psi.gsp.impl.gtag.GspContentTagDescriptorProvider"
+                                   order="first"/>
+
+    <extendWordSelectionHandler implementation="org.jetbrains.plugins.grails.editor.selection.GrailsGroovyInjectionSelectioner"/>
+    <extendWordSelectionHandler implementation="org.jetbrains.plugins.grails.editor.selection.GrailsDependencySelectioner"/>
+
+    <highlightErrorFilter implementation="org.jetbrains.plugins.grails.lang.gsp.GspErrorFilter" />
+
+    <projectService serviceImplementation="org.jetbrains.plugins.grails.config.GrailsSettingsService"/>
+
+    <projectService serviceImplementation="org.jetbrains.plugins.grails.lang.gsp.psi.gsp.impl.gtag.GspTagDescriptorService"/>
+
+    <pom.declarationSearcher implementation="org.jetbrains.plugins.grails.lang.gsp.psi.groovy.impl.ModelVariableDeclarationSearcher"/>
+    <pom.declarationSearcher implementation="org.jetbrains.plugins.grails.lang.gsp.psi.gsp.impl.gtag.GspTagVariableDeclarationSearcher"/>
+    <pom.declarationSearcher implementation="org.jetbrains.plugins.grails.references.domain.namedQuery.GormNamedQueryDeclarationSearcher"/>
+    <pom.declarationSearcher implementation="org.jetbrains.plugins.grails.references.domain.GrailsSyntheticFieldDeclarationSearcher"/>
+    <pom.declarationSearcher implementation="org.jetbrains.plugins.grails.pluginSupport.resources.GrailsResourcesPomDeclarationSearcher"/>
+
+    <pom.declarationSearcher implementation="org.jetbrains.plugins.grails.pluginSupport.webflow.WebFlowPomDeclarationSearcher"/>
+
+    <compileServer.plugin classpath="Grails.jps-plugin.jar;Grails.grails-compiler-patch.jar"/>
+
+    <multiHostInjector implementation="org.jetbrains.plugins.grails.lang.gsp.GspCssInjector"/>
+    <multiHostInjector implementation="org.jetbrains.plugins.grails.references.constraints.GormRegexInjector"/>
+
+    <gotoRelatedProvider implementation="org.jetbrains.plugins.grails.actions.GrailsGotoRelatedProvider"/>
+
+    <indexPatternBuilder implementation="org.jetbrains.plugins.grails.lang.gsp.highlighter.GspIndexPatternBuilder" />
+
+    <junitRecognizer implementation="org.jetbrains.plugins.grails.tests.GrailsJUnitRecognizer" />
+
+    <lang.elementManipulator forClass="org.jetbrains.plugins.grails.lang.gsp.psi.groovy.impl.GspOuterHtmlElementImpl"
+                             implementationClass="org.jetbrains.plugins.grails.lang.gsp.psi.groovy.impl.GspOuterHtmlElementManipulator"/>
+
+    <implicitUsageProvider implementation="org.jetbrains.plugins.grails.GrailsImplicitUsageProvider"/>
+
+    <lang.documentationProvider language="GSP" implementationClass="org.jetbrains.plugins.grails.lang.gsp.GspTagDocumentationProvider"/>
+    <lang.documentationProvider language="Groovy" implementationClass="org.jetbrains.plugins.grails.lang.gsp.GspTagDocumentationProvider"/>
+
+    <database.dataSourceDetector implementation="org.jetbrains.plugins.grails.database.GrailsDataSourceDetector"/>
+
+    <intentionAction>
+      <language>Groovy</language>
+      <className>org.jetbrains.plugins.grails.actions.GenerateViewIntention</className>
+      <categoryKey>library.name</categoryKey>
+    </intentionAction>
+
+    <intentionAction>
+      <language>Groovy</language>
+      <className>org.jetbrains.plugins.grails.actions.MakeNullableIntention</className>
+      <categoryKey>library.name</categoryKey>
+    </intentionAction>
+
+    <intentionAction>
+      <language>Groovy</language>
+      <className>org.jetbrains.plugins.grails.actions.MakeUniqueIntention</className>
+      <categoryKey>library.name</categoryKey>
+    </intentionAction>
+
+    <intentionAction>
+      <language>Groovy</language>
+      <className>org.jetbrains.plugins.grails.actions.GrailsCreateActionByUsageIntention</className>
+      <categoryKey>library.name</categoryKey>
+    </intentionAction>
+
+    <!-- Spring Support -->
+    <renamePsiElementProcessor implementation="org.jetbrains.plugins.grails.spring.GrLightVariableRenameProcessor" id="GrLightVariableRenameProcessor"/>
+    <spring.customModuleComponentsDiscoverer implementation="org.jetbrains.plugins.grails.spring.GrailsSpringBeanDiscoverer"/>
+
+    <codeInsight.lineMarkerProvider language="Groovy" implementationClass="org.jetbrains.plugins.grails.spring.SpringInjectionAnnotator"/>
+
+    <completion.contributor language="Groovy" implementationClass="org.jetbrains.plugins.grails.spring.GrailsInjectedBeanCompletionContributor" order="before groovyBasic"/>
+    <aliasingPsiTargetMapper implementation="org.jetbrains.plugins.grails.spring.SpringGrailsTargetMapper"/>
+    <aliasingPsiTargetMapper implementation="org.jetbrains.plugins.grails.spring.GrailsSpringStereotypeAliasingPsiTargetMapper"/>
+    <referencesSearch implementation="org.jetbrains.plugins.grails.spring.SpringBeanReferenceSearcher"/>
+
+    <debugger.positionManagerFactory implementation="org.jetbrains.plugins.grails.lang.gsp.debug.GspPositionManagerFactory"/>
+
+    <defaultLiveTemplates file="liveTemplates/gsp.xml"/>
+    <iconProvider implementation="org.jetbrains.plugins.grails.projectView.GrailsIconProvider"/>
+    <postStartupActivity implementation="org.jetbrains.plugins.grails.structure.sync.GrailsStartupActivity"/>
+    <postStartupActivity implementation="org.jetbrains.plugins.grails.structure.GrailsApplicationManagerRefresher"/>
+    <backgroundPostStartupActivity implementation="org.jetbrains.plugins.grails.structure.sync.GrailsMvcStructureRefresher"/>
+    <editorNotificationProvider implementation="org.jetbrains.plugins.grails.editor.GrailsEditorDecorator"/>
+    <projectViewPane implementation="org.jetbrains.plugins.grails.projectView.GrailsProjectViewPane"/>
+    <treeStructureProvider implementation="org.jetbrains.plugins.grails.projectView.GrailsTreeStructureProvider"/>
+    <multiHostInjector implementation="org.jetbrains.plugins.grails.pluginSupport.assetPipeline.AssetScriptInjector"/>
+    <psi.referenceContributor language="GSP"
+                              implementation="org.jetbrains.plugins.grails.pluginSupport.assetPipeline.AssetReferenceContributor"/>
+    <semContributor implementation="org.jetbrains.plugins.grails.spring.GrailsSpringSemContributor"/>
+
+    <ignoredFileProvider implementation="org.jetbrains.plugins.grails.config.GrailsIgnoredProvider"/>
+    <notificationGroup id="Grails notifications" displayType="TOOL_WINDOW" toolWindowId="Grails Console" bundle="messages.GrailsBundle" key="notification.group.grails.notifications"/>
+    <notificationGroup id="grails" displayType="BALLOON" bundle="messages.GrailsBundle" key="notification.group.grails"/>
+    <notificationGroup id="Grails Configure" displayType="BALLOON" key="notification.group.display.name.grails.configure"/>
+    <postStartupActivity implementation="org.jetbrains.plugins.groovy.mvc.MvcModuleStructureSynchronizer$MyPostStartUpActivity"/>
+    <roots.watchedRootsProvider implementation="org.jetbrains.plugins.groovy.mvc.MvcWatchedRootProvider"/>
+    <pathMacroContributor implementation="org.jetbrains.plugins.groovy.mvc.MvcPathMacros"/>
+
+    <dependencySupport coordinate="org.grails:grails-core" kind="java" displayName="Grails Framework"/>
+    <dependencySupport coordinate="org.grails:grails-datastore-gorm" kind="java" displayName="GORM"/>
+  </extensions>
+
+  <actions>
+    <action id="grails.ViewGrailsEditorToolbarAction" class="org.jetbrains.plugins.grails.editor.ViewGrailsEditorToolbarAction">
+      <add-to-group group-id="UIToggleActions" anchor="after" relative-to-action="ViewToolButtons"/>
+    </action>
+
+    <group id="grails.toolbar">
+      <group
+        id="grails.goto.domain"
+        class="org.jetbrains.plugins.grails.editor.toolbar.GoToDomainAction"
+        icon="AllIcons.Nodes.DataTables"/>
+      <group
+        id="grails.goto.controller"
+        class="org.jetbrains.plugins.grails.editor.toolbar.GoToControllerAction"
+        icon="AllIcons.Nodes.Controller"/>
+      <group
+        id="grails.goto.service"
+        class="org.jetbrains.plugins.grails.editor.toolbar.GoToServiceAction"
+        icon="AllIcons.FileTypes.Config"/>
+      <group
+        id="grails.goto.view"
+        class="org.jetbrains.plugins.grails.editor.toolbar.GoToViewAction"
+        icon="org.jetbrains.plugins.grails.GroovyMvcIcons.Gsp_logo"/>
+      <group
+        id="grails.goto.test"
+        class="org.jetbrains.plugins.grails.editor.toolbar.GoToTestAction"
+        icon="org.jetbrains.plugins.grails.GroovyMvcIcons.Grails_test"/>
+    </group>
+
+    <group id="Grails" class="org.jetbrains.plugins.grails.actions.GrailsActionGroup"
+           icon="org.jetbrains.plugins.grails.GroovyMvcIcons.Grails">
+      <action
+        id="Grails.DomainClass"
+        class="org.jetbrains.plugins.grails.actions.NewGrailsDomainClassAction"
+        icon="AllIcons.Nodes.DataTables"/>
+      <action
+        id="Grails.Controller"
+        class="org.jetbrains.plugins.grails.actions.NewGrailsControllerAction"
+        icon="AllIcons.Nodes.Controller"/>
+      <action
+        id="Grails.Service"
+        class="org.jetbrains.plugins.grails.actions.NewGrailsServiceAction"
+        icon="AllIcons.FileTypes.Config"/>
+      <action
+        id="Grails.Interceptor"
+        class="org.jetbrains.plugins.grails.actions.NewGrailsInterceptorAction"
+        icon="AllIcons.General.Filter"/>
+      <action
+        id="Grails.Filter"
+        class="org.jetbrains.plugins.grails.actions.NewGrailsFilterAction"
+        icon="AllIcons.General.Filter"/>
+      <action
+        id="Grails.Script"
+        class="org.jetbrains.plugins.grails.actions.NewGrailsScriptAction"
+        icon="org.jetbrains.plugins.grails.GroovyMvcIcons.Grails"/>
+      <action
+        id="Grails.TagLib"
+        class="org.jetbrains.plugins.grails.actions.NewGrailsTagLibAction"
+        icon="org.jetbrains.plugins.grails.GroovyMvcIcons.Taglib"/>
+      <action id="Grails.GSP" class="org.jetbrains.plugins.grails.actions.NewGspAction"
+              icon="org.jetbrains.plugins.grails.GroovyMvcIcons.Gsp_logo"/>
+      <separator/>
+      <add-to-group group-id="NewGroup" anchor="first"/>
+    </group>
+
+    <!-- old id kept to avoid breaking user keymaps -->
+    <action id="Mvc.RunTarget" class="org.jetbrains.plugins.grails.actions.GrailsRunCommandAction">
+      <keyboard-shortcut keymap="$default" first-keystroke="ctrl alt G"/>
+      <add-to-group group-id="ProjectViewPopupMenu" anchor="after" relative-to-action="AddFrameworkSupport"/>
+      <add-to-group group-id="NavbarPopupMenu" anchor="after" relative-to-action="AddFrameworkSupport"/>
+      <add-to-group group-id="ToolsMenu" anchor="last"/>
+    </action>
+
+    <group id="Grails.Actions" class="org.jetbrains.plugins.grails.actions.GrailsActionGroup"
+           icon="org.jetbrains.plugins.grails.GroovyMvcIcons.Grails" popup="true">
+      <action id="Grails.Plugins"
+              class="org.jetbrains.plugins.groovy.mvc.plugins.MvcPluginsAction"
+              icon="org.jetbrains.plugins.grails.GroovyMvcIcons.Groovy_mvc_plugin"/>
+      <action id="Grails.ChangeSDK" class="org.jetbrains.plugins.grails.actions.GrailsConfigureSDKAction"
+              icon="org.jetbrains.plugins.grails.GroovyMvcIcons.Grails"/>
+      <action id="Grails.UpdateSettings" class="org.jetbrains.plugins.grails.actions.UpdateGrailsSettingsAction"
+              icon="AllIcons.Actions.Refresh"/>
+      <reference ref="Mvc.RunTarget"/>
+      <add-to-group group-id="ProjectViewPopupMenu" anchor="after" relative-to-action="AddFrameworkSupport"/>
+      <add-to-group group-id="NavbarPopupMenu" anchor="after" relative-to-action="AddFrameworkSupport"/>
+      <add-to-group group-id="ToolsMenu" anchor="last"/>
+    </group>
+
+    <action id="Generate.GrailsView"
+            class="org.jetbrains.plugins.grails.actions.GrailsGenerateViewAction">
+      <add-to-group group-id="GenerateGroup"/>
+    </action>
+
+    <action id="Grails.WAR" class="org.jetbrains.plugins.grails.actions.GrailsWarAction">
+      <add-to-group group-id="BuildMenu" anchor="last"/>
+    </action>
+  </actions>
+
+  <xi:include href="grails-method-descriptors.xml"/>
+
+  <!-- GSON -->
+  <extensions defaultExtensionNs="com.intellij">
+    <fileType name="Groovy" extensions="gson"/>
+    <psi.referenceContributor language="Groovy" implementation="org.jetbrains.plugins.grails.gson.GsonReferenceContributor"/>
+    <readWriteAccessDetector id="gson" order="before defaultGroovy"
+                             implementation="org.jetbrains.plugins.grails.gson.GsonRWAccessDetector"/>
+    <referencesSearch implementation="org.jetbrains.plugins.grails.gson.GsonTemplateReferenceQueryExecutor"/>
+  </extensions>
+  <extensions defaultExtensionNs="org.intellij.groovy">
+    <expectedPackageNameProvider implementation="org.jetbrains.plugins.grails.gson.GsonExpectedPackageProvider"/>
+    <importContributor implementation="org.jetbrains.plugins.grails.gson.GsonImportContributor"/>
+    <astTransformationSupport implementation="org.jetbrains.plugins.grails.gson.GsonTransformationSupport" order="before defaultSupport"/>
+    <membersContributor implementation="org.jetbrains.plugins.grails.gson.GrailsStreamingJsonBuilderContributor"/>
+    <membersContributor implementation="org.jetbrains.plugins.grails.gson.GsonTemplateRendererContributor"/>
+    <namedArgumentProvider implementation="org.jetbrains.plugins.grails.gson.GsonModelArgumentsProvider"/>
+    <namedArgumentProvider implementation="org.jetbrains.plugins.grails.gson.GsonRenderNamedArgumentsProvider"/>
+    <positionManagerDelegate implementation="org.jetbrains.plugins.grails.gson.GsonPositionManagerDelegate"/>
+  </extensions>
+</idea-plugin>
diff --git a/resources/META-INF/pluginIcon.svg b/resources/META-INF/pluginIcon.svg
new file mode 100644
index 0000000..947f325
--- /dev/null
+++ b/resources/META-INF/pluginIcon.svg
@@ -0,0 +1,3 @@
+<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M19.9999 3C16.6376 3.00003 13.3508 3.99709 10.5552 5.8651C7.75957 7.73311 5.58066 10.3882 4.29399 13.4945C3.00733 16.6009 2.67071 20.019 3.32669 23.3167C3.98268 26.6144 5.60181 29.6435 7.97933 32.021C10.3569 34.3984 13.386 36.0175 16.6837 36.6734C19.9814 37.3293 23.3995 36.9926 26.5058 35.7059C29.6122 34.4191 32.2672 32.2402 34.1351 29.4445C36.0031 26.6488 37.0001 23.362 37 19.9997C37 17.7673 36.5603 15.5566 35.706 13.4941C34.8517 11.4316 33.5994 9.55749 32.0208 7.9789C30.4422 6 [...]
+</svg>
diff --git a/resources/fileTemplates/internal/Groovy Server Page.gsp.ft b/resources/fileTemplates/internal/Groovy Server Page.gsp.ft
new file mode 100644
index 0000000..2f59b5e
--- /dev/null
+++ b/resources/fileTemplates/internal/Groovy Server Page.gsp.ft	
@@ -0,0 +1,16 @@
+<%--
+  Created by IntelliJ IDEA.
+  User: ${USER}
+  Date: ${DATE}
+  Time: ${TIME}
+--%>
+
+<%@ page contentType="text/html;charset=UTF-8" %>
+<html>
+<head>
+  <title></title>
+</head>
+<body>
+
+</body>
+</html>
\ No newline at end of file
diff --git a/resources/fileTemplates/internal/Groovy Server Page.gsp.html b/resources/fileTemplates/internal/Groovy Server Page.gsp.html
new file mode 100644
index 0000000..3afe747
--- /dev/null
+++ b/resources/fileTemplates/internal/Groovy Server Page.gsp.html	
@@ -0,0 +1,30 @@
+<!--
+  ~  Copyright 2000-2007 JetBrains s.r.o.
+  ~  Licensed under the Apache License, Version 2.0 (the "License");
+  ~  you may not use this file except in compliance with the License.
+  ~  You may obtain a copy of the License at
+  ~
+  ~  http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~  Unless required by applicable law or agreed to in writing, software
+  ~  distributed under the License is distributed on an "AS IS" BASIS,
+  ~  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~  See the License for the specific language governing permissions and
+  ~  limitations under the License.
+  ~
+  -->
+
+<html>
+<body>
+<table width="100%" border="0" cellpadding="5" cellspacing="0" style="border-collapse: collapse" bordercolor="#111111">
+  <tr>
+    <td colspan="3">
+      <font face="verdana" size="-1">
+        This is a built-in template used each time you create new
+        Groovy Server Page
+      </font>
+    </td>
+  </tr>
+</table>
+</body>
+</html>
\ No newline at end of file
diff --git a/resources/icons/action_method.svg b/resources/icons/action_method.svg
new file mode 100644
index 0000000..cae4160
--- /dev/null
+++ b/resources/icons/action_method.svg
@@ -0,0 +1,6 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
+  <g fill="none" fill-rule="evenodd">
+    <path fill="#F98B9E" fill-opacity=".6" d="M8,15 C4.134,15 1,11.866 1,8 C1,4.134 4.134,1 8,1 C11.866,1 15,4.134 15,8 C15,11.866 11.866,15 8,15 Z"/>
+    <path fill="#231F20" fill-opacity=".7" d="M2.9834,1.2 L4.1,4 L1.8964,4 L2.9834,1.2 Z M4.8154,5.9999 L6.0004,5.9999 L3.4464,-0.0001 L2.5534,-0.0001 L0.0004,5.9999 L1.1514,5.9999 L1.5654,4.9999 L4.4064,4.9999 L4.8154,5.9999 Z" transform="translate(5 5)"/>
+  </g>
+</svg>
diff --git a/resources/icons/grails.svg b/resources/icons/grails.svg
new file mode 100644
index 0000000..c8ce550
--- /dev/null
+++ b/resources/icons/grails.svg
@@ -0,0 +1,3 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
+  <path fill="#FEB672" d="M7,0.00904615385 C3.13901538,0.00904615385 0.00893846154,3.13869231 0.00893846154,6.99989231 C0.00893846154,10.8609846 3.13912308,13.9909538 7,13.9909538 C10.8612,13.9909538 13.9911692,10.8609846 13.9911692,6.99989231 C13.9911692,3.13869231 10.8612,0.00904615385 7,0.00904615385 Z M5.14047692,7.61713822 C5.03569231,7.66323053 4.90193846,7.71556899 4.84970769,7.7433536 C4.75332308,7.79472283 4.60416923,7.9071536 4.52038462,8.03616899 C4.45835385,8.13212283 4.40063 [...]
+</svg>
diff --git a/resources/icons/grails_13.svg b/resources/icons/grails_13.svg
new file mode 100644
index 0000000..5e37cb3
--- /dev/null
+++ b/resources/icons/grails_13.svg
@@ -0,0 +1,3 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 0 13 13">
+  <path fill="#6E6E6E" fill-rule="evenodd" d="M6.0000126,0.00986853147 C2.69174424,0.00986853147 0.00975104895,2.69147569 0.00975104895,5.99990772 C0.00975104895,9.30824748 2.69183652,11.9901315 6.0000126,11.9901315 C9.30846552,11.9901315 11.9903664,9.30824748 11.9903664,5.99990772 C11.9903664,2.69147569 9.30846552,0.00986853147 6.0000126,0.00986853147 Z M4.40668811,6.52878894 C4.31690385,6.56828269 4.20229743,6.61312839 4.15754372,6.63693537 C4.07495697,6.68095061 3.94715513,6.77728583  [...]
+</svg>
diff --git a/resources/icons/grails_13_dark.svg b/resources/icons/grails_13_dark.svg
new file mode 100644
index 0000000..2450067
--- /dev/null
+++ b/resources/icons/grails_13_dark.svg
@@ -0,0 +1,3 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 0 13 13">
+  <path fill="#AFB1B3" fill-rule="evenodd" d="M6.5000126,0.509868531 C3.19174424,0.509868531 0.509751049,3.19147569 0.509751049,6.49990772 C0.509751049,9.80824748 3.19183652,12.4901315 6.5000126,12.4901315 C9.80846552,12.4901315 12.4903664,9.80824748 12.4903664,6.49990772 C12.4903664,3.19147569 9.80846552,0.509868531 6.5000126,0.509868531 Z M4.90668811,7.02878894 C4.81690385,7.06828269 4.70229743,7.11312839 4.65754372,7.13693537 C4.57495697,7.18095061 4.44715513,7.27728583 4.37536463,7.3 [...]
+</svg>
diff --git a/resources/icons/grails_app.svg b/resources/icons/grails_app.svg
new file mode 100644
index 0000000..5491579
--- /dev/null
+++ b/resources/icons/grails_app.svg
@@ -0,0 +1,6 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
+  <g fill="none" fill-rule="evenodd">
+    <path fill="#FEB672" fill-rule="nonzero" d="M7.21526869,13.987703 C7.1437772,13.989865 7.07201579,13.9909538 7,13.9909538 C3.13912308,13.9909538 0.00893846154,10.8609846 0.00893846154,6.99989231 C0.00893846154,3.13869231 3.13901538,0.00904615385 7,0.00904615385 C10.8612,0.00904615385 13.9911692,3.13869231 13.9911692,6.99989231 C13.9911692,7.67259102 13.8961611,8.32309697 13.7188305,8.9387239 L12.2249195,8.85558898 L11.6688558,7.3925297 C11.7008039,7.36990328 11.7308726,7.3476546 11.7 [...]
+    <polygon fill="#9AA7B0" points="11.498 14.163 8.718 15.956 9.564 12.758 7 10.668 10.303 10.484 11.498 7.4 12.693 10.484 15.996 10.668 13.432 12.758 14.278 15.956"/>
+  </g>
+</svg>
diff --git a/resources/icons/grails_sdk.svg b/resources/icons/grails_sdk.svg
new file mode 100644
index 0000000..9d9ca46
--- /dev/null
+++ b/resources/icons/grails_sdk.svg
@@ -0,0 +1,6 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
+  <g fill="none" fill-rule="evenodd">
+    <path fill="#9AA7B0" fill-opacity=".8" d="M15.0002,7.25732729 C14.0492304,6.4718838 12.8297224,6 11.5,6 C8.46243388,6 6,8.46243388 6,11.5 C6,12.0201209 6.07219743,12.5233792 6.20711735,13.0003 L1.0002,13.0003 L1.0002,2.0513 C1.0002,2.0233 1.0232,2.0003 1.0512,2.0003 L4.9782,2.0003 C5.5322,2.0003 6.3042,2.3203 6.6962,2.7113 L7.9842,4.0003 L15.0002,4.0003 L15.0002,7.25732729 Z"/>
+    <path fill="#FEB672" fill-rule="nonzero" d="M4.5,0.00581538462 C2.01793846,0.00581538462 0.00574615385,2.01773077 0.00574615385,4.49993077 C0.00574615385,6.98206154 2.01800769,8.99418462 4.5,8.99418462 C6.9822,8.99418462 8.99432308,6.98206154 8.99432308,4.49993077 C8.99432308,2.01773077 6.9822,0.00581538462 4.5,0.00581538462 Z M3.30459231,4.89673171 C3.23723077,4.92636248 3.15124615,4.96000863 3.11766923,4.97787017 C3.05570769,5.01089325 2.95982308,5.08317017 2.90596154,5.16610863 C2 [...]
+  </g>
+</svg>
diff --git a/resources/icons/grails_test.svg b/resources/icons/grails_test.svg
new file mode 100644
index 0000000..bb7bcfd
--- /dev/null
+++ b/resources/icons/grails_test.svg
@@ -0,0 +1,7 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
+  <g fill="none" fill-rule="evenodd">
+    <path fill="#FEB672" fill-rule="nonzero" d="M7.47506794,13.9750679 C7.31807533,13.985602 7.15966282,13.9909538 7,13.9909538 C3.13912308,13.9909538 0.00893846154,10.8609846 0.00893846154,6.99989231 C0.00893846154,3.13869231 3.13901538,0.00904615385 7,0.00904615385 C10.8612,0.00904615385 13.9911692,3.13869231 13.9911692,6.99989231 C13.9911692,7.75535098 13.871346,8.48282038 13.6496668,9.16433234 L11.7945676,7.29657721 C11.9129546,7.19653858 12.0989724,7.01166313 12.1763385,6.91541553 C [...]
+    <polygon fill="#62B543" points="13.75 10.75 17.25 14.25 10.25 14.25" transform="rotate(90 13.75 12.5)"/>
+    <polygon fill="#F26522" points="9.25 10.75 12.75 14.25 5.75 14.25" transform="rotate(-90 9.25 12.5)"/>
+  </g>
+</svg>
diff --git a/resources/icons/groovy_mvc_plugin.svg b/resources/icons/groovy_mvc_plugin.svg
new file mode 100644
index 0000000..d404c02
--- /dev/null
+++ b/resources/icons/groovy_mvc_plugin.svg
@@ -0,0 +1,7 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
+  <g fill="none" fill-rule="evenodd">
+    <path fill="#62B543" fill-opacity=".6" d="M14.9291115,9 L10.4931667,9 C9.1165,9 8,10.076 8,11.403 L8,14.596 C8,14.7336514 8.01199566,14.8685954 8.03503331,14.9999142 C8.02336227,14.9999714 8.01168448,15 8,15 C4.134,15 1,11.866 1,8 C1,4.134 4.134,1 8,1 C11.866,1 15,4.134 15,8 C15,8.33952142 14.9758282,8.67339709 14.9291115,9 Z"/>
+    <path fill="#231F20" fill-opacity=".7" d="M8.03503148,10.9993761 C6.25884694,10.9670938 5.0956,9.68570866 5.0956,8.01657459 L5.0956,8 C5.0956,6.37569061 6.33869392,5 8.08731271,5 C9.09836243,5 9.71990939,5.28176796 10.3165945,5.78729282 L9.67018564,6.55801105 C9.22267182,6.18508287 8.7834453,5.94475138 8.04587624,5.94475138 C6.97681547,5.94475138 6.16466077,6.87292818 6.16466077,7.98342541 L6.16466077,8 C6.16466077,9.19337017 6.95195359,10.0718232 8.13703646,10.0718232 C8.23614421,10 [...]
+    <path fill="#9AA7B0" d="M13,10 L13,15.625 L13,16 L12,16 L10.4246667,16 C9.638,16 9,15.193 9,14.197 L9,11.80225 C9,10.807 9.638,10 10.4246667,10 L13,10 Z M14,12 L14,11 L16,11 L16,12 L14,12 Z M14,15 L14,14 L16,14 L16,15 L14,15 Z"/>
+  </g>
+</svg>
diff --git a/resources/icons/gsp_logo.svg b/resources/icons/gsp_logo.svg
new file mode 100644
index 0000000..3077af4
--- /dev/null
+++ b/resources/icons/gsp_logo.svg
@@ -0,0 +1,10 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
+  <g fill="none" fill-rule="evenodd">
+    <polygon fill="#F26522" fill-opacity=".7" points="1 16 16 16 16 9 1 9"/>
+    <polygon fill="#9AA7B0" fill-opacity=".8" points="7 1 3 5 7 5"/>
+    <polygon fill="#9AA7B0" fill-opacity=".8" points="8 1 8 6 3 6 3 8 13 8 13 1"/>
+    <path fill="#231F20" fill-opacity=".7" d="M1.9,2.5 C2.564,2.5 3,2.38439941 3,1.75 C3,1.11560059 2.557,1 1.9,1 L1,1 L1,2.5 L1.9,2.5 Z M0,0 L1.971,0 C3.142,0 4,0.642 4,1.65 C4,3.28582764 2.971,3.5 1.872,3.5 L1,3.5 L1,5 L0,5 L0,0 Z" transform="translate(11 10)"/>
+    <path fill="#231F20" fill-opacity=".7" d="M0.972767969,1.50152588 C0.972767969,1.13305664 1.284,1 1.845,1 C1.85033333,1 2.23533333,1 3,1 L3,0 C2.26266667,0 1.88266667,0 1.86,0 C0.778,0 0,0.45916748 0,1.45 C0,2.31452637 0.419555664,2.69049072 1.47125244,2.91607666 C2.24158869,3.08131157 2.496155,3.22862939 2.496155,3.548 C2.496155,3.86737061 2.13842773,4 1.47125244,4 C1.46058577,4 1.07016829,4 0.3,4 L0.3,5 C1.07550163,5 1.46591911,5 1.47125244,5 C3.5,5 3.5,4 3.5,3.548 C3.5,2.91607666  [...]
+    <path fill="#231F20" fill-opacity=".7" d="M2.51990608,5.016 C1.00747514,5.016 0.013,3.93865193 0.013,2.52981215 L0.013,2.516 C0.013,1.16240884 1.0489116,0.016 2.50609392,0.016 C3.34863536,0.016 3.86659116,0.25080663 4.36382873,0.672077348 L3.8251547,1.31434254 C3.45222652,1.00356906 3.08620442,0.803292818 2.47156354,0.803292818 C1.58067956,0.803292818 0.903883978,1.57677348 0.903883978,2.50218785 L0.903883978,2.516 C0.903883978,3.51047514 1.55996133,4.24251934 2.54753039,4.24251934 C [...]
+  </g>
+</svg>
diff --git a/resources/icons/taglib.svg b/resources/icons/taglib.svg
new file mode 100644
index 0000000..4b5f1e5
--- /dev/null
+++ b/resources/icons/taglib.svg
@@ -0,0 +1,9 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
+  <g fill="none" fill-rule="evenodd">
+    <polygon fill="#9AA7B0" fill-opacity=".8" points="1 13 3 13 3 3 1 3"/>
+    <polygon fill="#62B543" points="4 13 6 13 6 3 4 3"/>
+    <path fill="#40B6E0" d="M10,6.20703249 L10,3 L12,3 L12,6.02242151 C11.835319,6.00758131 11.6685448,6 11.5,6 C10.9799881,6 10.4768318,6.07216716 10,6.20703249 Z"/>
+    <path fill="#9AA7B0" fill-opacity=".8" d="M7,8.33692087 L7,0.999 L9,0.999 L9,6.59970959 C8.20018089,7.00857314 7.51365933,7.60749845 7,8.33692087 Z M13,6.20703249 L13,5 L15,5 L15,7.25716211 C14.4209679,6.77896226 13.7423893,6.41700712 13,6.20703249 Z"/>
+    <path fill="#FEB672" fill-rule="nonzero" d="M4.5,0.00581538462 C2.01793846,0.00581538462 0.00574615385,2.01773077 0.00574615385,4.49993077 C0.00574615385,6.98206154 2.01800769,8.99418462 4.5,8.99418462 C6.9822,8.99418462 8.99432308,6.98206154 8.99432308,4.49993077 C8.99432308,2.01773077 6.9822,0.00581538462 4.5,0.00581538462 Z M3.30459231,4.89673171 C3.23723077,4.92636248 3.15124615,4.96000863 3.11766923,4.97787017 C3.05570769,5.01089325 2.95982308,5.08317017 2.90596154,5.16610863 C2 [...]
+  </g>
+</svg>
diff --git a/resources/inspectionDescriptions/GspInspection.html b/resources/inspectionDescriptions/GspInspection.html
new file mode 100644
index 0000000..5bbc5a1
--- /dev/null
+++ b/resources/inspectionDescriptions/GspInspection.html
@@ -0,0 +1,10 @@
+<html>
+<body>
+Reports inconsistencies in attributes of GSP tags.
+<p><b>Examples:</b></p>
+<ul>
+  <li>an attribute <code>contextPath</code> in a tag <code>&lt;g:render&gt;</code> is marked as unused if an attribute <code>plugin</code> is present</li>
+  <li>a tag <code>&lt;if&gt;</code> is marked with a warning if it has neither <code>test</code> attribute nor <code>env</code> attribute</li>
+</ul>
+</body>
+</html>
diff --git a/resources/inspectionDescriptions/metaInformation.json b/resources/inspectionDescriptions/metaInformation.json
new file mode 100644
index 0000000..abce0bb
--- /dev/null
+++ b/resources/inspectionDescriptions/metaInformation.json
@@ -0,0 +1,8 @@
+{
+  "inspections": [
+    {
+      "id": "GspInspection",
+      "codeQualityCategory": "Code Style"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/resources/intentionDescriptions/GenerateViewIntention/after.gsp.template b/resources/intentionDescriptions/GenerateViewIntention/after.gsp.template
new file mode 100644
index 0000000..da1e6ea
--- /dev/null
+++ b/resources/intentionDescriptions/GenerateViewIntention/after.gsp.template
@@ -0,0 +1,9 @@
+%{--new file: grails-app/views/book/index.gsp--}%
+<%@ page contentType="text/html;charset=UTF-8" %>
+<html>
+<head>
+    <title></title>
+</head>
+<body>
+</body>
+</html>
diff --git a/resources/intentionDescriptions/GenerateViewIntention/before.groovy.template b/resources/intentionDescriptions/GenerateViewIntention/before.groovy.template
new file mode 100644
index 0000000..39891bc
--- /dev/null
+++ b/resources/intentionDescriptions/GenerateViewIntention/before.groovy.template
@@ -0,0 +1,9 @@
+// grails-app/controllers/appPackage/BookController.groovy
+package appPackage
+
+class BookController {
+
+    def index() {
+        render "Some text"
+    }
+}
diff --git a/resources/intentionDescriptions/GenerateViewIntention/description.html b/resources/intentionDescriptions/GenerateViewIntention/description.html
new file mode 100644
index 0000000..003db56
--- /dev/null
+++ b/resources/intentionDescriptions/GenerateViewIntention/description.html
@@ -0,0 +1,5 @@
+<html>
+<body>
+Creates a Grails view by a Grails action using <i>Groovy Server Page</i> file template.
+</body>
+</html>
diff --git a/resources/intentionDescriptions/GrailsCreateActionByUsageIntention/after.groovy.template b/resources/intentionDescriptions/GrailsCreateActionByUsageIntention/after.groovy.template
new file mode 100644
index 0000000..bfbef5b
--- /dev/null
+++ b/resources/intentionDescriptions/GrailsCreateActionByUsageIntention/after.groovy.template
@@ -0,0 +1,8 @@
+class MeetingController {
+
+    def index() {
+        redirect(action: "foo")
+    }
+
+    <spot>def foo() {}</spot>
+}
diff --git a/resources/intentionDescriptions/GrailsCreateActionByUsageIntention/before.groovy.template b/resources/intentionDescriptions/GrailsCreateActionByUsageIntention/before.groovy.template
new file mode 100644
index 0000000..1d8b75b
--- /dev/null
+++ b/resources/intentionDescriptions/GrailsCreateActionByUsageIntention/before.groovy.template
@@ -0,0 +1,6 @@
+class MeetingController {
+
+    def index() {
+        redirect(action: "<spot>foo</spot>")
+    }
+}
diff --git a/resources/intentionDescriptions/GrailsCreateActionByUsageIntention/description.html b/resources/intentionDescriptions/GrailsCreateActionByUsageIntention/description.html
new file mode 100644
index 0000000..65021a5
--- /dev/null
+++ b/resources/intentionDescriptions/GrailsCreateActionByUsageIntention/description.html
@@ -0,0 +1,5 @@
+<html>
+<body>
+Creates a missing Grails action by its reference.
+</body>
+</html>
diff --git a/resources/intentionDescriptions/MakeNullableIntention/after.groovy.template b/resources/intentionDescriptions/MakeNullableIntention/after.groovy.template
new file mode 100644
index 0000000..b197918
--- /dev/null
+++ b/resources/intentionDescriptions/MakeNullableIntention/after.groovy.template
@@ -0,0 +1,8 @@
+class City {
+
+    String name
+
+    static constraints = {
+        <spot>name(nullable: true)</spot>
+    }
+}
diff --git a/resources/intentionDescriptions/MakeNullableIntention/before.groovy.template b/resources/intentionDescriptions/MakeNullableIntention/before.groovy.template
new file mode 100644
index 0000000..ddd71d4
--- /dev/null
+++ b/resources/intentionDescriptions/MakeNullableIntention/before.groovy.template
@@ -0,0 +1,7 @@
+class City {
+
+    String <spot>name</spot>
+
+    static constraints = {
+    }
+}
diff --git a/resources/intentionDescriptions/MakeNullableIntention/description.html b/resources/intentionDescriptions/MakeNullableIntention/description.html
new file mode 100644
index 0000000..57d5001
--- /dev/null
+++ b/resources/intentionDescriptions/MakeNullableIntention/description.html
@@ -0,0 +1,5 @@
+<html>
+<body>
+Adds <code>nullable</code> constraint for a property of a Grails domain class.
+</body>
+</html>
diff --git a/resources/intentionDescriptions/MakeUniqueIntention/after.groovy.template b/resources/intentionDescriptions/MakeUniqueIntention/after.groovy.template
new file mode 100644
index 0000000..43f3b41
--- /dev/null
+++ b/resources/intentionDescriptions/MakeUniqueIntention/after.groovy.template
@@ -0,0 +1,8 @@
+class City {
+
+    String name
+
+    static constraints = {
+        <spot>name(unique: true)</spot>
+    }
+}
diff --git a/resources/intentionDescriptions/MakeUniqueIntention/before.groovy.template b/resources/intentionDescriptions/MakeUniqueIntention/before.groovy.template
new file mode 100644
index 0000000..ddd71d4
--- /dev/null
+++ b/resources/intentionDescriptions/MakeUniqueIntention/before.groovy.template
@@ -0,0 +1,7 @@
+class City {
+
+    String <spot>name</spot>
+
+    static constraints = {
+    }
+}
diff --git a/resources/intentionDescriptions/MakeUniqueIntention/description.html b/resources/intentionDescriptions/MakeUniqueIntention/description.html
new file mode 100644
index 0000000..56b5e9b
--- /dev/null
+++ b/resources/intentionDescriptions/MakeUniqueIntention/description.html
@@ -0,0 +1,5 @@
+<html>
+<body>
+Adds <code>unique</code> constraint for a property of a Grails domain class.
+</body>
+</html>
diff --git a/resources/liveTemplates/gsp.xml b/resources/liveTemplates/gsp.xml
new file mode 100644
index 0000000..e12661d
--- /dev/null
+++ b/resources/liveTemplates/gsp.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<templateSet group="GSP">
+  <template name="each"
+            resource-bundle="messages.GrailsBundle" key="livetemplate.description.surround.gsp.each"
+            toReformat="true" toShortenFQNames="false" value="&lt;g:each in=&quot;$END$&quot; &gt;&#10;$SELECTION$&#10;&lt;/g:each&gt;">
+    <context>
+      <option name="JAVA_CODE" value="false"/>
+      <option name="JAVA_COMMENT" value="false"/>
+      <option name="JAVA_STRING" value="false"/>
+      <option name="XML" value="false"/>
+      <option name="HTML" value="false"/>
+      <option name="JSP" value="false"/>
+      <option name="GSP" value="true"/>
+      <option name="COMPLETION" value="false"/>
+      <option name="OTHER" value="false"/>
+    </context>
+  </template>
+
+  <template name="if"
+            resource-bundle="messages.GrailsBundle" key="livetemplate.description.surround.gsp.if"
+            toReformat="true" toShortenFQNames="false" value="&lt;g:if test=&quot;$END$&quot; &gt;&#10;$SELECTION$&#10;&lt;/g:if&gt;">
+    <context>
+      <option name="JAVA_CODE" value="false"/>
+      <option name="JAVA_COMMENT" value="false"/>
+      <option name="JAVA_STRING" value="false"/>
+      <option name="XML" value="false"/>
+      <option name="HTML" value="false"/>
+      <option name="JSP" value="false"/>
+      <option name="GSP" value="true"/>
+      <option name="COMPLETION" value="false"/>
+      <option name="OTHER" value="false"/>
+    </context>
+  </template>
+
+</templateSet>
+
diff --git a/resources/messages/GrailsBundle.properties b/resources/messages/GrailsBundle.properties
new file mode 100644
index 0000000..e1d8f11
--- /dev/null
+++ b/resources/messages/GrailsBundle.properties
@@ -0,0 +1,311 @@
+library.name=Grails
+language.gsp=GSP
+filetype.gsp.description=Groovy Server Pages
+
+#########################################################################################################################
+# Grails plugin menu actions
+########################################################################################################################
+action.Grails.NewDomainClass.text=Grails Domain Class
+action.Grails.NewController.text=Grails Controller
+action.Grails.NewService.text=Grails Service
+action.Grails.NewInterceptor.text=Grails Interceptor
+action.Grails.NewFilter.text=Grails Filter
+action.Grails.NewScript.text=Grails Script
+action.Grails.NewTagLib.text=Grails TagLib
+########################################################################################################################
+# Create GSP action
+########################################################################################################################
+action.Grails.GSP.text=GSP
+action.Grails.GSP.description=Creates new Groovy Server Page
+gsp.dlg.prompt=Enter name for new Groovy Server Page
+gsp.dlg.title=New Groovy Server Page
+
+##########################################################################################################################
+# Grails artefact generators
+########################################################################################################################
+generate.dlg.exist=The following files already exist:\
+{0}\
+They may be overwritten. Continue?
+
+########################################################################################################################
+# GSP parsing
+########################################################################################################################
+expr.closing.end.tag.expected=Expression closing tag expected
+closing.grails.tag.expected=Closing Grails tag for ''<{0}>'' expected
+closing.tag.brace.expected='>' expected
+closing.tag.brace.expected1='>' or '/>' expected
+script.end.tag.expected=Scriptlet closing tag expected
+declaraion.expected=Class level declaration expected
+wrong.attributes=Wrong grails tag attributes
+clos.tag.in.wrong.place=Closing tag in wrong place
+equal.expected='=' expected
+delim.val.expected=''' or '"' expected
+gsp.unparseable.content=Cannot parse GSP content
+quote.expected=Quote expected
+attribute.value.expected=Attribute value expected
+expected.attribute.eq.sign='=' sign expected
+
+########################################################################################################################
+# Intentions & error messages
+########################################################################################################################
+fork.mode.warning.2.3.5=<html><body>\
+  IDEA doesn''t support debugging in fork mode for Grails version ''{0}'' due to bug in Grails.\
+  <br/>\
+  Please set <code>grails.project.fork.run</code> and <code>grails.project.fork.test</code> to <code>false</code> \
+  or upgrade Grails to 2.3.10.\
+  </body></html>
+fork.mode.warning.2.3.10=<html><body>\
+  IDEA doesn''t support debugging tests in fork mode for Grails version ''{0}'' due to bug in Grails. \
+  <br/>\
+  Please set <code>grails.project.fork.test</code> to <code>false</code> \
+  or upgrade Grails to 2.3.10.\
+  </body></html>
+
+##########################################################################################################################
+# Domain Classes Relations
+########################################################################################################################
+domain.classes.relations.has.many=1..*
+domain.classes.relations.belongs.to=1..1
+domain.classes.dependencies=Domain classes dependencies
+add.field.to.class=Add field to ''{0}''
+create.relation=Create Relation
+such.relation.already.defined=Such Relation Already Defined
+Relation.already.defined.Could.you.create.it.in.any.way=Relation has been already defined. Could you create it in any way?
+domain.classes.relations.strong=*..1
+destination.class.cannot.be.resolved=Destination class is in the default package, it cannot be resolved. Create relation anywhere?
+
+#annotator
+domain.classes.relations.strong.strong=1..1
+domain.classes.relations.has.many.to.many=*..*
+must.be.domain.class.name=Domain class name expected
+duplicate.property.name=Duplicate property
+domain.annotator.duplicate.type=Duplicate type
+domain.annotator.class.name.expected=Class name expected
+must.contain.map='java.util.Map' expected
+property.is.abmbigous=Property ''{0}'' is already declared with type ''{1}''
+mapped.by.is.used.without.has.many=Property 'mappedBy' is used without ''hasMany'' property
+property.is.absent.in.has.many=Property ''{0}'' does not exist in ''hasMany'' property
+property.is.absent=Domain class ''{0}'' does not have property ''{1}''
+property.has.wrong.type=Property ''{0}'' has incompatible type ''{1}''
+
+########################################################################################################################
+# Upgrade grails application
+########################################################################################################################
+grails.upgrade.app=Upgrade Application
+grails.malformed.version=<html>Application <b>{2}</b> has Grails version <b>{0}</b>, but current Grails SDK version is <b>{1}</b>. Upgrade application?<html>
+button.upgrade.yes=Yes, upgrade
+button.upgrade.no=Not now
+button.upgrade.ignore=Ignore this version ({0})
+
+########################################################################################################################
+# Grails Editor decorator messages
+########################################################################################################################
+no.controller.found=No Controller Class found
+generate.controller.first=Generate {0}Controller class first
+no.domain.class.found=No Domain Class found
+generate.domain.class.first=Generate {0} domain class first
+Warning=Warning
+
+########################################################################################################################
+# GSP Templates.
+########################################################################################################################
+livetemplate.description.surround.gsp.each=Surround with <g:each></g:each>
+livetemplate.description.surround.gsp.if=Surround with <g:if></g:if>
+
+########################################################################################################################
+# Grails 3
+########################################################################################################################
+grails.sdk.not.found.title=Grails SDK not found
+grails.sdk.not.found.content=\
+<html><body>\
+  IDEA cannot run Grails commands in {0}.\
+  <br/>\
+  <a href="configure_sdk">Configure Grails SDK</a>\
+</body></html>
+action.Grails.ChangeSDK.text=Configure Grails SDK
+unresolved.pattern.cannot.resolve.template.0.gsp=Cannot resolve template ''_{0}.gsp''
+quick.fix.family.name.create.template.0=Create template ''{0}''
+task.queue.title.grails.background.tasks=Grails background tasks
+checkbox.tooltip.text.html.nbsp.0.html=<html>&nbsp;{0}</html>
+intention.text.create.action=Create action
+intention.text.extract.selected.text.to.message.properties=Extract selected text to message.properties
+notification.content.failed.to.create.0.see.log.for.more.details=Failed to create {0}. See log for more details.
+notification.title.grails.setting.synchronization.failed=Grails setting synchronization failed
+notification.title.failed.to.run.grails.command=Failed to run grails command
+dialog.title.conflict=Conflict
+dialog.message.name=Name:
+quick.fix.don.t.check.message.code=Don't check message code if default value is specified
+label.text.ignore.if.default.value.specified=Ignore if default value specified
+intention.text.create.view.gsp.page=Generate GSP view
+dialog.title.grails.application.forge.error=Grails Application Forge Error
+dialog.message.initialization.failed=Initialization failed for ''{0}''\nPlease check network and proxy settings.\n\nError message:\n{1}
+failed.to.execute.grails.command=Failed to execute Grails command
+dialog.title.run.grails.command=Run Grails Command
+action.Grails.UpdateSettings.description=Refresh IntelliJ IDEA project structure so that it matches Grails build settings
+action.Grails.UpdateSettings.text=Synchronize Grails settings
+module.description.grails.application.module.builder=Grails application module builder
+notification.title.failed.to.create.grails.app.structure=Failed to create Grails app structure
+notification.content.failed.to.save.0.1=Failed to save {0}/{1}
+notification.title.io.error=IO error
+action.MyKillProcessAction.description=Kill process
+content.display.name.grails.executing=Grails:Executing\u2026
+action.text.auto.close.when.done=Auto Close when Done
+intention.text.make.property.unique=Make property unique
+error.message.identifier.expected=Identifier expected
+command.name.foo=foo
+intention.text.make.property.nullable=Make property nullable
+progress.indicator.text.0.in.1={0} in {1}
+notification.title.failed.to.execute.grails.war=Failed to execute 'grails war'
+notification.group.display.name.grails.configure=Grails SDK not found
+runner.configuration.label.text.add.empty.classpath=Add --classpath
+runner.configuration.label.text.add.classpath=Add --classpath: {0}
+failed.to.create.settings.gradle.notification.title=Failed to create settings.gradle
+module.builder.cannot.setup.root.path.error=Cannot setup root path
+forge.module.builder.title=Grails App Forge
+forge.module.builder.description=Grails Application Forge module builder
+forge.module.builder.dialog.message.invalid.name=Invalid name: {0}
+attache.source.from.grails.action.title=Attach Source From {0}
+run.configuration.error.application.not.selected=Grails application is not selected
+run.configuration.error.command.not.specified=Command is not specified
+run.configuration.error.cannot.execute.command=Cannot execute: {0}
+run.configuration.error.invalid.launch.url=Invalid launch URL
+new.artifact.action.title=New {0}
+sdk.home.form.label.text.sdk.not.selected=Grails SDK is not selected
+sdk.home.form.label.text.cannot.determine.grails.sdk.version=Cannot determine Grails SDK version
+sdk.home.form.label.text.version=Grails SDK version {0}
+sdk.home.form.label.text.version.range.error=Grails SDK version must be {0}
+domain.annotator.error.message.already.defined=Constraint ''{0}'' already defined for field ''{1}''
+synchronize.structure.annotator.error.text.outdated=Grails configuration may be outdated
+synchronize.structure.annotator.fix.text.apply.changes=Apply Grails changes to IDEA project structure
+synchronize.structure.annotator.fix.family.apply=Apply
+synchronize.structure.annotator.ignore.intention=Ignore
+status.bar.text.no.plugins=There are no plugins to install or uninstall
+install.uninstall.plugins.dialog.title=Install/Uninstall Plugins
+install.uninstall.plugins.dialog.to.remove=Plugins to remove:
+install.uninstall.plugins.dialog.to.install=Plugins to install:
+install.uninstall.plugins.dialog.no.version=No version
+install.uninstall.plugins.dialog.zip.release.version={0} (zip release)
+install.uninstall.plugins.dialog.latest.version={0} (latest)
+mvc.plugins.column.name.enable=Enable
+mvc.plugins.action.text.apply.changes=&Apply Changes
+mvc.plugins.dialog.title=Grails Plugins
+mvc.plugins.proxy.settings.title=Grails Proxy Settings
+mvc.plugins.dialog.message.failed.to.set.proxy=Failed to set proxy: Grails console is busy
+mvc.plugins.dialog.title.failed.to.execute.command=Can''t Execute Grails Command
+mvc.plugins.label.value.not.specified=(not specified)
+mvc.plugins.notification.title.failed.to.load.plugin.list=Failed to load plugin list
+mvc.plugins.notification.content.failed.to.parse=Failed to parse {0}
+mvc.plugins.notification.title.process.cannot.start.set.proxy.process=Process to set proxy cannot start
+mvc.plugins.action.text.add.custom.plugin=Add Custom Plugin
+mvc.plugins.action.description.add.custom.plugin=Add custom plugin
+mvc.plugins.dialog.message.failed.to.read.plugin.archive=Failed to read plugin archive
+mvc.plugins.dialog.title.failed.to.read.plugin.archive=Add Plugin Error
+mvc.plugins.dialog.message.failed.to.reload.plugin.list=Failed to reload plugin list: Grails console is busy
+mvc.plugins.action.text.reload.list=Reload Plugin List
+mvc.plugins.action.description.reload.list=Reload plugin list
+dialog.message.grails.sdk.not.defined=Grails SDK is not defined
+dialog.message.cannot.create.command.line.for=Cannot create command line for ''{0}'' in {1}
+dialog.message.grails.application.not.found=Grails application is not found
+dialog.message.cannot.execute.grails.command=Cannot execute Grails command: {0}
+dialog.message.command.parameters.are.empty=Grails command parameters are empty
+dialog.message.failed.to.open.server.socket=Failed to open server socket
+dialog.message.failed.to.create.temp.file=Failed to create temp file
+dialog.message.maven.cannot.execute=Cannot execute {0}
+dialog.message.cannot.find.grails.application.for.module=Cannot find Grails application for module: {0}
+dialog.message.cannot.create.application.with.grails.sdk=Cannot create application with Grails SDK: {0}
+dialog.message.no.module.root=There is no module root
+ignored.files.description.framework.dir=Grails Framework Directory
+project.view.application.node.version.label=(Grails {0})
+view.group.title=Grails Views
+dialog.message.valid.class.name.check=Please enter a valid class name.
+dialog.message.valid.script.name.check=Enter a valid script name
+progress.text.execute.add.proxy.command=Execute ''add-proxy'' command
+progress.text.execute.set.proxy.command=Execute ''set-proxy'' command
+progress.text.execute.clear.proxy.command=Execute ''clear-proxy'' command
+progress.text.updating.plugin.list=Updating plugin list\u2026
+tooltip.injected.spring.bean=Injected Spring bean: {0}
+column.info.mvc.plugin.name=Name
+column.info.mvc.plugin.title=Title
+column.info.mvc.plugin.version=Version
+progress.title.check.run.configuration=Check Grails Run Configuration
+progress.title.check.sdk=Check Grails SDK
+action.text.generate.views=Generate Views
+action.text.generate.async.controller=Generate Async Controller
+action.text.generate.controller=Generate Controller
+action.text.generate.tests.unit=Generate {0}Tests\u2026 (unit)
+action.text.generate.tests.integration=Generate {0}Tests\u2026 (integration)
+action.text.generate.controller.tests.unit="Generate {0}ControllerTests\u2026 (unit)"
+action.text.generate.controller.tests.integration=Generate {0}ControllerTests\u2026 (integration)
+action.text.go.to.artefact=Go to {0}
+notification.content.settings.sync.completed=Settings synchronization completed
+notification.content.settings.sync.failed=Settings synchronization failed
+action.text.go.to.tests={0} Tests
+action.text.go.to.views={0} Views
+tab.title.maven.settings=Maven Settings
+combobox.label.none.selected=[none]
+wizard.form.lable.create=&Create:
+wizard.form.checkbox.create.app=create-&app
+wizard.form.checkbox.create.plugin=create-&plugin
+wizard.form.label.options=&Options:
+wizard.form.label.add.as.module.to=Add as module to
+form.label.variable.name=Variable name
+relation.form.checkbox.belongsto=&belongsTo
+relation.form.checkbox.hasmany=&hasMany
+relation.form.checkbox.strong=&Strong
+run.form.label.application=&Application:
+run.form.label.command.line=&Command line:
+run.form.label.vm.options=&VM options:
+run.form.label.launch.browser=&Launch browser
+run.form.label.options=&Options:
+sdk.form.label.application=&Application:
+run.command.label.application=&Application:
+run.command.label.command=&Command:
+run.command.label.vm.options=&VM options:
+sdk.home.form.label.grails.sdk.home=Grails SDK &Home:
+mvc.plugins.form.versions=Versions:
+mvc.plugins.form.description.default=<html>  <head> </head>  <body> <p style="margin-top: 0"></p></body></html>
+group.grails.goto.domain.text=Go To Domain
+group.grails.goto.controller.text=Go To Controller
+group.grails.goto.service.text=Go To Service
+group.grails.goto.view.text=Go To View
+group.grails.goto.test.text=Go To Test
+group.Grails.text=Grails
+action.Grails.DomainClass.text=Create Domain
+action.Grails.DomainClass.description=Create new Grails domain class
+action.Grails.Controller.text=Create Controller
+action.Grails.Controller.description=Create new Grails controller class
+action.Grails.Service.text=Create Service
+action.Grails.Service.description=Create new Grails service class
+action.Grails.Interceptor.text=Create Interceptor
+action.Grails.Interceptor.description=Create new Grails interceptor class
+action.Grails.Filter.text=Create Filter
+action.Grails.Filter.description=Create new Grails filter class
+action.Grails.Script.text=Create Script
+action.Grails.Script.description=Create new Grails script
+action.Grails.TagLib.text=Create Taglib
+action.Grails.TagLib.description=Create new Grails tag library
+group.Grails.Actions.text=Grails
+action.Grails.Plugins.text=Plugins
+action.Grails.Plugins.description=Manage plugins from UI
+action.grails.ViewGrailsEditorToolbarAction.text="Grails Editor Toolbar"
+action.Generate.GrailsView.text=View
+action.Grails.WAR.text=Make Grails Web Archive
+action.Grails.WAR.description=Generate Web Archive
+inspection.invalid.i.18.n.property.display.name=Invalid i18n property key
+
+gutter.name.grails.spring.beans=Grails Spring injected beans
+grails.gutter.spring.nav.group=Spring
+
+action.Mvc.RunTarget.text=Run Grails Command
+inspections.groupPath=Groovy
+inspections.groupName=Grails
+
+notification.group.grails.notifications=Grails console command execution failed
+notification.group.grails=Grails command execution failed
+
+module.builder.servlet.type=Embedded Servlet:
+module.builder.servlet.type.none=None
+module.builder.servlet.type.tomcat=Tomcat
+module.builder.servlet.type.jetty=Jetty
+module.builder.servlet.type.undertow=Undertow
+module.builder.gorm.type=Gorm
diff --git a/resources/org/jetbrains/plugins/grails/lang/gsp/resolve/taglib/tld/grails.tld b/resources/org/jetbrains/plugins/grails/lang/gsp/resolve/taglib/tld/grails.tld
new file mode 100644
index 0000000..ec64986
--- /dev/null
+++ b/resources/org/jetbrains/plugins/grails/lang/gsp/resolve/taglib/tld/grails.tld
@@ -0,0 +1,2674 @@
+
+<taglib xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.w3.org/2001/XMLSchema-instance" version="2.0">
+    <tlib-version>0.2</tlib-version>
+    <short-name>grails</short-name>
+    <uri>http://grails.codehaus.org/tags</uri>
+    <tag>
+        <name>actionSubmit</name>
+        <tag-class>org.xxx.AClass</tag-class>
+        <dynamic-attributes>true</dynamic-attributes>
+        <description>Creates a submit button that maps to a
+specific action, which allows you to have multiple submit buttons
+in a single form. Javascript event handlers can be added using the
+same parameter names as in HTML.</description>
+        <attribute>
+            <name>value</name>
+            <required>true</required>
+            <rtexprvalue>true</rtexprvalue>
+            <description>The title of the button and
+name of action when not explicitly defined.</description>
+        </attribute>
+        <attribute>
+            <name>action</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+            <description>The name of the action to be
+executed, otherwise it is derived from the value.</description>
+        </attribute>
+    </tag>
+    <tag>
+        <name>actionSubmitImage</name>
+        <tag-class>org.xxx.AClass</tag-class>
+        <dynamic-attributes>true</dynamic-attributes>
+        <description>Creates a submit button using the input
+type="image" with the indicated value. Javascript event handlers
+can be added using the same parameter names as in HTML.</description>
+        <attribute>
+            <name>value</name>
+            <required>true</required>
+            <rtexprvalue>true</rtexprvalue>
+            <description>The title of the button and name of action
+when not explicitly defined</description>
+        </attribute>
+        <attribute>
+            <name>src</name>
+            <rtexprvalue>true</rtexprvalue>
+            <description>The source of the image to use</description>
+        </attribute>
+        <attribute>
+            <name>action</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+            <description>The name of the action to be
+executed</description>
+        </attribute>
+    </tag>
+    <tag>
+        <name>applyLayout</name>
+        <tag-class>org.xxx.AClass</tag-class>
+        <dynamic-attributes>true</dynamic-attributes>
+        <description>Applies the specified layout to either the
+body, a given template and an arbitrary URL allowing the
+development of "portlet" style applications and mashups</description>
+        <attribute>
+            <name>name</name>
+            <rtexprvalue>true</rtexprvalue>
+            <description>The name of the layout to apply</description>
+        </attribute>
+        <attribute>
+            <name>url</name>
+            <rtexprvalue>true</rtexprvalue>
+            <description>The URL of the content to apply, could be an
+external URL</description>
+        </attribute>
+        <attribute>
+            <name>template</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+            <description>The name of the template to
+apply</description>
+        </attribute>
+        <attribute>
+            <name>bean</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+            <description>The bean to apply the template
+against</description>
+        </attribute>
+        <attribute>
+            <name>model</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+            <description>The model to apply the template
+against as a java.util.Map</description>
+        </attribute>
+        <attribute>
+            <name>collection</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+            <description>A collection of model
+objects to apply the template to</description>
+        </attribute>
+        <attribute>
+            <name>view</name>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+            <name>contentType</name>
+            <rtexprvalue>true</rtexprvalue>
+            <description>Optional. The content type to use, default is "text/html"</description>
+        </attribute>
+        <attribute>
+            <name>encoding</name>
+            <rtexprvalue>true</rtexprvalue>
+            <description>Optional. The encoding to use</description>
+        </attribute>
+        <attribute>
+            <name>params</name>
+            <rtexprvalue>true</rtexprvalue>
+            <description>Optiona. The params to pass onto the page object</description>
+        </attribute>
+    </tag>
+    <tag>
+        <name>checkBox</name>
+        <tag-class>org.xxx.AClass</tag-class>
+        <dynamic-attributes>true</dynamic-attributes>
+        <description>Creates a checkbox form field. All the usual
+HTML elements apply.</description>
+        <attribute>
+            <name>name</name>
+            <required>true</required>
+            <rtexprvalue>true</rtexprvalue>
+            <description>The name of the checkbox</description>
+        </attribute>
+        <attribute>
+            <name>value</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+            <description>The value of the checkbox</description>
+        </attribute>
+        <attribute>
+            <name>checked</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+            <description>Expression if evaluates to
+&lt;code&gt;true&lt;/code&gt; sets to checkbox to checked</description>
+        </attribute>
+        <attribute>
+            <name>disabled</name>
+            <rtexprvalue>true</rtexprvalue>
+            <description>if evaluates to true sets to checkbox to disabled</description>
+        </attribute>
+        <attribute>
+            <name>id</name>
+            <rtexprvalue>true</rtexprvalue>
+            <description>DOM element id; defaults to name</description>
+        </attribute>
+    </tag>
+    <tag>
+        <name>collect</name>
+        <tag-class>org.xxx.AClass</tag-class>
+        <dynamic-attributes>true</dynamic-attributes>
+        <description>Uses the Groovy JDK &lt;code&gt;collect&lt;/code&gt;
+method to iterate over each element of the specified object
+transforming the result using the expression in the closure</description>
+        <attribute>
+            <name>in</name>
+            <rtexprvalue>true</rtexprvalue>
+            <description>The object to iterative over</description>
+        </attribute>
+        <attribute>
+            <name>expr</name>
+            <rtexprvalue>true</rtexprvalue>
+            <description>A GPath expression</description>
+        </attribute>
+    </tag>
+    <tag>
+        <name>cookie</name>
+        <tag-class>org.xxx.AClass</tag-class>
+        <dynamic-attributes>true</dynamic-attributes>
+        <description>Obtains the value of a named cookie</description>
+        <attribute>
+            <name>name</name>
+            <required>true</required>
+            <rtexprvalue>true</rtexprvalue>
+            <description>The name of the cookie</description>
+        </attribute>
+    </tag>
+    <tag>
+        <name>country</name>
+        <tag-class>org.xxx.AClass</tag-class>
+        <dynamic-attributes>true</dynamic-attributes>
+        <description>Renders the default English name for a given
+ISO3166_3 3-letter country code</description>
+        <attribute>
+            <name>code</name>
+            <required>true</required>
+            <rtexprvalue>true</rtexprvalue>
+            <description>The 3-letter lowercase ISO3166 country
+code</description>
+        </attribute>
+    </tag>
+    <tag>
+        <name>countrySelect</name>
+        <tag-class>org.xxx.AClass</tag-class>
+        <dynamic-attributes>true</dynamic-attributes>
+        <description>Helper tag for creating HTML select boxes
+containing Country names.</description>
+        <attribute>
+            <name>from</name>
+            <rtexprvalue>true</rtexprvalue>
+            <description>The list or range to select
+from</description>
+        </attribute>
+        <attribute>
+            <name>value</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+            <description>The current selected value that
+evaluates equals() to true for one of the elements in the from
+list.</description>
+        </attribute>
+        <attribute>
+            <name>default</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
... 113154 lines suppressed ...